Some cleanup, use OtterGui.

This commit is contained in:
Ottermandias 2022-07-03 16:35:56 +02:00
parent a36d1f1935
commit 0fc8992271
40 changed files with 2686 additions and 2792 deletions

4
.gitmodules vendored Normal file
View file

@ -0,0 +1,4 @@
[submodule "OtterGui"]
path = OtterGui
url = git@github.com:Ottermandias/OtterGui.git
branch = main

View file

@ -3,39 +3,39 @@ using System.ComponentModel;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
namespace Glamourer
namespace Glamourer;
public static class WriteExtensions
{
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;
var address = (byte*)characterPtr + offset;
if (id.HasValue)
*(ushort*) address = (ushort) id.Value;
*(ushort*)address = (ushort)id.Value;
if (type.HasValue)
*(ushort*) (address + 2) = (ushort) type.Value;
*(ushort*)(address + 2) = (ushort)type.Value;
if (variant.HasValue)
*(ushort*) (address + 4) = variant.Value;
*(ushort*)(address + 4) = variant.Value;
if (stain.HasValue)
*(address + 6) = (byte) stain.Value;
*(address + 6) = (byte)stain.Value;
}
void WriteEquip(int offset)
{
var address = (byte*) characterPtr + offset;
var address = (uint*)characterPtr + offset;
if (id.HasValue)
*(ushort*) address = (ushort) id.Value;
*(ushort*)address = (ushort)id.Value;
if (variant < byte.MaxValue)
*(address + 2) = (byte) variant.Value;
*(address + 2) = (byte)variant.Value;
if (stain.HasValue)
*(address + 3) = (byte) stain.Value;
*(address + 3) = (byte)stain.Value;
}
switch (slot)
@ -110,7 +110,7 @@ namespace Glamourer
fixed (CharacterArmor* equipment = &equip.Head)
{
Buffer.MemoryCopy(equipment, (byte*) characterAddress + CharacterEquipment.EquipmentOffset,
Buffer.MemoryCopy(equipment, (byte*)characterAddress + CharacterEquipment.EquipmentOffset,
CharacterEquipment.EquipmentSlots * sizeof(CharacterArmor), CharacterEquipment.EquipmentSlots * sizeof(CharacterArmor));
}
}
@ -164,5 +164,4 @@ namespace Glamourer
if (models.HasFlag(CharacterEquipMask.RFinger))
Write(characterAddress, EquipSlot.RFinger, equip.RFinger.Set, null, equip.RFinger.Variant, null);
}
}
}

View file

@ -1,23 +0,0 @@
using System;
namespace Glamourer
{
[Flags]
public enum CharacterEquipMask : ushort
{
None = 0,
MainHand = 0b000000000001,
OffHand = 0b000000000010,
Head = 0b000000000100,
Body = 0b000000001000,
Hands = 0b000000010000,
Legs = 0b000000100000,
Feet = 0b000001000000,
Ears = 0b000010000000,
Neck = 0b000100000000,
Wrists = 0b001000000000,
RFinger = 0b010000000000,
LFinger = 0b100000000000,
All = 0b111111111111,
}
}

View file

@ -1,28 +1,28 @@
using System;
using System.Runtime.InteropServices;
using System.Text;
using Dalamud.Game.ClientState.Objects.Types;
using Penumbra.GameData.Enums;
namespace Glamourer.Customization
namespace Glamourer.Customization;
public unsafe struct LazyCustomization
{
public unsafe struct LazyCustomization
{
public CharacterCustomization* Address;
public LazyCustomization(IntPtr characterPtr)
=> Address = (CharacterCustomization*) (characterPtr + CharacterCustomization.CustomizationOffset);
=> Address = (CharacterCustomization*)(characterPtr + CharacterCustomization.CustomizationOffset);
public ref CharacterCustomization Value
=> ref *Address;
public LazyCustomization(CharacterCustomization data)
=> Address = &data;
}
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CharacterCustomization
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct CharacterCustomization
{
public const int CustomizationOffset = 0x830;
public const int CustomizationBytes = 26;
@ -84,14 +84,14 @@ namespace Glamourer.Customization
public Gender Gender
{
get => (Gender) (_gender + 1);
set => _gender = (byte) (value - 1);
get => (Gender)(_gender + 1);
set => _gender = (byte)(value - 1);
}
public bool HighlightsOn
{
get => (_highlightsOn & 128) == 128;
set => _highlightsOn = (byte) (value ? _highlightsOn | 128 : _highlightsOn & 127);
set => _highlightsOn = (byte)(value ? _highlightsOn | 128 : _highlightsOn & 127);
}
public bool FacialFeature(int idx)
@ -100,46 +100,46 @@ namespace Glamourer.Customization
public void FacialFeature(int idx, bool set)
{
if (set)
FacialFeatures |= (byte) (1 << idx);
FacialFeatures |= (byte)(1 << idx);
else
FacialFeatures &= (byte) ~(1 << idx);
FacialFeatures &= (byte)~(1 << idx);
}
public byte EyeShape
{
get => (byte) (_eyeShape & 127);
set => _eyeShape = (byte) ((value & 127) | (_eyeShape & 128));
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);
set => _eyeShape = (byte)(value ? _eyeShape | 128 : _eyeShape & 127);
}
public byte Mouth
{
get => (byte) (_mouth & 127);
set => _mouth = (byte) ((value & 127) | (_mouth & 128));
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);
set => _mouth = (byte)(value ? _mouth | 128 : _mouth & 127);
}
public byte FacePaint
{
get => (byte) (_facePaint & 127);
set => _facePaint = (byte) ((value & 127) | (_facePaint & 128));
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);
set => _facePaint = (byte)(value ? _facePaint | 128 : _facePaint & 127);
}
public unsafe void Read(IntPtr customizeAddress)
@ -150,6 +150,9 @@ namespace Glamourer.Customization
}
}
public unsafe void Read(Customization* customize)
=> Read((IntPtr)customize);
public void Read(Character character)
=> Read(character.Address + CustomizationOffset);
@ -163,11 +166,11 @@ namespace Glamourer.Customization
{
get => id switch
{
CustomizationId.Race => (byte) Race,
CustomizationId.Gender => (byte) Gender,
CustomizationId.Race => (byte)Race,
CustomizationId.Gender => (byte)Gender,
CustomizationId.BodyType => BodyType,
CustomizationId.Height => Height,
CustomizationId.Clan => (byte) Clan,
CustomizationId.Clan => (byte)Clan,
CustomizationId.Face => Face,
CustomizationId.Hairstyle => Hairstyle,
CustomizationId.HighlightsOnFlag => _highlightsOn,
@ -196,10 +199,10 @@ namespace Glamourer.Customization
switch (id)
{
case CustomizationId.Race:
Race = (Race) value;
Race = (Race)value;
break;
case CustomizationId.Gender:
Gender = (Gender) value;
Gender = (Gender)value;
break;
case CustomizationId.BodyType:
BodyType = value;
@ -208,7 +211,7 @@ namespace Glamourer.Customization
Height = value;
break;
case CustomizationId.Clan:
Clan = (SubRace) value;
Clan = (SubRace)value;
break;
case CustomizationId.Face:
Face = value;
@ -282,7 +285,7 @@ namespace Glamourer.Customization
{
fixed (Race* ptr = &Race)
{
Buffer.MemoryCopy(ptr, (byte*) characterAddress + CustomizationOffset, CustomizationBytes, CustomizationBytes);
Buffer.MemoryCopy(ptr, (byte*)characterAddress + CustomizationOffset, CustomizationBytes, CustomizationBytes);
}
}
@ -300,5 +303,21 @@ namespace Glamourer.Customization
WriteBytes(ret);
return ret;
}
public string HumanReadable()
{
// TODO
var sb = new StringBuilder();
sb.Append($"Race: {Race.ToName()} - {Clan.ToName()}\n");
sb.Append($"Gender: {Gender.ToName()}\n");
sb.Append($"Height: {Height}%\n");
sb.Append($"Face: #{Face}\n");
sb.Append($"Hairstyle: #{Hairstyle}\n");
sb.Append($"Haircolor: #{HairColor}");
if (HighlightsOn)
sb.Append($" with Highlights #{HighlightsColor}\n");
else
sb.Append('\n');
return sb.ToString();
}
}

View file

@ -1,10 +1,10 @@
using Dalamud.Data;
using Dalamud.Plugin;
namespace Glamourer
namespace Glamourer;
public class CmpFile
{
public class CmpFile
{
public readonly Lumina.Data.FileResource File;
public readonly uint[] RgbaColors;
@ -15,10 +15,9 @@ namespace Glamourer
for (var i = 0; i < File.Data.Length; i += 4)
{
RgbaColors[i >> 2] = File.Data[i]
| (uint) (File.Data[i + 1] << 8)
| (uint) (File.Data[i + 2] << 16)
| (uint) (File.Data[i + 3] << 24);
}
| (uint)(File.Data[i + 1] << 8)
| (uint)(File.Data[i + 2] << 16)
| (uint)(File.Data[i + 3] << 24);
}
}
}

View file

@ -5,19 +5,19 @@ using System.Reflection;
using Dalamud;
using Dalamud.Data;
using Dalamud.Plugin;
using Glamourer.Util;
using Lumina.Data;
using Lumina.Excel;
using Lumina.Excel.GeneratedSheets;
using OtterGui.Classes;
using Penumbra.GameData.Enums;
using Race = Penumbra.GameData.Enums.Race;
namespace Glamourer.Customization
namespace Glamourer.Customization;
public partial 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();
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();
internal static readonly Gender[] Genders =
{
@ -44,28 +44,28 @@ namespace Glamourer.Customization
if (race == SubRace.Unknown || gender != Gender.Female && gender != Gender.Male)
ThrowException(race, gender);
var ret = (int) race - 1;
var ret = (int)race - 1;
ret = ret * Genders.Length + (gender == Gender.Female ? 1 : 0);
return ret;
}
private Customization[] GetHairStyles(SubRace race, Gender gender)
{
var row = _hairSheet.GetRow(((uint) race - 1) * 2 - 1 + (uint) gender)!;
var row = _hairSheet.GetRow(((uint)race - 1) * 2 - 1 + (uint)gender)!;
var hairList = new List<Customization>(row.Unknown30);
for (var i = 0; i < row.Unknown30; ++i)
{
var name = $"Unknown{66 + i * 9}";
var customizeIdx =
(uint?) row.GetType().GetProperty(name, BindingFlags.Public | BindingFlags.Instance)?.GetValue(row)
(uint?)row.GetType().GetProperty(name, BindingFlags.Public | BindingFlags.Instance)?.GetValue(row)
?? uint.MaxValue;
if (customizeIdx == uint.MaxValue)
continue;
var hairRow = _customizeSheet.GetRow(customizeIdx);
hairList.Add(hairRow != null
? new Customization(CustomizationId.Hairstyle, hairRow.FeatureID, hairRow.Icon, (ushort) hairRow.RowId)
: new Customization(CustomizationId.Hairstyle, (byte) i, customizeIdx, 0));
? new Customization(CustomizationId.Hairstyle, hairRow.FeatureID, hairRow.Icon, (ushort)hairRow.RowId)
: new Customization(CustomizationId.Hairstyle, (byte)i, customizeIdx, 0));
}
return hairList.ToArray();
@ -73,7 +73,7 @@ namespace Glamourer.Customization
private Customization[] CreateColorPicker(CustomizationId id, int offset, int num, bool light = false)
=> _cmpFile.RgbaColors.Skip(offset).Take(num)
.Select((c, i) => new Customization(id, (byte) (light ? 128 + i : 0 + i), c, (ushort) (offset + i)))
.Select((c, i) => new Customization(id, (byte)(light ? 128 + i : 0 + i), c, (ushort)(offset + i)))
.ToArray();
private (Customization[], Customization[]) GetColors(SubRace race, Gender gender)
@ -82,7 +82,7 @@ namespace Glamourer.Customization
throw new ArgumentOutOfRangeException(nameof(race), race, null);
var gv = gender == Gender.Male ? 0 : 1;
var idx = ((int) race * 2 + gv) * 5 + 3;
var idx = ((int)race * 2 + gv) * 5 + 3;
return (CreateColorPicker(CustomizationId.SkinColor, idx << 8, 192),
CreateColorPicker(CustomizationId.HairColor, (idx + 1) << 8, 192));
@ -92,8 +92,8 @@ 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 static int GetListSize(CharaMakeParams row, CustomizationId id)
@ -147,7 +147,7 @@ namespace Glamourer.Customization
private CustomizationSet GetSet(SubRace race, Gender gender)
{
var (skin, hair) = GetColors(race, gender);
var row = _listSheet.GetRow(((uint) race - 1) * 2 - 1 + (uint) gender)!;
var row = _listSheet.GetRow(((uint)race - 1) * 2 - 1 + (uint)gender)!;
var set = new CustomizationSet(race, gender)
{
HairStyles = GetHairStyles(race, gender),
@ -201,14 +201,14 @@ namespace Glamourer.Customization
for (var i = 0; i < count; ++i)
{
featureDict.Add(row.FacialFeatureByFace[i].Icons.Select((val, idx)
=> new Customization(CustomizationId.FacialFeaturesTattoos, (byte) (1 << idx), val, (ushort) (i * 8 + idx)))
.Append(new Customization(CustomizationId.FacialFeaturesTattoos, 1 << 7, 137905, (ushort) ((i + 1) * 8)))
=> new Customization(CustomizationId.FacialFeaturesTattoos, (byte)(1 << idx), val, (ushort)(i * 8 + idx)))
.Append(new Customization(CustomizationId.FacialFeaturesTattoos, 1 << 7, 137905, (ushort)((i + 1) * 8)))
.ToArray());
}
set.FeaturesTattoos = featureDict;
var nameArray = ((CustomizationId[]) Enum.GetValues(typeof(CustomizationId))).Select(c =>
var nameArray = ((CustomizationId[])Enum.GetValues(typeof(CustomizationId))).Select(c =>
{
var menu = row.Menus
.Cast<CharaMakeParams.Menu?>()
@ -228,11 +228,11 @@ namespace Glamourer.Customization
var textRow = _lobby.GetRow(menu.Value.Id);
return textRow?.Text.ToString() ?? c.ToDefaultName();
}).ToArray();
nameArray[(int) CustomizationId.EyeColorL] = nameArray[(int) CustomizationId.EyeColorR];
nameArray[(int) CustomizationId.EyeColorR] = GetName(CustomName.OddEyes);
nameArray[(int)CustomizationId.EyeColorL] = nameArray[(int)CustomizationId.EyeColorR];
nameArray[(int)CustomizationId.EyeColorR] = GetName(CustomName.OddEyes);
set.OptionName = nameArray;
set.Types = ((CustomizationId[]) Enum.GetValues(typeof(CustomizationId))).Select(c =>
set.Types = ((CustomizationId[])Enum.GetValues(typeof(CustomizationId))).Select(c =>
{
switch (c)
{
@ -263,10 +263,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];
private readonly string[] _names = new string[(int)CustomName.Num];
public string GetName(CustomName name)
=> _names[(int) name];
=> _names[(int)name];
private static Language FromClientLanguage(ClientLanguage language)
=> language switch
@ -323,52 +323,51 @@ namespace Glamourer.Customization
private void SetNames(DataManager gameData)
{
var subRace = gameData.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.IrisSize] = _lobby.GetRow(244)?.Text ?? "Iris Size";
_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();
}
_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.IrisSize] = _lobby.GetRow(244)?.Text ?? "Iris Size";
_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();
}
}

View file

@ -3,13 +3,16 @@ using System.Collections.Generic;
using System.Linq;
using Dalamud;
using Dalamud.Data;
using Glamourer.Structs;
using Lumina.Excel.GeneratedSheets;
using Penumbra.GameData.Enums;
using Item = Glamourer.Structs.Item;
using Stain = Glamourer.Structs.Stain;
namespace Glamourer
namespace Glamourer;
public static class GameData
{
public static class GameData
{
private static Dictionary<byte, Stain>? _stains;
private static Dictionary<EquipSlot, List<Item>>? _itemsBySlot;
private static Dictionary<byte, Job>? _jobs;
@ -23,7 +26,7 @@ namespace Glamourer
var sheet = dataManager.GetExcelSheet<ModelChara>()!;
_models = new SortedList<uint, ModelChara>((int) sheet.RowCount);
_models = new SortedList<uint, ModelChara>((int)sheet.RowCount);
foreach (var model in sheet.Where(m => m.Type != 0))
_models.Add(model.RowId, model);
return _models;
@ -35,7 +38,7 @@ namespace Glamourer
return _stains;
var sheet = dataManager.GetExcelSheet<Lumina.Excel.GeneratedSheets.Stain>()!;
_stains = sheet.Where(s => s.Color != 0).ToDictionary(s => (byte) s.RowId, s => new Stain((byte) s.RowId, s));
_stains = sheet.Where(s => s.Color != 0).ToDictionary(s => (byte)s.RowId, s => new Stain((byte)s.RowId, s));
return _stains;
}
@ -54,17 +57,53 @@ namespace Glamourer
_itemsBySlot = new Dictionary<EquipSlot, List<Item>>()
{
[EquipSlot.Head] = new(200) { EmptySlot(EquipSlot.Head), EmptyNpc(EquipSlot.Head) },
[EquipSlot.Body] = new(200) { EmptySlot(EquipSlot.Body), EmptyNpc(EquipSlot.Body) },
[EquipSlot.Hands] = new(200) { EmptySlot(EquipSlot.Hands), EmptyNpc(EquipSlot.Hands) },
[EquipSlot.Legs] = new(200) { EmptySlot(EquipSlot.Legs), EmptyNpc(EquipSlot.Legs) },
[EquipSlot.Feet] = new(200) { EmptySlot(EquipSlot.Feet), EmptyNpc(EquipSlot.Feet) },
[EquipSlot.RFinger] = new(200) { EmptySlot(EquipSlot.RFinger), EmptyNpc(EquipSlot.RFinger) },
[EquipSlot.Neck] = new(200) { EmptySlot(EquipSlot.Neck), EmptyNpc(EquipSlot.Neck) },
[EquipSlot.Head] = new(200)
{
EmptySlot(EquipSlot.Head),
EmptyNpc(EquipSlot.Head),
},
[EquipSlot.Body] = new(200)
{
EmptySlot(EquipSlot.Body),
EmptyNpc(EquipSlot.Body),
},
[EquipSlot.Hands] = new(200)
{
EmptySlot(EquipSlot.Hands),
EmptyNpc(EquipSlot.Hands),
},
[EquipSlot.Legs] = new(200)
{
EmptySlot(EquipSlot.Legs),
EmptyNpc(EquipSlot.Legs),
},
[EquipSlot.Feet] = new(200)
{
EmptySlot(EquipSlot.Feet),
EmptyNpc(EquipSlot.Feet),
},
[EquipSlot.RFinger] = new(200)
{
EmptySlot(EquipSlot.RFinger),
EmptyNpc(EquipSlot.RFinger),
},
[EquipSlot.Neck] = new(200)
{
EmptySlot(EquipSlot.Neck),
EmptyNpc(EquipSlot.Neck),
},
[EquipSlot.MainHand] = new(1000) { EmptySlot(EquipSlot.MainHand) },
[EquipSlot.OffHand] = new(200) { EmptySlot(EquipSlot.OffHand) },
[EquipSlot.Wrists] = new(200) { EmptySlot(EquipSlot.Wrists), EmptyNpc(EquipSlot.Wrists) },
[EquipSlot.Ears] = new(200) { EmptySlot(EquipSlot.Ears), EmptyNpc(EquipSlot.Ears) },
[EquipSlot.Wrists] = new(200)
{
EmptySlot(EquipSlot.Wrists),
EmptyNpc(EquipSlot.Wrists),
},
[EquipSlot.Ears] = new(200)
{
EmptySlot(EquipSlot.Ears),
EmptyNpc(EquipSlot.Ears),
},
};
foreach (var item in sheet)
@ -73,7 +112,7 @@ namespace Glamourer
if (!name.Any())
continue;
var slot = (EquipSlot) item.EquipSlotCategory.Row;
var slot = (EquipSlot)item.EquipSlotCategory.Row;
if (slot == EquipSlot.Unknown)
continue;
@ -111,7 +150,7 @@ namespace Glamourer
static bool ValidIndex(uint idx)
{
if (idx > 0 && idx < 36)
if (idx is > 0 and < 36)
return true;
return idx switch
@ -137,8 +176,7 @@ namespace Glamourer
}
_jobGroups = sheet.Where(j => ValidIndex(j.RowId))
.ToDictionary(j => (ushort) j.RowId, j => new JobGroup(j, jobs));
.ToDictionary(j => (ushort)j.RowId, j => new JobGroup(j, jobs));
return _jobGroups;
}
}
}

View file

@ -58,6 +58,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Penumbra\OtterGui\OtterGui.csproj" />
<ProjectReference Include="..\..\Penumbra\Penumbra.GameData\Penumbra.GameData.csproj" />
</ItemGroup>
</Project>

View file

@ -1,55 +0,0 @@
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
namespace Glamourer
{
public readonly struct Item
{
private static (SetId id, WeaponType type, ushort variant) ParseModel(EquipSlot slot, ulong data)
{
if (slot == EquipSlot.MainHand || slot == EquipSlot.OffHand)
{
var id = (SetId) (data & 0xFFFF);
var type = (WeaponType) ((data >> 16) & 0xFFFF);
var variant = (ushort) ((data >> 32) & 0xFFFF);
return (id, type, variant);
}
else
{
var id = (SetId) (data & 0xFFFF);
var variant = (byte) ((data >> 16) & 0xFF);
return (id, new WeaponType(), variant);
}
}
public readonly Lumina.Excel.GeneratedSheets.Item Base;
public readonly string Name;
public readonly EquipSlot EquippableTo;
public (SetId id, WeaponType type, ushort variant) MainModel
=> ParseModel(EquippableTo, Base.ModelMain);
public bool HasSubModel
=> Base.ModelSub != 0;
public (SetId id, WeaponType type, ushort variant) SubModel
=> ParseModel(EquippableTo, Base.ModelSub);
public Item(Lumina.Excel.GeneratedSheets.Item item, string name, EquipSlot slot = EquipSlot.Unknown)
{
Base = item;
Name = name;
EquippableTo = slot == EquipSlot.Unknown ? ((EquipSlot) item.EquipSlotCategory.Row).ToSlot() : slot;
}
public static Item Nothing(EquipSlot slot)
=> new("Nothing", slot);
private Item(string name, EquipSlot slot)
{
Name = name;
Base = new Lumina.Excel.GeneratedSheets.Item();
EquippableTo = slot;
}
}
}

View file

@ -1,21 +0,0 @@
using Lumina.Excel.GeneratedSheets;
namespace Glamourer
{
public readonly struct Job
{
public readonly string Name;
public readonly string Abbreviation;
public readonly ClassJob Base;
public uint Id
=> Base.RowId;
public Job(ClassJob job)
{
Base = job;
Name = job.Name.ToString();
Abbreviation = job.Abbreviation.ToString();
}
}
}

View file

@ -1,50 +0,0 @@
using System;
using System.Diagnostics;
using System.Linq;
using Lumina.Excel;
using Lumina.Excel.GeneratedSheets;
namespace Glamourer
{
public readonly struct JobGroup
{
public readonly string Name;
private readonly ulong _flags;
public readonly int Count;
public readonly uint Id;
public JobGroup(ClassJobCategory group, ExcelSheet<ClassJob> jobs)
{
Count = 0;
_flags = 0ul;
Id = group.RowId;
Name = group.Name.ToString();
Debug.Assert(jobs.RowCount < 64);
foreach (var job in jobs)
{
var abbr = job.Abbreviation.ToString();
if (!abbr.Any())
continue;
var prop = group.GetType().GetProperty(abbr);
Debug.Assert(prop != null);
if (!(bool) prop.GetValue(group)!)
continue;
++Count;
_flags |= 1ul << (int) job.RowId;
}
}
public bool Fits(Job job)
=> Fits(job.Id);
public bool Fits(uint jobId)
{
var flag = 1ul << (int)jobId;
return (flag & _flags) != 0;
}
}
}

View file

@ -1,50 +0,0 @@
using Penumbra.GameData.Structs;
namespace Glamourer
{
public readonly struct Stain
{
public readonly string Name;
public readonly uint RgbaColor;
private readonly uint _seColorId;
public byte R
=> (byte) (RgbaColor & 0xFF);
public byte G
=> (byte) ((RgbaColor >> 8) & 0xFF);
public byte B
=> (byte) ((RgbaColor >> 16) & 0xFF);
public byte Intensity
=> (byte) ((1 + R + G + B) / 3);
public uint SeColor
=> _seColorId & 0x00FFFFFF;
public StainId RowIndex
=> (StainId) (_seColorId >> 24);
public static uint SeColorToRgba(uint color)
=> ((color & 0xFF) << 16) | ((color >> 16) & 0xFF) | (color & 0xFF00) | 0xFF000000;
public Stain(byte index, Lumina.Excel.GeneratedSheets.Stain stain)
{
Name = stain.Name.ToString();
_seColorId = stain.Color | ((uint) index << 24);
RgbaColor = SeColorToRgba(stain.Color);
}
public static readonly Stain None = new("None");
private Stain(string name)
{
Name = name;
_seColorId = 0;
RgbaColor = 0;
}
}
}

View file

@ -0,0 +1,43 @@
using System;
using Penumbra.GameData.Enums;
namespace Glamourer.Structs;
[Flags]
public enum CharacterEquipMask : ushort
{
None = 0,
MainHand = 0b000000000001,
OffHand = 0b000000000010,
Head = 0b000000000100,
Body = 0b000000001000,
Hands = 0b000000010000,
Legs = 0b000000100000,
Feet = 0b000001000000,
Ears = 0b000010000000,
Neck = 0b000100000000,
Wrists = 0b001000000000,
RFinger = 0b010000000000,
LFinger = 0b100000000000,
All = 0b111111111111,
}
public static class CharacterEquipMaskExtensions
{
public static bool Fits(this CharacterEquipMask mask, EquipSlot slot)
=> slot switch
{
EquipSlot.Unknown => false,
EquipSlot.Head => mask.HasFlag(CharacterEquipMask.Head),
EquipSlot.Body => mask.HasFlag(CharacterEquipMask.Body),
EquipSlot.Hands => mask.HasFlag(CharacterEquipMask.Hands),
EquipSlot.Legs => mask.HasFlag(CharacterEquipMask.Legs),
EquipSlot.Feet => mask.HasFlag(CharacterEquipMask.Feet),
EquipSlot.Ears => mask.HasFlag(CharacterEquipMask.Ears),
EquipSlot.Neck => mask.HasFlag(CharacterEquipMask.Neck),
EquipSlot.Wrists => mask.HasFlag(CharacterEquipMask.Wrists),
EquipSlot.RFinger => mask.HasFlag(CharacterEquipMask.RFinger),
EquipSlot.LFinger => mask.HasFlag(CharacterEquipMask.LFinger),
_ => false,
};
}

View file

@ -0,0 +1,54 @@
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
namespace Glamourer.Structs;
public readonly struct Item
{
private static (SetId id, WeaponType type, ushort variant) ParseModel(EquipSlot slot, ulong data)
{
if (slot is EquipSlot.MainHand or EquipSlot.OffHand)
{
var id = (SetId)(data & 0xFFFF);
var type = (WeaponType)((data >> 16) & 0xFFFF);
var variant = (ushort)((data >> 32) & 0xFFFF);
return (id, type, variant);
}
else
{
var id = (SetId)(data & 0xFFFF);
var variant = (byte)((data >> 16) & 0xFF);
return (id, new WeaponType(), variant);
}
}
public readonly Lumina.Excel.GeneratedSheets.Item Base;
public readonly string Name;
public readonly EquipSlot EquippableTo;
public (SetId id, WeaponType type, ushort variant) MainModel
=> ParseModel(EquippableTo, Base.ModelMain);
public bool HasSubModel
=> Base.ModelSub != 0;
public (SetId id, WeaponType type, ushort variant) SubModel
=> ParseModel(EquippableTo, Base.ModelSub);
public Item(Lumina.Excel.GeneratedSheets.Item item, string name, EquipSlot slot = EquipSlot.Unknown)
{
Base = item;
Name = name;
EquippableTo = slot == EquipSlot.Unknown ? ((EquipSlot)item.EquipSlotCategory.Row).ToSlot() : slot;
}
public static Item Nothing(EquipSlot slot)
=> new("Nothing", slot);
private Item(string name, EquipSlot slot)
{
Name = name;
Base = new Lumina.Excel.GeneratedSheets.Item();
EquippableTo = slot;
}
}

View file

@ -0,0 +1,20 @@
using Lumina.Excel.GeneratedSheets;
namespace Glamourer.Structs;
public readonly struct Job
{
public readonly string Name;
public readonly string Abbreviation;
public readonly ClassJob Base;
public uint Id
=> Base.RowId;
public Job(ClassJob job)
{
Base = job;
Name = job.Name.ToString();
Abbreviation = job.Abbreviation.ToString();
}
}

View file

@ -0,0 +1,48 @@
using System.Diagnostics;
using System.Linq;
using Lumina.Excel;
using Lumina.Excel.GeneratedSheets;
namespace Glamourer.Structs;
public readonly struct JobGroup
{
public readonly string Name;
private readonly ulong _flags;
public readonly int Count;
public readonly uint Id;
public JobGroup(ClassJobCategory group, ExcelSheet<ClassJob> jobs)
{
Count = 0;
_flags = 0ul;
Id = group.RowId;
Name = group.Name.ToString();
Debug.Assert(jobs.RowCount < 64);
foreach (var job in jobs)
{
var abbr = job.Abbreviation.ToString();
if (!abbr.Any())
continue;
var prop = group.GetType().GetProperty(abbr);
Debug.Assert(prop != null);
if (!(bool)prop.GetValue(group)!)
continue;
++Count;
_flags |= 1ul << (int)job.RowId;
}
}
public bool Fits(Job job)
=> Fits(job.Id);
public bool Fits(uint jobId)
{
var flag = 1ul << (int)jobId;
return (flag & _flags) != 0;
}
}

View file

@ -0,0 +1,49 @@
using Penumbra.GameData.Structs;
namespace Glamourer.Structs;
public readonly struct Stain
{
public readonly string Name;
public readonly uint RgbaColor;
private readonly uint _seColorId;
public byte R
=> (byte)(RgbaColor & 0xFF);
public byte G
=> (byte)(RgbaColor >> 8 & 0xFF);
public byte B
=> (byte)(RgbaColor >> 16 & 0xFF);
public byte Intensity
=> (byte)((1 + R + G + B) / 3);
public uint SeColor
=> _seColorId & 0x00FFFFFF;
public StainId RowIndex
=> (StainId)(_seColorId >> 24);
public static uint SeColorToRgba(uint color)
=> (color & 0xFF) << 16 | color >> 16 & 0xFF | color & 0xFF00 | 0xFF000000;
public Stain(byte index, Lumina.Excel.GeneratedSheets.Stain stain)
{
Name = stain.Name.ToString();
_seColorId = stain.Color | (uint)index << 24;
RgbaColor = SeColorToRgba(stain.Color);
}
public static readonly Stain None = new("None");
private Stain(string name)
{
Name = name;
_seColorId = 0;
RgbaColor = 0;
}
}

View file

@ -1,59 +0,0 @@
using System;
using System.Collections.Generic;
using Dalamud.Data;
using Dalamud.Plugin;
using Dalamud.Utility;
using ImGuiScene;
using Lumina.Data.Files;
namespace Glamourer.Util
{
public class IconStorage : IDisposable
{
private readonly DalamudPluginInterface _pi;
private readonly DataManager _gameData;
private readonly Dictionary<uint, TextureWrap> _icons;
public IconStorage(DalamudPluginInterface pi, DataManager gameData, int size = 0)
{
_pi = pi;
_gameData = gameData;
_icons = new Dictionary<uint, TextureWrap>(size);
}
public TextureWrap this[int id]
=> LoadIcon(id);
private TexFile? LoadIconHq(uint id)
{
var path = $"ui/icon/{id / 1000 * 1000:000000}/{id:000000}_hr1.tex";
return _gameData.GetFile<TexFile>(path);
}
public TextureWrap LoadIcon(int id)
=> LoadIcon((uint) id);
public TextureWrap LoadIcon(uint id)
{
if (_icons.TryGetValue(id, out var ret))
return ret;
var icon = LoadIconHq(id) ?? _gameData.GetIcon(id)!;
var iconData = icon.GetRgbaImageData();
ret = _pi.UiBuilder.LoadImageRaw(iconData, icon.Header.Width, icon.Header.Height, 4);
_icons[id] = ret;
return ret;
}
public void Dispose()
{
foreach (var icon in _icons.Values)
icon.Dispose();
_icons.Clear();
}
~IconStorage()
=> Dispose();
}
}

View file

@ -17,6 +17,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.GameData", "..\Pen
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Penumbra.PlayerWatch", "..\Penumbra\Penumbra.PlayerWatch\Penumbra.PlayerWatch.csproj", "{FECEDB39-C103-4333-82A6-A422BDC51EEE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OtterGui", "..\Penumbra\OtterGui\OtterGui.csproj", "{6A4F7788-DB91-41B6-A264-7FD9CCACD7AA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -75,6 +77,18 @@ Global
{FECEDB39-C103-4333-82A6-A422BDC51EEE}.Release|x64.Build.0 = Release|Any CPU
{FECEDB39-C103-4333-82A6-A422BDC51EEE}.Release|x86.ActiveCfg = Release|Any CPU
{FECEDB39-C103-4333-82A6-A422BDC51EEE}.Release|x86.Build.0 = Release|Any CPU
{6A4F7788-DB91-41B6-A264-7FD9CCACD7AA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6A4F7788-DB91-41B6-A264-7FD9CCACD7AA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6A4F7788-DB91-41B6-A264-7FD9CCACD7AA}.Debug|x64.ActiveCfg = Debug|Any CPU
{6A4F7788-DB91-41B6-A264-7FD9CCACD7AA}.Debug|x64.Build.0 = Debug|Any CPU
{6A4F7788-DB91-41B6-A264-7FD9CCACD7AA}.Debug|x86.ActiveCfg = Debug|Any CPU
{6A4F7788-DB91-41B6-A264-7FD9CCACD7AA}.Debug|x86.Build.0 = Debug|Any CPU
{6A4F7788-DB91-41B6-A264-7FD9CCACD7AA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6A4F7788-DB91-41B6-A264-7FD9CCACD7AA}.Release|Any CPU.Build.0 = Release|Any CPU
{6A4F7788-DB91-41B6-A264-7FD9CCACD7AA}.Release|x64.ActiveCfg = Release|Any CPU
{6A4F7788-DB91-41B6-A264-7FD9CCACD7AA}.Release|x64.Build.0 = Release|Any CPU
{6A4F7788-DB91-41B6-A264-7FD9CCACD7AA}.Release|x86.ActiveCfg = Release|Any CPU
{6A4F7788-DB91-41B6-A264-7FD9CCACD7AA}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View file

@ -2,6 +2,7 @@
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Logging;
using Dalamud.Plugin.Ipc;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Glamourer.Gui;
using ImGuiNET;
using Penumbra.GameData.Enums;
@ -17,10 +18,14 @@ public class PenumbraAttach : IDisposable
private ICallGateSubscriber<MouseButton, ChangedItemType, uint, object>? _clickSubscriber;
private ICallGateSubscriber<string, int, object>? _redrawSubscriberName;
private ICallGateSubscriber<GameObject, int, object>? _redrawSubscriberObject;
private ICallGateSubscriber<IntPtr, (IntPtr, string)>? _drawObjectInfo;
private ICallGateSubscriber<IntPtr, string, IntPtr, IntPtr, object?>? _creatingCharacterBase;
private readonly ICallGateSubscriber<object?> _initializedEvent;
private readonly ICallGateSubscriber<object?> _disposedEvent;
public event Action<IntPtr, IntPtr, IntPtr>? CreatingCharacterBase;
public PenumbraAttach(bool attach)
{
_initializedEvent = Dalamud.PluginInterface.GetIpcSubscriber<object?>("Penumbra.Initialized");
@ -47,6 +52,7 @@ public class PenumbraAttach : IDisposable
_redrawSubscriberName = Dalamud.PluginInterface.GetIpcSubscriber<string, int, object>("Penumbra.RedrawObjectByName");
_redrawSubscriberObject = Dalamud.PluginInterface.GetIpcSubscriber<GameObject, int, object>("Penumbra.RedrawObject");
_drawObjectInfo = Dalamud.PluginInterface.GetIpcSubscriber<IntPtr, (IntPtr, string)>("Penumbra.GetDrawObjectInfo");
if (!attach)
return;
@ -54,8 +60,11 @@ public class PenumbraAttach : IDisposable
_tooltipSubscriber = Dalamud.PluginInterface.GetIpcSubscriber<ChangedItemType, uint, object>("Penumbra.ChangedItemTooltip");
_clickSubscriber =
Dalamud.PluginInterface.GetIpcSubscriber<MouseButton, ChangedItemType, uint, object>("Penumbra.ChangedItemClick");
_creatingCharacterBase =
Dalamud.PluginInterface.GetIpcSubscriber<IntPtr, string, IntPtr, IntPtr, object?>("Penumbra.CreatingCharacterBase");
_tooltipSubscriber.Subscribe(PenumbraTooltip);
_clickSubscriber.Subscribe(PenumbraRightClick);
_creatingCharacterBase.Subscribe(SubscribeCharacterBase);
PluginLog.Debug("Glamourer attached to Penumbra.");
}
catch (Exception e)
@ -64,13 +73,19 @@ public class PenumbraAttach : IDisposable
}
}
private void SubscribeCharacterBase(IntPtr gameObject, string _, IntPtr customize, IntPtr equipment)
=> CreatingCharacterBase?.Invoke(gameObject, customize, equipment);
public void Unattach()
{
_tooltipSubscriber?.Unsubscribe(PenumbraTooltip);
_clickSubscriber?.Unsubscribe(PenumbraRightClick);
_creatingCharacterBase?.Unsubscribe(SubscribeCharacterBase);
_tooltipSubscriber = null;
_clickSubscriber = null;
_creatingCharacterBase = null;
_redrawSubscriberName = null;
_drawObjectInfo = null;
if (_redrawSubscriberObject != null)
{
PluginLog.Debug("Glamourer detached from Penumbra.");
@ -112,6 +127,9 @@ public class PenumbraAttach : IDisposable
}
}
public unsafe FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject* GameObjectFromDrawObject(IntPtr drawObject)
=> (FFXIVClientStructs.FFXIV.Client.Game.Object.GameObject*)(_drawObjectInfo?.InvokeFunc(drawObject).Item1 ?? IntPtr.Zero);
public void RedrawObject(GameObject actor, RedrawType settings, bool repeat)
{
if (_redrawSubscriberObject != null)

View file

@ -1,68 +0,0 @@
using System;
namespace Glamourer
{
[Flags]
public enum CharacterFlag : ulong
{
MainHand = 1ul << 0,
OffHand = 1ul << 1,
Head = 1ul << 2,
Body = 1ul << 3,
Hands = 1ul << 4,
Legs = 1ul << 5,
Feet = 1ul << 6,
Ears = 1ul << 7,
Neck = 1ul << 8,
Wrists = 1ul << 9,
RFinger = 1ul << 10,
LFinger = 1ul << 11,
ModelMask = (1ul << 12) - 1,
StainMainHand = MainHand << 16,
StainOffHand = OffHand << 16,
StainHead = Head << 16,
StainBody = Body << 16,
StainHands = Hands << 16,
StainLegs = Legs << 16,
StainFeet = Feet << 16,
StainEars = Ears << 16,
StainNeck = Neck << 16,
StainWrists = Wrists << 16,
StainRFinger = RFinger << 16,
StainLFinger = LFinger << 16,
StainMask = ModelMask << 16,
EquipMask = ModelMask | StainMask,
Race = 1ul << 32,
Gender = 1ul << 33,
BodyType = 1ul << 34,
Height = 1ul << 35,
Clan = 1ul << 36,
Face = 1ul << 37,
Hairstyle = 1ul << 38,
Highlights = 1ul << 39,
SkinColor = 1ul << 40,
EyeColorRight = 1ul << 41,
HairColor = 1ul << 42,
HighlightsColor = 1ul << 43,
FacialFeatures = 1ul << 44,
TattooColor = 1ul << 45,
Eyebrows = 1ul << 46,
EyeColorLeft = 1ul << 47,
EyeShape = 1ul << 48,
IrisSize = 1ul << 49,
NoseShape = 1ul << 50,
JawShape = 1ul << 51,
MouthShape = 1ul << 52,
Lipstick = 1ul << 53,
LipColor = 1ul << 54,
MuscleMass = 1ul << 55,
TailShape = 1ul << 56,
BustSize = 1ul << 57,
FacePaint = 1ul << 58,
FacePaintReversed = 1ul << 59,
FacePaintColor = 1ul << 60,
CustomizeMask = ((1ul << 61) - 1) & ~EquipMask,
}
}

View file

@ -1,56 +1,29 @@
using Dalamud.Data;
using Dalamud.Game;
using Dalamud.Game.ClientState;
using Dalamud.Game.ClientState.Buddy;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Game.ClientState.Fates;
using Dalamud.Game.ClientState.JobGauge;
using Dalamud.Game.ClientState.Keys;
using Dalamud.Game.ClientState.Objects;
using Dalamud.Game.ClientState.Party;
using Dalamud.Game.Command;
using Dalamud.Game.Gui;
using Dalamud.Game.Gui.FlyText;
using Dalamud.Game.Gui.PartyFinder;
using Dalamud.Game.Gui.Toast;
using Dalamud.Game.Libc;
using Dalamud.Game.Network;
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.IoC;
using Dalamud.Plugin;
// ReSharper disable AutoPropertyCanBeMadeGetOnly.Local
namespace Glamourer
namespace Glamourer;
public class Dalamud
{
public class Dalamud
{
public static void Initialize(DalamudPluginInterface pluginInterface)
=> pluginInterface.Create<Dalamud>();
// @formatter:off
[PluginService][RequiredVersion("1.0")] public static DalamudPluginInterface PluginInterface { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public static CommandManager Commands { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public static SigScanner SigScanner { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public static DataManager GameData { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public static ClientState ClientState { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public static ChatGui Chat { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static SeStringManager SeStrings { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static ChatHandlers ChatHandlers { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public static Framework Framework { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static GameNetwork Network { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public static Condition Conditions { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static KeyState Keys { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static GameGui GameGui { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static FlyTextGui FlyTexts { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static ToastGui Toasts { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static JobGauges Gauges { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static PartyFinderGui PartyFinder { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static BuddyList Buddies { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static PartyList Party { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public static TargetManager Targets { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public static ObjectTable Objects { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static FateTable Fates { get; private set; } = null!;
//[PluginService][RequiredVersion("1.0")] public static LibcFunction LibC { get; private set; } = null!;
// @formatter:on
}
}

View file

@ -5,6 +5,7 @@ using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Logging;
using Glamourer.FileSystem;
using Glamourer.Structs;
using Penumbra.GameData.Enums;
using Penumbra.PlayerWatch;
@ -122,19 +123,19 @@ namespace Glamourer.Designs
private void OnPlayerChange(Character character)
{
var name = character.Name.ToString();
if (!EnabledDesigns.TryGetValue(name, out var designs))
return;
var design = designs.OrderBy(d => d.Jobs.Count).FirstOrDefault(d => d.Jobs.Fits(character.ClassJob.Id));
if (design == null)
return;
PluginLog.Debug("Redrawing {CharacterName} with {DesignName} for job {JobGroup}.", name, design.Design.FullName(),
design.Jobs.Name);
design.Design.Data.Apply(character);
Glamourer.PlayerWatcher.UpdatePlayerWithoutEvent(character);
Glamourer.Penumbra.RedrawObject(character, RedrawType.Redraw, false);
//var name = character.Name.ToString();
//if (!EnabledDesigns.TryGetValue(name, out var designs))
// return;
//
//var design = designs.OrderBy(d => d.Jobs.Count).FirstOrDefault(d => d.Jobs.Fits(character.ClassJob.Id));
//if (design == null)
// return;
//
//PluginLog.Debug("Redrawing {CharacterName} with {DesignName} for job {JobGroup}.", name, design.Design.FullName(),
// design.Jobs.Name);
//design.Design.Data.Apply(character);
//Glamourer.PlayerWatcher.UpdatePlayerWithoutEvent(character);
//Glamourer.Penumbra.RedrawObject(character, RedrawType.Redraw, false);
}
public void Add(string name, Design design, JobGroup group, bool enabled = false)

View file

@ -1,21 +1,148 @@
using System;
using System.Linq;
using System.Reflection;
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Game.Command;
using Dalamud.Hooking;
using Dalamud.Plugin;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using Glamourer.Api;
using Glamourer.Customization;
using Glamourer.Designs;
using Glamourer.FileSystem;
using Glamourer.Gui;
using ImGuiNET;
using Penumbra.GameData.ByteString;
using Penumbra.GameData.Enums;
using Penumbra.PlayerWatch;
namespace Glamourer
namespace Glamourer;
public unsafe class FixedDesignManager : IDisposable
{
public class Glamourer : IDalamudPlugin
public delegate ulong FlagSlotForUpdateDelegate(Human* drawObject, uint slot, uint* data);
[Signature("48 89 5C 24 ?? 48 89 74 24 ?? 57 48 83 EC 20 8B DA 49 8B F0 48 8B F9 83 FA 0A",
DetourName = nameof(FlagSlotForUpdateDetour))]
public Hook<FlagSlotForUpdateDelegate>? FlagSlotForUpdateHook;
public readonly FixedDesigns FixedDesigns;
public FixedDesignManager(DesignManager designs)
{
SignatureHelper.Initialise(this);
FixedDesigns = new FixedDesigns(designs);
if (Glamourer.Config.ApplyFixedDesigns)
Enable();
}
public void Enable()
{
FlagSlotForUpdateHook?.Enable();
Glamourer.Penumbra.CreatingCharacterBase += ApplyFixedDesign;
}
public void Disable()
{
FlagSlotForUpdateHook?.Disable();
Glamourer.Penumbra.CreatingCharacterBase -= ApplyFixedDesign;
}
public void Dispose()
{
FlagSlotForUpdateHook?.Dispose();
}
private void ApplyFixedDesign(IntPtr addr, IntPtr customize, IntPtr equipData)
{
var human = (FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)addr;
if (human->GameObject.ObjectKind is (byte)ObjectKind.EventNpc or (byte)ObjectKind.BattleNpc or (byte)ObjectKind.Player
&& human->ModelCharaId == 0)
{
var name = new Utf8String(human->GameObject.Name).ToString();
if (FixedDesigns.EnabledDesigns.TryGetValue(name, out var designs))
{
var design = designs.OrderBy(d => d.Jobs.Count).FirstOrDefault(d => d.Jobs.Fits(human->ClassJob));
if (design != null)
{
if (design.Design.Data.WriteCustomizations)
*(CharacterCustomization*)customize = design.Design.Data.Customizations;
var data = (uint*)equipData;
for (var i = 0u; i < 10; ++i)
{
var slot = i.ToEquipSlot();
if (design.Design.Data.WriteEquipment.Fits(slot))
data[i] = slot switch
{
EquipSlot.Head => design.Design.Data.Equipment.Head.Value,
EquipSlot.Body => design.Design.Data.Equipment.Body.Value,
EquipSlot.Hands => design.Design.Data.Equipment.Hands.Value,
EquipSlot.Legs => design.Design.Data.Equipment.Legs.Value,
EquipSlot.Feet => design.Design.Data.Equipment.Feet.Value,
EquipSlot.Ears => design.Design.Data.Equipment.Ears.Value,
EquipSlot.Neck => design.Design.Data.Equipment.Neck.Value,
EquipSlot.Wrists => design.Design.Data.Equipment.Wrists.Value,
EquipSlot.RFinger => design.Design.Data.Equipment.RFinger.Value,
EquipSlot.LFinger => design.Design.Data.Equipment.LFinger.Value,
_ => 0,
};
}
}
}
}
}
private ulong FlagSlotForUpdateDetour(Human* drawObject, uint slotIdx, uint* data)
{
ulong ret;
var slot = slotIdx.ToEquipSlot();
try
{
if (slot != EquipSlot.Unknown)
{
var gameObject =
(FFXIVClientStructs.FFXIV.Client.Game.Character.Character*)Glamourer.Penumbra.GameObjectFromDrawObject((IntPtr)drawObject);
if (gameObject != null)
{
var name = new Utf8String(gameObject->GameObject.Name).ToString();
if (FixedDesigns.EnabledDesigns.TryGetValue(name, out var designs))
{
var design = designs.OrderBy(d => d.Jobs.Count).FirstOrDefault(d => d.Jobs.Fits(gameObject->ClassJob));
if (design != null && design.Design.Data.WriteEquipment.Fits(slot))
*data = slot switch
{
EquipSlot.Head => design.Design.Data.Equipment.Head.Value,
EquipSlot.Body => design.Design.Data.Equipment.Body.Value,
EquipSlot.Hands => design.Design.Data.Equipment.Hands.Value,
EquipSlot.Legs => design.Design.Data.Equipment.Legs.Value,
EquipSlot.Feet => design.Design.Data.Equipment.Feet.Value,
EquipSlot.Ears => design.Design.Data.Equipment.Ears.Value,
EquipSlot.Neck => design.Design.Data.Equipment.Neck.Value,
EquipSlot.Wrists => design.Design.Data.Equipment.Wrists.Value,
EquipSlot.RFinger => design.Design.Data.Equipment.RFinger.Value,
EquipSlot.LFinger => design.Design.Data.Equipment.LFinger.Value,
_ => 0,
};
}
}
}
}
finally
{
ret = FlagSlotForUpdateHook!.Original(drawObject, slotIdx, data);
}
return ret;
}
}
public class Glamourer : IDalamudPlugin
{
private const string HelpString = "[Copy|Apply|Save],[Name or PlaceHolder],<Name for Save>";
public string Name
@ -24,9 +151,10 @@ namespace Glamourer
public static GlamourerConfig Config = null!;
public static IPlayerWatcher PlayerWatcher = null!;
public static ICustomizationManager Customization = null!;
public static FixedDesignManager FixedDesignManager = null!;
private readonly Interface _interface;
public readonly DesignManager Designs;
public readonly FixedDesigns FixedDesigns;
public static RevertableDesigns RevertableDesigns = new();
public readonly GlamourerIpc GlamourerIpc;
@ -44,11 +172,10 @@ namespace Glamourer
Penumbra = new PenumbraAttach(Config.AttachToPenumbra);
PlayerWatcher = PlayerWatchFactory.Create(Dalamud.Framework, Dalamud.ClientState, Dalamud.Objects);
GlamourerIpc = new GlamourerIpc(Dalamud.ClientState, Dalamud.Objects, Dalamud.PluginInterface);
FixedDesignManager = new FixedDesignManager(Designs);
if (!Config.ApplyFixedDesigns)
PlayerWatcher.Disable();
FixedDesigns = new FixedDesigns(Designs);
Dalamud.Commands.AddHandler("/glamourer", new CommandInfo(OnGlamourer)
{
HelpMessage = "Open or close the Glamourer window.",
@ -199,7 +326,7 @@ namespace Glamourer
public void Dispose()
{
FixedDesigns.Dispose();
FixedDesignManager.Dispose();
Penumbra.Dispose();
PlayerWatcher.Dispose();
_interface.Dispose();
@ -207,5 +334,4 @@ namespace Glamourer
Dalamud.Commands.RemoveHandler("/glamour");
Dalamud.Commands.RemoveHandler("/glamourer");
}
}
}

View file

@ -48,6 +48,10 @@
<ItemGroup>
<Reference Include="Dalamud">
<HintPath>$(appdata)\XIVLauncher\addon\Hooks\dev\Dalamud.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="FFXIVClientStructs">
<HintPath>$(appdata)\XIVLauncher\addon\Hooks\dev\FFXIVClientStructs.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="ImGui.NET">

View file

@ -4,10 +4,10 @@ using System.Linq;
using System.Numerics;
using ImGuiNET;
namespace Glamourer.Gui
namespace Glamourer.Gui;
public class ComboWithFilter<T>
{
public class ComboWithFilter<T>
{
private readonly string _label;
private readonly string _filterLabel;
private readonly string _listLabel;
@ -103,7 +103,7 @@ namespace Glamourer.Gui
_focus = true;
}
var scrollY = Math.Max((int) (ImGui.GetScrollY() / _heightPerItem) - 1, 0);
var scrollY = Math.Max((int)(ImGui.GetScrollY() / _heightPerItem) - 1, 0);
var restHeight = scrollY * _heightPerItem;
numItems = 0;
nodeIdx = 0;
@ -204,5 +204,4 @@ namespace Glamourer.Gui
return ret;
}
}
}

View file

@ -1,14 +0,0 @@
using System.Linq;
using ImGuiNET;
namespace Glamourer.Gui
{
public static partial class ImGuiCustom
{
public static void HoverTooltip(string text)
{
if (text.Any() && ImGui.IsItemHovered())
ImGui.SetTooltip(text);
}
}
}

View file

@ -1,155 +0,0 @@
using System;
using System.Collections.Generic;
using System.Numerics;
using ImGuiNET;
namespace Glamourer.Gui
{
public sealed class ImGuiRaii : IDisposable
{
private int _colorStack;
private int _fontStack;
private int _styleStack;
private float _indentation;
private Stack<Action>? _onDispose;
public static ImGuiRaii NewGroup()
=> new ImGuiRaii().Group();
public ImGuiRaii Group()
=> Begin(ImGui.BeginGroup, ImGui.EndGroup);
public static ImGuiRaii NewTooltip()
=> new ImGuiRaii().Tooltip();
public ImGuiRaii Tooltip()
=> Begin(ImGui.BeginTooltip, ImGui.EndTooltip);
public ImGuiRaii PushColor(ImGuiCol which, uint color)
{
ImGui.PushStyleColor(which, color);
++_colorStack;
return this;
}
public ImGuiRaii PushColor(ImGuiCol which, Vector4 color)
{
ImGui.PushStyleColor(which, color);
++_colorStack;
return this;
}
public ImGuiRaii PopColors(int n = 1)
{
var actualN = Math.Min(n, _colorStack);
if (actualN > 0)
{
ImGui.PopStyleColor(actualN);
_colorStack -= actualN;
}
return this;
}
public ImGuiRaii PushStyle(ImGuiStyleVar style, Vector2 value)
{
ImGui.PushStyleVar(style, value);
++_styleStack;
return this;
}
public ImGuiRaii PushStyle(ImGuiStyleVar style, float value)
{
ImGui.PushStyleVar(style, value);
++_styleStack;
return this;
}
public ImGuiRaii PopStyles(int n = 1)
{
var actualN = Math.Min(n, _styleStack);
if (actualN > 0)
{
ImGui.PopStyleVar(actualN);
_styleStack -= actualN;
}
return this;
}
public ImGuiRaii PushFont(ImFontPtr font)
{
ImGui.PushFont(font);
++_fontStack;
return this;
}
public ImGuiRaii PopFonts(int n = 1)
{
var actualN = Math.Min(n, _fontStack);
while (actualN-- > 0)
{
ImGui.PopFont();
--_fontStack;
}
return this;
}
public ImGuiRaii Indent(float width)
{
if (width != 0)
{
ImGui.Indent(width);
_indentation += width;
}
return this;
}
public ImGuiRaii Unindent(float width)
=> Indent(-width);
public bool Begin(Func<bool> begin, Action end)
{
if (begin())
{
_onDispose ??= new Stack<Action>();
_onDispose.Push(end);
return true;
}
return false;
}
public ImGuiRaii Begin(Action begin, Action end)
{
begin();
_onDispose ??= new Stack<Action>();
_onDispose.Push(end);
return this;
}
public void End(int n = 1)
{
var actualN = Math.Min(n, _onDispose?.Count ?? 0);
while (actualN-- > 0)
_onDispose!.Pop()();
}
public void Dispose()
{
Unindent(_indentation);
PopColors(_colorStack);
PopStyles(_styleStack);
PopFonts(_fontStack);
if (_onDispose != null)
{
End(_onDispose.Count);
_onDispose = null;
}
}
}
}

View file

@ -7,13 +7,14 @@ using Dalamud.Game.ClientState.Objects.Types;
using Glamourer.Designs;
using ImGuiNET;
using Lumina.Excel.GeneratedSheets;
using OtterGui.Raii;
using Penumbra.GameData;
using Penumbra.GameData.Enums;
namespace Glamourer.Gui
namespace Glamourer.Gui;
internal partial class Interface : IDisposable
{
internal partial class Interface : IDisposable
{
public const float SelectorWidth = 200;
public const float MinWindowWidth = 675;
public const int GPoseObjectId = 201;
@ -82,8 +83,8 @@ namespace Glamourer.Gui
try
{
using var raii = new ImGuiRaii();
if (!raii.Begin(() => ImGui.BeginTabBar("##tabBar"), ImGui.EndTabBar))
using var tabBar = ImRaii.TabBar("##tabBar");
if (!tabBar)
return;
_inGPose = Dalamud.Objects[GPoseObjectId] != null;
@ -106,5 +107,4 @@ namespace Glamourer.Gui
ImGui.End();
}
}
}
}

View file

@ -11,13 +11,15 @@ using Glamourer.Customization;
using Glamourer.Designs;
using Glamourer.FileSystem;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Structs;
using Penumbra.PlayerWatch;
namespace Glamourer.Gui
namespace Glamourer.Gui;
internal partial class Interface
{
internal partial class Interface
{
private readonly CharacterSave _currentSave = new();
private string _newDesignName = string.Empty;
private bool _keyboardFocus;
@ -31,13 +33,12 @@ namespace Glamourer.Gui
{
var color = _player == null ? RedHeaderColor : GreenHeaderColor;
var buttonColor = ImGui.GetColorU32(ImGuiCol.FrameBg);
using var raii = new ImGuiRaii()
.PushColor(ImGuiCol.Text, color)
.PushColor(ImGuiCol.Button, buttonColor)
.PushColor(ImGuiCol.ButtonHovered, buttonColor)
.PushColor(ImGuiCol.ButtonActive, buttonColor)
.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.PushStyle(ImGuiStyleVar.FrameRounding, 0);
using var c = ImRaii.PushColor(ImGuiCol.Text, color)
.Push(ImGuiCol.Button, buttonColor)
.Push(ImGuiCol.ButtonHovered, buttonColor)
.Push(ImGuiCol.ButtonActive, buttonColor);
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.Push(ImGuiStyleVar.FrameRounding, 0);
ImGui.Button($"{_currentLabel}##playerHeader", -Vector2.UnitX * 0.0001f);
}
@ -47,7 +48,7 @@ namespace Glamourer.Gui
if (ImGui.Button(FontAwesomeIcon.Clipboard.ToIconString()))
ImGui.SetClipboardText(save.ToBase64());
ImGui.PopFont();
ImGuiCustom.HoverTooltip("Copy customization code to clipboard.");
ImGuiUtil.HoverTooltip("Copy customization code to clipboard.");
}
private static void ConditionalApply(CharacterSave save, Character player)
@ -88,7 +89,8 @@ namespace Glamourer.Gui
ImGui.PushFont(UiBuilder.IconFont);
var applyButton = ImGui.Button(FontAwesomeIcon.Paste.ToIconString()) && _player != null;
ImGui.PopFont();
ImGuiCustom.HoverTooltip("Apply customization code from clipboard.\nHold Shift to apply only customizations.\nHold Control to apply only equipment.");
ImGuiUtil.HoverTooltip(
"Apply customization code from clipboard.\nHold Shift to apply only customizations.\nHold Control to apply only equipment.");
if (!applyButton)
return false;
@ -98,6 +100,7 @@ namespace Glamourer.Gui
var text = ImGui.GetClipboardText();
if (!text.Any())
return false;
var save = CharacterSave.FromString(text);
ConditionalApply(save, _player!);
}
@ -117,7 +120,7 @@ namespace Glamourer.Gui
OpenDesignNamePopup(DesignNameUse.SaveCurrent);
ImGui.PopFont();
ImGuiCustom.HoverTooltip("Save the current design.\nHold Shift to save only customizations.\nHold Control to save only equipment.");
ImGuiUtil.HoverTooltip("Save the current design.\nHold Shift to save only customizations.\nHold Control to save only equipment.");
DrawDesignNamePopup(DesignNameUse.SaveCurrent);
}
@ -134,7 +137,7 @@ namespace Glamourer.Gui
return;
var player = _inGPose
? (Character?) Dalamud.Objects[GPoseObjectId]
? (Character?)Dalamud.Objects[GPoseObjectId]
: Dalamud.ClientState.LocalPlayer;
var fallback = _inGPose ? Dalamud.ClientState.LocalPlayer : null;
if (player == null)
@ -178,7 +181,7 @@ namespace Glamourer.Gui
private void DrawRevertButton()
{
if (!DrawDisableButton("Revert", _player == null))
if (!ImGuiUtil.DrawDisabledButton("Revert", Vector2.Zero, string.Empty, _player == null))
return;
Glamourer.RevertableDesigns.Revert(_player!);
@ -227,8 +230,8 @@ namespace Glamourer.Gui
}
var currentModel = _player!.ModelType();
using var raii = new ImGuiRaii();
if (!raii.Begin(() => ImGui.BeginCombo("Model Id", currentModel.ToString()), ImGui.EndCombo))
using var combo = ImRaii.Combo("Model Id", currentModel.ToString());
if (!combo)
return;
foreach (var (id, _) in _models.Skip(1))
@ -236,7 +239,7 @@ namespace Glamourer.Gui
if (!ImGui.Selectable($"{id:D6}##models", id == currentModel) || id == currentModel)
continue;
_player!.SetModelType((int) id);
_player!.SetModelType((int)id);
Glamourer.Penumbra.UpdateCharacters(_player!);
}
}
@ -291,20 +294,15 @@ namespace Glamourer.Gui
private void DrawActorPanel()
{
using var raii = ImGuiRaii.NewGroup();
using var group = ImRaii.Group();
DrawPlayerHeader();
if (!ImGui.BeginChild("##playerData", -Vector2.One, true))
{
ImGui.EndChild();
using var child = ImRaii.Child("##playerData", -Vector2.One, true);
if (!child)
return;
}
if (_player == null || _player.ModelType() == 0)
DrawPlayerPanel();
else
DrawMonsterPanel();
ImGui.EndChild();
}
}
}

View file

@ -7,6 +7,8 @@ using Dalamud.Game.ClientState.Objects.Types;
using Dalamud.Interface;
using Dalamud.Logging;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using Penumbra.PlayerWatch;
namespace Glamourer.Gui;
@ -27,9 +29,8 @@ internal partial class Interface
private void DrawPlayerFilter()
{
using var raii = new ImGuiRaii()
.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.PushStyle(ImGuiStyleVar.FrameRounding, 0);
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.Push(ImGuiStyleVar.FrameRounding, 0);
ImGui.SetNextItemWidth(SelectorWidth * ImGui.GetIO().FontGlobalScale);
if (ImGui.InputTextWithHint("##playerFilter", "Filter Players...", ref _playerFilter, 32))
_playerFilterLower = _playerFilter.ToLowerInvariant();
@ -115,23 +116,22 @@ internal partial class Interface
private void DrawSelectionButtons()
{
using var raii = new ImGuiRaii()
.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.PushStyle(ImGuiStyleVar.FrameRounding, 0)
.PushFont(UiBuilder.IconFont);
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.Push(ImGuiStyleVar.FrameRounding, 0);
using var font = ImRaii.PushFont(UiBuilder.IconFont);
Character? select = null;
var buttonWidth = Vector2.UnitX * SelectorWidth / 2;
if (ImGui.Button(FontAwesomeIcon.UserCircle.ToIconString(), buttonWidth))
select = Dalamud.ClientState.LocalPlayer;
raii.PopFonts();
ImGuiCustom.HoverTooltip("Select the local player character.");
font.Pop();
ImGuiUtil.HoverTooltip("Select the local player character.");
ImGui.SameLine();
raii.PushFont(UiBuilder.IconFont);
font.Push(UiBuilder.IconFont);
if (_inGPose)
{
raii.PushStyle(ImGuiStyleVar.Alpha, 0.5f);
style.Push(ImGuiStyleVar.Alpha, 0.5f);
ImGui.Button(FontAwesomeIcon.HandPointer.ToIconString(), buttonWidth);
raii.PopStyles();
style.Pop();
}
else
{
@ -139,8 +139,8 @@ internal partial class Interface
select = CharacterFactory.Convert(Dalamud.Targets.Target);
}
raii.PopFonts();
ImGuiCustom.HoverTooltip("Select the current target, if it is in the list.");
font.Pop();
ImGuiUtil.HoverTooltip("Select the current target, if it is in the list.");
if (select == null)
return;
@ -196,7 +196,7 @@ internal partial class Interface
}
using (var _ = new ImGuiRaii().PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero))
using (var _ = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero))
{
ImGui.EndChild();
}
@ -207,14 +207,14 @@ internal partial class Interface
private void DrawPlayerTab()
{
using var raii = new ImGuiRaii();
using var tab = ImRaii.TabItem("Current Players");
_player = null;
if (!raii.Begin(() => ImGui.BeginTabItem("Current Players"), ImGui.EndTabItem))
if (!tab)
return;
DrawPlayerSelector();
if (!_currentLabel.Any())
if (_currentLabel.Length == 0)
return;
ImGui.SameLine();

View file

@ -1,6 +1,8 @@
using System;
using System.Numerics;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
namespace Glamourer.Gui
{
@ -11,7 +13,7 @@ namespace Glamourer.Gui
if (DrawCheckMark(label, value, setter))
Glamourer.Config.Save();
ImGuiCustom.HoverTooltip(tooltip);
ImGuiUtil.HoverTooltip(tooltip);
}
private static void ChangeAndSave<T>(T value, T currentValue, Action<T> setter) where T : IEquatable<T>
@ -33,19 +35,19 @@ namespace Glamourer.Gui
ImGui.SameLine();
if (ImGui.Button($"Default##{name}"))
ChangeAndSave(defaultValue, value, setter);
ImGuiCustom.HoverTooltip(
ImGuiUtil.HoverTooltip(
$"Reset to default: #{defaultValue & 0xFF:X2}{(defaultValue >> 8) & 0xFF:X2}{(defaultValue >> 16) & 0xFF:X2}{defaultValue >> 24:X2}");
ImGui.SameLine();
ImGui.Text(name);
ImGuiCustom.HoverTooltip(tooltip);
ImGuiUtil.HoverTooltip(tooltip);
}
private void DrawRestorePenumbraButton()
private static void DrawRestorePenumbraButton()
{
const string buttonLabel = "Re-Register Penumbra";
if (!Glamourer.Config.AttachToPenumbra)
{
using var raii = new ImGuiRaii().PushStyle(ImGuiStyleVar.Alpha, 0.5f);
using var style = ImRaii.PushStyle(ImGuiStyleVar.Alpha, 0.5f);
ImGui.Button(buttonLabel);
return;
}
@ -53,14 +55,14 @@ namespace Glamourer.Gui
if (ImGui.Button(buttonLabel))
Glamourer.Penumbra.Reattach(true);
ImGuiCustom.HoverTooltip(
ImGuiUtil.HoverTooltip(
"If Penumbra did not register the functions for some reason, pressing this button might help restore functionality.");
}
private void DrawConfigTab()
private static void DrawConfigTab()
{
using var raii = new ImGuiRaii();
if (!raii.Begin(() => ImGui.BeginTabItem("Config"), ImGui.EndTabItem))
using var tab = ImRaii.TabItem("Config");
if (!tab)
return;
var cfg = Glamourer.Config;

View file

@ -5,6 +5,8 @@ using Dalamud.Interface;
using Dalamud.Logging;
using Glamourer.Customization;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Enums;
namespace Glamourer.Gui
@ -14,13 +16,14 @@ namespace Glamourer.Gui
private static bool DrawColorPickerPopup(string label, CustomizationSet set, CustomizationId id, out Customization.Customization value)
{
value = default;
if (!ImGui.BeginPopup(label, ImGuiWindowFlags.AlwaysAutoResize))
using var popup = ImRaii.Popup(label, ImGuiWindowFlags.AlwaysAutoResize);
if (!popup)
return false;
var ret = false;
var count = set.Count(id);
using var raii = new ImGuiRaii().PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.PushStyle(ImGuiStyleVar.FrameRounding, 0);
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.Push(ImGuiStyleVar.FrameRounding, 0);
for (var i = 0; i < count; ++i)
{
var custom = set.Data(id, i);
@ -35,7 +38,6 @@ namespace Glamourer.Gui
ImGui.SameLine();
}
ImGui.EndPopup();
return ret;
}
@ -58,7 +60,7 @@ namespace Glamourer.Gui
ret = true;
}
ImGuiCustom.HoverTooltip($"Input Range: [{minValue}, {maxValue}]");
ImGuiUtil.HoverTooltip($"Input Range: [{minValue}, {maxValue}]");
return ret;
}
@ -92,7 +94,7 @@ namespace Glamourer.Gui
ImGui.SameLine();
using (var _ = ImGuiRaii.NewGroup())
using (var _ = ImRaii.Group())
{
if (InputInt($"##text_{id}", ref current, 1, count))
{
@ -102,7 +104,7 @@ namespace Glamourer.Gui
ImGui.Text(label);
ImGuiCustom.HoverTooltip(tooltip);
ImGuiUtil.HoverTooltip(tooltip);
}
if (!DrawColorPickerPopup(popupName, set, id, out var newCustom))
@ -117,7 +119,7 @@ namespace Glamourer.Gui
private bool DrawListSelector(string label, string tooltip, ref CharacterCustomization customization, CustomizationId id,
CustomizationSet set)
{
using var bigGroup = ImGuiRaii.NewGroup();
using var bigGroup = ImRaii.Group();
var ret = false;
int current = customization[id];
var count = set.Count(id);
@ -146,7 +148,7 @@ namespace Glamourer.Gui
ImGui.SameLine();
ImGui.Text(label);
ImGuiCustom.HoverTooltip(tooltip);
ImGuiUtil.HoverTooltip(tooltip);
return ret;
}
@ -157,10 +159,10 @@ namespace Glamourer.Gui
private bool DrawMultiSelector(ref CharacterCustomization customization, CustomizationSet set)
{
using var bigGroup = ImGuiRaii.NewGroup();
using var bigGroup = ImRaii.Group();
var ret = false;
var count = set.Count(CustomizationId.FacialFeaturesTattoos);
using (var _ = ImGuiRaii.NewGroup())
using (var _ = ImRaii.Group())
{
var face = customization.Face;
if (set.Faces.Count < face)
@ -180,11 +182,7 @@ namespace Glamourer.Gui
customization.FacialFeature(i, !enabled);
}
if (ImGui.IsItemHovered())
{
using var tt = ImGuiRaii.NewTooltip();
ImGui.Image(icon.ImGuiHandle, new Vector2(icon.Width, icon.Height));
}
ImGuiUtil.HoverIconTooltip(icon, _iconSize);
if (i % 4 != 3)
ImGui.SameLine();
@ -192,7 +190,7 @@ namespace Glamourer.Gui
}
ImGui.SameLine();
using var group = ImGuiRaii.NewGroup();
using var group = ImRaii.Group();
ImGui.SetCursorPosY(ImGui.GetCursorPosY() + ImGui.GetTextLineHeightWithSpacing() + 3 * ImGui.GetStyle().ItemSpacing.Y / 2);
int value = customization[CustomizationId.FacialFeaturesTattoos];
if (InputInt($"##{CustomizationId.FacialFeaturesTattoos}", ref value, 1, 256))
@ -201,7 +199,7 @@ namespace Glamourer.Gui
ret = true;
}
ImGui.Text(set.Option(CustomizationId.FacialFeaturesTattoos));
ImGui.TextUnformatted(set.Option(CustomizationId.FacialFeaturesTattoos));
return ret;
}
@ -210,13 +208,14 @@ namespace Glamourer.Gui
private bool DrawIconPickerPopup(string label, CustomizationSet set, CustomizationId id, out Customization.Customization value)
{
value = default;
if (!ImGui.BeginPopup(label, ImGuiWindowFlags.AlwaysAutoResize))
using var popup = ImRaii.Popup(label, ImGuiWindowFlags.AlwaysAutoResize);
if (!popup)
return false;
var ret = false;
var count = set.Count(id);
using var raii = new ImGuiRaii().PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.PushStyle(ImGuiStyleVar.FrameRounding, 0);
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.Push(ImGuiStyleVar.FrameRounding, 0);
for (var i = 0; i < count; ++i)
{
var custom = set.Data(id, i);
@ -229,11 +228,7 @@ namespace Glamourer.Gui
ImGui.CloseCurrentPopup();
}
if (ImGui.IsItemHovered())
{
using var tt = ImGuiRaii.NewTooltip();
ImGui.Image(icon.ImGuiHandle, new Vector2(icon.Width, icon.Height));
}
ImGuiUtil.HoverIconTooltip(icon, _iconSize);
var text = custom.Value.ToString();
var textWidth = ImGui.CalcTextSize(text).X;
@ -244,14 +239,13 @@ namespace Glamourer.Gui
ImGui.SameLine();
}
ImGui.EndPopup();
return ret;
}
private bool DrawIconSelector(string label, string tooltip, ref CharacterCustomization customization, CustomizationId id,
CustomizationSet set)
{
using var bigGroup = ImGuiRaii.NewGroup();
using var bigGroup = ImRaii.Group();
var ret = false;
var count = set.Count(id);
@ -268,14 +262,10 @@ namespace Glamourer.Gui
if (ImGui.ImageButton(icon.ImGuiHandle, _iconSize))
ImGui.OpenPopup(popupName);
if (ImGui.IsItemHovered())
{
using var tt = ImGuiRaii.NewTooltip();
ImGui.Image(icon.ImGuiHandle, new Vector2(icon.Width, icon.Height));
}
ImGuiUtil.HoverIconTooltip(icon, _iconSize);
ImGui.SameLine();
using var group = ImGuiRaii.NewGroup();
using var group = ImRaii.Group();
if (InputInt($"##text_{id}", ref current, 1, count))
{
customization[id] = set.Data(id, current).Value;
@ -288,8 +278,8 @@ namespace Glamourer.Gui
ret = true;
}
ImGui.Text($"{label} ({custom.Value.Value})");
ImGuiCustom.HoverTooltip(tooltip);
ImGui.TextUnformatted($"{label} ({custom.Value.Value})");
ImGuiUtil.HoverTooltip(tooltip);
return ret;
}
@ -298,7 +288,7 @@ namespace Glamourer.Gui
private bool DrawPercentageSelector(string label, string tooltip, ref CharacterCustomization customization, CustomizationId id,
CustomizationSet set)
{
using var bigGroup = ImGuiRaii.NewGroup();
using var bigGroup = ImRaii.Group();
var ret = false;
int value = customization[id];
var count = set.Count(id);
@ -318,15 +308,15 @@ namespace Glamourer.Gui
}
ImGui.SameLine();
ImGui.Text(label);
ImGuiCustom.HoverTooltip(tooltip);
ImGui.TextUnformatted(label);
ImGuiUtil.HoverTooltip(tooltip);
return ret;
}
private bool DrawRaceSelector(ref CharacterCustomization customization)
{
using var group = ImGuiRaii.NewGroup();
using var group = ImRaii.Group();
var ret = false;
ImGui.SetNextItemWidth(_raceSelectorWidth);
if (ImGui.BeginCombo("##subRaceCombo", ClanName(customization.Clan, customization.Gender)))
@ -343,7 +333,7 @@ namespace Glamourer.Gui
ImGui.EndCombo();
}
ImGui.Text(
ImGui.TextUnformatted(
$"{Glamourer.Customization.GetName(CustomName.Gender)} & {Glamourer.Customization.GetName(CustomName.Clan)}");
return ret;
@ -352,7 +342,7 @@ namespace Glamourer.Gui
private bool DrawGenderSelector(ref CharacterCustomization customization)
{
var ret = false;
ImGui.PushFont(UiBuilder.IconFont);
using var font = ImRaii.PushFont(UiBuilder.IconFont);
var icon = customization.Gender == Gender.Male ? FontAwesomeIcon.Mars : FontAwesomeIcon.Venus;
var restricted = false;
if (customization.Race == Race.Hrothgar)
@ -370,7 +360,6 @@ namespace Glamourer.Gui
if (restricted)
ImGui.PopStyleVar();
ImGui.PopFont();
return ret;
}

View file

@ -6,11 +6,13 @@ using Dalamud.Logging;
using Glamourer.Designs;
using Glamourer.FileSystem;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
namespace Glamourer.Gui
namespace Glamourer.Gui;
internal partial class Interface
{
internal partial class Interface
{
private int _totalObject;
private bool _inDesignMode;
@ -44,7 +46,7 @@ namespace Glamourer.Gui
if (_selection!.Data.WriteProtected)
ImGui.PopStyleVar();
ImGuiCustom.HoverTooltip("Overwrite with customization code from clipboard.");
ImGuiUtil.HoverTooltip("Overwrite with customization code from clipboard.");
if (_selection!.Data.WriteProtected || !applyButton)
return;
@ -70,7 +72,7 @@ namespace Glamourer.Gui
if (ImGui.Button(FontAwesomeIcon.FolderPlus.ToIconString(), Vector2.UnitX * SelectorWidth / 5))
OpenDesignNamePopup(DesignNameUse.NewFolder);
ImGui.PopFont();
ImGuiCustom.HoverTooltip("Create a new, empty Folder.");
ImGuiUtil.HoverTooltip("Create a new, empty Folder.");
DrawDesignNamePopup(DesignNameUse.NewFolder);
}
@ -81,7 +83,7 @@ namespace Glamourer.Gui
if (ImGui.Button(FontAwesomeIcon.Plus.ToIconString(), Vector2.UnitX * SelectorWidth / 5))
OpenDesignNamePopup(DesignNameUse.NewDesign);
ImGui.PopFont();
ImGuiCustom.HoverTooltip("Create a new, empty Design.");
ImGuiUtil.HoverTooltip("Create a new, empty Design.");
DrawDesignNamePopup(DesignNameUse.NewDesign);
}
@ -92,7 +94,7 @@ namespace Glamourer.Gui
if (ImGui.Button(FontAwesomeIcon.Paste.ToIconString(), Vector2.UnitX * SelectorWidth / 5))
OpenDesignNamePopup(DesignNameUse.FromClipboard);
ImGui.PopFont();
ImGuiCustom.HoverTooltip("Create a new design from the customization string in your clipboard.");
ImGuiUtil.HoverTooltip("Create a new design from the customization string in your clipboard.");
DrawDesignNamePopup(DesignNameUse.FromClipboard);
}
@ -112,7 +114,7 @@ namespace Glamourer.Gui
ImGui.PopFont();
if (style)
ImGui.PopStyleVar();
ImGuiCustom.HoverTooltip("Delete the currently selected Design.");
ImGuiUtil.HoverTooltip("Delete the currently selected Design.");
}
private void DrawDuplicateDesignButton()
@ -125,16 +127,16 @@ namespace Glamourer.Gui
ImGui.PopFont();
if (_selection == null)
ImGui.PopStyleVar();
ImGuiCustom.HoverTooltip("Clone the currently selected Design.\nHold Shift to only clone the customizations.\nHold Control to only clone the equipment.");
ImGuiUtil.HoverTooltip(
"Clone the currently selected Design.\nHold Shift to only clone the customizations.\nHold Control to only clone the equipment.");
DrawDesignNamePopup(DesignNameUse.DuplicateDesign);
}
private void DrawDesignSelectorButtons()
{
using var raii = new ImGuiRaii()
.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.PushStyle(ImGuiStyleVar.FrameRounding, 0f);
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.Push(ImGuiStyleVar.FrameRounding, 0f);
DrawNewFolderButton();
ImGui.SameLine();
@ -200,9 +202,9 @@ namespace Glamourer.Gui
private void DrawSaves()
{
using var raii = new ImGuiRaii();
raii.PushStyle(ImGuiStyleVar.IndentSpacing, 12.5f * ImGui.GetIO().FontGlobalScale);
_inDesignMode = raii.Begin(() => ImGui.BeginTabItem("Designs"), ImGui.EndTabItem);
using var style = ImRaii.PushStyle(ImGuiStyleVar.IndentSpacing, 12.5f * ImGui.GetIO().FontGlobalScale);
using var tab = ImRaii.TabItem("Designs");
_inDesignMode = tab.Success;
if (!_inDesignMode)
return;
@ -279,7 +281,7 @@ namespace Glamourer.Gui
{
if (ImGui.MenuItem("Delete") && ImGui.GetIO().KeyCtrl && ImGui.GetIO().KeyShift)
_designs.DeleteAllChildren(child, false);
ImGuiCustom.HoverTooltip("Hold Control and Shift to delete.");
ImGuiUtil.HoverTooltip("Hold Control and Shift to delete.");
RenameChildInput(child);
@ -342,19 +344,18 @@ namespace Glamourer.Gui
++_totalObject;
var color = GetDesignColor(d.Data);
using var raii = new ImGuiRaii()
.PushColor(ImGuiCol.Text, color);
using var c = ImRaii.PushColor(ImGuiCol.Text, color);
var selected = ImGui.Selectable($"{child.Name}##{_totalObject}", ReferenceEquals(child, _selection));
raii.PopColors();
c.Pop();
DrawOrnaments(child);
if (Glamourer.Config.ShowLocks && d.Data.WriteProtected)
{
ImGui.SameLine();
raii.PushFont(UiBuilder.IconFont)
.PushColor(ImGuiCol.Text, color);
ImGui.Text(FontAwesomeIcon.Lock.ToIconString());
using var font = ImRaii.PushFont(UiBuilder.IconFont);
c.Push(ImGuiCol.Text, color);
ImGui.TextUnformatted(FontAwesomeIcon.Lock.ToIconString());
}
if (selected)
@ -370,5 +371,4 @@ namespace Glamourer.Gui
_designs.UpdateAllChildren(oldPath, draggedFolder!);
ContextMenu(child);
}
}
}

View file

@ -1,6 +1,7 @@
using Dalamud.Interface;
using ImGuiNET;
using Lumina.Text;
using OtterGui;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
@ -20,7 +21,7 @@ namespace Glamourer.Gui
var change = stainCombo.Draw(string.Empty, out var newStain) && !newStain.RowIndex.Equals(stainIdx);
if (!change && (byte) stainIdx != 0)
{
ImGuiCustom.HoverTooltip("Right-click to clear.");
ImGuiUtil.HoverTooltip("Right-click to clear.");
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
{
change = true;
@ -46,7 +47,7 @@ namespace Glamourer.Gui
var change = equipCombo.Draw(currentName, out var newItem, _itemComboWidth) && newItem.Base.RowId != item.RowId;
if (!change && !ReferenceEquals(item, SmallClothes))
{
ImGuiCustom.HoverTooltip("Right-click to clear.");
ImGuiUtil.HoverTooltip("Right-click to clear.");
if (ImGui.IsItemClicked(ImGuiMouseButton.Right))
{
change = true;

View file

@ -6,11 +6,12 @@ using Dalamud.Interface;
using Glamourer.Designs;
using Glamourer.FileSystem;
using ImGuiNET;
using OtterGui.Raii;
namespace Glamourer.Gui
namespace Glamourer.Gui;
internal partial class Interface
{
internal partial class Interface
{
private const string FixDragDropLabel = "##FixDragDrop";
private List<string>? _fullPathCache;
@ -25,44 +26,45 @@ namespace Glamourer.Gui
private void DrawFixedDesignsTab()
{
_newFixDesignGroup ??= _plugin.FixedDesigns.JobGroups[1];
_newFixDesignGroup ??= Glamourer.FixedDesignManager.FixedDesigns.JobGroups[1];
using var raii = new ImGuiRaii();
if (!raii.Begin(() => ImGui.BeginTabItem("Fixed Designs"), ImGui.EndTabItem))
using var tabItem = ImRaii.TabItem("Fixed Designs");
if (!tabItem)
{
_fullPathCache = null;
_newFixDesign = null;
_newFixDesignPath = string.Empty;
_newFixDesignGroup = _plugin.FixedDesigns.JobGroups[1];
_newFixDesignGroup = Glamourer.FixedDesignManager.FixedDesigns.JobGroups[1];
return;
}
_fullPathCache ??= _plugin.FixedDesigns.Data.Select(d => d.Design.FullName()).ToList();
raii.Begin(() => ImGui.BeginTable("##FixedTable", 4), ImGui.EndTable);
_fullPathCache ??= Glamourer.FixedDesignManager.FixedDesigns.Data.Select(d => d.Design.FullName()).ToList();
using var table = ImRaii.Table("##FixedTable", 4);
var buttonWidth = 23.5f * ImGuiHelpers.GlobalScale;
ImGui.TableSetupColumn("##DeleteColumn", ImGuiTableColumnFlags.WidthFixed, 2 * buttonWidth);
ImGui.TableSetupColumn("Character", ImGuiTableColumnFlags.WidthFixed, 200 * ImGuiHelpers.GlobalScale);
ImGui.TableSetupColumn("Jobs", ImGuiTableColumnFlags.WidthFixed, 175 * ImGuiHelpers.GlobalScale);
ImGui.TableSetupColumn("Design", ImGuiTableColumnFlags.WidthStretch);
ImGui.TableHeadersRow();
var xPos = 0f;
using var style = new ImRaii.Style();
using var font = new ImRaii.Font();
for (var i = 0; i < _fullPathCache.Count; ++i)
{
var path = _fullPathCache[i];
var name = _plugin.FixedDesigns.Data[i];
var name = Glamourer.FixedDesignManager.FixedDesigns.Data[i];
ImGui.TableNextRow();
ImGui.TableNextColumn();
raii.PushStyle(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing / 2);
raii.PushFont(UiBuilder.IconFont);
style.Push(ImGuiStyleVar.ItemSpacing, ImGui.GetStyle().ItemSpacing / 2);
font.Push(UiBuilder.IconFont);
if (ImGui.Button($"{FontAwesomeIcon.Trash.ToIconChar()}##{i}"))
{
_fullPathCache.RemoveAt(i--);
_plugin.FixedDesigns.Remove(name);
Glamourer.FixedDesignManager.FixedDesigns.Remove(name);
continue;
}
@ -70,15 +72,15 @@ namespace Glamourer.Gui
ImGui.SameLine();
xPos = ImGui.GetCursorPosX();
if (ImGui.Checkbox($"##Enabled{i}", ref tmp))
if (tmp && _plugin.FixedDesigns.EnableDesign(name)
|| !tmp && _plugin.FixedDesigns.DisableDesign(name))
if (tmp && Glamourer.FixedDesignManager.FixedDesigns.EnableDesign(name)
|| !tmp && Glamourer.FixedDesignManager.FixedDesigns.DisableDesign(name))
{
Glamourer.Config.FixedDesigns[i].Enabled = tmp;
Glamourer.Config.Save();
}
raii.PopStyles();
raii.PopFonts();
style.Pop();
font.Pop();
ImGui.TableNextColumn();
ImGui.Selectable($"{name.Name}##Fix{i}");
if (ImGui.BeginDragDropSource())
@ -88,67 +90,68 @@ namespace Glamourer.Gui
ImGui.Text($"Dragging {name.Name} ({path})...");
ImGui.EndDragDropSource();
}
if (ImGui.BeginDragDropTarget())
{
if (IsDropping() && _fixDragDropIdx >= 0)
{
var d = _plugin.FixedDesigns.Data[_fixDragDropIdx];
_plugin.FixedDesigns.Move(d, i);
var d = Glamourer.FixedDesignManager.FixedDesigns.Data[_fixDragDropIdx];
Glamourer.FixedDesignManager.FixedDesigns.Move(d, i);
var p = _fullPathCache[_fixDragDropIdx];
_fullPathCache.RemoveAt(_fixDragDropIdx);
_fullPathCache.Insert(i, p);
_fixDragDropIdx = -1;
}
ImGui.EndDragDropTarget();
}
ImGui.TableNextColumn();
ImGui.Text(_plugin.FixedDesigns.Data[i].Jobs.Name);
ImGui.Text(Glamourer.FixedDesignManager.FixedDesigns.Data[i].Jobs.Name);
ImGui.TableNextColumn();
ImGui.Text(path);
}
ImGui.TableNextRow();
ImGui.TableNextColumn();
raii.PushFont(UiBuilder.IconFont);
font.Push(UiBuilder.IconFont);
ImGui.SetCursorPosX(xPos);
if (_newFixDesign == null || _newFixCharacterName == string.Empty)
{
raii.PushStyle(ImGuiStyleVar.Alpha, 0.5f);
style.Push(ImGuiStyleVar.Alpha, 0.5f);
ImGui.Button($"{FontAwesomeIcon.Plus.ToIconChar()}##NewFix");
raii.PopStyles();
style.Pop();
}
else if (ImGui.Button($"{FontAwesomeIcon.Plus.ToIconChar()}##NewFix"))
{
_fullPathCache.Add(_newFixDesignPath);
_plugin.FixedDesigns.Add(_newFixCharacterName, _newFixDesign, _newFixDesignGroup.Value, false);
Glamourer.FixedDesignManager.FixedDesigns.Add(_newFixCharacterName, _newFixDesign, _newFixDesignGroup.Value, false);
_newFixCharacterName = string.Empty;
_newFixDesignPath = string.Empty;
_newFixDesign = null;
_newFixDesignGroup = _plugin.FixedDesigns.JobGroups[1];
_newFixDesignGroup = Glamourer.FixedDesignManager.FixedDesigns.JobGroups[1];
}
raii.PopFonts();
font.Pop();
ImGui.TableNextColumn();
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
ImGui.InputTextWithHint("##NewFix", "Enter new Character", ref _newFixCharacterName, 32);
ImGui.TableNextColumn();
ImGui.SetNextItemWidth(-1);
if (raii.Begin(() => ImGui.BeginCombo("##NewFixDesignGroup", _newFixDesignGroup.Value.Name), ImGui.EndCombo))
{
foreach (var (id, group) in _plugin.FixedDesigns.JobGroups)
using var combo = ImRaii.Combo("##NewFixDesignGroup", _newFixDesignGroup.Value.Name);
if (combo)
foreach (var (id, group) in Glamourer.FixedDesignManager.FixedDesigns.JobGroups)
{
ImGui.SetNextItemWidth(-1);
if (ImGui.Selectable($"{group.Name}##NewFixDesignGroup", group.Name == _newFixDesignGroup.Value.Name))
_newFixDesignGroup = group;
}
raii.End();
}
ImGui.TableNextColumn();
ImGui.SetNextItemWidth(-1);
if (!raii.Begin(() => ImGui.BeginCombo("##NewFixPath", _newFixDesignPath), ImGui.EndCombo))
using var combo2 = ImRaii.Combo("##NewFixPath", _newFixDesignPath);
if (!combo2)
return;
foreach (var design in _plugin.Designs.FileSystem.Root.AllLeaves(SortMode.Lexicographical).Cast<Design>())
@ -162,5 +165,4 @@ namespace Glamourer.Gui
_newFixDesign = design;
}
}
}
}

View file

@ -2,6 +2,7 @@
using System.Linq;
using Dalamud.Logging;
using Glamourer.Customization;
using Glamourer.Structs;
using ImGuiNET;
using Penumbra.GameData.Enums;

View file

@ -4,6 +4,7 @@ using System.Reflection;
using ImGuiNET;
using Penumbra.GameData.Enums;
using Lumina.Excel.GeneratedSheets;
using Glamourer.Structs;
namespace Glamourer.Gui
{

View file

@ -2,10 +2,10 @@
using Dalamud.Game.ClientState.Objects.Types;
using ImGuiNET;
namespace Glamourer.Gui
namespace Glamourer.Gui;
internal partial class Interface
{
internal partial class Interface
{
private static bool DrawCheckMark(string label, bool value, Action<bool> setter)
{
var startValue = value;
@ -18,17 +18,6 @@ namespace Glamourer.Gui
return false;
}
private static bool DrawDisableButton(string label, bool disabled)
{
if (!disabled)
return ImGui.Button(label);
using var raii = new ImGuiRaii();
raii.PushStyle(ImGuiStyleVar.Alpha, 0.5f);
ImGui.Button(label);
return false;
}
private static bool DrawMiscellaneous(CharacterSave save, Character? player)
{
var ret = false;
@ -62,7 +51,7 @@ namespace Glamourer.Gui
var alpha = save.Alpha;
if (ImGui.DragFloat("Alpha", ref alpha, 0.01f, 0f, 1f, "%.2f") && alpha != save.Alpha)
{
alpha = (float) Math.Round(alpha > 1 ? 1 : alpha < 0 ? 0 : alpha, 2);
alpha = (float)Math.Round(alpha > 1 ? 1 : alpha < 0 ? 0 : alpha, 2);
save.Alpha = alpha;
ret = true;
if (player != null)
@ -71,5 +60,4 @@ namespace Glamourer.Gui
return ret;
}
}
}

View file

@ -2,11 +2,12 @@
using System.Linq;
using System.Numerics;
using ImGuiNET;
using OtterGui.Raii;
namespace Glamourer.Gui
namespace Glamourer.Gui;
internal partial class Interface
{
internal partial class Interface
{
private string? _currentRevertableName;
private CharacterSave? _currentRevertable;
@ -31,7 +32,7 @@ namespace Glamourer.Gui
}
}
using (var _ = new ImGuiRaii().PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero))
using (var _ = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero))
{
ImGui.EndChild();
}
@ -42,16 +43,15 @@ namespace Glamourer.Gui
private void DrawRevertablePanel()
{
using var group = ImGuiRaii.NewGroup();
using var group = ImRaii.Group();
{
var buttonColor = ImGui.GetColorU32(ImGuiCol.FrameBg);
using var raii = new ImGuiRaii()
.PushColor(ImGuiCol.Text, GreenHeaderColor)
.PushColor(ImGuiCol.Button, buttonColor)
.PushColor(ImGuiCol.ButtonHovered, buttonColor)
.PushColor(ImGuiCol.ButtonActive, buttonColor)
.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.PushStyle(ImGuiStyleVar.FrameRounding, 0);
using var color = ImRaii.PushColor(ImGuiCol.Text, GreenHeaderColor)
.Push(ImGuiCol.Button, buttonColor)
.Push(ImGuiCol.ButtonHovered, buttonColor)
.Push(ImGuiCol.ButtonActive, buttonColor);
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, Vector2.Zero)
.Push(ImGuiStyleVar.FrameRounding, 0);
ImGui.Button($"{_currentRevertableName}##playerHeader", -Vector2.UnitX * 0.0001f);
}
@ -72,8 +72,8 @@ namespace Glamourer.Gui
[Conditional("DEBUG")]
private void DrawRevertablesTab()
{
using var raii = new ImGuiRaii();
if (!raii.Begin(() => ImGui.BeginTabItem("Revertables"), ImGui.EndTabItem))
using var tabItem = ImRaii.TabItem("Revertables");
if (!tabItem)
return;
DrawRevertablesSelector();
@ -84,5 +84,4 @@ namespace Glamourer.Gui
ImGui.SameLine();
DrawRevertablePanel();
}
}
}