Update gamedata and services.

This commit is contained in:
Ottermandias 2023-12-21 15:06:56 +01:00
parent 36d95c37bc
commit a982c0a1c1
74 changed files with 907 additions and 960 deletions

View file

@ -12,9 +12,9 @@ public class CustomizationManager : ICustomizationManager
private CustomizationManager()
{ }
public static ICustomizationManager Create(ITextureProvider textures, IDataManager gameData, IPluginLog log)
public static ICustomizationManager Create(ITextureProvider textures, IDataManager gameData, IPluginLog log, NpcCustomizeSet npcCustomizeSet)
{
_options ??= new CustomizationOptions(textures, gameData, log);
_options ??= new CustomizationOptions(textures, gameData, log, npcCustomizeSet);
return new CustomizationManager();
}

View file

@ -1,240 +1,13 @@
using System;
using Dalamud.Plugin.Services;
using Lumina.Excel;
using Lumina.Excel.GeneratedSheets;
using OtterGui;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.GameData.Enums;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
using Penumbra.GameData;
namespace Glamourer.Customization;
public static class CustomizationNpcOptions
{
public unsafe struct NpcData
public static Dictionary<(SubRace, Gender), IReadOnlyList<(CustomizeIndex, CustomizeValue)>> CreateNpcData(CustomizationSet[] sets, NpcCustomizeSet npcCustomizeSet)
{
public string Name;
public Customize Customize;
private fixed byte _equip[40];
public CharacterWeapon Mainhand;
public CharacterWeapon Offhand;
public uint Id;
public bool VisorToggled;
public ObjectKind Kind;
public ReadOnlySpan<CharacterArmor> Equip
{
get
{
fixed (byte* ptr = _equip)
{
return new ReadOnlySpan<CharacterArmor>((CharacterArmor*)ptr, 10);
}
}
}
public string WriteGear()
{
var sb = new StringBuilder(128);
var span = Equip;
for (var i = 0; i < 10; ++i)
{
sb.Append(span[i].Set.Id.ToString("D4"));
sb.Append('-');
sb.Append(span[i].Variant.Id.ToString("D3"));
sb.Append('-');
sb.Append(span[i].Stain.Id.ToString("D3"));
sb.Append(", ");
}
sb.Append(Mainhand.Set.Id.ToString("D4"));
sb.Append('-');
sb.Append(Mainhand.Type.Id.ToString("D4"));
sb.Append('-');
sb.Append(Mainhand.Variant.Id.ToString("D3"));
sb.Append('-');
sb.Append(Mainhand.Stain.Id.ToString("D4"));
sb.Append(", ");
sb.Append(Offhand.Set.Id.ToString("D4"));
sb.Append('-');
sb.Append(Offhand.Type.Id.ToString("D4"));
sb.Append('-');
sb.Append(Offhand.Variant.Id.ToString("D3"));
sb.Append('-');
sb.Append(Offhand.Stain.Id.ToString("D3"));
return sb.ToString();
}
internal void Set(int idx, uint value)
{
fixed (byte* ptr = _equip)
{
((uint*)ptr)[idx] = value;
}
}
public bool DataEquals(in NpcData other)
{
if (VisorToggled != other.VisorToggled)
return false;
if (!Customize.Equals(other.Customize))
return false;
if (!Mainhand.Equals(other.Mainhand))
return false;
if (!Offhand.Equals(other.Offhand))
return false;
fixed (byte* ptr1 = _equip, ptr2 = other._equip)
{
return new ReadOnlySpan<byte>(ptr1, 40).SequenceEqual(new ReadOnlySpan<byte>(ptr2, 40));
}
}
}
private static void ApplyNpcEquip(ref NpcData data, NpcEquip row)
{
data.Set(0, row.ModelHead | (row.DyeHead.Row << 24));
data.Set(1, row.ModelBody | (row.DyeBody.Row << 24));
data.Set(2, row.ModelHands | (row.DyeHands.Row << 24));
data.Set(3, row.ModelLegs | (row.DyeLegs.Row << 24));
data.Set(4, row.ModelFeet | (row.DyeFeet.Row << 24));
data.Set(5, row.ModelEars | (row.DyeEars.Row << 24));
data.Set(6, row.ModelNeck | (row.DyeNeck.Row << 24));
data.Set(7, row.ModelWrists | (row.DyeWrists.Row << 24));
data.Set(8, row.ModelRightRing | (row.DyeRightRing.Row << 24));
data.Set(9, row.ModelLeftRing | (row.DyeLeftRing.Row << 24));
data.Mainhand = new CharacterWeapon(row.ModelMainHand | ((ulong)row.DyeMainHand.Row << 48));
data.Offhand = new CharacterWeapon(row.ModelOffHand | ((ulong)row.DyeOffHand.Row << 48));
data.VisorToggled = row.Visor;
}
public static unsafe IReadOnlyList<NpcData> CreateNpcData(IReadOnlyDictionary<uint, string> eNpcs,
IReadOnlyDictionary<uint, string> bnpcNames, IObjectIdentifier identifier, IDataManager data)
{
var enpcSheet = data.GetExcelSheet<ENpcBase>()!;
var bnpcSheet = data.GetExcelSheet<BNpcBase>()!;
var list = new List<NpcData>(eNpcs.Count + (int)bnpcSheet.RowCount);
foreach (var (id, name) in eNpcs)
{
var row = enpcSheet.GetRow(id);
if (row == null || name.IsNullOrWhitespace())
continue;
var (valid, customize) = FromEnpcBase(row);
if (!valid)
continue;
var ret = new NpcData
{
Name = name,
Customize = customize,
Id = id,
Kind = ObjectKind.EventNpc,
};
if (row.NpcEquip.Row != 0 && row.NpcEquip.Value is { } equip)
{
ApplyNpcEquip(ref ret, equip);
}
else
{
ret.Set(0, row.ModelHead | (row.DyeHead.Row << 24));
ret.Set(1, row.ModelBody | (row.DyeBody.Row << 24));
ret.Set(2, row.ModelHands | (row.DyeHands.Row << 24));
ret.Set(3, row.ModelLegs | (row.DyeLegs.Row << 24));
ret.Set(4, row.ModelFeet | (row.DyeFeet.Row << 24));
ret.Set(5, row.ModelEars | (row.DyeEars.Row << 24));
ret.Set(6, row.ModelNeck | (row.DyeNeck.Row << 24));
ret.Set(7, row.ModelWrists | (row.DyeWrists.Row << 24));
ret.Set(8, row.ModelRightRing | (row.DyeRightRing.Row << 24));
ret.Set(9, row.ModelLeftRing | (row.DyeLeftRing.Row << 24));
ret.Mainhand = new CharacterWeapon(row.ModelMainHand | ((ulong)row.DyeMainHand.Row << 48));
ret.Offhand = new CharacterWeapon(row.ModelOffHand | ((ulong)row.DyeOffHand.Row << 48));
ret.VisorToggled = row.Visor;
}
list.Add(ret);
}
foreach (var baseRow in bnpcSheet)
{
if (baseRow.ModelChara.Value!.Type != 1)
continue;
var bnpcNameIds = identifier.GetBnpcNames(baseRow.RowId);
if (bnpcNameIds.Count == 0)
continue;
var (valid, customize) = FromBnpcCustomize(baseRow.BNpcCustomize.Value!);
if (!valid)
continue;
var equip = baseRow.NpcEquip.Value!;
var ret = new NpcData
{
Customize = customize,
Id = baseRow.RowId,
Kind = ObjectKind.BattleNpc,
};
ApplyNpcEquip(ref ret, equip);
foreach (var bnpcNameId in bnpcNameIds)
{
if (bnpcNames.TryGetValue(bnpcNameId.Id, out var name) && !name.IsNullOrWhitespace())
list.Add(ret with { Name = name });
}
}
var groups = list.GroupBy(d => d.Name).ToDictionary(g => g.Key, g => g.ToList());
list.Clear();
foreach (var (name, duplicates) in groups.OrderBy(kvp => kvp.Key))
{
for (var i = 0; i < duplicates.Count; ++i)
{
var current = duplicates[i];
var add = true;
for (var j = 0; j < i; ++j)
{
if (current.DataEquals(duplicates[j]))
{
duplicates.RemoveAt(i--);
break;
}
}
}
if (duplicates.Count == 1)
list.Add(duplicates[0]);
else
list.AddRange(duplicates
.Select(duplicate => duplicate with { Name = $"{name} ({(duplicate.Kind is ObjectKind.BattleNpc ? 'B' : 'E')}{duplicate.Id})" }));
}
var lastWeird = list.FindIndex(d => char.IsAsciiLetterOrDigit(d.Name[0]));
if (lastWeird != -1)
{
list.AddRange(list.Take(lastWeird));
list.RemoveRange(0, lastWeird);
}
return list;
}
public static Dictionary<(SubRace, Gender), IReadOnlyList<(CustomizeIndex, CustomizeValue)>> CreateNpcData(CustomizationSet[] sets,
ExcelSheet<BNpcCustomize> bNpc, ExcelSheet<ENpcBase> eNpc)
{
var customizes = bNpc.SelectWhere(FromBnpcCustomize)
.Concat(eNpc.SelectWhere(FromEnpcBase)).ToList();
var dict = new Dictionary<(SubRace, Gender), HashSet<(CustomizeIndex, CustomizeValue)>>();
var customizeIndices = new[]
{
@ -251,7 +24,7 @@ public static class CustomizationNpcOptions
CustomizeIndex.EyeColorRight,
};
foreach (var customize in customizes)
foreach (var customize in npcCustomizeSet.Select(s => s.Customize))
{
var set = sets[CustomizationOptions.ToIndex(customize.Clan, customize.Gender)];
foreach (var customizeIndex in customizeIndices)
@ -265,7 +38,7 @@ public static class CustomizationNpcOptions
if (!dict.TryGetValue((set.Clan, set.Gender), out var npcSet))
{
npcSet = new HashSet<(CustomizeIndex, CustomizeValue)> { (customizeIndex, value) };
npcSet = [(customizeIndex, value)];
dict.Add((set.Clan, set.Gender), npcSet);
}
else
@ -278,85 +51,4 @@ public static class CustomizationNpcOptions
return dict.ToDictionary(kvp => kvp.Key,
kvp => (IReadOnlyList<(CustomizeIndex, CustomizeValue)>)kvp.Value.OrderBy(p => p.Item1).ThenBy(p => p.Item2.Value).ToArray());
}
private static (bool, Customize) FromBnpcCustomize(BNpcCustomize bnpcCustomize)
{
var customize = new Customize();
customize.Data.Set(0, (byte)bnpcCustomize.Race.Row);
customize.Data.Set(1, bnpcCustomize.Gender);
customize.Data.Set(2, bnpcCustomize.BodyType);
customize.Data.Set(3, bnpcCustomize.Height);
customize.Data.Set(4, (byte)bnpcCustomize.Tribe.Row);
customize.Data.Set(5, bnpcCustomize.Face);
customize.Data.Set(6, bnpcCustomize.HairStyle);
customize.Data.Set(7, bnpcCustomize.HairHighlight);
customize.Data.Set(8, bnpcCustomize.SkinColor);
customize.Data.Set(9, bnpcCustomize.EyeHeterochromia);
customize.Data.Set(10, bnpcCustomize.HairColor);
customize.Data.Set(11, bnpcCustomize.HairHighlightColor);
customize.Data.Set(12, bnpcCustomize.FacialFeature);
customize.Data.Set(13, bnpcCustomize.FacialFeatureColor);
customize.Data.Set(14, bnpcCustomize.Eyebrows);
customize.Data.Set(15, bnpcCustomize.EyeColor);
customize.Data.Set(16, bnpcCustomize.EyeShape);
customize.Data.Set(17, bnpcCustomize.Nose);
customize.Data.Set(18, bnpcCustomize.Jaw);
customize.Data.Set(19, bnpcCustomize.Mouth);
customize.Data.Set(20, bnpcCustomize.LipColor);
customize.Data.Set(21, bnpcCustomize.BustOrTone1);
customize.Data.Set(22, bnpcCustomize.ExtraFeature1);
customize.Data.Set(23, bnpcCustomize.ExtraFeature2OrBust);
customize.Data.Set(24, bnpcCustomize.FacePaint);
customize.Data.Set(25, bnpcCustomize.FacePaintColor);
if (customize.BodyType.Value != 1
|| !CustomizationOptions.Races.Contains(customize.Race)
|| !CustomizationOptions.Clans.Contains(customize.Clan)
|| !CustomizationOptions.Genders.Contains(customize.Gender))
return (false, Customize.Default);
return (true, customize);
}
private static (bool, Customize) FromEnpcBase(ENpcBase enpcBase)
{
if (enpcBase.ModelChara.Value?.Type != 1)
return (false, Customize.Default);
var customize = new Customize();
customize.Data.Set(0, (byte)enpcBase.Race.Row);
customize.Data.Set(1, enpcBase.Gender);
customize.Data.Set(2, enpcBase.BodyType);
customize.Data.Set(3, enpcBase.Height);
customize.Data.Set(4, (byte)enpcBase.Tribe.Row);
customize.Data.Set(5, enpcBase.Face);
customize.Data.Set(6, enpcBase.HairStyle);
customize.Data.Set(7, enpcBase.HairHighlight);
customize.Data.Set(8, enpcBase.SkinColor);
customize.Data.Set(9, enpcBase.EyeHeterochromia);
customize.Data.Set(10, enpcBase.HairColor);
customize.Data.Set(11, enpcBase.HairHighlightColor);
customize.Data.Set(12, enpcBase.FacialFeature);
customize.Data.Set(13, enpcBase.FacialFeatureColor);
customize.Data.Set(14, enpcBase.Eyebrows);
customize.Data.Set(15, enpcBase.EyeColor);
customize.Data.Set(16, enpcBase.EyeShape);
customize.Data.Set(17, enpcBase.Nose);
customize.Data.Set(18, enpcBase.Jaw);
customize.Data.Set(19, enpcBase.Mouth);
customize.Data.Set(20, enpcBase.LipColor);
customize.Data.Set(21, enpcBase.BustOrTone1);
customize.Data.Set(22, enpcBase.ExtraFeature1);
customize.Data.Set(23, enpcBase.ExtraFeature2OrBust);
customize.Data.Set(24, enpcBase.FacePaint);
customize.Data.Set(25, enpcBase.FacePaintColor);
if (customize.BodyType.Value != 1
|| !CustomizationOptions.Races.Contains(customize.Race)
|| !CustomizationOptions.Clans.Contains(customize.Clan)
|| !CustomizationOptions.Genders.Contains(customize.Gender))
return (false, Customize.Default);
return (true, customize);
}
}

View file

@ -62,7 +62,7 @@ public partial class CustomizationOptions
public string GetName(CustomName name)
=> _names[(int)name];
internal CustomizationOptions(ITextureProvider textures, IDataManager gameData, IPluginLog log)
internal CustomizationOptions(ITextureProvider textures, IDataManager gameData, IPluginLog log, NpcCustomizeSet npcCustomizeSet)
{
var tmp = new TemporaryData(gameData, this, log);
_icons = new IconStorage(textures, gameData);
@ -73,7 +73,7 @@ public partial class CustomizationOptions
_customizationSets[ToIndex(race, gender)] = tmp.GetSet(race, gender);
}
tmp.SetNpcData(_customizationSets);
tmp.SetNpcData(_customizationSets, npcCustomizeSet);
}
// Obtain localized names of customization options and race names from the game data.
@ -163,9 +163,9 @@ public partial class CustomizationOptions
return set;
}
public void SetNpcData(CustomizationSet[] sets)
public void SetNpcData(CustomizationSet[] sets, NpcCustomizeSet npcCustomizeSet)
{
var data = CustomizationNpcOptions.CreateNpcData(sets, _bnpcCustomize, _enpcBase);
var data = CustomizationNpcOptions.CreateNpcData(sets, npcCustomizeSet);
foreach (var set in sets)
{
if (data.TryGetValue((set.Clan, set.Gender), out var npcData))

View file

@ -1,13 +1,14 @@
using System;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
namespace Glamourer.Customization;
public unsafe struct Customize
{
public Penumbra.GameData.Structs.CustomizeData Data;
public CustomizeArray Data;
public Customize(in Penumbra.GameData.Structs.CustomizeData data)
public Customize(in CustomizeArray data)
{
Data = data.Clone();
}

View file

@ -144,14 +144,14 @@ public static class CustomizationExtensions
};
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static unsafe CustomizeValue Get(this in Penumbra.GameData.Structs.CustomizeData data, CustomizeIndex index)
public static unsafe CustomizeValue Get(this in Penumbra.GameData.Structs.CustomizeArray data, CustomizeIndex index)
{
var (offset, mask) = index.ToByteAndMask();
return (CustomizeValue)(data.Data[offset] & mask);
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public static unsafe bool Set(this ref Penumbra.GameData.Structs.CustomizeData data, CustomizeIndex index, CustomizeValue value)
public static unsafe bool Set(this ref Penumbra.GameData.Structs.CustomizeArray data, CustomizeIndex index, CustomizeValue value)
{
var (offset, mask) = index.ToByteAndMask();
return mask != 0xFF

View file

@ -3,13 +3,14 @@ using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Dalamud.Memory;
using Penumbra.GameData.Structs;
namespace Glamourer.Customization;
[StructLayout(LayoutKind.Explicit, Size = Size)]
public unsafe struct DatCharacterFile
{
public const int Size = 4 + 4 + 4 + 4 + Penumbra.GameData.Structs.CustomizeData.Size + 2 + 4 + 41 * 4; // 212
public const int Size = 4 + 4 + 4 + 4 + CustomizeArray.Size + 2 + 4 + 41 * 4; // 212
[FieldOffset(0)]
private fixed byte _data[Size];
@ -27,12 +28,12 @@ public unsafe struct DatCharacterFile
private readonly uint _padding = 0;
[FieldOffset(16)]
private Penumbra.GameData.Structs.CustomizeData _customize;
private CustomizeArray _customize;
[FieldOffset(16 + Penumbra.GameData.Structs.CustomizeData.Size)]
[FieldOffset(16 + CustomizeArray.Size)]
private ushort _voice;
[FieldOffset(16 + Penumbra.GameData.Structs.CustomizeData.Size + 2)]
[FieldOffset(16 + CustomizeArray.Size + 2)]
private uint _timeStamp;
[FieldOffset(Size - 41 * 4)]

View file

@ -0,0 +1,282 @@
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Dalamud.Plugin.Services;
using Dalamud.Utility;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
using Lumina.Excel.GeneratedSheets;
using OtterGui.Services;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Structs;
namespace Glamourer.Customization;
public class NpcCustomizeSet : IAsyncDataContainer, IReadOnlyList<NpcData>
{
public string Name
=> nameof(NpcCustomizeSet);
private readonly List<NpcData> _data = [];
public long Time { get; private set; }
public long Memory { get; private set; }
public int TotalCount
=> _data.Count;
public Task Awaiter { get; }
public NpcCustomizeSet(IDataManager data, DictENpc eNpcs, DictBNpc bNpcs, DictBNpcNames bNpcNames)
{
var waitTask = Task.WhenAll(eNpcs.Awaiter, bNpcs.Awaiter, bNpcNames.Awaiter);
Awaiter = waitTask.ContinueWith(_ =>
{
var watch = Stopwatch.StartNew();
var eNpcTask = Task.Run(() => CreateEnpcData(data, eNpcs));
var bNpcTask = Task.Run(() => CreateBnpcData(data, bNpcs, bNpcNames));
FilterAndOrderNpcData(eNpcTask.Result, bNpcTask.Result);
Time = watch.ElapsedMilliseconds;
});
}
private static List<NpcData> CreateEnpcData(IDataManager data, DictENpc eNpcs)
{
var enpcSheet = data.GetExcelSheet<ENpcBase>()!;
var list = new List<NpcData>(eNpcs.Count);
foreach (var (id, name) in eNpcs)
{
var row = enpcSheet.GetRow(id.Id);
if (row == null || name.IsNullOrWhitespace())
continue;
var (valid, customize) = FromEnpcBase(row);
if (!valid)
continue;
var ret = new NpcData
{
Name = name,
Customize = customize,
Id = id,
Kind = ObjectKind.EventNpc,
};
if (row.NpcEquip.Row != 0 && row.NpcEquip.Value is { } equip)
{
ApplyNpcEquip(ref ret, equip);
}
else
{
ret.Set(0, row.ModelHead | (row.DyeHead.Row << 24));
ret.Set(1, row.ModelBody | (row.DyeBody.Row << 24));
ret.Set(2, row.ModelHands | (row.DyeHands.Row << 24));
ret.Set(3, row.ModelLegs | (row.DyeLegs.Row << 24));
ret.Set(4, row.ModelFeet | (row.DyeFeet.Row << 24));
ret.Set(5, row.ModelEars | (row.DyeEars.Row << 24));
ret.Set(6, row.ModelNeck | (row.DyeNeck.Row << 24));
ret.Set(7, row.ModelWrists | (row.DyeWrists.Row << 24));
ret.Set(8, row.ModelRightRing | (row.DyeRightRing.Row << 24));
ret.Set(9, row.ModelLeftRing | (row.DyeLeftRing.Row << 24));
ret.Mainhand = new CharacterWeapon(row.ModelMainHand | ((ulong)row.DyeMainHand.Row << 48));
ret.Offhand = new CharacterWeapon(row.ModelOffHand | ((ulong)row.DyeOffHand.Row << 48));
ret.VisorToggled = row.Visor;
}
list.Add(ret);
}
return list;
}
private static List<NpcData> CreateBnpcData(IDataManager data, DictBNpc bNpcs, DictBNpcNames bNpcNames)
{
var bnpcSheet = data.GetExcelSheet<BNpcBase>()!;
var list = new List<NpcData>((int)bnpcSheet.RowCount);
foreach (var baseRow in bnpcSheet)
{
if (baseRow.ModelChara.Value!.Type != 1)
continue;
var bnpcNameIds = bNpcNames[baseRow.RowId];
if (bnpcNameIds.Count == 0)
continue;
var (valid, customize) = FromBnpcCustomize(baseRow.BNpcCustomize.Value!);
if (!valid)
continue;
var equip = baseRow.NpcEquip.Value!;
var ret = new NpcData
{
Customize = customize,
Id = baseRow.RowId,
Kind = ObjectKind.BattleNpc,
};
ApplyNpcEquip(ref ret, equip);
foreach (var bnpcNameId in bnpcNameIds)
{
if (bNpcs.TryGetValue(bnpcNameId.Id, out var name) && !name.IsNullOrWhitespace())
list.Add(ret with { Name = name });
}
}
return list;
}
private void FilterAndOrderNpcData(List<NpcData> eNpcEquip, List<NpcData> bNpcEquip)
{
_data.Clear();
_data.EnsureCapacity(eNpcEquip.Count + bNpcEquip.Count);
var groups = eNpcEquip.Concat(bNpcEquip).GroupBy(d => d.Name).ToDictionary(g => g.Key, g => g.ToList());
foreach (var (name, duplicates) in groups.OrderBy(kvp => kvp.Key))
{
for (var i = 0; i < duplicates.Count; ++i)
{
var current = duplicates[i];
for (var j = 0; j < i; ++j)
{
if (current.DataEquals(duplicates[j]))
{
duplicates.RemoveAt(i--);
break;
}
}
}
if (duplicates.Count == 1)
{
_data.Add(duplicates[0]);
Memory += 96;
}
else
{
_data.AddRange(duplicates
.Select(duplicate => duplicate with
{
Name = $"{name} ({(duplicate.Kind is ObjectKind.BattleNpc ? 'B' : 'E')}{duplicate.Id})"
}));
Memory += 96 * duplicates.Count + duplicates.Sum(d => d.Name.Length * 2);
}
}
var lastWeird = _data.FindIndex(d => char.IsAsciiLetterOrDigit(d.Name[0]));
if (lastWeird != -1)
{
_data.AddRange(_data.Take(lastWeird));
_data.RemoveRange(0, lastWeird);
}
_data.TrimExcess();
}
private static void ApplyNpcEquip(ref NpcData data, NpcEquip row)
{
data.Set(0, row.ModelHead | (row.DyeHead.Row << 24));
data.Set(1, row.ModelBody | (row.DyeBody.Row << 24));
data.Set(2, row.ModelHands | (row.DyeHands.Row << 24));
data.Set(3, row.ModelLegs | (row.DyeLegs.Row << 24));
data.Set(4, row.ModelFeet | (row.DyeFeet.Row << 24));
data.Set(5, row.ModelEars | (row.DyeEars.Row << 24));
data.Set(6, row.ModelNeck | (row.DyeNeck.Row << 24));
data.Set(7, row.ModelWrists | (row.DyeWrists.Row << 24));
data.Set(8, row.ModelRightRing | (row.DyeRightRing.Row << 24));
data.Set(9, row.ModelLeftRing | (row.DyeLeftRing.Row << 24));
data.Mainhand = new CharacterWeapon(row.ModelMainHand | ((ulong)row.DyeMainHand.Row << 48));
data.Offhand = new CharacterWeapon(row.ModelOffHand | ((ulong)row.DyeOffHand.Row << 48));
data.VisorToggled = row.Visor;
}
private static (bool, Customize) FromBnpcCustomize(BNpcCustomize bnpcCustomize)
{
var customize = new Customize();
customize.Data.Set(0, (byte)bnpcCustomize.Race.Row);
customize.Data.Set(1, bnpcCustomize.Gender);
customize.Data.Set(2, bnpcCustomize.BodyType);
customize.Data.Set(3, bnpcCustomize.Height);
customize.Data.Set(4, (byte)bnpcCustomize.Tribe.Row);
customize.Data.Set(5, bnpcCustomize.Face);
customize.Data.Set(6, bnpcCustomize.HairStyle);
customize.Data.Set(7, bnpcCustomize.HairHighlight);
customize.Data.Set(8, bnpcCustomize.SkinColor);
customize.Data.Set(9, bnpcCustomize.EyeHeterochromia);
customize.Data.Set(10, bnpcCustomize.HairColor);
customize.Data.Set(11, bnpcCustomize.HairHighlightColor);
customize.Data.Set(12, bnpcCustomize.FacialFeature);
customize.Data.Set(13, bnpcCustomize.FacialFeatureColor);
customize.Data.Set(14, bnpcCustomize.Eyebrows);
customize.Data.Set(15, bnpcCustomize.EyeColor);
customize.Data.Set(16, bnpcCustomize.EyeShape);
customize.Data.Set(17, bnpcCustomize.Nose);
customize.Data.Set(18, bnpcCustomize.Jaw);
customize.Data.Set(19, bnpcCustomize.Mouth);
customize.Data.Set(20, bnpcCustomize.LipColor);
customize.Data.Set(21, bnpcCustomize.BustOrTone1);
customize.Data.Set(22, bnpcCustomize.ExtraFeature1);
customize.Data.Set(23, bnpcCustomize.ExtraFeature2OrBust);
customize.Data.Set(24, bnpcCustomize.FacePaint);
customize.Data.Set(25, bnpcCustomize.FacePaintColor);
if (customize.BodyType.Value != 1
|| !CustomizationOptions.Races.Contains(customize.Race)
|| !CustomizationOptions.Clans.Contains(customize.Clan)
|| !CustomizationOptions.Genders.Contains(customize.Gender))
return (false, Customize.Default);
return (true, customize);
}
private static (bool, Customize) FromEnpcBase(ENpcBase enpcBase)
{
if (enpcBase.ModelChara.Value?.Type != 1)
return (false, Customize.Default);
var customize = new Customize();
customize.Data.Set(0, (byte)enpcBase.Race.Row);
customize.Data.Set(1, enpcBase.Gender);
customize.Data.Set(2, enpcBase.BodyType);
customize.Data.Set(3, enpcBase.Height);
customize.Data.Set(4, (byte)enpcBase.Tribe.Row);
customize.Data.Set(5, enpcBase.Face);
customize.Data.Set(6, enpcBase.HairStyle);
customize.Data.Set(7, enpcBase.HairHighlight);
customize.Data.Set(8, enpcBase.SkinColor);
customize.Data.Set(9, enpcBase.EyeHeterochromia);
customize.Data.Set(10, enpcBase.HairColor);
customize.Data.Set(11, enpcBase.HairHighlightColor);
customize.Data.Set(12, enpcBase.FacialFeature);
customize.Data.Set(13, enpcBase.FacialFeatureColor);
customize.Data.Set(14, enpcBase.Eyebrows);
customize.Data.Set(15, enpcBase.EyeColor);
customize.Data.Set(16, enpcBase.EyeShape);
customize.Data.Set(17, enpcBase.Nose);
customize.Data.Set(18, enpcBase.Jaw);
customize.Data.Set(19, enpcBase.Mouth);
customize.Data.Set(20, enpcBase.LipColor);
customize.Data.Set(21, enpcBase.BustOrTone1);
customize.Data.Set(22, enpcBase.ExtraFeature1);
customize.Data.Set(23, enpcBase.ExtraFeature2OrBust);
customize.Data.Set(24, enpcBase.FacePaint);
customize.Data.Set(25, enpcBase.FacePaintColor);
if (customize.BodyType.Value != 1
|| !CustomizationOptions.Races.Contains(customize.Race)
|| !CustomizationOptions.Clans.Contains(customize.Clan)
|| !CustomizationOptions.Genders.Contains(customize.Gender))
return (false, Customize.Default);
return (true, customize);
}
public IEnumerator<NpcData> GetEnumerator()
=> _data.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator();
public int Count
=> _data.Count;
public NpcData this[int index]
=> _data[index];
}

View file

@ -0,0 +1,89 @@
using System;
using System.Text;
using FFXIVClientStructs.FFXIV.Client.Game.Object;
using Penumbra.GameData.Structs;
namespace Glamourer.Customization;
public unsafe struct NpcData
{
public string Name;
public Customize Customize;
private fixed byte _equip[40];
public CharacterWeapon Mainhand;
public CharacterWeapon Offhand;
public NpcId Id;
public bool VisorToggled;
public ObjectKind Kind;
public ReadOnlySpan<CharacterArmor> Equip
{
get
{
fixed (byte* ptr = _equip)
{
return new ReadOnlySpan<CharacterArmor>((CharacterArmor*)ptr, 10);
}
}
}
public string WriteGear()
{
var sb = new StringBuilder(128);
var span = Equip;
for (var i = 0; i < 10; ++i)
{
sb.Append(span[i].Set.Id.ToString("D4"));
sb.Append('-');
sb.Append(span[i].Variant.Id.ToString("D3"));
sb.Append('-');
sb.Append(span[i].Stain.Id.ToString("D3"));
sb.Append(", ");
}
sb.Append(Mainhand.Skeleton.Id.ToString("D4"));
sb.Append('-');
sb.Append(Mainhand.Weapon.Id.ToString("D4"));
sb.Append('-');
sb.Append(Mainhand.Variant.Id.ToString("D3"));
sb.Append('-');
sb.Append(Mainhand.Stain.Id.ToString("D4"));
sb.Append(", ");
sb.Append(Offhand.Skeleton.Id.ToString("D4"));
sb.Append('-');
sb.Append(Offhand.Weapon.Id.ToString("D4"));
sb.Append('-');
sb.Append(Offhand.Variant.Id.ToString("D3"));
sb.Append('-');
sb.Append(Offhand.Stain.Id.ToString("D3"));
return sb.ToString();
}
internal void Set(int idx, uint value)
{
fixed (byte* ptr = _equip)
{
((uint*)ptr)[idx] = value;
}
}
public bool DataEquals(in NpcData other)
{
if (VisorToggled != other.VisorToggled)
return false;
if (!Customize.Equals(other.Customize))
return false;
if (!Mainhand.Equals(other.Mainhand))
return false;
if (!Offhand.Equals(other.Offhand))
return false;
fixed (byte* ptr1 = _equip, ptr2 = other._equip)
{
return new ReadOnlySpan<byte>(ptr1, 40).SequenceEqual(new ReadOnlySpan<byte>(ptr2, 40));
}
}
}

View file

@ -7,10 +7,10 @@ using Glamourer.Automation;
using Glamourer.Designs;
using Glamourer.Events;
using Glamourer.Interop;
using Glamourer.Services;
using Glamourer.State;
using Penumbra.Api.Helpers;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
using Penumbra.String;
namespace Glamourer.Api;
@ -22,11 +22,11 @@ public partial class GlamourerIpc : IDisposable
private readonly StateManager _stateManager;
private readonly ObjectManager _objects;
private readonly ActorService _actors;
private readonly ActorManager _actors;
private readonly DesignConverter _designConverter;
private readonly AutoDesignApplier _autoDesignApplier;
public GlamourerIpc(DalamudPluginInterface pi, StateManager stateManager, ObjectManager objects, ActorService actors,
public GlamourerIpc(DalamudPluginInterface pi, StateManager stateManager, ObjectManager objects, ActorManager actors,
DesignConverter designConverter, StateChanged stateChangedEvent, GPoseService gPose, AutoDesignApplier autoDesignApplier)
{
_stateManager = stateManager;
@ -142,7 +142,7 @@ public partial class GlamourerIpc : IDisposable
private IEnumerable<ActorIdentifier> FindActors(Character? character)
{
var id = _actors.AwaitedService.FromObject(character, true, true, false);
var id = _actors.FromObject(character, true, true, false);
if (!id.IsValid)
yield break;

View file

@ -15,7 +15,7 @@ using Glamourer.Structs;
using Glamourer.Unlocks;
using OtterGui.Classes;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Data;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
@ -28,7 +28,7 @@ public class AutoDesignApplier : IDisposable
private readonly StateManager _state;
private readonly JobService _jobs;
private readonly EquippedGearset _equippedGearset;
private readonly ActorService _actors;
private readonly ActorManager _actors;
private readonly CustomizationService _customizations;
private readonly CustomizeUnlockManager _customizeUnlocks;
private readonly ItemUnlockManager _itemUnlocks;
@ -50,7 +50,7 @@ public class AutoDesignApplier : IDisposable
}
public AutoDesignApplier(Configuration config, AutoDesignManager manager, StateManager state, JobService jobs,
CustomizationService customizations, ActorService actors, ItemUnlockManager itemUnlocks, CustomizeUnlockManager customizeUnlocks,
CustomizationService customizations, ActorManager actors, ItemUnlockManager itemUnlocks, CustomizeUnlockManager customizeUnlocks,
AutomationChanged @event, ObjectManager objects, WeaponLoading weapons, HumanModelList humans, IClientState clientState,
EquippedGearset equippedGearset)
{
@ -87,7 +87,7 @@ public class AutoDesignApplier : IDisposable
if (_jobChangeState == null || !_config.EnableAutoDesigns)
return;
var id = actor.GetIdentifier(_actors.AwaitedService);
var id = actor.GetIdentifier(_actors);
if (id == _jobChangeState.Identifier)
{
var current = _jobChangeState.BaseData.Item(slot);
@ -161,7 +161,7 @@ public class AutoDesignApplier : IDisposable
{
foreach (var actor in data.Objects)
{
var specificId = actor.GetIdentifier(_actors.AwaitedService);
var specificId = actor.GetIdentifier(_actors);
if (_state.GetOrCreate(specificId, actor, out var state))
{
Reduce(actor, state, newSet, false, false);
@ -203,7 +203,7 @@ public class AutoDesignApplier : IDisposable
private void OnJobChange(Actor actor, Job oldJob, Job newJob)
{
if (!_config.EnableAutoDesigns || !actor.Identifier(_actors.AwaitedService, out var id))
if (!_config.EnableAutoDesigns || !actor.Identifier(_actors, out var id))
return;
if (!GetPlayerSet(id, out var set))
@ -312,13 +312,13 @@ public class AutoDesignApplier : IDisposable
if (_manager.EnabledSets.TryGetValue(identifier, out set))
return true;
identifier = _actors.AwaitedService.CreatePlayer(identifier.PlayerName, ushort.MaxValue);
identifier = _actors.CreatePlayer(identifier.PlayerName, ushort.MaxValue);
return _manager.EnabledSets.TryGetValue(identifier, out set);
case IdentifierType.Retainer:
case IdentifierType.Npc:
return _manager.EnabledSets.TryGetValue(identifier, out set);
case IdentifierType.Owned:
identifier = _actors.AwaitedService.CreateNpc(identifier.Kind, identifier.DataId);
identifier = _actors.CreateNpc(identifier.Kind, identifier.DataId);
return _manager.EnabledSets.TryGetValue(identifier, out set);
default:
set = null;
@ -470,7 +470,7 @@ public class AutoDesignApplier : IDisposable
totalCustomizeFlags |= CustomizeFlag.Face;
}
var set = _customizations.AwaitedService.GetList(state.ModelData.Customize.Clan, state.ModelData.Customize.Gender);
var set = _customizations.Service.GetList(state.ModelData.Customize.Clan, state.ModelData.Customize.Gender);
var face = state.ModelData.Customize.Face;
foreach (var index in Enum.GetValues<CustomizeIndex>())
{

View file

@ -17,6 +17,8 @@ using OtterGui;
using OtterGui.Classes;
using OtterGui.Filesystem;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
namespace Glamourer.Automation;
@ -28,17 +30,17 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
private readonly JobService _jobs;
private readonly DesignManager _designs;
private readonly ActorService _actors;
private readonly ActorManager _actors;
private readonly AutomationChanged _event;
private readonly DesignChanged _designEvent;
private readonly List<AutoDesignSet> _data = new();
private readonly Dictionary<ActorIdentifier, AutoDesignSet> _enabled = new();
private readonly List<AutoDesignSet> _data = [];
private readonly Dictionary<ActorIdentifier, AutoDesignSet> _enabled = [];
public IReadOnlyDictionary<ActorIdentifier, AutoDesignSet> EnabledSets
=> _enabled;
public AutoDesignManager(JobService jobs, ActorService actors, SaveService saveService, DesignManager designs, AutomationChanged @event,
public AutoDesignManager(JobService jobs, ActorManager actors, SaveService saveService, DesignManager designs, AutomationChanged @event,
FixedDesignMigrator migrator, DesignFileSystem fileSystem, DesignChanged designEvent)
{
_jobs = jobs;
@ -419,7 +421,7 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
continue;
}
var id = _actors.AwaitedService.FromJson(obj["Identifier"] as JObject);
var id = _actors.FromJson(obj["Identifier"] as JObject);
if (!IdentifierValid(id, out var group))
{
Glamourer.Messager.NotificationMessage("Skipped loading Automation Set: Invalid Identifier.", NotificationType.Warning);
@ -562,9 +564,9 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
var name = manager.Data.ToName(identifier.Kind, identifier.DataId);
var table = identifier.Kind switch
{
ObjectKind.BattleNpc => manager.Data.BNpcs,
ObjectKind.BattleNpc => (IReadOnlyDictionary<NpcId, string>)manager.Data.BNpcs,
ObjectKind.EventNpc => manager.Data.ENpcs,
_ => new Dictionary<uint, string>(),
_ => new Dictionary<NpcId, string>(),
};
return table.Where(kvp => kvp.Value == name)
.Select(kvp => manager.CreateIndividualUnchecked(identifier.Type, identifier.PlayerName, identifier.HomeWorld.Id,
@ -580,12 +582,12 @@ public class AutoDesignManager : ISavable, IReadOnlyList<AutoDesignSet>, IDispos
},
IdentifierType.Retainer => new[]
{
_actors.AwaitedService.CreateRetainer(identifier.PlayerName,
_actors.CreateRetainer(identifier.PlayerName,
identifier.Retainer == ActorIdentifier.RetainerType.Mannequin
? ActorIdentifier.RetainerType.Mannequin
: ActorIdentifier.RetainerType.Bell).CreatePermanent(),
},
IdentifierType.Npc => CreateNpcs(_actors.AwaitedService, identifier),
IdentifierType.Npc => CreateNpcs(_actors, identifier),
_ => Array.Empty<ActorIdentifier>(),
};
}

View file

@ -20,7 +20,7 @@ public class FixedDesignMigrator
public FixedDesignMigrator(JobService jobs)
=> _jobs = jobs;
public void ConsumeMigratedData(ActorService actors, DesignFileSystem designFileSystem, AutoDesignManager autoManager)
public void ConsumeMigratedData(ActorManager actors, DesignFileSystem designFileSystem, AutoDesignManager autoManager)
{
if (_migratedData == null)
return;
@ -35,15 +35,15 @@ public class FixedDesignMigrator
var id = ActorIdentifier.Invalid;
if (ByteString.FromString(data.Name, out var byteString, false))
{
id = actors.AwaitedService.CreatePlayer(byteString, ushort.MaxValue);
id = actors.CreatePlayer(byteString, ushort.MaxValue);
if (!id.IsValid)
id = actors.AwaitedService.CreateRetainer(byteString, ActorIdentifier.RetainerType.Both);
id = actors.CreateRetainer(byteString, ActorIdentifier.RetainerType.Both);
}
if (!id.IsValid)
{
byteString = ByteString.FromSpanUnsafe("Mig Ration"u8, true, false, true);
id = actors.AwaitedService.CreatePlayer(byteString, actors.AwaitedService.Data.Worlds.First().Key);
id = actors.CreatePlayer(byteString, actors.Data.Worlds.First().Key);
if (!id.IsValid)
{
Glamourer.Messager.NotificationMessage($"Could not migrate fixed design {data.Name}.", NotificationType.Error);

View file

@ -4,11 +4,11 @@ using Glamourer.Services;
using Glamourer.Structs;
using Newtonsoft.Json.Linq;
using OtterGui.Classes;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using System;
using System.Linq;
using Penumbra.GameData.DataContainers;
namespace Glamourer.Designs;
@ -91,7 +91,7 @@ public class DesignBase
return false;
_designData.Customize.Load(customize);
CustomizationSet = customizationService.AwaitedService.GetList(customize.Clan, customize.Gender);
CustomizationSet = customizationService.Service.GetList(customize.Clan, customize.Gender);
return true;
}
@ -243,8 +243,8 @@ public class DesignBase
private CustomizationSet SetCustomizationSet(CustomizationService customize)
=> !_designData.IsHuman
? customize.AwaitedService.GetList(SubRace.Midlander, Gender.Male)
: customize.AwaitedService.GetList(_designData.Customize.Clan, _designData.Customize.Gender);
? customize.Service.GetList(SubRace.Midlander, Gender.Male)
: customize.Service.GetList(_designData.Customize.Clan, _designData.Customize.Gender);
#endregion

View file

@ -3,7 +3,7 @@ using Glamourer.Customization;
using Glamourer.Services;
using Glamourer.Structs;
using OtterGui;
using Penumbra.GameData.Data;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
@ -121,9 +121,9 @@ public class DesignBase64Migration
data.SetStain(slot, mdl.Stain);
}
var main = cur[0].Set.Id == 0
var main = cur[0].X.Id == 0
? items.DefaultSword
: items.Identify(EquipSlot.MainHand, cur[0].Set, cur[0].Type, cur[0].Variant);
: items.Identify(EquipSlot.MainHand, cur[0].X, cur[0].Y, cur[0].Variant);
if (!main.Valid)
{
Glamourer.Log.Warning("Base64 string invalid, weapon could not be identified.");
@ -135,10 +135,10 @@ public class DesignBase64Migration
EquipItem off;
// Fist weapon hack
if (main.ModelId.Id is > 1600 and < 1651 && cur[1].Variant == 0)
if (main.PrimaryId.Id is > 1600 and < 1651 && cur[1].Variant == 0)
{
off = items.Identify(EquipSlot.OffHand, (SetId)(main.ModelId.Id + 50), main.WeaponType, main.Variant, main.Type);
var gauntlet = items.Identify(EquipSlot.Hands, cur[1].Set, (Variant)cur[1].Type.Id);
off = items.Identify(EquipSlot.OffHand, (PrimaryId)(main.PrimaryId.Id + 50), main.SecondaryId, main.Variant, main.Type);
var gauntlet = items.Identify(EquipSlot.Hands, cur[1].X, (Variant)cur[1].Y.Id);
if (gauntlet.Valid)
{
data.SetItem(EquipSlot.Hands, gauntlet);
@ -147,9 +147,9 @@ public class DesignBase64Migration
}
else
{
off = cur[0].Set.Id == 0
off = cur[0].X.Id == 0
? ItemManager.NothingItem(FullEquipType.Shield)
: items.Identify(EquipSlot.OffHand, cur[1].Set, cur[1].Type, cur[1].Variant, main.Type);
: items.Identify(EquipSlot.OffHand, cur[1].X, cur[1].Y, cur[1].Variant, main.Type);
}
if (main.Type.ValidOffhand() != FullEquipType.Unknown && !off.Valid)

View file

@ -9,7 +9,7 @@ using Glamourer.Structs;
using Glamourer.Utility;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Penumbra.GameData.Data;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
@ -165,7 +165,7 @@ public class DesignConverter(ItemManager _items, DesignManager _designs, Customi
yield return (slot, item, armor.Stain);
}
var mh = _items.Identify(EquipSlot.MainHand, mainhand.Set, mainhand.Type, mainhand.Variant, FullEquipType.Unknown);
var mh = _items.Identify(EquipSlot.MainHand, mainhand.X, mainhand.Y, mainhand.Variant, FullEquipType.Unknown);
if (!mh.Valid)
{
Glamourer.Log.Warning($"Appearance data {mainhand} for mainhand weapon invalid, item could not be identified.");
@ -174,7 +174,7 @@ public class DesignConverter(ItemManager _items, DesignManager _designs, Customi
yield return (EquipSlot.MainHand, mh, mainhand.Stain);
var oh = _items.Identify(EquipSlot.OffHand, offhand.Set, offhand.Type, offhand.Variant, mh.Type);
var oh = _items.Identify(EquipSlot.OffHand, offhand.X, offhand.Y, offhand.Variant, mh.Type);
if (!oh.Valid)
{
Glamourer.Log.Warning($"Appearance data {offhand} for offhand weapon invalid, item could not be identified.");

View file

@ -7,7 +7,6 @@ using OtterGui.Classes;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Penumbra.String.Functions;
using CustomizeData = Penumbra.GameData.Structs.CustomizeData;
namespace Glamourer.Designs;
@ -31,8 +30,8 @@ public unsafe struct DesignData
public Customize Customize = Customize.Default;
public uint ModelId;
public CrestFlag CrestVisibility;
private WeaponType _secondaryMainhand;
private WeaponType _secondaryOffhand;
private SecondaryId _secondaryMainhand;
private SecondaryId _secondaryOffhand;
private FullEquipType _typeMainhand;
private FullEquipType _typeOffhand;
private byte _states;
@ -75,18 +74,18 @@ public unsafe struct DesignData
=> slot.ToIndex() switch
{
// @formatter:off
0 => EquipItem.FromIds(_itemIds[ 0], _iconIds[ 0], (SetId)(_equipmentBytes[ 0] | (_equipmentBytes[ 1] << 8)), (WeaponType)0, _equipmentBytes[ 2], FullEquipType.Head, name: _nameHead ),
1 => EquipItem.FromIds(_itemIds[ 1], _iconIds[ 1], (SetId)(_equipmentBytes[ 4] | (_equipmentBytes[ 5] << 8)), (WeaponType)0, _equipmentBytes[ 6], FullEquipType.Body, name: _nameBody ),
2 => EquipItem.FromIds(_itemIds[ 2], _iconIds[ 2], (SetId)(_equipmentBytes[ 8] | (_equipmentBytes[ 9] << 8)), (WeaponType)0, _equipmentBytes[10], FullEquipType.Hands, name: _nameHands ),
3 => EquipItem.FromIds(_itemIds[ 3], _iconIds[ 3], (SetId)(_equipmentBytes[12] | (_equipmentBytes[13] << 8)), (WeaponType)0, _equipmentBytes[14], FullEquipType.Legs, name: _nameLegs ),
4 => EquipItem.FromIds(_itemIds[ 4], _iconIds[ 4], (SetId)(_equipmentBytes[16] | (_equipmentBytes[17] << 8)), (WeaponType)0, _equipmentBytes[18], FullEquipType.Feet, name: _nameFeet ),
5 => EquipItem.FromIds(_itemIds[ 5], _iconIds[ 5], (SetId)(_equipmentBytes[20] | (_equipmentBytes[21] << 8)), (WeaponType)0, _equipmentBytes[22], FullEquipType.Ears, name: _nameEars ),
6 => EquipItem.FromIds(_itemIds[ 6], _iconIds[ 6], (SetId)(_equipmentBytes[24] | (_equipmentBytes[25] << 8)), (WeaponType)0, _equipmentBytes[26], FullEquipType.Neck, name: _nameNeck ),
7 => EquipItem.FromIds(_itemIds[ 7], _iconIds[ 7], (SetId)(_equipmentBytes[28] | (_equipmentBytes[29] << 8)), (WeaponType)0, _equipmentBytes[30], FullEquipType.Wrists, name: _nameWrists ),
8 => EquipItem.FromIds(_itemIds[ 8], _iconIds[ 8], (SetId)(_equipmentBytes[32] | (_equipmentBytes[33] << 8)), (WeaponType)0, _equipmentBytes[34], FullEquipType.Finger, name: _nameRFinger ),
9 => EquipItem.FromIds(_itemIds[ 9], _iconIds[ 9], (SetId)(_equipmentBytes[36] | (_equipmentBytes[37] << 8)), (WeaponType)0, _equipmentBytes[38], FullEquipType.Finger, name: _nameLFinger ),
10 => EquipItem.FromIds(_itemIds[10], _iconIds[10], (SetId)(_equipmentBytes[40] | (_equipmentBytes[41] << 8)), _secondaryMainhand, _equipmentBytes[42], _typeMainhand, name: _nameMainhand),
11 => EquipItem.FromIds(_itemIds[11], _iconIds[11], (SetId)(_equipmentBytes[44] | (_equipmentBytes[45] << 8)), _secondaryOffhand, _equipmentBytes[46], _typeOffhand, name: _nameOffhand ),
0 => EquipItem.FromIds((ItemId)_itemIds[ 0], (IconId)_iconIds[ 0], (PrimaryId)(_equipmentBytes[ 0] | (_equipmentBytes[ 1] << 8)), (SecondaryId)0, (Variant)_equipmentBytes[ 2], FullEquipType.Head, name: _nameHead ),
1 => EquipItem.FromIds((ItemId)_itemIds[ 1], (IconId)_iconIds[ 1], (PrimaryId)(_equipmentBytes[ 4] | (_equipmentBytes[ 5] << 8)), (SecondaryId)0, (Variant)_equipmentBytes[ 6], FullEquipType.Body, name: _nameBody ),
2 => EquipItem.FromIds((ItemId)_itemIds[ 2], (IconId)_iconIds[ 2], (PrimaryId)(_equipmentBytes[ 8] | (_equipmentBytes[ 9] << 8)), (SecondaryId)0, (Variant)_equipmentBytes[10], FullEquipType.Hands, name: _nameHands ),
3 => EquipItem.FromIds((ItemId)_itemIds[ 3], (IconId)_iconIds[ 3], (PrimaryId)(_equipmentBytes[12] | (_equipmentBytes[13] << 8)), (SecondaryId)0, (Variant)_equipmentBytes[14], FullEquipType.Legs, name: _nameLegs ),
4 => EquipItem.FromIds((ItemId)_itemIds[ 4], (IconId)_iconIds[ 4], (PrimaryId)(_equipmentBytes[16] | (_equipmentBytes[17] << 8)), (SecondaryId)0, (Variant)_equipmentBytes[18], FullEquipType.Feet, name: _nameFeet ),
5 => EquipItem.FromIds((ItemId)_itemIds[ 5], (IconId)_iconIds[ 5], (PrimaryId)(_equipmentBytes[20] | (_equipmentBytes[21] << 8)), (SecondaryId)0, (Variant)_equipmentBytes[22], FullEquipType.Ears, name: _nameEars ),
6 => EquipItem.FromIds((ItemId)_itemIds[ 6], (IconId)_iconIds[ 6], (PrimaryId)(_equipmentBytes[24] | (_equipmentBytes[25] << 8)), (SecondaryId)0, (Variant)_equipmentBytes[26], FullEquipType.Neck, name: _nameNeck ),
7 => EquipItem.FromIds((ItemId)_itemIds[ 7], (IconId)_iconIds[ 7], (PrimaryId)(_equipmentBytes[28] | (_equipmentBytes[29] << 8)), (SecondaryId)0, (Variant)_equipmentBytes[30], FullEquipType.Wrists, name: _nameWrists ),
8 => EquipItem.FromIds((ItemId)_itemIds[ 8], (IconId)_iconIds[ 8], (PrimaryId)(_equipmentBytes[32] | (_equipmentBytes[33] << 8)), (SecondaryId)0, (Variant)_equipmentBytes[34], FullEquipType.Finger, name: _nameRFinger ),
9 => EquipItem.FromIds((ItemId)_itemIds[ 9], (IconId)_iconIds[ 9], (PrimaryId)(_equipmentBytes[36] | (_equipmentBytes[37] << 8)), (SecondaryId)0, (Variant)_equipmentBytes[38], FullEquipType.Finger, name: _nameLFinger ),
10 => EquipItem.FromIds((ItemId)_itemIds[10], (IconId)_iconIds[10], (PrimaryId)(_equipmentBytes[40] | (_equipmentBytes[41] << 8)), _secondaryMainhand, (Variant)_equipmentBytes[42], _typeMainhand, name: _nameMainhand),
11 => EquipItem.FromIds((ItemId)_itemIds[11], (IconId)_iconIds[11], (PrimaryId)(_equipmentBytes[44] | (_equipmentBytes[45] << 8)), _secondaryOffhand, (Variant)_equipmentBytes[46], _typeOffhand, name: _nameOffhand ),
_ => new EquipItem(),
// @formatter:on
};
@ -129,8 +128,8 @@ public unsafe struct DesignData
_itemIds[index] = item.ItemId.Id;
_iconIds[index] = item.IconId.Id;
_equipmentBytes[4 * index + 0] = (byte)item.ModelId.Id;
_equipmentBytes[4 * index + 1] = (byte)(item.ModelId.Id >> 8);
_equipmentBytes[4 * index + 0] = (byte)item.PrimaryId.Id;
_equipmentBytes[4 * index + 1] = (byte)(item.PrimaryId.Id >> 8);
_equipmentBytes[4 * index + 2] = item.Variant.Id;
switch (index)
{
@ -148,12 +147,12 @@ public unsafe struct DesignData
// @formatter:on
case 10:
_nameMainhand = item.Name;
_secondaryMainhand = item.WeaponType;
_secondaryMainhand = item.SecondaryId;
_typeMainhand = item.Type;
return true;
case 11:
_nameOffhand = item.Name;
_secondaryOffhand = item.WeaponType;
_secondaryOffhand = item.SecondaryId;
_typeOffhand = item.Type;
return true;
}
@ -294,7 +293,7 @@ public unsafe struct DesignData
public readonly byte[] GetCustomizeBytes()
{
var ret = new byte[CustomizeData.Size];
var ret = new byte[CustomizeArray.Size];
fixed (byte* retPtr = ret, inPtr = Customize.Data.Data)
{
MemoryUtility.MemCpyUnchecked(retPtr, inPtr, ret.Length);

View file

@ -12,7 +12,7 @@ using Glamourer.Structs;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using OtterGui;
using Penumbra.GameData.Data;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;

View file

@ -8,6 +8,7 @@ using Glamourer.State;
using Microsoft.Extensions.DependencyInjection;
using OtterGui.Classes;
using OtterGui.Log;
using OtterGui.Services;
namespace Glamourer;
@ -25,21 +26,21 @@ public class Glamourer : IDalamudPlugin
public static readonly Logger Log = new();
public static MessageService Messager { get; private set; } = null!;
private readonly ServiceProvider _services;
private readonly ServiceManager _services;
public Glamourer(DalamudPluginInterface pluginInterface)
{
try
{
_services = ServiceManager.CreateProvider(pluginInterface, Log);
Messager = _services.GetRequiredService<MessageService>();
_services.GetRequiredService<VisorService>();
_services.GetRequiredService<WeaponService>();
_services.GetRequiredService<ScalingService>();
_services.GetRequiredService<StateListener>(); // Initialize State Listener.
_services.GetRequiredService<GlamourerWindowSystem>(); // initialize ui.
_services.GetRequiredService<CommandService>(); // initialize commands.
_services.GetRequiredService<GlamourerIpc>(); // initialize IPC.
_services = ServiceManagerA.CreateProvider(pluginInterface, Log);
Messager = _services.GetService<MessageService>();
_services.GetService<VisorService>();
_services.GetService<WeaponService>();
_services.GetService<ScalingService>();
_services.GetService<StateListener>(); // Initialize State Listener.
_services.GetService<GlamourerWindowSystem>(); // initialize ui.
_services.GetService<CommandService>(); // initialize commands.
_services.GetService<GlamourerIpc>(); // initialize IPC.
Log.Information($"Glamourer v{Version} loaded successfully.");
}
catch

View file

@ -1,6 +1,5 @@
using System;
using System.Numerics;
using Dalamud.Interface.Utility;
using Glamourer.Customization;
using ImGuiNET;
using OtterGui;
@ -28,7 +27,7 @@ public partial class CustomizationDrawer
npc = true;
}
var icon = _service.AwaitedService.GetIcon(custom!.Value.IconId);
var icon = _service.Service.GetIcon(custom!.Value.IconId);
using (var disabled = ImRaii.Disabled(_locked || _currentIndex is CustomizeIndex.Face && _lockedRedraw))
{
if (ImGui.ImageButton(icon.ImGuiHandle, _iconSize))
@ -68,7 +67,7 @@ public partial class CustomizationDrawer
for (var i = 0; i < _currentCount; ++i)
{
var custom = _set.Data(_currentIndex, i, _customize.Face);
var icon = _service.AwaitedService.GetIcon(custom.IconId);
var icon = _service.Service.GetIcon(custom.IconId);
using (var _ = ImRaii.Group())
{
using var frameColor = ImRaii.PushColor(ImGuiCol.Button, Colors.SelectedRed, current == i);
@ -179,8 +178,8 @@ public partial class CustomizationDrawer
var enabled = _customize.Get(featureIdx) != CustomizeValue.Zero;
var feature = _set.Data(featureIdx, 0, face);
var icon = featureIdx == CustomizeIndex.LegacyTattoo
? _legacyTattoo ?? _service.AwaitedService.GetIcon(feature.IconId)
: _service.AwaitedService.GetIcon(feature.IconId);
? _legacyTattoo ?? _service.Service.GetIcon(feature.IconId)
: _service.Service.GetIcon(feature.IconId);
if (ImGui.ImageButton(icon.ImGuiHandle, _iconSize, Vector2.Zero, Vector2.One, (int)ImGui.GetStyle().FramePadding.X,
Vector4.Zero, enabled ? Vector4.One : _redTint))
{

View file

@ -10,7 +10,7 @@ using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Enums;
using CustomizeData = Penumbra.GameData.Structs.CustomizeData;
using Penumbra.GameData.Structs;
namespace Glamourer.Gui.Customization;
@ -120,7 +120,7 @@ public partial class CustomizationDrawer(DalamudPluginInterface pi, Customizatio
return DrawArtisan();
DrawRaceGenderSelector();
_set = _service.AwaitedService.GetList(_customize.Clan, _customize.Gender);
_set = _service.Service.GetList(_customize.Clan, _customize.Gender);
foreach (var id in _set.Order[CharaMakeParams.MenuType.Percentage])
PercentageSelector(id);
@ -153,7 +153,7 @@ public partial class CustomizationDrawer(DalamudPluginInterface pi, Customizatio
private unsafe bool DrawArtisan()
{
for (var i = 0; i < CustomizeData.Size; ++i)
for (var i = 0; i < CustomizeArray.Size; ++i)
{
using var id = ImRaii.PushId(i);
int value = _customize.Data.Data[i];

View file

@ -12,7 +12,7 @@ using Glamourer.Unlocks;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Data;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
@ -24,7 +24,7 @@ public class EquipmentDrawer
private readonly ItemManager _items;
private readonly GlamourerColorCombo _stainCombo;
private readonly StainData _stainData;
private readonly DictStains _stainData;
private readonly ItemCombo[] _itemCombo;
private readonly Dictionary<FullEquipType, WeaponCombo> _weaponCombo;
private readonly CodeService _codes;
@ -66,8 +66,8 @@ public class EquipmentDrawer
_iconSize = new Vector2(2 * ImGui.GetFrameHeight() + ImGui.GetStyle().ItemSpacing.Y);
_comboLength = DefaultWidth * ImGuiHelpers.GlobalScale;
if (_requiredComboWidthUnscaled == 0)
_requiredComboWidthUnscaled = _items.ItemService.AwaitedService.AllItems(true)
.Concat(_items.ItemService.AwaitedService.AllItems(false))
_requiredComboWidthUnscaled = _items.ItemData.AllItems(true)
.Concat(_items.ItemData.AllItems(false))
.Max(i => ImGui.CalcTextSize($"{i.Item2.Name} ({i.Item2.ModelString})").X)
/ ImGuiHelpers.GlobalScale;
@ -102,7 +102,7 @@ public class EquipmentDrawer
public void DrawWeapons(EquipDrawData mainhand, EquipDrawData offhand, bool allWeapons)
{
if (mainhand.CurrentItem.ModelId.Id == 0)
if (mainhand.CurrentItem.PrimaryId.Id == 0)
return;
if (_config.HideApplyCheckmarks)
@ -202,24 +202,24 @@ public class EquipmentDrawer
void DrawWeapon(in EquipDrawData current)
{
int setId = current.CurrentItem.ModelId.Id;
int type = current.CurrentItem.WeaponType.Id;
int setId = current.CurrentItem.PrimaryId.Id;
int type = current.CurrentItem.SecondaryId.Id;
int variant = current.CurrentItem.Variant.Id;
ImGui.SetNextItemWidth(80 * ImGuiHelpers.GlobalScale);
if (ImGui.InputInt("##setId", ref setId, 0, 0))
{
var newSetId = (SetId)Math.Clamp(setId, 0, ushort.MaxValue);
if (newSetId.Id != current.CurrentItem.ModelId.Id)
current.ItemSetter(_items.Identify(current.Slot, newSetId, current.CurrentItem.WeaponType, current.CurrentItem.Variant));
var newSetId = (PrimaryId)Math.Clamp(setId, 0, ushort.MaxValue);
if (newSetId.Id != current.CurrentItem.PrimaryId.Id)
current.ItemSetter(_items.Identify(current.Slot, newSetId, current.CurrentItem.SecondaryId, current.CurrentItem.Variant));
}
ImGui.SameLine();
ImGui.SetNextItemWidth(80 * ImGuiHelpers.GlobalScale);
if (ImGui.InputInt("##type", ref type, 0, 0))
{
var newType = (WeaponType)Math.Clamp(type, 0, ushort.MaxValue);
if (newType.Id != current.CurrentItem.WeaponType.Id)
current.ItemSetter(_items.Identify(current.Slot, current.CurrentItem.ModelId, newType, current.CurrentItem.Variant));
var newType = (SecondaryId)Math.Clamp(type, 0, ushort.MaxValue);
if (newType.Id != current.CurrentItem.SecondaryId.Id)
current.ItemSetter(_items.Identify(current.Slot, current.CurrentItem.PrimaryId, newType, current.CurrentItem.Variant));
}
ImGui.SameLine();
@ -228,7 +228,8 @@ public class EquipmentDrawer
{
var newVariant = (Variant)Math.Clamp(variant, 0, byte.MaxValue);
if (newVariant.Id != current.CurrentItem.Variant.Id)
current.ItemSetter(_items.Identify(current.Slot, current.CurrentItem.ModelId, current.CurrentItem.WeaponType, newVariant));
current.ItemSetter(_items.Identify(current.Slot, current.CurrentItem.PrimaryId, current.CurrentItem.SecondaryId,
newVariant));
}
}
}
@ -249,13 +250,13 @@ public class EquipmentDrawer
/// <summary> Draw an input for armor that can set arbitrary values instead of choosing items. </summary>
private void DrawArmorArtisan(EquipDrawData data)
{
int setId = data.CurrentItem.ModelId.Id;
int setId = data.CurrentItem.PrimaryId.Id;
int variant = data.CurrentItem.Variant.Id;
ImGui.SetNextItemWidth(80 * ImGuiHelpers.GlobalScale);
if (ImGui.InputInt("##setId", ref setId, 0, 0))
{
var newSetId = (SetId)Math.Clamp(setId, 0, ushort.MaxValue);
if (newSetId.Id != data.CurrentItem.ModelId.Id)
var newSetId = (PrimaryId)Math.Clamp(setId, 0, ushort.MaxValue);
if (newSetId.Id != data.CurrentItem.PrimaryId.Id)
data.ItemSetter(_items.Identify(data.Slot, newSetId, data.CurrentItem.Variant));
}
@ -265,7 +266,7 @@ public class EquipmentDrawer
{
var newVariant = (byte)Math.Clamp(variant, 0, byte.MaxValue);
if (newVariant != data.CurrentItem.Variant)
data.ItemSetter(_items.Identify(data.Slot, data.CurrentItem.ModelId, newVariant));
data.ItemSetter(_items.Identify(data.Slot, data.CurrentItem.PrimaryId, newVariant));
}
}
@ -454,7 +455,7 @@ public class EquipmentDrawer
else if (combo.CustomVariant.Id > 0)
data.ItemSetter(_items.Identify(data.Slot, combo.CustomSetId, combo.CustomVariant));
if (!data.Locked && data.CurrentItem.ModelId.Id != 0)
if (!data.Locked && data.CurrentItem.PrimaryId.Id != 0)
{
if (clear || ImGui.IsItemClicked(ImGuiMouseButton.Right))
data.ItemSetter(ItemManager.NothingItem(data.Slot));

View file

@ -8,12 +8,12 @@ using Dalamud.Interface.Utility.Raii;
using Glamourer.Unlocks;
using ImGuiNET;
using OtterGui.Widgets;
using Penumbra.GameData.Data;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Structs;
namespace Glamourer.Gui.Equipment;
public sealed class GlamourerColorCombo(float _comboWidth, StainData _stains, FavoriteManager _favorites)
public sealed class GlamourerColorCombo(float _comboWidth, DictStains _stains, FavoriteManager _favorites)
: FilterComboColors(_comboWidth, CreateFunc(_stains, _favorites), Glamourer.Log)
{
protected override bool DrawSelectable(int globalIdx, bool selected)
@ -40,8 +40,9 @@ public sealed class GlamourerColorCombo(float _comboWidth, StainData _stains, Fa
return base.DrawSelectable(globalIdx, selected);
}
private static Func<IReadOnlyList<KeyValuePair<byte, (string Name, uint Color, bool Gloss)>>> CreateFunc(StainData stains,
private static Func<IReadOnlyList<KeyValuePair<byte, (string Name, uint Color, bool Gloss)>>> CreateFunc(DictStains stains,
FavoriteManager favorites)
=> () => stains.Data.Select(kvp => (kvp, favorites.Contains((StainId)kvp.Key))).OrderBy(p => !p.Item2).Select(p => p.kvp)
.Prepend(new KeyValuePair<byte, (string Name, uint Dye, bool Gloss)>(0, ("None", 0, false))).ToList();
=> () => stains.Select(kvp => (kvp, favorites.Contains(kvp.Key))).OrderBy(p => !p.Item2).Select(p => p.kvp)
.Prepend(new KeyValuePair<StainId, Stain>(Stain.None.RowIndex, Stain.None)).Select(kvp
=> new KeyValuePair<byte, (string, uint, bool)>(kvp.Key.Id, (kvp.Value.Name, kvp.Value.RgbaColor, kvp.Value.Gloss))).ToList();
}

View file

@ -23,8 +23,8 @@ public sealed class ItemCombo : FilterComboCache<EquipItem>
private ItemId _currentItem;
private float _innerWidth;
public SetId CustomSetId { get; private set; }
public Variant CustomVariant { get; private set; }
public PrimaryId CustomSetId { get; private set; }
public Variant CustomVariant { get; private set; }
public ItemCombo(IDataManager gameData, ItemManager items, EquipSlot slot, Logger log, FavoriteManager favorites)
: base(() => GetItems(favorites, items, slot), log)
@ -83,7 +83,7 @@ public sealed class ItemCombo : FilterComboCache<EquipItem>
}
protected override bool IsVisible(int globalIndex, LowerString filter)
=> base.IsVisible(globalIndex, filter) || filter.IsContained(Items[globalIndex].ModelId.Id.ToString());
=> base.IsVisible(globalIndex, filter) || filter.IsContained(Items[globalIndex].PrimaryId.Id.ToString());
protected override string ToString(EquipItem obj)
=> obj.Name;
@ -111,7 +111,7 @@ public sealed class ItemCombo : FilterComboCache<EquipItem>
private static IReadOnlyList<EquipItem> GetItems(FavoriteManager favorites, ItemManager items, EquipSlot slot)
{
var nothing = ItemManager.NothingItem(slot);
if (!items.ItemService.AwaitedService.TryGetValue(slot.ToEquipType(), out var list))
if (!items.ItemData.ByType.TryGetValue(slot.ToEquipType(), out var list))
return new[]
{
nothing,

View file

@ -60,12 +60,12 @@ public sealed class WeaponCombo : FilterComboCache<EquipItem>
var ret = ImGui.Selectable(name, selected);
ImGui.SameLine();
using var color = ImRaii.PushColor(ImGuiCol.Text, 0xFF808080);
ImGuiUtil.RightAlign($"({obj.ModelId.Id}-{obj.WeaponType.Id}-{obj.Variant})");
ImGuiUtil.RightAlign($"({obj.PrimaryId.Id}-{obj.SecondaryId.Id}-{obj.Variant})");
return ret;
}
protected override bool IsVisible(int globalIndex, LowerString filter)
=> base.IsVisible(globalIndex, filter) || filter.IsContained(Items[globalIndex].ModelId.Id.ToString());
=> base.IsVisible(globalIndex, filter) || filter.IsContained(Items[globalIndex].PrimaryId.Id.ToString());
protected override string ToString(EquipItem obj)
=> obj.Name;
@ -80,14 +80,14 @@ public sealed class WeaponCombo : FilterComboCache<EquipItem>
var enumerable = Array.Empty<EquipItem>().AsEnumerable();
foreach (var t in Enum.GetValues<FullEquipType>().Where(e => e.ToSlot() is EquipSlot.MainHand))
{
if (items.ItemService.AwaitedService.TryGetValue(t, out var l))
if (items.ItemData.ByType.TryGetValue(t, out var l))
enumerable = enumerable.Concat(l);
}
return enumerable.OrderBy(e => e.Name).ToList();
}
if (!items.ItemService.AwaitedService.TryGetValue(type, out var list))
if (!items.ItemData.ByType.TryGetValue(type, out var list))
return Array.Empty<EquipItem>();
if (type.AllowsNothing())

View file

@ -8,6 +8,7 @@ using Glamourer.Events;
using Glamourer.Gui.Tabs;
using Glamourer.Gui.Tabs.ActorTab;
using Glamourer.Gui.Tabs.AutomationTab;
using Glamourer.Gui.Tabs.DebugTab;
using Glamourer.Gui.Tabs.DesignTab;
using Glamourer.Gui.Tabs.UnlocksTab;
using ImGuiNET;
@ -65,8 +66,8 @@ public class MainWindow : Window, IDisposable
Messages = messages;
_quickBar = quickBar;
_config = config;
_tabs = new ITab[]
{
_tabs =
[
settings,
actors,
designs,
@ -74,7 +75,7 @@ public class MainWindow : Window, IDisposable
unlocks,
messages,
debugTab,
};
];
_event.Subscribe(OnTabSelected, TabSelected.Priority.MainWindow);
IsOpen = _config.OpenWindowAtStart;
}

View file

@ -166,7 +166,7 @@ public class PenumbraChangedItemTooltip : IDisposable
{
case ChangedItemType.ItemOffhand:
case ChangedItemType.Item:
if (!_items.ItemService.AwaitedService.TryGetValue(id, type is ChangedItemType.Item ? EquipSlot.MainHand : EquipSlot.OffHand, out var item))
if (!_items.ItemData.TryGetValue(id, type is ChangedItemType.Item ? EquipSlot.MainHand : EquipSlot.OffHand, out var item))
return;
CreateTooltip(item, "[Glamourer] ", false);
@ -177,7 +177,7 @@ public class PenumbraChangedItemTooltip : IDisposable
private bool CanApplyWeapon(EquipSlot slot, EquipItem item)
{
var main = _objects.Player.GetMainhand();
var mainItem = _items.Identify(slot, main.Set, main.Type, main.Variant);
var mainItem = _items.Identify(slot, main.X, main.Y, main.Variant);
if (slot == EquipSlot.MainHand)
return item.Type == mainItem.Type;
@ -197,7 +197,7 @@ public class PenumbraChangedItemTooltip : IDisposable
if (!Player(out var state))
return;
if (!_items.ItemService.AwaitedService.TryGetValue(id, type is ChangedItemType.Item ? EquipSlot.MainHand : EquipSlot.OffHand, out var item))
if (!_items.ItemData.TryGetValue(id, type is ChangedItemType.Item ? EquipSlot.MainHand : EquipSlot.OffHand, out var item))
return;
ApplyItem(state, item);

View file

@ -13,7 +13,6 @@ using Glamourer.Gui.Customization;
using Glamourer.Gui.Equipment;
using Glamourer.Interop;
using Glamourer.Interop.Structs;
using Glamourer.Services;
using Glamourer.State;
using Glamourer.Structs;
using ImGuiNET;
@ -21,14 +20,24 @@ using OtterGui;
using OtterGui.Classes;
using OtterGui.Raii;
using Penumbra.GameData.Actors;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums;
namespace Glamourer.Gui.Tabs.ActorTab;
public class ActorPanel(ActorSelector _selector, StateManager _stateManager, CustomizationDrawer _customizationDrawer,
EquipmentDrawer _equipmentDrawer, IdentifierService _identification, AutoDesignApplier _autoDesignApplier,
Configuration _config, DesignConverter _converter, ObjectManager _objects, DesignManager _designManager, ImportService _importService,
ICondition _conditions)
public class ActorPanel(
ActorSelector _selector,
StateManager _stateManager,
CustomizationDrawer _customizationDrawer,
EquipmentDrawer _equipmentDrawer,
AutoDesignApplier _autoDesignApplier,
Configuration _config,
DesignConverter _converter,
ObjectManager _objects,
DesignManager _designManager,
ImportService _importService,
ICondition _conditions,
DictModelChara _modelChara)
{
private ActorIdentifier _identifier;
private string _actorName = string.Empty;
@ -154,7 +163,7 @@ public class ActorPanel(ActorSelector _selector, StateManager _stateManager, Cus
}
var mainhand = EquipDrawData.FromState(_stateManager, _state, EquipSlot.MainHand);
var offhand = EquipDrawData.FromState(_stateManager, _state, EquipSlot.OffHand);
var offhand = EquipDrawData.FromState(_stateManager, _state, EquipSlot.OffHand);
_equipmentDrawer.DrawWeapons(mainhand, offhand, GameMain.IsInGPose());
ImGui.Dummy(new Vector2(ImGui.GetTextLineHeight() / 2));
@ -187,7 +196,7 @@ public class ActorPanel(ActorSelector _selector, StateManager _stateManager, Cus
private void DrawMonsterPanel()
{
var names = _identification.AwaitedService.ModelCharaNames(_state!.ModelData.ModelId);
var names = _modelChara[_state!.ModelData.ModelId];
var turnHuman = ImGui.Button("Turn Human");
ImGui.Separator();
using (var box = ImRaii.ListBox("##MonsterList",
@ -295,9 +304,9 @@ public class ActorPanel(ActorSelector _selector, StateManager _stateManager, Cus
private void SaveDesignOpen()
{
ImGui.OpenPopup("Save as Design");
_newName = _state!.Identifier.ToName();
_newName = _state!.Identifier.ToName();
var (applyGear, applyCustomize, applyCrest) = UiHelpers.ConvertKeysToFlags();
_newDesign = _converter.Convert(_state, applyGear, applyCustomize, applyCrest);
_newDesign = _converter.Convert(_state, applyGear, applyCustomize, applyCrest);
}
private void SaveDesignDrawPopup()

View file

@ -17,11 +17,11 @@ public class ActorSelector
{
private readonly EphemeralConfig _config;
private readonly ObjectManager _objects;
private readonly ActorService _actors;
private readonly ActorManager _actors;
private ActorIdentifier _identifier = ActorIdentifier.Invalid;
public ActorSelector(ObjectManager objects, ActorService actors, EphemeralConfig config)
public ActorSelector(ObjectManager objects, ActorManager actors, EphemeralConfig config)
{
_objects = objects;
_actors = actors;
@ -93,7 +93,7 @@ public class ActorSelector
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.UserCircle.ToIconString(), buttonWidth
, "Select the local player character.", !_objects.Player, true))
_identifier = _objects.Player.GetIdentifier(_actors.AwaitedService);
_identifier = _objects.Player.GetIdentifier(_actors);
ImGui.SameLine();
var (id, data) = _objects.TargetData;

View file

@ -3,23 +3,23 @@ using System.Collections.Generic;
using System.Linq;
using Dalamud.Game.ClientState.Objects.Enums;
using Dalamud.Utility;
using Glamourer.Services;
using ImGuiNET;
using OtterGui.Custom;
using OtterGui.Log;
using OtterGui.Widgets;
using Penumbra.GameData.Data;
using Penumbra.GameData.DataContainers;
namespace Glamourer.Gui.Tabs.AutomationTab;
public sealed class HumanNpcCombo : FilterComboCache<(string Name, ObjectKind Kind, uint[] Ids)>
public sealed class HumanNpcCombo(
string label,
DictModelChara modelCharaDict,
DictBNpcNames bNpcNames,
DictBNpc bNpcs,
HumanModelList humans,
Logger log)
: FilterComboCache<(string Name, ObjectKind Kind, uint[] Ids)>(() => CreateList(modelCharaDict, bNpcNames, bNpcs, humans), log)
{
private readonly string _label;
public HumanNpcCombo(string label, IdentifierService service, HumanModelList humans, Logger log)
: base(() => CreateList(service, humans), log)
=> _label = label;
protected override string ToString((string Name, ObjectKind Kind, uint[] Ids) obj)
=> obj.Name;
@ -36,7 +36,8 @@ public sealed class HumanNpcCombo : FilterComboCache<(string Name, ObjectKind Ki
}
public bool Draw(float width)
=> Draw(_label, CurrentSelection.Name.IsNullOrEmpty() ? "Human Non-Player-Characters..." : CurrentSelection.Name, string.Empty, width, ImGui.GetTextLineHeightWithSpacing());
=> Draw(label, CurrentSelection.Name.IsNullOrEmpty() ? "Human Non-Player-Characters..." : CurrentSelection.Name, string.Empty, width,
ImGui.GetTextLineHeightWithSpacing());
/// <summary> Compare strings in a way that letters and numbers are sorted before any special symbols. </summary>
@ -61,15 +62,16 @@ public sealed class HumanNpcCombo : FilterComboCache<(string Name, ObjectKind Ki
}
}
private static IReadOnlyList<(string Name, ObjectKind Kind, uint[] Ids)> CreateList(IdentifierService service, HumanModelList humans)
private static IReadOnlyList<(string Name, ObjectKind Kind, uint[] Ids)> CreateList(DictModelChara modelCharaDict, DictBNpcNames bNpcNames,
DictBNpc bNpcs, HumanModelList humans)
{
var ret = new List<(string Name, ObjectKind Kind, uint Id)>(1024);
for (var modelChara = 0u; modelChara < service.AwaitedService.NumModelChara; ++modelChara)
for (var modelChara = 0u; modelChara < modelCharaDict.Count; ++modelChara)
{
if (!humans.IsHuman(modelChara))
continue;
var list = service.AwaitedService.ModelCharaNames(modelChara);
var list = modelCharaDict[modelChara];
if (list.Count == 0)
continue;
@ -78,8 +80,8 @@ public sealed class HumanNpcCombo : FilterComboCache<(string Name, ObjectKind Ki
switch (kind)
{
case ObjectKind.BattleNpc:
var nameIds = service.AwaitedService.GetBnpcNames(id);
ret.AddRange(nameIds.Select(nameId => (service.AwaitedService.Name(ObjectKind.BattleNpc, nameId), kind, nameId.Id)));
var nameIds = bNpcNames[id];
ret.AddRange(nameIds.Select(nameId => (bNpcs[nameId], kind, nameId.Id)));
break;
case ObjectKind.EventNpc:
ret.Add((name, kind, id));

View file

@ -1,9 +1,9 @@
using Dalamud.Game.ClientState.Objects.Enums;
using Glamourer.Services;
using ImGuiNET;
using OtterGui.Custom;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Data;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Gui;
using Penumbra.GameData.Structs;
using Penumbra.String;
namespace Glamourer.Gui.Tabs.AutomationTab;
@ -12,7 +12,7 @@ public class IdentifierDrawer
{
private readonly WorldCombo _worldCombo;
private readonly HumanNpcCombo _humanNpcCombo;
private readonly ActorService _actors;
private readonly ActorManager _actors;
private string _characterName = string.Empty;
@ -21,11 +21,12 @@ public class IdentifierDrawer
public ActorIdentifier RetainerIdentifier { get; private set; } = ActorIdentifier.Invalid;
public ActorIdentifier MannequinIdentifier { get; private set; } = ActorIdentifier.Invalid;
public IdentifierDrawer(ActorService actors, IdentifierService identifier, HumanModelList humans)
public IdentifierDrawer(ActorManager actors, DictWorld dictWorld, DictModelChara dictModelChara, DictBNpcNames bNpcNames, DictBNpc bNpc,
HumanModelList humans)
{
_actors = actors;
_worldCombo = new WorldCombo(actors.AwaitedService.Data.Worlds, Glamourer.Log);
_humanNpcCombo = new HumanNpcCombo("##npcs", identifier, humans, Glamourer.Log);
_worldCombo = new WorldCombo(dictWorld, Glamourer.Log);
_humanNpcCombo = new HumanNpcCombo("##npcs", dictModelChara, bNpcNames, bNpc, humans, Glamourer.Log);
}
public void DrawName(float width)
@ -63,13 +64,13 @@ public class IdentifierDrawer
{
if (ByteString.FromString(_characterName, out var byteName))
{
PlayerIdentifier = _actors.AwaitedService.CreatePlayer(byteName, _worldCombo.CurrentSelection.Key);
RetainerIdentifier = _actors.AwaitedService.CreateRetainer(byteName, ActorIdentifier.RetainerType.Bell);
MannequinIdentifier = _actors.AwaitedService.CreateRetainer(byteName, ActorIdentifier.RetainerType.Mannequin);
PlayerIdentifier = _actors.CreatePlayer(byteName, _worldCombo.CurrentSelection.Key);
RetainerIdentifier = _actors.CreateRetainer(byteName, ActorIdentifier.RetainerType.Bell);
MannequinIdentifier = _actors.CreateRetainer(byteName, ActorIdentifier.RetainerType.Mannequin);
}
NpcIdentifier = _humanNpcCombo.CurrentSelection.Kind is ObjectKind.EventNpc or ObjectKind.BattleNpc
? _actors.AwaitedService.CreateNpc(_humanNpcCombo.CurrentSelection.Kind, _humanNpcCombo.CurrentSelection.Ids[0])
? _actors.CreateNpc(_humanNpcCombo.CurrentSelection.Kind, _humanNpcCombo.CurrentSelection.Ids[0])
: ActorIdentifier.Invalid;
}
}

View file

@ -333,7 +333,7 @@ public class SetPanel
if (!design.Design.DesignData.IsHuman)
sb.AppendLine("The base model id can not be changed automatically to something non-human.");
var set = _customizations.AwaitedService.GetList(customize.Clan, customize.Gender);
var set = _customizations.Service.GetList(customize.Clan, customize.Gender);
foreach (var type in CustomizationExtensions.All)
{
var flag = type.ToFlag();

View file

@ -7,11 +7,11 @@ using Dalamud.Interface.Utility;
using Glamourer.Automation;
using Glamourer.Events;
using Glamourer.Interop;
using Glamourer.Services;
using ImGuiNET;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Raii;
using Penumbra.GameData.Actors;
using Penumbra.String;
using ImGuiClip = OtterGui.ImGuiClip;
@ -22,9 +22,9 @@ public class SetSelector : IDisposable
private readonly Configuration _config;
private readonly AutoDesignManager _manager;
private readonly AutomationChanged _event;
private readonly ActorService _actors;
private readonly ActorManager _actors;
private readonly ObjectManager _objects;
private readonly List<(AutoDesignSet, int)> _list = new();
private readonly List<(AutoDesignSet, int)> _list = [];
public AutoDesignSet? Selection { get; private set; }
public int SelectionIndex { get; private set; } = -1;
@ -44,7 +44,7 @@ public class SetSelector : IDisposable
internal int _dragDesignIndex = -1;
public SetSelector(AutoDesignManager manager, AutomationChanged @event, Configuration config, ActorService actors, ObjectManager objects)
public SetSelector(AutoDesignManager manager, AutomationChanged @event, Configuration config, ActorManager actors, ObjectManager objects)
{
_manager = manager;
_event = @event;
@ -289,9 +289,9 @@ public class SetSelector : IDisposable
private void NewSetButton(Vector2 size)
{
var id = _actors.AwaitedService.GetCurrentPlayer();
var id = _actors.GetCurrentPlayer();
if (!id.IsValid)
id = _actors.AwaitedService.CreatePlayer(ByteString.FromSpanUnsafe("New Design"u8, true, false, true), ushort.MaxValue);
id = _actors.CreatePlayer(ByteString.FromSpanUnsafe("New Design"u8, true, false, true), ushort.MaxValue);
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Plus.ToIconString(), size,
$"Create a new Automatic Design Set for {id}. The associated player can be changed later.", !id.IsValid, true))
_manager.AddDesignSet("New Design", id);

View file

@ -69,7 +69,7 @@ public class ActiveStatePanel(StateManager _stateManager, ObjectManager _objectM
static string ItemString(in DesignData data, EquipSlot slot)
{
var item = data.Item(slot);
return $"{item.Name} ({item.ModelId.Id}{(item.WeaponType != 0 ? $"-{item.WeaponType.Id}" : string.Empty)}-{item.Variant})";
return $"{item.Name} ({item.PrimaryId.Id}{(item.SecondaryId != 0 ? $"-{item.SecondaryId.Id}" : string.Empty)}-{item.Variant})";
}
PrintRow("Model ID", state.BaseData.ModelId, state.ModelData.ModelId, state[ActorState.MetaIndex.ModelId]);

View file

@ -1,23 +1,23 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using Dalamud.Interface.Utility;
using Glamourer.Services;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Actors;
using Penumbra.GameData.DataContainers;
using ImGuiClip = OtterGui.ImGuiClip;
namespace Glamourer.Gui.Tabs.DebugTab;
public class ActorServicePanel(ActorService _actors, ItemManager _items) : IDebugTabTree
public class ActorManagerPanel(ActorManager _actors, DictBNpcNames _bNpcNames) : IDebugTabTree
{
public string Label
=> "Actor Service";
public bool Disabled
=> !_actors.Valid;
=> !_actors.Awaiter.IsCompletedSuccessfully;
private string _bnpcFilter = string.Empty;
private string _enpcFilter = string.Empty;
@ -29,11 +29,11 @@ public class ActorServicePanel(ActorService _actors, ItemManager _items) : IDebu
public void Draw()
{
DrawBnpcTable();
DebugTab.DrawNameTable("ENPCs", ref _enpcFilter, _actors.AwaitedService.Data.ENpcs.Select(kvp => (kvp.Key, kvp.Value)));
DebugTab.DrawNameTable("Companions", ref _companionFilter, _actors.AwaitedService.Data.Companions.Select(kvp => (kvp.Key, kvp.Value)));
DebugTab.DrawNameTable("Mounts", ref _mountFilter, _actors.AwaitedService.Data.Mounts.Select(kvp => (kvp.Key, kvp.Value)));
DebugTab.DrawNameTable("Ornaments", ref _ornamentFilter, _actors.AwaitedService.Data.Ornaments.Select(kvp => (kvp.Key, kvp.Value)));
DebugTab.DrawNameTable("Worlds", ref _worldFilter, _actors.AwaitedService.Data.Worlds.Select(kvp => ((uint)kvp.Key, kvp.Value)));
DebugTab.DrawNameTable("ENPCs", ref _enpcFilter, _actors.Data.ENpcs.Select(kvp => (kvp.Key.Id, kvp.Value)));
DebugTab.DrawNameTable("Companions", ref _companionFilter, _actors.Data.Companions.Select(kvp => (kvp.Key.Id, kvp.Value)));
DebugTab.DrawNameTable("Mounts", ref _mountFilter, _actors.Data.Mounts.Select(kvp => (kvp.Key.Id, kvp.Value)));
DebugTab.DrawNameTable("Ornaments", ref _ornamentFilter, _actors.Data.Ornaments.Select(kvp => (kvp.Key.Id, kvp.Value)));
DebugTab.DrawNameTable("Worlds", ref _worldFilter, _actors.Data.Worlds.Select(kvp => ((uint)kvp.Key.Id, kvp.Value)));
}
private void DrawBnpcTable()
@ -58,15 +58,15 @@ public class ActorServicePanel(ActorService _actors, ItemManager _items) : IDebu
ImGui.TableNextColumn();
var skips = ImGuiClip.GetNecessarySkips(height);
ImGui.TableNextRow();
var data = _actors.AwaitedService.Data.BNpcs.Select(kvp => (kvp.Key, kvp.Key.ToString("D5"), kvp.Value));
var data = _actors.Data.BNpcs.Select(kvp => (kvp.Key, kvp.Key.Id.ToString("D5"), kvp.Value));
var remainder = ImGuiClip.FilteredClippedDraw(data, skips,
p => p.Item2.Contains(_bnpcFilter) || p.Item3.Contains(_bnpcFilter, StringComparison.OrdinalIgnoreCase),
p => p.Item2.Contains(_bnpcFilter) || p.Value.Contains(_bnpcFilter, StringComparison.OrdinalIgnoreCase),
p =>
{
ImGuiUtil.DrawTableColumn(p.Item2);
ImGuiUtil.DrawTableColumn(p.Item3);
var bnpcs = _items.IdentifierService.AwaitedService.GetBnpcsFromName(p.Item1);
ImGuiUtil.DrawTableColumn(string.Join(", ", bnpcs.Select(b => b.Id.ToString())));
ImGuiUtil.DrawTableColumn(p.Value);
var bNpcs = _bNpcNames.GetBNpcsFromName(p.Key.BNpcNameId);
ImGuiUtil.DrawTableColumn(string.Join(", ", bNpcs.Select(b => b.Id.ToString())));
});
ImGuiClip.DrawEndDummy(remainder, height);
}

View file

@ -13,15 +13,15 @@ public class CustomizationServicePanel(CustomizationService _customization) : ID
=> "Customization Service";
public bool Disabled
=> !_customization.Valid;
=> !_customization.Awaiter.IsCompletedSuccessfully;
public void Draw()
{
foreach (var clan in _customization.AwaitedService.Clans)
foreach (var clan in _customization.Service.Clans)
{
foreach (var gender in _customization.AwaitedService.Genders)
foreach (var gender in _customization.Service.Genders)
{
var set = _customization.AwaitedService.GetList(clan, gender);
var set = _customization.Service.GetList(clan, gender);
DrawCustomizationInfo(set);
DrawNpcCustomizationInfo(set);
}

View file

@ -3,10 +3,11 @@ using System.Collections.Generic;
using ImGuiNET;
using Microsoft.Extensions.DependencyInjection;
using OtterGui.Raii;
using OtterGui.Services;
namespace Glamourer.Gui.Tabs.DebugTab;
public interface IDebugTabTree
public interface IDebugTabTree : IService
{
public string Label { get; }
public void Draw();
@ -54,7 +55,7 @@ public class DebugTabHeader(string label, params IDebugTabTree[] subTrees)
"Game Data",
provider.GetRequiredService<IdentifierPanel>(),
provider.GetRequiredService<RestrictedGearPanel>(),
provider.GetRequiredService<ActorServicePanel>(),
provider.GetRequiredService<ActorManagerPanel>(),
provider.GetRequiredService<ItemManagerPanel>(),
provider.GetRequiredService<StainPanel>(),
provider.GetRequiredService<CustomizationServicePanel>(),

View file

@ -8,7 +8,7 @@ using Glamourer.Structs;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Data;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums;
namespace Glamourer.Gui.Tabs.DebugTab;

View file

@ -3,18 +3,19 @@ using Dalamud.Interface.Utility;
using Glamourer.Services;
using ImGuiNET;
using OtterGui;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
namespace Glamourer.Gui.Tabs.DebugTab;
public class IdentifierPanel(ItemManager _items) : IDebugTabTree
public class IdentifierPanel(ItemManager _items, GamePathParser _gamePathParser) : IDebugTabTree
{
public string Label
=> "Identifier Service";
public bool Disabled
=> !_items.IdentifierService.Valid;
=> !_items.ObjectIdentification.Awaiter.IsCompletedSuccessfully;
private string _gamePath = string.Empty;
private int _setId;
@ -33,10 +34,10 @@ public class IdentifierPanel(ItemManager _items) : IDebugTabTree
ImGui.SameLine();
ImGui.SetNextItemWidth(300 * ImGuiHelpers.GlobalScale);
ImGui.InputTextWithHint("##gamePath", "Enter game path...", ref _gamePath, 256);
var fileInfo = _items.IdentifierService.AwaitedService.GamePathParser.GetFileInfo(_gamePath);
var fileInfo = _gamePathParser.GetFileInfo(_gamePath);
ImGui.TextUnformatted(
$"{fileInfo.ObjectType} {fileInfo.EquipSlot} {fileInfo.PrimaryId} {fileInfo.SecondaryId} {fileInfo.Variant} {fileInfo.BodySlot} {fileInfo.CustomizationType}");
Text(string.Join("\n", _items.IdentifierService.AwaitedService.Identify(_gamePath).Keys));
Text(string.Join("\n", _items.ObjectIdentification.Identify(_gamePath).Keys));
ImGui.Separator();
ImGui.AlignTextToFramePadding();
@ -46,16 +47,16 @@ public class IdentifierPanel(ItemManager _items) : IDebugTabTree
foreach (var slot in EquipSlotExtensions.EqdpSlots)
{
var identified = _items.Identify(slot, (SetId)_setId, (Variant)_variant);
var identified = _items.Identify(slot, (PrimaryId)_setId, 0, (Variant)_variant);
Text(identified.Name);
ImGuiUtil.HoverTooltip(string.Join("\n",
_items.IdentifierService.AwaitedService.Identify((SetId)_setId, (Variant)_variant, slot)
_items.ObjectIdentification.Identify((PrimaryId)_setId, 0, (Variant)_variant, slot)
.Select(i => $"{i.Name} {i.Id} {i.ItemId} {i.IconId}")));
}
var weapon = _items.Identify(EquipSlot.MainHand, (SetId)_setId, (WeaponType)_secondaryId, (Variant)_variant);
var weapon = _items.Identify(EquipSlot.MainHand, (PrimaryId)_setId, (SecondaryId)_secondaryId, (Variant)_variant);
Text(weapon.Name);
ImGuiUtil.HoverTooltip(string.Join("\n",
_items.IdentifierService.AwaitedService.Identify((SetId)_setId, (WeaponType)_secondaryId, (Variant)_variant, EquipSlot.MainHand)));
_items.ObjectIdentification.Identify((PrimaryId)_setId, (SecondaryId)_secondaryId, (Variant)_variant, EquipSlot.MainHand)));
}
}

View file

@ -13,7 +13,7 @@ public class ItemManagerPanel(ItemManager _items) : IDebugTabTree
=> "Item Manager";
public bool Disabled
=> !_items.ItemService.Valid;
=> !_items.ItemData.Awaiter.IsCompletedSuccessfully;
private string _itemFilter = string.Empty;
@ -22,18 +22,18 @@ public class ItemManagerPanel(ItemManager _items) : IDebugTabTree
ImRaii.TreeNode($"Default Sword: {_items.DefaultSword.Name} ({_items.DefaultSword.ItemId}) ({_items.DefaultSword.Weapon()})",
ImGuiTreeNodeFlags.Leaf).Dispose();
DebugTab.DrawNameTable("All Items (Main)", ref _itemFilter,
_items.ItemService.AwaitedService.AllItems(true).Select(p => (p.Item1.Id,
$"{p.Item2.Name} ({(p.Item2.WeaponType == 0 ? p.Item2.Armor().ToString() : p.Item2.Weapon().ToString())})"))
_items.ItemData.AllItems(true).Select(p => (p.Item1.Id,
$"{p.Item2.Name} ({(p.Item2.SecondaryId == 0 ? p.Item2.Armor().ToString() : p.Item2.Weapon().ToString())})"))
.OrderBy(p => p.Item1));
DebugTab.DrawNameTable("All Items (Off)", ref _itemFilter,
_items.ItemService.AwaitedService.AllItems(false).Select(p => (p.Item1.Id,
$"{p.Item2.Name} ({(p.Item2.WeaponType == 0 ? p.Item2.Armor().ToString() : p.Item2.Weapon().ToString())})"))
_items.ItemData.AllItems(false).Select(p => (p.Item1.Id,
$"{p.Item2.Name} ({(p.Item2.SecondaryId == 0 ? p.Item2.Armor().ToString() : p.Item2.Weapon().ToString())})"))
.OrderBy(p => p.Item1));
foreach (var type in Enum.GetValues<FullEquipType>().Skip(1))
{
DebugTab.DrawNameTable(type.ToName(), ref _itemFilter,
_items.ItemService.AwaitedService[type]
.Select(p => (p.ItemId.Id, $"{p.Name} ({(p.WeaponType == 0 ? p.Armor().ToString() : p.Weapon().ToString())})")));
_items.ItemData.ByType[type]
.Select(p => (p.ItemId.Id, $"{p.Name} ({(p.SecondaryId.Id == 0 ? p.Armor().ToString() : p.Weapon().ToString())})")));
}
}
}

View file

@ -39,7 +39,7 @@ public class ItemUnlockPanel(ItemUnlockManager _itemUnlocks, ItemManager _items)
var remainder = ImGuiClip.ClippedDraw(_itemUnlocks, skips, t =>
{
ImGuiUtil.DrawTableColumn(t.Key.ToString());
if (_items.ItemService.AwaitedService.TryGetValue(t.Key, EquipSlot.MainHand, out var equip))
if (_items.ItemData.TryGetValue(t.Key, EquipSlot.MainHand, out var equip))
{
ImGuiUtil.DrawTableColumn(equip.Name);
ImGuiUtil.DrawTableColumn(equip.Type.ToName());

View file

@ -199,7 +199,7 @@ public unsafe class ModelEvaluationPanel(
if (ImGui.SmallButton("Change Piece"))
_updateSlotService.UpdateArmor(model, slot,
new CharacterArmor((SetId)(slot == EquipSlot.Hands ? 6064 : slot == EquipSlot.Head ? 6072 : 1), 1, 0));
new CharacterArmor((PrimaryId)(slot == EquipSlot.Hands ? 6064 : slot == EquipSlot.Head ? 6072 : 1), 1, 0));
ImGui.SameLine();
if (ImGui.SmallButton("Change Stain"))
_updateSlotService.UpdateStain(model, slot, 5);
@ -213,11 +213,11 @@ public unsafe class ModelEvaluationPanel(
{
using var id = ImRaii.PushId("Customize");
var actorCustomize = new Customize(actor.IsCharacter
? *(Penumbra.GameData.Structs.CustomizeData*)&actor.AsCharacter->DrawData.CustomizeData
: new Penumbra.GameData.Structs.CustomizeData());
? *(CustomizeArray*)&actor.AsCharacter->DrawData.CustomizeData
: new CustomizeArray());
var modelCustomize = new Customize(model.IsHuman
? *(Penumbra.GameData.Structs.CustomizeData*)model.AsHuman->Customize.Data
: new Penumbra.GameData.Structs.CustomizeData());
? *(CustomizeArray*)model.AsHuman->Customize.Data
: new CustomizeArray());
foreach (var type in Enum.GetValues<CustomizeIndex>())
{
using var id2 = ImRaii.PushId((int)type);

View file

@ -55,7 +55,7 @@ public class NpcAppearancePanel(NpcCombo _npcCombo, StateManager _state, ObjectM
return;
void Draw(CustomizationNpcOptions.NpcData data)
void Draw(NpcData data)
{
using var id = ImRaii.PushId(idx++);
var disabled = !_state.GetOrCreate(_objectManager.Player, out var state);

View file

@ -3,14 +3,14 @@ using System.Globalization;
using System.Linq;
using System.Numerics;
using Glamourer.Interop;
using Glamourer.Services;
using ImGuiNET;
using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Actors;
namespace Glamourer.Gui.Tabs.DebugTab;
public class ObjectManagerPanel(ObjectManager _objectManager, ActorService _actors) : IDebugTabTree
public class ObjectManagerPanel(ObjectManager _objectManager, ActorManager _actors) : IDebugTabTree
{
public string Label
=> "Object Manager";
@ -33,7 +33,7 @@ public class ObjectManagerPanel(ObjectManager _objectManager, ActorService _acto
ImGui.TableNextColumn();
ImGuiUtil.DrawTableColumn("World");
ImGuiUtil.DrawTableColumn(_actors.Valid ? _actors.AwaitedService.Data.ToWorldName(_objectManager.World) : "Service Missing");
ImGuiUtil.DrawTableColumn(_actors.Awaiter.IsCompletedSuccessfully ? _actors.Data.ToWorldName(_objectManager.World) : "Service Missing");
ImGuiUtil.DrawTableColumn(_objectManager.World.ToString());
ImGuiUtil.DrawTableColumn("Player Character");

View file

@ -59,7 +59,7 @@ public unsafe class PenumbraPanel(PenumbraService _penumbra, PenumbraChangedItem
ImGui.SetNextItemWidth(200 * ImGuiHelpers.GlobalScale);
ImGui.InputInt("##CutsceneIndex", ref _gameObjectIndex, 0, 0);
ImGuiUtil.DrawTableColumn(_penumbra.Available
? _penumbra.CutsceneParent(_gameObjectIndex).ToString()
? _penumbra.CutsceneParent((ushort) _gameObjectIndex).ToString()
: "Penumbra Unavailable");
ImGuiUtil.DrawTableColumn("Redraw Object");

View file

@ -32,7 +32,7 @@ public class RestrictedGearPanel(ItemManager _items) : IDebugTabTree
foreach (var slot in EquipSlotExtensions.EqdpSlots)
{
var (replaced, model) =
_items.RestrictedGear.ResolveRestricted(new CharacterArmor((SetId)_setId, (Variant)_variant, 0), slot, race, gender);
_items.RestrictedGear.ResolveRestricted(new CharacterArmor((PrimaryId)_setId, (Variant)_variant, 0), slot, race, gender);
if (replaced)
ImGui.TextUnformatted($"{race.ToName()} - {gender} - {slot.ToName()} resolves to {model}.");
}

View file

@ -40,7 +40,7 @@ public class UnlockableItemsPanel(ItemUnlockManager _itemUnlocks, ItemManager _i
var remainder = ImGuiClip.ClippedDraw(_itemUnlocks.Unlockable, skips, t =>
{
ImGuiUtil.DrawTableColumn(t.Key.ToString());
if (_items.ItemService.AwaitedService.TryGetValue(t.Key, EquipSlot.MainHand, out var equip))
if (_items.ItemData.TryGetValue(t.Key, EquipSlot.MainHand, out var equip))
{
ImGuiUtil.DrawTableColumn(equip.Name);
ImGuiUtil.DrawTableColumn(equip.Type.ToName());

View file

@ -1,36 +1,11 @@
using System.Collections;
using System.Collections.Generic;
using System.Threading.Tasks;
using Dalamud.Plugin.Services;
using Glamourer.Customization;
using Glamourer.Services;
using Glamourer.Customization;
using OtterGui.Widgets;
using Penumbra.GameData;
namespace Glamourer.Gui.Tabs;
public class NpcCombo(ActorService actorManager, IdentifierService identifier, IDataManager data)
: FilterComboBase<CustomizationNpcOptions.NpcData>(new LazyList(actorManager, identifier, data), false, Glamourer.Log)
public class NpcCombo(NpcCustomizeSet npcCustomizeSet)
: FilterComboCache<NpcData>(npcCustomizeSet, Glamourer.Log)
{
private class LazyList(ActorService actorManager, IdentifierService identifier, IDataManager data)
: IReadOnlyList<CustomizationNpcOptions.NpcData>
{
private readonly Task<IReadOnlyList<CustomizationNpcOptions.NpcData>> _task
= Task.Run(() => CustomizationNpcOptions.CreateNpcData(actorManager.AwaitedService.Data.ENpcs, actorManager.AwaitedService.Data.BNpcs, identifier.AwaitedService, data));
public IEnumerator<CustomizationNpcOptions.NpcData> GetEnumerator()
=> _task.Result.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator();
public int Count
=> _task.Result.Count;
public CustomizationNpcOptions.NpcData this[int index]
=> _task.Result[index];
}
protected override string ToString(CustomizationNpcOptions.NpcData obj)
protected override string ToString(NpcData obj)
=> obj.Name;
}

View file

@ -60,7 +60,7 @@ public class SettingsTab : ITab
if (!child)
return;
Checkbox("Enable Auto Designs", "Enable the application of designs associated to characters to be applied automatically.",
Checkbox("Enable Auto Designs", "Enable the application of designs associated to characters in the Automation tab to be applied automatically.",
_config.EnableAutoDesigns, v => _config.EnableAutoDesigns = v);
ImGui.NewLine();
ImGui.NewLine();

View file

@ -41,7 +41,7 @@ public class UnlockOverview
foreach (var type in Enum.GetValues<FullEquipType>())
{
if (type.IsOffhandType() || !_items.ItemService.AwaitedService.TryGetValue(type, out var items) || items.Count == 0)
if (type.IsOffhandType() || !_items.ItemData.ByType.TryGetValue(type, out var items) || items.Count == 0)
continue;
if (ImGui.Selectable(type.ToName(), _selected1 == type))
@ -52,11 +52,11 @@ public class UnlockOverview
}
}
foreach (var clan in _customizations.AwaitedService.Clans)
foreach (var clan in _customizations.Service.Clans)
{
foreach (var gender in _customizations.AwaitedService.Genders)
foreach (var gender in _customizations.Service.Genders)
{
if (_customizations.AwaitedService.GetList(clan, gender).HairStyles.Count == 0)
if (_customizations.Service.GetList(clan, gender).HairStyles.Count == 0)
continue;
if (ImGui.Selectable($"{(gender is Gender.Male ? '♂' : '♀')} {clan.ToShortName()} Hair & Paint",
@ -107,7 +107,7 @@ public class UnlockOverview
private void DrawCustomizations()
{
var set = _customizations.AwaitedService.GetList(_selected2, _selected3);
var set = _customizations.Service.GetList(_selected2, _selected3);
var spacing = IconSpacing;
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing);
@ -121,7 +121,7 @@ public class UnlockOverview
continue;
var unlocked = _customizeUnlocks.IsUnlocked(customize, out var time);
var icon = _customizations.AwaitedService.GetIcon(customize.IconId);
var icon = _customizations.Service.GetIcon(customize.IconId);
ImGui.Image(icon.ImGuiHandle, iconSize, Vector2.Zero, Vector2.One,
unlocked || _codes.EnabledShirts ? Vector4.One : UnavailableTint);
@ -150,7 +150,7 @@ public class UnlockOverview
private void DrawItems()
{
if (!_items.ItemService.AwaitedService.TryGetValue(_selected1, out var items))
if (!_items.ItemData.ByType.TryGetValue(_selected1, out var items))
return;
var spacing = IconSpacing;
@ -160,6 +160,30 @@ public class UnlockOverview
var numRows = (items.Count + iconsPerRow - 1) / iconsPerRow;
var numVisibleRows = (int)(Math.Ceiling(ImGui.GetContentRegionAvail().Y / (iconSize.Y + spacing.Y)) + 0.5f) + 1;
var skips = ImGuiClip.GetNecessarySkips(iconSize.Y + spacing.Y);
var end = Math.Min(numVisibleRows * iconsPerRow + skips * iconsPerRow, items.Count);
var counter = 0;
for (var idx = skips * iconsPerRow; idx < end; ++idx)
{
DrawItem(items[idx]);
if (counter != iconsPerRow - 1)
{
ImGui.SameLine();
++counter;
}
else
{
counter = 0;
}
}
if (ImGui.GetCursorPosX() != 0)
ImGui.NewLine();
var remainder = numRows - numVisibleRows - skips;
if (remainder > 0)
ImGuiClip.DrawEndDummy(remainder, iconSize.Y + spacing.Y);
return;
void DrawItem(EquipItem item)
{
var unlocked = _itemUnlocks.IsUnlocked(item.Id, out var time);
@ -189,7 +213,7 @@ public class UnlockOverview
ImGui.TextUnformatted($"{item.Type.ToName()} ({slot.ToName()})");
if (item.Type.ValidOffhand().IsOffhandType())
ImGui.TextUnformatted(
$"{item.Weapon()}{(_items.ItemService.AwaitedService.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand) ? $" | {offhand.Weapon()}" : string.Empty)}");
$"{item.Weapon()}{(_items.ItemData.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand) ? $" | {offhand.Weapon()}" : string.Empty)}");
else
ImGui.TextUnformatted(slot is EquipSlot.MainHand ? $"{item.Weapon()}" : $"{item.Armor()}");
ImGui.TextUnformatted(
@ -219,29 +243,6 @@ public class UnlockOverview
_tooltip.CreateTooltip(item, string.Empty, false);
}
}
var skips = ImGuiClip.GetNecessarySkips(iconSize.Y + spacing.Y);
var end = Math.Min(numVisibleRows * iconsPerRow + skips * iconsPerRow, items.Count);
var counter = 0;
for (var idx = skips * iconsPerRow; idx < end; ++idx)
{
DrawItem(items[idx]);
if (counter != iconsPerRow - 1)
{
ImGui.SameLine();
++counter;
}
else
{
counter = 0;
}
}
if (ImGui.GetCursorPosX() != 0)
ImGui.NewLine();
var remainder = numRows - numVisibleRows - skips;
if (remainder > 0)
ImGuiClip.DrawEndDummy(remainder, iconSize.Y + spacing.Y);
}
private static Vector2 IconSpacing

View file

@ -278,7 +278,7 @@ public class UnlockTable : Table<EquipItem>, IDisposable
ImGuiUtil.RightAlign(item.ModelString);
if (ImGui.IsItemHovered()
&& item.Type.ValidOffhand().IsOffhandType()
&& _items.ItemService.AwaitedService.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand))
&& _items.ItemData.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand))
{
using var tt = ImRaii.Tooltip();
ImGui.TextUnformatted("Offhand: " + offhand.ModelString);
@ -297,7 +297,7 @@ public class UnlockTable : Table<EquipItem>, IDisposable
return true;
if (item.Type.ValidOffhand().IsOffhandType()
&& _items.ItemService.AwaitedService.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand))
&& _items.ItemData.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand))
return FilterRegex?.IsMatch(offhand.ModelString)
?? offhand.ModelString.Contains(FilterValue, StringComparison.OrdinalIgnoreCase);
@ -411,21 +411,16 @@ public class UnlockTable : Table<EquipItem>, IDisposable
=> item.Flags.HasFlag(ItemFlags.IsCrestWorthy);
}
private sealed class ItemList : IReadOnlyCollection<EquipItem>
private sealed class ItemList(ItemManager items) : IReadOnlyCollection<EquipItem>
{
private readonly ItemManager _items;
public ItemList(ItemManager items)
=> _items = items;
public IEnumerator<EquipItem> GetEnumerator()
=> _items.ItemService.AwaitedService.AllItems(true).Select(i => i.Item2).GetEnumerator();
=> items.ItemData.AllItems(true).Select(i => i.Item2).GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
=> GetEnumerator();
public int Count
=> _items.ItemService.AwaitedService.TotalItemCount(true);
=> items.ItemData.Primary.Count;
}
private void OnObjectUnlock(ObjectUnlocked.Type _1, uint _2, DateTimeOffset _3)

View file

@ -27,7 +27,7 @@ public static class UiHelpers
public static void DrawIcon(this EquipItem item, TextureService textures, Vector2 size, EquipSlot slot)
{
var isEmpty = item.ModelId.Id == 0;
var isEmpty = item.PrimaryId.Id == 0;
var (ptr, textureSize, empty) = textures.GetIcon(item, slot);
if (empty)
{

View file

@ -1,5 +1,4 @@
using System;
using System.Threading;
using Dalamud.Hooking;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
@ -7,7 +6,7 @@ using Glamourer.Customization;
using Glamourer.Events;
using Glamourer.Interop.Structs;
using OtterGui.Classes;
using CustomizeData = Penumbra.GameData.Structs.CustomizeData;
using Penumbra.GameData.Structs;
namespace Glamourer.Interop;
@ -64,7 +63,7 @@ public unsafe class ChangeCustomizeService : EventWrapper<Action<Model, Ref<Cust
private Hook<ChangeCustomizeDelegate> _changeCustomizeHook;
public bool UpdateCustomize(Model model, CustomizeData customize)
public bool UpdateCustomize(Model model, CustomizeArray customize)
{
if (!model.IsHuman)
return false;
@ -75,14 +74,14 @@ public unsafe class ChangeCustomizeService : EventWrapper<Action<Model, Ref<Cust
return ret;
}
public bool UpdateCustomize(Actor actor, CustomizeData customize)
public bool UpdateCustomize(Actor actor, CustomizeArray customize)
=> UpdateCustomize(actor.Model, customize);
private bool ChangeCustomizeDetour(Human* human, byte* data, byte skipEquipment)
{
if (!InUpdate.InMethod)
{
var customize = new Ref<Customize>(new Customize(*(CustomizeData*)data));
var customize = new Ref<Customize>(new Customize(*(CustomizeArray*)data));
Invoke(this, (Model)human, customize);
((Customize*)data)->Load(customize.Value);
}

View file

@ -78,8 +78,8 @@ public sealed class CmaFile
return;
}
var set = mainhand["Item1"]?.ToObject<ushort>() ?? items.DefaultSword.ModelId;
var type = mainhand["Item2"]?.ToObject<ushort>() ?? items.DefaultSword.WeaponType;
var set = mainhand["Item1"]?.ToObject<ushort>() ?? items.DefaultSword.PrimaryId;
var type = mainhand["Item2"]?.ToObject<ushort>() ?? items.DefaultSword.SecondaryId;
var variant = mainhand["Item3"]?.ToObject<byte>() ?? items.DefaultSword.Variant;
var stain = mainhand["Item4"]?.ToObject<byte>() ?? 0;
var item = items.Identify(EquipSlot.MainHand, set, type, variant);
@ -95,17 +95,17 @@ public sealed class CmaFile
if (offhand == null)
{
data.SetItem(EquipSlot.MainHand, defaultOffhand);
data.SetStain(EquipSlot.MainHand, defaultOffhand.ModelId.Id == 0 ? 0 : data.Stain(EquipSlot.MainHand));
data.SetStain(EquipSlot.MainHand, defaultOffhand.PrimaryId.Id == 0 ? 0 : data.Stain(EquipSlot.MainHand));
return;
}
var set = offhand["Item1"]?.ToObject<ushort>() ?? items.DefaultSword.ModelId;
var type = offhand["Item2"]?.ToObject<ushort>() ?? items.DefaultSword.WeaponType;
var set = offhand["Item1"]?.ToObject<ushort>() ?? items.DefaultSword.PrimaryId;
var type = offhand["Item2"]?.ToObject<ushort>() ?? items.DefaultSword.SecondaryId;
var variant = offhand["Item3"]?.ToObject<byte>() ?? items.DefaultSword.Variant;
var stain = offhand["Item4"]?.ToObject<byte>() ?? 0;
var item = items.Identify(EquipSlot.OffHand, set, type, variant, data.MainhandType);
data.SetItem(EquipSlot.OffHand, item.Valid ? item : defaultOffhand);
data.SetStain(EquipSlot.OffHand, defaultOffhand.ModelId.Id == 0 ? 0 : (StainId)stain);
data.SetStain(EquipSlot.OffHand, defaultOffhand.PrimaryId.Id == 0 ? 0 : (StainId)stain);
}
}

View file

@ -68,7 +68,7 @@ public class ContextMenuService : IDisposable
if (itemId > 500000)
itemId -= 500000;
if (!_items.ItemService.AwaitedService.TryGetValue(itemId, EquipSlot.MainHand, out var item))
if (!_items.ItemData.TryGetValue(itemId, EquipSlot.MainHand, out var item))
return null;
return new InventoryContextMenuItem(TryOnString, GetInventoryAction(item));
@ -80,7 +80,7 @@ public class ContextMenuService : IDisposable
if (itemId > 500000)
itemId -= 500000;
if (!_items.ItemService.AwaitedService.TryGetValue(itemId, EquipSlot.MainHand, out var item))
if (!_items.ItemData.TryGetValue(itemId, EquipSlot.MainHand, out var item))
return null;
return new GameObjectContextMenuItem(TryOnString, GetGameObjectAction(item));
@ -122,10 +122,10 @@ public class ContextMenuService : IDisposable
_state.ChangeEquip(state, slot, item, 0, StateChanged.Source.Manual);
if (item.Type.ValidOffhand().IsOffhandType())
{
if (item.ModelId.Id is > 1600 and < 1651
&& _items.ItemService.AwaitedService.TryGetValue(item.ItemId, EquipSlot.Hands, out var gauntlets))
if (item.PrimaryId.Id is > 1600 and < 1651
&& _items.ItemData.TryGetValue(item.ItemId, EquipSlot.Hands, out var gauntlets))
_state.ChangeEquip(state, EquipSlot.Hands, gauntlets, 0, StateChanged.Source.Manual);
if (_items.ItemService.AwaitedService.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand))
if (_items.ItemData.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand))
_state.ChangeEquip(state, EquipSlot.OffHand, offhand, 0, StateChanged.Source.Manual);
}
};
@ -146,10 +146,10 @@ public class ContextMenuService : IDisposable
_state.ChangeEquip(state, slot, item, 0, StateChanged.Source.Manual);
if (item.Type.ValidOffhand().IsOffhandType())
{
if (item.ModelId.Id is > 1600 and < 1651
&& _items.ItemService.AwaitedService.TryGetValue(item.ItemId, EquipSlot.Hands, out var gauntlets))
if (item.PrimaryId.Id is > 1600 and < 1651
&& _items.ItemData.TryGetValue(item.ItemId, EquipSlot.Hands, out var gauntlets))
_state.ChangeEquip(state, EquipSlot.Hands, gauntlets, 0, StateChanged.Source.Manual);
if (_items.ItemService.AwaitedService.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand))
if (_items.ItemData.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand))
_state.ChangeEquip(state, EquipSlot.OffHand, offhand, 0, StateChanged.Source.Manual);
}
};

View file

@ -178,7 +178,7 @@ public class ImportService(CustomizationService _customizations, IDragDropManage
if (input.BodyType.Value != 1)
return false;
var set = _customizations.AwaitedService.GetList(input.Clan, input.Gender);
var set = _customizations.Service.GetList(input.Clan, input.Gender);
voice = set.Voices[0];
if (inputVoice.HasValue && !set.Voices.Contains(inputVoice.Value))
return false;

View file

@ -5,8 +5,8 @@ using Dalamud.Game.ClientState.Objects;
using Dalamud.Plugin.Services;
using FFXIVClientStructs.FFXIV.Client.Game.Control;
using Glamourer.Interop.Structs;
using Glamourer.Services;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
namespace Glamourer.Interop;
@ -15,13 +15,13 @@ public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
private readonly IFramework _framework;
private readonly IClientState _clientState;
private readonly IObjectTable _objects;
private readonly ActorService _actors;
private readonly ActorManager _actors;
private readonly ITargetManager _targets;
public IObjectTable Objects
=> _objects;
public ObjectManager(IFramework framework, IClientState clientState, IObjectTable objects, ActorService actors, ITargetManager targets)
public ObjectManager(IFramework framework, IClientState clientState, IObjectTable objects, ActorManager actors, ITargetManager targets)
{
_framework = framework;
_clientState = clientState;
@ -57,7 +57,7 @@ public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
for (var i = 0; i < (int)ScreenActor.CutsceneStart; ++i)
{
Actor character = _objects.GetObjectAddress(i);
if (character.Identifier(_actors.AwaitedService, out var identifier))
if (character.Identifier(_actors, out var identifier))
HandleIdentifier(identifier, character);
}
@ -70,13 +70,13 @@ public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
if (!character.Valid && i == (int)ScreenActor.CutsceneStart)
break;
HandleIdentifier(character.GetIdentifier(_actors.AwaitedService), character);
HandleIdentifier(character.GetIdentifier(_actors), character);
}
void AddSpecial(ScreenActor idx, string label)
{
Actor actor = _objects.GetObjectAddress((int)idx);
if (actor.Identifier(_actors.AwaitedService, out var ident))
if (actor.Identifier(_actors, out var ident))
{
var data = new ActorData(actor, label);
_identifiers.Add(ident, data);
@ -95,7 +95,7 @@ public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
for (var i = (int)ScreenActor.ScreenEnd; i < _objects.Length; ++i)
{
Actor character = _objects.GetObjectAddress(i);
if (character.Identifier(_actors.AwaitedService, out var identifier))
if (character.Identifier(_actors, out var identifier))
HandleIdentifier(identifier, character);
}
@ -120,7 +120,7 @@ public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
if (identifier.Type is IdentifierType.Player or IdentifierType.Owned)
{
var allWorld = _actors.AwaitedService.CreateIndividualUnchecked(identifier.Type, identifier.PlayerName, ushort.MaxValue,
var allWorld = _actors.CreateIndividualUnchecked(identifier.Type, identifier.PlayerName, ushort.MaxValue,
identifier.Kind,
identifier.DataId);
@ -137,7 +137,7 @@ public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
if (identifier.Type is IdentifierType.Owned)
{
var nonOwned = _actors.AwaitedService.CreateNpc(identifier.Kind, identifier.DataId);
var nonOwned = _actors.CreateNpc(identifier.Kind, identifier.DataId);
if (!_nonOwnedIdentifiers.TryGetValue(nonOwned, out var nonOwnedData))
{
nonOwnedData = new ActorData(character, nonOwned.ToString());
@ -170,7 +170,7 @@ public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
get
{
Update();
return Player.Identifier(_actors.AwaitedService, out var ident) && _identifiers.TryGetValue(ident, out var data)
return Player.Identifier(_actors, out var ident) && _identifiers.TryGetValue(ident, out var data)
? (ident, data)
: (ident, ActorData.Invalid);
}
@ -181,7 +181,7 @@ public class ObjectManager : IReadOnlyDictionary<ActorIdentifier, ActorData>
get
{
Update();
return Target.Identifier(_actors.AwaitedService, out var ident) && _identifiers.TryGetValue(ident, out var data)
return Target.Identifier(_actors, out var ident) && _identifiers.TryGetValue(ident, out var data)
? (ident, data)
: (ident, ActorData.Invalid);
}

View file

@ -221,8 +221,8 @@ public unsafe class PenumbraService : IDisposable
=> Available ? _drawObjectInfo.Invoke(drawObject.Address).Item1 : Actor.Null;
/// <summary> Obtain the parent of a cutscene actor if it is known. </summary>
public int CutsceneParent(int idx)
=> Available ? _cutsceneParent.Invoke(idx) : -1;
public short CutsceneParent(ushort idx)
=> (short) (Available ? _cutsceneParent.Invoke(idx) : -1);
/// <summary> Try to redraw the given actor. </summary>
public void RedrawObject(Actor actor, RedrawType settings)

View file

@ -76,7 +76,7 @@ public unsafe class WeaponService : IDisposable
if (tmpWeapon.Value != weapon.Value)
{
if (tmpWeapon.Set.Id == 0)
if (tmpWeapon.X.Id == 0)
tmpWeapon.Stain = 0;
_loadWeaponHook.Original(drawData, slot, tmpWeapon.Value, 1, unk2, 1, unk4);
}
@ -119,7 +119,7 @@ public unsafe class WeaponService : IDisposable
var mdl = character.Model;
var (_, _, mh, oh) = mdl.GetWeapons(character);
var value = slot == EquipSlot.OffHand ? oh : mh;
var weapon = value.With(value.Set.Id == 0 ? 0 : stain);
var weapon = value.With(value.X.Id == 0 ? 0 : stain);
LoadWeapon(character, slot, weapon);
}
}

View file

@ -16,6 +16,7 @@ using ImGuiNET;
using OtterGui;
using OtterGui.Classes;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Enums;
namespace Glamourer.Services;
@ -27,7 +28,7 @@ public class CommandService : IDisposable
private readonly ICommandManager _commands;
private readonly MainWindow _mainWindow;
private readonly IChatGui _chat;
private readonly ActorService _actors;
private readonly ActorManager _actors;
private readonly ObjectManager _objects;
private readonly StateManager _stateManager;
private readonly AutoDesignApplier _autoDesignApplier;
@ -37,7 +38,7 @@ public class CommandService : IDisposable
private readonly DesignFileSystem _designFileSystem;
private readonly Configuration _config;
public CommandService(ICommandManager commands, MainWindow mainWindow, IChatGui chat, ActorService actors, ObjectManager objects,
public CommandService(ICommandManager commands, MainWindow mainWindow, IChatGui chat, ActorManager actors, ObjectManager objects,
AutoDesignApplier autoDesignApplier, StateManager stateManager, DesignManager designManager, DesignConverter converter,
DesignFileSystem designFileSystem, AutoDesignManager autoDesignManager, Configuration config)
{
@ -402,7 +403,7 @@ public class CommandService : IDisposable
{
foreach (var actor in actors.Objects)
{
if (_stateManager.GetOrCreate(actor.GetIdentifier(_actors.AwaitedService), actor, out var state))
if (_stateManager.GetOrCreate(actor.GetIdentifier(_actors), actor, out var state))
_stateManager.ApplyDesign(design, state, StateChanged.Source.Manual);
}
}
@ -537,7 +538,7 @@ public class CommandService : IDisposable
{
if (_objects.GetName(argument.ToLowerInvariant(), out var obj))
{
var identifier = _actors.AwaitedService.FromObject(obj.AsObject, out _, true, true, true);
var identifier = _actors.FromObject(obj.AsObject, out _, true, true, true);
if (!identifier.IsValid)
{
_chat.Print(new SeStringBuilder().AddText("The placeholder ").AddGreen(argument)
@ -547,7 +548,7 @@ public class CommandService : IDisposable
}
if (allowIndex && identifier.Type is IdentifierType.Npc)
identifier = _actors.AwaitedService.CreateNpc(identifier.Kind, identifier.DataId, obj.Index);
identifier = _actors.CreateNpc(identifier.Kind, identifier.DataId, obj.Index);
identifiers = new[]
{
identifier,
@ -555,7 +556,7 @@ public class CommandService : IDisposable
}
else
{
identifiers = _actors.AwaitedService.FromUserString(argument, allowIndex);
identifiers = _actors.FromUserString(argument, allowIndex);
if (!allowAnyWorld
&& identifiers[0].Type is IdentifierType.Player or IdentifierType.Owned
&& identifiers[0].HomeWorld == ushort.MaxValue)

View file

@ -1,19 +1,34 @@
using System.Linq;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using Dalamud.Plugin.Services;
using Glamourer.Customization;
using Penumbra.GameData.Data;
using OtterGui.Services;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums;
namespace Glamourer.Services;
public sealed class CustomizationService : AsyncServiceWrapper<ICustomizationManager>
public sealed class CustomizationService(
ITextureProvider textures,
IDataManager gameData,
HumanModelList humanModels,
IPluginLog log,
NpcCustomizeSet npcCustomizeSet)
: IAsyncService
{
public readonly HumanModelList HumanModels;
public readonly HumanModelList HumanModels = humanModels;
public CustomizationService(ITextureProvider textures, IDataManager gameData, HumanModelList humanModels, IPluginLog log)
: base(nameof(CustomizationService), () => CustomizationManager.Create(textures, gameData, log))
=> HumanModels = humanModels;
private ICustomizationManager? _service;
private readonly Task<ICustomizationManager> _task = Task.WhenAll(humanModels.Awaiter, npcCustomizeSet.Awaiter)
.ContinueWith(_ => CustomizationManager.Create(textures, gameData, log, npcCustomizeSet));
public ICustomizationManager Service
=> _service ??= _task.Result;
public Task Awaiter
=> _task;
public (Customize NewValue, CustomizeFlag Applied, CustomizeFlag Changed) Combine(Customize oldValues, Customize newValues,
CustomizeFlag applyWhich, bool allowUnknown)
@ -36,7 +51,7 @@ public sealed class CustomizationService : AsyncServiceWrapper<ICustomizationMan
}
var set = AwaitedService.GetList(ret.Clan, ret.Gender);
var set = Service.GetList(ret.Clan, ret.Gender);
applyWhich = applyWhich.FixApplication(set);
foreach (var index in CustomizationExtensions.AllBasic)
{
@ -66,38 +81,38 @@ public sealed class CustomizationService : AsyncServiceWrapper<ICustomizationMan
gender = Gender.Male;
return (gender, race) switch
{
(Gender.Male, SubRace.Midlander) => AwaitedService.GetName(CustomName.MidlanderM),
(Gender.Male, SubRace.Highlander) => AwaitedService.GetName(CustomName.HighlanderM),
(Gender.Male, SubRace.Wildwood) => AwaitedService.GetName(CustomName.WildwoodM),
(Gender.Male, SubRace.Duskwight) => AwaitedService.GetName(CustomName.DuskwightM),
(Gender.Male, SubRace.Plainsfolk) => AwaitedService.GetName(CustomName.PlainsfolkM),
(Gender.Male, SubRace.Dunesfolk) => AwaitedService.GetName(CustomName.DunesfolkM),
(Gender.Male, SubRace.SeekerOfTheSun) => AwaitedService.GetName(CustomName.SeekerOfTheSunM),
(Gender.Male, SubRace.KeeperOfTheMoon) => AwaitedService.GetName(CustomName.KeeperOfTheMoonM),
(Gender.Male, SubRace.Seawolf) => AwaitedService.GetName(CustomName.SeawolfM),
(Gender.Male, SubRace.Hellsguard) => AwaitedService.GetName(CustomName.HellsguardM),
(Gender.Male, SubRace.Raen) => AwaitedService.GetName(CustomName.RaenM),
(Gender.Male, SubRace.Xaela) => AwaitedService.GetName(CustomName.XaelaM),
(Gender.Male, SubRace.Helion) => AwaitedService.GetName(CustomName.HelionM),
(Gender.Male, SubRace.Lost) => AwaitedService.GetName(CustomName.LostM),
(Gender.Male, SubRace.Rava) => AwaitedService.GetName(CustomName.RavaM),
(Gender.Male, SubRace.Veena) => AwaitedService.GetName(CustomName.VeenaM),
(Gender.Female, SubRace.Midlander) => AwaitedService.GetName(CustomName.MidlanderF),
(Gender.Female, SubRace.Highlander) => AwaitedService.GetName(CustomName.HighlanderF),
(Gender.Female, SubRace.Wildwood) => AwaitedService.GetName(CustomName.WildwoodF),
(Gender.Female, SubRace.Duskwight) => AwaitedService.GetName(CustomName.DuskwightF),
(Gender.Female, SubRace.Plainsfolk) => AwaitedService.GetName(CustomName.PlainsfolkF),
(Gender.Female, SubRace.Dunesfolk) => AwaitedService.GetName(CustomName.DunesfolkF),
(Gender.Female, SubRace.SeekerOfTheSun) => AwaitedService.GetName(CustomName.SeekerOfTheSunF),
(Gender.Female, SubRace.KeeperOfTheMoon) => AwaitedService.GetName(CustomName.KeeperOfTheMoonF),
(Gender.Female, SubRace.Seawolf) => AwaitedService.GetName(CustomName.SeawolfF),
(Gender.Female, SubRace.Hellsguard) => AwaitedService.GetName(CustomName.HellsguardF),
(Gender.Female, SubRace.Raen) => AwaitedService.GetName(CustomName.RaenF),
(Gender.Female, SubRace.Xaela) => AwaitedService.GetName(CustomName.XaelaF),
(Gender.Female, SubRace.Helion) => AwaitedService.GetName(CustomName.HelionM),
(Gender.Female, SubRace.Lost) => AwaitedService.GetName(CustomName.LostM),
(Gender.Female, SubRace.Rava) => AwaitedService.GetName(CustomName.RavaF),
(Gender.Female, SubRace.Veena) => AwaitedService.GetName(CustomName.VeenaF),
(Gender.Male, SubRace.Midlander) => Service.GetName(CustomName.MidlanderM),
(Gender.Male, SubRace.Highlander) => Service.GetName(CustomName.HighlanderM),
(Gender.Male, SubRace.Wildwood) => Service.GetName(CustomName.WildwoodM),
(Gender.Male, SubRace.Duskwight) => Service.GetName(CustomName.DuskwightM),
(Gender.Male, SubRace.Plainsfolk) => Service.GetName(CustomName.PlainsfolkM),
(Gender.Male, SubRace.Dunesfolk) => Service.GetName(CustomName.DunesfolkM),
(Gender.Male, SubRace.SeekerOfTheSun) => Service.GetName(CustomName.SeekerOfTheSunM),
(Gender.Male, SubRace.KeeperOfTheMoon) => Service.GetName(CustomName.KeeperOfTheMoonM),
(Gender.Male, SubRace.Seawolf) => Service.GetName(CustomName.SeawolfM),
(Gender.Male, SubRace.Hellsguard) => Service.GetName(CustomName.HellsguardM),
(Gender.Male, SubRace.Raen) => Service.GetName(CustomName.RaenM),
(Gender.Male, SubRace.Xaela) => Service.GetName(CustomName.XaelaM),
(Gender.Male, SubRace.Helion) => Service.GetName(CustomName.HelionM),
(Gender.Male, SubRace.Lost) => Service.GetName(CustomName.LostM),
(Gender.Male, SubRace.Rava) => Service.GetName(CustomName.RavaM),
(Gender.Male, SubRace.Veena) => Service.GetName(CustomName.VeenaM),
(Gender.Female, SubRace.Midlander) => Service.GetName(CustomName.MidlanderF),
(Gender.Female, SubRace.Highlander) => Service.GetName(CustomName.HighlanderF),
(Gender.Female, SubRace.Wildwood) => Service.GetName(CustomName.WildwoodF),
(Gender.Female, SubRace.Duskwight) => Service.GetName(CustomName.DuskwightF),
(Gender.Female, SubRace.Plainsfolk) => Service.GetName(CustomName.PlainsfolkF),
(Gender.Female, SubRace.Dunesfolk) => Service.GetName(CustomName.DunesfolkF),
(Gender.Female, SubRace.SeekerOfTheSun) => Service.GetName(CustomName.SeekerOfTheSunF),
(Gender.Female, SubRace.KeeperOfTheMoon) => Service.GetName(CustomName.KeeperOfTheMoonF),
(Gender.Female, SubRace.Seawolf) => Service.GetName(CustomName.SeawolfF),
(Gender.Female, SubRace.Hellsguard) => Service.GetName(CustomName.HellsguardF),
(Gender.Female, SubRace.Raen) => Service.GetName(CustomName.RaenF),
(Gender.Female, SubRace.Xaela) => Service.GetName(CustomName.XaelaF),
(Gender.Female, SubRace.Helion) => Service.GetName(CustomName.HelionM),
(Gender.Female, SubRace.Lost) => Service.GetName(CustomName.LostM),
(Gender.Female, SubRace.Rava) => Service.GetName(CustomName.RavaF),
(Gender.Female, SubRace.Veena) => Service.GetName(CustomName.VeenaF),
_ => "Unknown",
};
}
@ -105,12 +120,12 @@ public sealed class CustomizationService : AsyncServiceWrapper<ICustomizationMan
/// <summary> Returns whether a clan is valid. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public bool IsClanValid(SubRace clan)
=> AwaitedService.Clans.Contains(clan);
=> Service.Clans.Contains(clan);
/// <summary> Returns whether a gender is valid for the given race. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public bool IsGenderValid(Race race, Gender gender)
=> race is Race.Hrothgar ? gender == Gender.Male : AwaitedService.Genders.Contains(gender);
=> race is Race.Hrothgar ? gender == Gender.Male : Service.Genders.Contains(gender);
/// <inheritdoc cref="IsCustomizationValid(CustomizationSet,CustomizeValue,CustomizeIndex,CustomizeValue, out CustomizeData?)"/>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
@ -126,7 +141,7 @@ public sealed class CustomizationService : AsyncServiceWrapper<ICustomizationMan
/// <summary> Returns whether a customization value is valid for a given clan, gender and face. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public bool IsCustomizationValid(SubRace race, Gender gender, CustomizeValue face, CustomizeIndex type, CustomizeValue value)
=> IsCustomizationValid(AwaitedService.GetList(race, gender), face, type, value);
=> IsCustomizationValid(Service.GetList(race, gender), face, type, value);
/// <summary>
/// Check that the given race and clan are valid.
@ -145,10 +160,10 @@ public sealed class CustomizationService : AsyncServiceWrapper<ICustomizationMan
return string.Empty;
}
if (AwaitedService.Races.Contains(race))
if (Service.Races.Contains(race))
{
actualRace = race;
actualClan = AwaitedService.Clans.FirstOrDefault(c => c.ToRace() == race, SubRace.Unknown);
actualClan = Service.Clans.FirstOrDefault(c => c.ToRace() == race, SubRace.Unknown);
// This should not happen.
if (actualClan == SubRace.Unknown)
{
@ -174,7 +189,7 @@ public sealed class CustomizationService : AsyncServiceWrapper<ICustomizationMan
/// </summary>
public string ValidateGender(Race race, Gender gender, out Gender actualGender)
{
if (!AwaitedService.Genders.Contains(gender))
if (!Service.Genders.Contains(gender))
{
actualGender = Gender.Male;
return $"The gender {gender.ToName()} is unknown, reset to {Gender.Male.ToName()}.";
@ -251,7 +266,7 @@ public sealed class CustomizationService : AsyncServiceWrapper<ICustomizationMan
flags |= CustomizeFlag.Gender;
}
var set = AwaitedService.GetList(customize.Clan, customize.Gender);
var set = Service.GetList(customize.Clan, customize.Gender);
return FixValues(set, ref customize) | flags;
}
@ -269,7 +284,7 @@ public sealed class CustomizationService : AsyncServiceWrapper<ICustomizationMan
return 0;
customize.Gender = newGender;
var set = AwaitedService.GetList(customize.Clan, customize.Gender);
var set = Service.GetList(customize.Clan, customize.Gender);
return FixValues(set, ref customize) | CustomizeFlag.Gender;
}

View file

@ -1,55 +1,30 @@
using Dalamud.Game.ClientState.Objects;
using Dalamud.Interface.DragDrop;
using Dalamud.IoC;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using Microsoft.Extensions.DependencyInjection;
using OtterGui.Services;
namespace Glamourer.Services;
public class DalamudServices
{
public DalamudServices(DalamudPluginInterface pi)
public static void AddServices(ServiceManager services, DalamudPluginInterface pi)
{
pi.Inject(this);
services.AddExistingService(pi);
services.AddExistingService(pi.UiBuilder);
services.AddDalamudService<ICommandManager>(pi);
services.AddDalamudService<IDataManager>(pi);
services.AddDalamudService<IClientState>(pi);
services.AddDalamudService<ICondition>(pi);
services.AddDalamudService<IGameGui>(pi);
services.AddDalamudService<IChatGui>(pi);
services.AddDalamudService<IFramework>(pi);
services.AddDalamudService<ITargetManager>(pi);
services.AddDalamudService<IObjectTable>(pi);
services.AddDalamudService<IKeyState>(pi);
services.AddDalamudService<IDragDropManager>(pi);
services.AddDalamudService<ITextureProvider>(pi);
services.AddDalamudService<IPluginLog>(pi);
services.AddDalamudService<IGameInteropProvider>(pi);
}
public void AddServices(IServiceCollection services)
{
services.AddSingleton(PluginInterface);
services.AddSingleton(Commands);
services.AddSingleton(GameData);
services.AddSingleton(ClientState);
services.AddSingleton(Condition);
services.AddSingleton(GameGui);
services.AddSingleton(Chat);
services.AddSingleton(Framework);
services.AddSingleton(Targets);
services.AddSingleton(Objects);
services.AddSingleton(KeyState);
services.AddSingleton(this);
services.AddSingleton(PluginInterface.UiBuilder);
services.AddSingleton(DragDropManager);
services.AddSingleton(TextureProvider);
services.AddSingleton(Log);
services.AddSingleton(Interop);
}
// @formatter:off
[PluginService][RequiredVersion("1.0")] public DalamudPluginInterface PluginInterface { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public ICommandManager Commands { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public IDataManager GameData { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public IClientState ClientState { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public ICondition Condition { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public IGameGui GameGui { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public IChatGui Chat { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public IFramework Framework { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public ITargetManager Targets { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public IObjectTable Objects { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public IKeyState KeyState { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public IDragDropManager DragDropManager { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public ITextureProvider TextureProvider { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public IPluginLog Log { get; private set; } = null!;
[PluginService][RequiredVersion("1.0")] public IGameInteropProvider Interop { get; private set; } = null!;
// @formatter:on
}

View file

@ -0,0 +1,6 @@
namespace Glamourer.Services
{
internal interface IGamePathParser
{
}
}

View file

@ -1,17 +1,17 @@
using System;
using System.Linq;
using System.Runtime.CompilerServices;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using Lumina.Excel;
using Penumbra.GameData.Data;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Race = Penumbra.GameData.Enums.Race;
namespace Glamourer.Services;
public class ItemManager : IDisposable
public class ItemManager
{
public const string Nothing = "Nothing";
public const string SmallClothesNpc = "Smallclothes (NPC)";
@ -19,30 +19,24 @@ public class ItemManager : IDisposable
private readonly Configuration _config;
public readonly IdentifierService IdentifierService;
public readonly ObjectIdentification ObjectIdentification;
public readonly ExcelSheet<Lumina.Excel.GeneratedSheets.Item> ItemSheet;
public readonly StainData Stains;
public readonly ItemService ItemService;
public readonly DictStains Stains;
public readonly ItemData ItemData;
public readonly RestrictedGear RestrictedGear;
public readonly EquipItem DefaultSword;
public ItemManager(Configuration config, DalamudPluginInterface pi, IDataManager gameData, IdentifierService identifierService,
ItemService itemService, IPluginLog log)
public ItemManager(Configuration config, IDataManager gameData, ObjectIdentification objectIdentification,
ItemData itemData, DictStains stains, RestrictedGear restrictedGear)
{
_config = config;
ItemSheet = gameData.GetExcelSheet<Lumina.Excel.GeneratedSheets.Item>()!;
IdentifierService = identifierService;
Stains = new StainData(pi, gameData, gameData.Language, log);
ItemService = itemService;
RestrictedGear = new RestrictedGear(pi, gameData.Language, gameData, log);
DefaultSword = EquipItem.FromMainhand(ItemSheet.GetRow(1601)!); // Weathered Shortsword
}
public void Dispose()
{
Stains.Dispose();
RestrictedGear.Dispose();
_config = config;
ItemSheet = gameData.GetExcelSheet<Lumina.Excel.GeneratedSheets.Item>()!;
ObjectIdentification = objectIdentification;
ItemData = itemData;
Stains = stains;
RestrictedGear = restrictedGear;
DefaultSword = EquipItem.FromMainhand(ItemSheet.GetRow(1601)!); // Weathered Shortsword
}
public (bool, CharacterArmor) ResolveRestrictedGear(CharacterArmor armor, EquipSlot slot, Race race, Gender gender)
@ -74,11 +68,12 @@ public class ItemManager : IDisposable
if (itemId == SmallclothesId(slot))
return SmallClothesItem(slot);
if (!itemId.IsItem || !ItemService.AwaitedService.TryGetValue(itemId.Item, slot, out var item))
if (!itemId.IsItem || !ItemData.TryGetValue(itemId.Item, slot, out var item))
return EquipItem.FromId(itemId);
if (item.Type.ToSlot() != slot)
return new EquipItem(string.Intern($"Invalid #{itemId}"), itemId, item.IconId, item.ModelId, item.WeaponType, item.Variant, 0, 0, 0,
return new EquipItem(string.Intern($"Invalid #{itemId}"), itemId, item.IconId, item.PrimaryId, item.SecondaryId, item.Variant, 0, 0,
0,
0);
return item;
@ -89,12 +84,13 @@ public class ItemManager : IDisposable
if (itemId == NothingId(type))
return NothingItem(type);
if (!ItemService.AwaitedService.TryGetValue(itemId, type is FullEquipType.Shield ? EquipSlot.MainHand : EquipSlot.OffHand,
if (!ItemData.TryGetValue(itemId, type is FullEquipType.Shield ? EquipSlot.MainHand : EquipSlot.OffHand,
out var item))
return EquipItem.FromId(itemId);
if (item.Type != type)
return new EquipItem(string.Intern($"Invalid #{itemId}"), itemId, item.IconId, item.ModelId, item.WeaponType, item.Variant, 0, 0, 0,
return new EquipItem(string.Intern($"Invalid #{itemId}"), itemId, item.IconId, item.PrimaryId, item.SecondaryId, item.Variant, 0, 0,
0,
0);
return item;
@ -103,7 +99,7 @@ public class ItemManager : IDisposable
public EquipItem Resolve(FullEquipType type, CustomItemId id)
=> id.IsItem ? Resolve(type, id.Item) : EquipItem.FromId(id);
public EquipItem Identify(EquipSlot slot, SetId id, Variant variant)
public EquipItem Identify(EquipSlot slot, PrimaryId id, Variant variant)
{
slot = slot.ToSlot();
if (slot.ToIndex() == uint.MaxValue)
@ -114,7 +110,7 @@ public class ItemManager : IDisposable
case 0: return NothingItem(slot);
case SmallClothesNpcModel: return SmallClothesItem(slot);
default:
var item = IdentifierService.AwaitedService.Identify(id, variant, slot).FirstOrDefault();
var item = ObjectIdentification.Identify(id, 0, variant, slot).FirstOrDefault();
return item.Valid
? item
: EquipItem.FromIds(0, 0, id, 0, variant, slot.ToEquipType());
@ -131,7 +127,8 @@ public class ItemManager : IDisposable
return NothingItem(offhandType);
}
public EquipItem Identify(EquipSlot slot, SetId id, WeaponType type, Variant variant, FullEquipType mainhandType = FullEquipType.Unknown)
public EquipItem Identify(EquipSlot slot, PrimaryId id, SecondaryId type, Variant variant,
FullEquipType mainhandType = FullEquipType.Unknown)
{
if (slot is EquipSlot.OffHand)
{
@ -143,7 +140,7 @@ public class ItemManager : IDisposable
if (slot is not EquipSlot.MainHand and not EquipSlot.OffHand)
return new EquipItem($"Invalid ({id.Id}-{type.Id}-{variant})", 0, 0, id, type, variant, 0, 0, 0, 0);
var item = IdentifierService.AwaitedService.Identify(id, type, variant, slot).FirstOrDefault(i => i.Type.ToSlot() == slot);
var item = ObjectIdentification.Identify(id, type, variant, slot).FirstOrDefault(i => i.Type.ToSlot() == slot);
return item.Valid
? item
: EquipItem.FromIds(0, 0, id, type, variant, slot.ToEquipType());

View file

@ -1,7 +1,4 @@
using System;
using System.Linq;
using System.Reflection;
using Dalamud.Plugin;
using Dalamud.Plugin;
using Glamourer.Api;
using Glamourer.Automation;
using Glamourer.Designs;
@ -18,22 +15,26 @@ using Glamourer.Gui.Tabs.UnlocksTab;
using Glamourer.Interop;
using Glamourer.Interop.Penumbra;
using Glamourer.State;
using Glamourer.Structs;
using Glamourer.Unlocks;
using Microsoft.Extensions.DependencyInjection;
using OtterGui.Classes;
using OtterGui.Log;
using OtterGui.Services;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Data;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Structs;
namespace Glamourer.Services;
public static class ServiceManager
public static class ServiceManagerA
{
public static ServiceProvider CreateProvider(DalamudPluginInterface pi, Logger log)
public static ServiceManager CreateProvider(DalamudPluginInterface pi, Logger log)
{
EventWrapper.ChangeLogger(log);
var services = new ServiceCollection()
.AddSingleton(log)
.AddDalamud(pi)
var services = new ServiceManager(log)
.AddExistingService(log)
.AddMeta()
.AddInterop()
.AddEvents()
@ -41,19 +42,16 @@ public static class ServiceManager
.AddDesigns()
.AddState()
.AddUi()
.AddApi()
.AddDebug();
return services.BuildServiceProvider(new ServiceProviderOptions { ValidateOnBuild = true });
}
private static IServiceCollection AddDalamud(this IServiceCollection services, DalamudPluginInterface pi)
{
new DalamudServices(pi).AddServices(services);
.AddApi();
DalamudServices.AddServices(services, pi);
services.AddIServices(typeof(EquipItem).Assembly);
services.AddIServices(typeof(Glamourer).Assembly);
services.AddIServices(typeof(EquipFlag).Assembly);
services.CreateProvider();
return services;
}
private static IServiceCollection AddMeta(this IServiceCollection services)
private static ServiceManager AddMeta(this ServiceManager services)
=> services.AddSingleton<MessageService>()
.AddSingleton<FilenameService>()
.AddSingleton<BackupService>()
@ -66,7 +64,7 @@ public static class ServiceManager
.AddSingleton<TextureService>()
.AddSingleton<FavoriteManager>();
private static IServiceCollection AddEvents(this IServiceCollection services)
private static ServiceManager AddEvents(this ServiceManager services)
=> services.AddSingleton<VisorStateChanged>()
.AddSingleton<SlotUpdating>()
.AddSingleton<DesignChanged>()
@ -82,21 +80,23 @@ public static class ServiceManager
.AddSingleton<GPoseService>()
.AddSingleton<PenumbraReloaded>();
private static IServiceCollection AddData(this IServiceCollection services)
=> services.AddSingleton<IdentifierService>()
.AddSingleton<ItemService>()
.AddSingleton<ActorService>()
private static ServiceManager AddData(this ServiceManager services)
=> services.AddSingleton<ObjectIdentification>()
.AddSingleton<ItemData>()
.AddSingleton<ActorManager>()
.AddSingleton<CustomizationService>()
.AddSingleton<ItemManager>()
.AddSingleton<GamePathParser>()
.AddSingleton<HumanModelList>();
private static IServiceCollection AddInterop(this IServiceCollection services)
private static ServiceManager AddInterop(this ServiceManager services)
=> services.AddSingleton<VisorService>()
.AddSingleton<ChangeCustomizeService>()
.AddSingleton<MetaService>()
.AddSingleton<UpdateSlotService>()
.AddSingleton<WeaponService>()
.AddSingleton<PenumbraService>()
.AddSingleton(p => new CutsceneResolver(p.GetRequiredService<PenumbraService>().CutsceneParent))
.AddSingleton<ObjectManager>()
.AddSingleton<PenumbraAutoRedraw>()
.AddSingleton<JobService>()
@ -108,7 +108,7 @@ public static class ServiceManager
.AddSingleton<ContextMenuService>()
.AddSingleton<ScalingService>();
private static IServiceCollection AddDesigns(this IServiceCollection services)
private static ServiceManager AddDesigns(this ServiceManager services)
=> services.AddSingleton<DesignManager>()
.AddSingleton<DesignFileSystem>()
.AddSingleton<AutoDesignManager>()
@ -117,14 +117,14 @@ public static class ServiceManager
.AddSingleton<DesignConverter>()
.AddSingleton<DesignColors>();
private static IServiceCollection AddState(this IServiceCollection services)
private static ServiceManager AddState(this ServiceManager services)
=> services.AddSingleton<StateManager>()
.AddSingleton<StateApplier>()
.AddSingleton<StateEditor>()
.AddSingleton<StateListener>()
.AddSingleton<FunModule>();
private static IServiceCollection AddUi(this IServiceCollection services)
private static ServiceManager AddUi(this ServiceManager services)
=> services.AddSingleton<DebugTab>()
.AddSingleton<MessagesTab>()
.AddSingleton<SettingsTab>()
@ -157,16 +157,7 @@ public static class ServiceManager
.AddSingleton<DesignColorUi>()
.AddSingleton<NpcCombo>();
private static IServiceCollection AddDebug(this IServiceCollection services)
{
services.AddSingleton(p => new DebugTab(p));
var iType = typeof(IDebugTabTree);
foreach (var type in Assembly.GetAssembly(iType)!.GetTypes().Where(t => !t.IsInterface && iType.IsAssignableFrom(t)))
services.AddSingleton(type);
return services;
}
private static IServiceCollection AddApi(this IServiceCollection services)
private static ServiceManager AddApi(this ServiceManager services)
=> services.AddSingleton<CommandService>()
.AddSingleton<GlamourerIpc>();
}

View file

@ -1,93 +0,0 @@
using Dalamud.Plugin;
using Penumbra.GameData.Actors;
using System;
using System.Threading.Tasks;
using Dalamud.Plugin.Services;
using Glamourer.Interop.Penumbra;
using Penumbra.GameData.Data;
using Penumbra.GameData;
namespace Glamourer.Services;
public abstract class AsyncServiceWrapper<T> : IDisposable
{
public string Name { get; }
public T? Service { get; private set; }
public T AwaitedService
{
get
{
_task?.Wait();
return Service!;
}
}
public bool Valid
=> Service != null && !_isDisposed;
public event Action? FinishedCreation;
private Task? _task;
private bool _isDisposed;
protected AsyncServiceWrapper(string name, Func<T> factory)
{
Name = name;
_task = Task.Run(() =>
{
var service = factory();
if (_isDisposed)
{
if (service is IDisposable d)
d.Dispose();
}
else
{
Service = service;
Glamourer.Log.Verbose($"[{Name}] Created.");
_task = null;
}
});
_task.ContinueWith((t, x) =>
{
if (!_isDisposed)
FinishedCreation?.Invoke();
}, null);
}
public void Dispose()
{
if (_isDisposed)
return;
_isDisposed = true;
_task = null;
if (Service is IDisposable d)
d.Dispose();
Glamourer.Log.Verbose($"[{Name}] Disposed.");
}
}
public sealed class IdentifierService : AsyncServiceWrapper<IObjectIdentifier>
{
public IdentifierService(DalamudPluginInterface pi, IDataManager data, ItemService itemService, IPluginLog log)
: base(nameof(IdentifierService), () => Penumbra.GameData.GameData.GetIdentifier(pi, data, itemService.AwaitedService, log))
{ }
}
public sealed class ItemService : AsyncServiceWrapper<ItemData>
{
public ItemService(DalamudPluginInterface pi, IDataManager gameData, IPluginLog log)
: base(nameof(ItemService), () => new ItemData(pi, gameData, gameData.Language, log))
{ }
}
public sealed class ActorService : AsyncServiceWrapper<ActorManager>
{
public ActorService(DalamudPluginInterface pi, IObjectTable objects, IClientState clientState, IFramework framework, IGameInteropProvider interop, IDataManager gameData,
IGameGui gui, PenumbraService penumbra, IPluginLog log)
: base(nameof(ActorService),
() => new ActorManager(pi, objects, clientState, framework, interop, gameData, gui, idx => (short)penumbra.CutsceneParent(idx), log))
{ }
}

View file

@ -164,20 +164,21 @@ public unsafe class FunModule : IDisposable
if (!_codes.EnabledEmperor)
return;
void SetItem(EquipSlot slot2, ref CharacterArmor armor)
{
var list = _items.ItemService.AwaitedService[slot2.ToEquipType()];
var rng = _rng.Next(0, list.Count - 1);
var item = list[rng];
armor.Set = item.ModelId;
armor.Variant = item.Variant;
}
if (armors.Length == 1)
SetItem(slot, ref armors[0]);
else
for (var i = 0u; i < armors.Length; ++i)
SetItem(i.ToEquipSlot(), ref armors[(int)i]);
return;
void SetItem(EquipSlot slot2, ref CharacterArmor armor)
{
var list = _items.ItemData.ByType[slot2.ToEquipType()];
var rng = _rng.Next(0, list.Count - 1);
var item = list[rng];
armor.Set = item.PrimaryId;
armor.Variant = item.Variant;
}
}
public void ApplyOops(ref Customize customize)
@ -197,7 +198,7 @@ public unsafe class FunModule : IDisposable
if (!_codes.EnabledIndividual)
return;
var set = _customizations.AwaitedService.GetList(customize.Clan, customize.Gender);
var set = _customizations.Service.GetList(customize.Clan, customize.Gender);
foreach (var index in Enum.GetValues<CustomizeIndex>())
{
if (index is CustomizeIndex.Face || !set.IsAvailable(index))

View file

@ -188,7 +188,7 @@ public class StateApplier(UpdateSlotService _updateSlot, VisorService _visor, We
/// <summary> Apply a weapon to the offhand. </summary>
public void ChangeOffhand(ActorData data, EquipItem weapon, StainId stain)
{
stain = weapon.ModelId.Id == 0 ? 0 : stain;
stain = weapon.PrimaryId.Id == 0 ? 0 : stain;
foreach (var actor in data.Objects.Where(a => a.Model.IsHuman))
_weapon.LoadWeapon(actor, EquipSlot.OffHand, weapon.Weapon().With(stain));
}

View file

@ -5,7 +5,7 @@ using Glamourer.Customization;
using Glamourer.Events;
using Glamourer.Services;
using Glamourer.Structs;
using Penumbra.GameData.Data;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
@ -74,7 +74,7 @@ public class StateEditor
state[CustomizeIndex.Clan] = source;
state[CustomizeIndex.Gender] = source;
var set = _customizations.AwaitedService.GetList(state.ModelData.Customize.Clan, state.ModelData.Customize.Gender);
var set = _customizations.Service.GetList(state.ModelData.Customize.Clan, state.ModelData.Customize.Gender);
foreach (var index in Enum.GetValues<CustomizeIndex>().Where(set.IsAvailable))
state[index] = source;
}

View file

@ -7,13 +7,13 @@ using Glamourer.Interop.Structs;
using Glamourer.Services;
using OtterGui.Classes;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using System;
using Dalamud.Game.ClientState.Conditions;
using Dalamud.Plugin.Services;
using Glamourer.Structs;
using Penumbra.GameData.DataContainers;
namespace Glamourer.State;
@ -25,7 +25,7 @@ namespace Glamourer.State;
public class StateListener : IDisposable
{
private readonly Configuration _config;
private readonly ActorService _actors;
private readonly ActorManager _actors;
private readonly ObjectManager _objects;
private readonly StateManager _manager;
private readonly StateApplier _applier;
@ -50,7 +50,7 @@ public class StateListener : IDisposable
private ActorState? _creatingState;
private CharacterWeapon _lastFistOffhand = CharacterWeapon.Empty;
public StateListener(StateManager manager, ItemManager items, PenumbraService penumbra, ActorService actors, Configuration config,
public StateListener(StateManager manager, ItemManager items, PenumbraService penumbra, ActorManager actors, Configuration config,
SlotUpdating slotUpdating, WeaponLoading weaponLoading, VisorStateChanged visorState, WeaponVisibilityChanged weaponVisibility,
HeadGearVisibilityChanged headGearVisibility, AutoDesignApplier autoDesignApplier, FunModule funModule, HumanModelList humans,
StateApplier applier, MovedEquipment movedEquipment, ObjectManager objects, GPoseService gPose,
@ -111,7 +111,7 @@ public class StateListener : IDisposable
if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart)
return;
_creatingIdentifier = actor.GetIdentifier(_actors.AwaitedService);
_creatingIdentifier = actor.GetIdentifier(_actors);
ref var modelId = ref *(uint*)modelPtr;
ref var customize = ref *(Customize*)customizePtr;
@ -149,7 +149,7 @@ public class StateListener : IDisposable
if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart)
return;
if (!actor.Identifier(_actors.AwaitedService, out var identifier)
if (!actor.Identifier(_actors, out var identifier)
|| !_manager.TryGetValue(identifier, out var state))
return;
@ -169,7 +169,7 @@ public class StateListener : IDisposable
return;
}
var set = _customizations.AwaitedService.GetList(model.Clan, model.Gender);
var set = _customizations.Service.GetList(model.Clan, model.Gender);
foreach (var index in CustomizationExtensions.AllBasic)
{
if (state[index] is not StateChanged.Source.Fixed)
@ -211,7 +211,7 @@ public class StateListener : IDisposable
// then we do not want to use our restricted gear protection
// since we assume the player has that gear modded to availability.
var locked = false;
if (actor.Identifier(_actors.AwaitedService, out var identifier)
if (actor.Identifier(_actors, out var identifier)
&& _manager.TryGetValue(identifier, out var state))
{
HandleEquipSlot(actor, state, slot, ref armor.Value);
@ -238,7 +238,7 @@ public class StateListener : IDisposable
var currentItem = state.BaseData.Item(slot);
var model = state.ModelData.Weapon(slot);
var current = currentItem.Weapon(state.BaseData.Stain(slot));
if (model.Value == current.Value || !_items.ItemService.AwaitedService.TryGetValue(item, EquipSlot.MainHand, out var changedItem))
if (model.Value == current.Value || !_items.ItemData.TryGetValue(item, EquipSlot.MainHand, out var changedItem))
continue;
var changed = changedItem.Weapon(stain);
@ -272,10 +272,10 @@ public class StateListener : IDisposable
return;
// Fist weapon gauntlet hack.
if (slot is EquipSlot.OffHand && weapon.Value.Variant == 0 && weapon.Value.Set.Id != 0 && _lastFistOffhand.Set.Id != 0)
if (slot is EquipSlot.OffHand && weapon.Value.Variant == 0 && weapon.Value.Y.Id != 0 && _lastFistOffhand.Y.Id != 0)
weapon.Value = _lastFistOffhand;
if (!actor.Identifier(_actors.AwaitedService, out var identifier)
if (!actor.Identifier(_actors, out var identifier)
|| !_manager.TryGetValue(identifier, out var state))
return;
@ -308,13 +308,13 @@ public class StateListener : IDisposable
var newWeapon = state.ModelData.Weapon(slot);
if (baseType is FullEquipType.Unknown || baseType == state.ModelData.Item(slot).Type || _gPose.InGPose && actor.IsGPoseOrCutscene)
actorWeapon = newWeapon;
else if (actorWeapon.Set.Id != 0)
else if (actorWeapon.X.Id != 0)
actorWeapon = actorWeapon.With(newWeapon.Stain);
}
// Fist Weapon Offhand hack.
if (slot is EquipSlot.MainHand && weapon.Value.Set.Id is > 1600 and < 1651)
_lastFistOffhand = new CharacterWeapon((SetId)(weapon.Value.Set.Id + 50), weapon.Value.Type, weapon.Value.Variant,
if (slot is EquipSlot.MainHand && weapon.Value.X.Id is > 1600 and < 1651)
_lastFistOffhand = new CharacterWeapon((PrimaryId)(weapon.Value.X.Id + 50), weapon.Value.Y, weapon.Value.Variant,
weapon.Value.Stain);
_funModule.ApplyFun(actor, ref weapon.Value, slot);
@ -329,7 +329,7 @@ public class StateListener : IDisposable
return false;
var offhand = actor.GetOffhand();
return offhand.Variant == 0 && offhand.Set.Id != 0 && armor.Set.Id == offhand.Set.Id;
return offhand.Variant == 0 && offhand.Y.Id != 0 && armor.Set.Id == offhand.Y.Id;
}
var actorArmor = actor.GetArmor(slot);
@ -413,7 +413,7 @@ public class StateListener : IDisposable
if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart)
return;
if (!actor.Identifier(_actors.AwaitedService, out var identifier)
if (!actor.Identifier(_actors, out var identifier)
|| !_manager.TryGetValue(identifier, out var state))
return;
@ -439,7 +439,7 @@ public class StateListener : IDisposable
if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart)
return;
if (!actor.Identifier(_actors.AwaitedService, out var identifier)
if (!actor.Identifier(_actors, out var identifier)
|| !_manager.TryGetValue(identifier, out var state))
return;
@ -474,7 +474,7 @@ public class StateListener : IDisposable
var change = UpdateState.NoChange;
// Fist weapon bug hack
if (slot is EquipSlot.OffHand && weapon.Value == 0 && actor.GetMainhand().Set.Id is > 1600 and < 1651)
if (slot is EquipSlot.OffHand && weapon.Value == 0 && actor.GetMainhand().X.Id is > 1600 and < 1651)
return UpdateState.NoChange;
if (baseData.Stain != weapon.Stain)
@ -483,9 +483,9 @@ public class StateListener : IDisposable
change = UpdateState.Change;
}
if (baseData.Set.Id != weapon.Set.Id || baseData.Type.Id != weapon.Type.Id || baseData.Variant != weapon.Variant)
if (baseData.X.Id != weapon.X.Id || baseData.Y.Id != weapon.Y.Id || baseData.Variant != weapon.Variant)
{
var item = _items.Identify(slot, weapon.Set, weapon.Type, weapon.Variant,
var item = _items.Identify(slot, weapon.X, weapon.Y, weapon.Variant,
slot is EquipSlot.OffHand ? state.BaseData.Item(EquipSlot.MainHand).Type : FullEquipType.Unknown);
state.BaseData.SetItem(slot, item);
change = UpdateState.Change;
@ -555,7 +555,7 @@ public class StateListener : IDisposable
if (_condition[ConditionFlag.CreatingCharacter] && actor.Index >= ObjectIndex.CutsceneStart)
return;
if (!actor.Identifier(_actors.AwaitedService, out var identifier))
if (!actor.Identifier(_actors, out var identifier))
return;
if (!_manager.TryGetValue(identifier, out var state))
@ -588,7 +588,7 @@ public class StateListener : IDisposable
// We do not need to handle fixed designs,
// if there is no model that caused a fixed design to exist yet,
// we also do not care about the invisible model.
if (!actor.Identifier(_actors.AwaitedService, out var identifier))
if (!actor.Identifier(_actors, out var identifier))
return;
if (!_manager.TryGetValue(identifier, out var state))
@ -621,7 +621,7 @@ public class StateListener : IDisposable
// We do not need to handle fixed designs,
// if there is no model that caused a fixed design to exist yet,
// we also do not care about the invisible model.
if (!actor.Identifier(_actors.AwaitedService, out var identifier))
if (!actor.Identifier(_actors, out var identifier))
return;
if (!_manager.TryGetValue(identifier, out var state))

View file

@ -12,17 +12,17 @@ using Glamourer.Interop.Structs;
using Glamourer.Services;
using Glamourer.Structs;
using Penumbra.GameData.Actors;
using Penumbra.GameData.Data;
using Penumbra.GameData.DataContainers;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
namespace Glamourer.State;
public class StateManager(ActorService _actors, ItemManager _items, StateChanged _event, StateApplier _applier, StateEditor _editor,
public class StateManager(ActorManager _actors, ItemManager _items, StateChanged _event, StateApplier _applier, StateEditor _editor,
HumanModelList _humans, ICondition _condition, IClientState _clientState)
: IReadOnlyDictionary<ActorIdentifier, ActorState>
{
private readonly Dictionary<ActorIdentifier, ActorState> _states = new();
private readonly Dictionary<ActorIdentifier, ActorState> _states = [];
public IEnumerator<KeyValuePair<ActorIdentifier, ActorState>> GetEnumerator()
=> _states.GetEnumerator();
@ -50,7 +50,7 @@ public class StateManager(ActorService _actors, ItemManager _items, StateChanged
/// <inheritdoc cref="GetOrCreate(ActorIdentifier, Actor, out ActorState?)"/>
public bool GetOrCreate(Actor actor, [NotNullWhen(true)] out ActorState? state)
=> GetOrCreate(actor.GetIdentifier(_actors.AwaitedService), actor, out state);
=> GetOrCreate(actor.GetIdentifier(_actors), actor, out state);
/// <summary> Try to obtain or create a new state for an existing actor. Returns false if no state could be created. </summary>
public unsafe bool GetOrCreate(ActorIdentifier identifier, Actor actor, [NotNullWhen(true)] out ActorState? state)
@ -170,8 +170,8 @@ public class StateManager(ActorService _actors, ItemManager _items, StateChanged
}
// Set the weapons regardless of source.
var mainItem = _items.Identify(EquipSlot.MainHand, main.Set, main.Type, main.Variant);
var offItem = _items.Identify(EquipSlot.OffHand, off.Set, off.Type, off.Variant, mainItem.Type);
var mainItem = _items.Identify(EquipSlot.MainHand, main.Skeleton, main.Weapon, main.Variant);
var offItem = _items.Identify(EquipSlot.OffHand, off.Skeleton, off.Weapon, off.Variant, mainItem.Type);
ret.SetItem(EquipSlot.MainHand, mainItem);
ret.SetStain(EquipSlot.MainHand, main.Stain);
ret.SetItem(EquipSlot.OffHand, offItem);
@ -190,13 +190,13 @@ public class StateManager(ActorService _actors, ItemManager _items, StateChanged
/// <summary> This is hardcoded in the game. </summary>
private void FistWeaponHack(ref DesignData ret, ref CharacterWeapon mainhand, ref CharacterWeapon offhand)
{
if (mainhand.Set.Id is < 1601 or >= 1651)
if (mainhand.Skeleton.Id is < 1601 or >= 1651)
return;
var gauntlets = _items.Identify(EquipSlot.Hands, offhand.Set, (Variant)offhand.Type.Id);
offhand.Set = (SetId)(mainhand.Set.Id + 50);
var gauntlets = _items.Identify(EquipSlot.Hands, offhand.Skeleton, (Variant)offhand.Weapon.Id);
offhand.Skeleton = (PrimaryId)(mainhand.Skeleton.Id + 50);
offhand.Variant = mainhand.Variant;
offhand.Type = mainhand.Type;
offhand.Weapon = mainhand.Weapon;
ret.SetItem(EquipSlot.Hands, gauntlets);
ret.SetStain(EquipSlot.Hands, mainhand.Stain);
}

View file

@ -178,11 +178,11 @@ public class CustomizeUnlockManager : IDisposable, ISavable
{
var ret = new Dictionary<CustomizeData, (uint Data, string Name)>();
var sheet = gameData.GetExcelSheet<CharaMakeCustomize>(ClientLanguage.English)!;
foreach (var clan in customizations.AwaitedService.Clans)
foreach (var clan in customizations.Service.Clans)
{
foreach (var gender in customizations.AwaitedService.Genders)
foreach (var gender in customizations.Service.Genders)
{
var list = customizations.AwaitedService.GetList(clan, gender);
var list = customizations.Service.GetList(clan, gender);
foreach (var hair in list.HairStyles)
{
var x = sheet.FirstOrDefault(f => f.FeatureID == hair.Value.Value);

View file

@ -9,6 +9,7 @@ using FFXIVClientStructs.FFXIV.Client.Game.UI;
using Glamourer.Events;
using Glamourer.Services;
using Lumina.Excel.GeneratedSheets;
using Penumbra.GameData.Data;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
using Cabinet = Lumina.Excel.GeneratedSheets.Cabinet;
@ -22,7 +23,7 @@ public class ItemUnlockManager : ISavable, IDisposable, IReadOnlyDictionary<Item
private readonly IClientState _clientState;
private readonly IFramework _framework;
private readonly ObjectUnlocked _event;
private readonly IdentifierService _identifier;
private readonly ObjectIdentification _identifier;
private readonly Dictionary<uint, long> _unlocked = new();
@ -45,7 +46,7 @@ public class ItemUnlockManager : ISavable, IDisposable, IReadOnlyDictionary<Item
public readonly IReadOnlyDictionary<ItemId, UnlockRequirements> Unlockable;
public ItemUnlockManager(SaveService saveService, ItemManager items, IClientState clientState, IDataManager gameData, IFramework framework,
ObjectUnlocked @event, IdentifierService identifier, IGameInteropProvider interop)
ObjectUnlocked @event, ObjectIdentification identifier, IGameInteropProvider interop)
{
interop.InitializeFromAttributes(this);
_saveService = saveService;
@ -100,12 +101,12 @@ public class ItemUnlockManager : ISavable, IDisposable, IReadOnlyDictionary<Item
private bool AddItem(ItemId itemId, long time)
{
itemId = itemId.StripModifiers;
if (!_items.ItemService.AwaitedService.TryGetValue(itemId, EquipSlot.MainHand, out var equip)
if (!_items.ItemData.TryGetValue(itemId, EquipSlot.MainHand, out var equip)
|| !_unlocked.TryAdd(equip.ItemId.Id, time))
return false;
_event.Invoke(ObjectUnlocked.Type.Item, equip.ItemId.Id, DateTimeOffset.FromUnixTimeMilliseconds(time));
var ident = _identifier.AwaitedService.Identify(equip.ModelId, equip.WeaponType, equip.Variant, equip.Type.ToSlot());
var ident = _identifier.Identify(equip.PrimaryId, equip.SecondaryId, equip.Variant, equip.Type.ToSlot());
foreach (var item in ident)
{
if (_unlocked.TryAdd(item.ItemId.Id, time))
@ -272,7 +273,7 @@ public class ItemUnlockManager : ISavable, IDisposable, IReadOnlyDictionary<Item
private void Load()
{
var version = UnlockDictionaryHelpers.Load(ToFilename(_saveService.FileNames), _unlocked,
id => _items.ItemService.AwaitedService.TryGetValue(id, EquipSlot.MainHand, out _), "item");
id => _items.ItemData.TryGetValue(id, EquipSlot.MainHand, out _), "item");
UpdateModels(version);
}
@ -282,7 +283,7 @@ public class ItemUnlockManager : ISavable, IDisposable, IReadOnlyDictionary<Item
var cabinet = gameData.GetExcelSheet<Cabinet>()!;
foreach (var row in cabinet)
{
if (items.ItemService.AwaitedService.TryGetValue(row.Item.Row, EquipSlot.MainHand, out var item))
if (items.ItemData.TryGetValue(row.Item.Row, EquipSlot.MainHand, out var item))
ret.TryAdd(item.ItemId, new UnlockRequirements(row.RowId, 0, 0, 0, UnlockType.Cabinet));
}
@ -290,7 +291,7 @@ public class ItemUnlockManager : ISavable, IDisposable, IReadOnlyDictionary<Item
var gilShop = gameData.GetExcelSheet<GilShop>()!;
foreach (var row in gilShopItem)
{
if (!items.ItemService.AwaitedService.TryGetValue(row.Item.Row, EquipSlot.MainHand, out var item))
if (!items.ItemData.TryGetValue(row.Item.Row, EquipSlot.MainHand, out var item))
continue;
var quest1 = row.QuestRequired[0].Row;
@ -323,10 +324,10 @@ public class ItemUnlockManager : ISavable, IDisposable, IReadOnlyDictionary<Item
foreach (var (item, time) in _unlocked.ToArray())
{
if (!_items.ItemService.AwaitedService.TryGetValue(item, EquipSlot.MainHand, out var equip))
if (!_items.ItemData.TryGetValue(item, EquipSlot.MainHand, out var equip))
continue;
var ident = _identifier.AwaitedService.Identify(equip.ModelId, equip.WeaponType, equip.Variant, equip.Type.ToSlot());
var ident = _identifier.Identify(equip.PrimaryId, equip.SecondaryId, equip.Variant, equip.Type.ToSlot());
foreach (var item2 in ident)
{
if (_unlocked.TryAdd(item2.ItemId.Id, time))

@ -1 +1 @@
Subproject commit 6a73878abbb477dc441ade70ad40352e2da9cc4c
Subproject commit 197d23eee167c232000f22ef40a7a2bded913b6c

@ -1 +1 @@
Subproject commit ffdb966fec5a657893289e655c641ceb3af1d59f
Subproject commit 37b9bcf6727bd902fdb19a0a8b9d80b94f4cdd10