Remove BonusItem and replace with normal EquipItems everywhere.

This commit is contained in:
Ottermandias 2024-10-11 18:18:33 +02:00
parent 415ac63767
commit 9d99d936aa
25 changed files with 112 additions and 118 deletions

View file

@ -85,7 +85,7 @@ public class ItemsApi(ApiHelpers helpers, ItemManager itemManager, StateManager
return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args); return ApiHelpers.Return(GlamourerApiEc.InvalidKey, args);
var settings = new ApplySettings(Source: flags.HasFlag(ApplyFlag.Once) ? StateSource.IpcManual : StateSource.IpcFixed, Key: key); var settings = new ApplySettings(Source: flags.HasFlag(ApplyFlag.Once) ? StateSource.IpcManual : StateSource.IpcFixed, Key: key);
stateManager.ChangeBonusItem(state, item.Slot, item, settings); stateManager.ChangeBonusItem(state, item.Type.ToBonus(), item, settings);
ApiHelpers.Lock(state, key, flags); ApiHelpers.Lock(state, key, flags);
return GlamourerApiEc.Success; return GlamourerApiEc.Success;
} }
@ -111,7 +111,7 @@ public class ItemsApi(ApiHelpers helpers, ItemManager itemManager, StateManager
continue; continue;
anyUnlocked = true; anyUnlocked = true;
stateManager.ChangeBonusItem(state, item.Slot, item, settings); stateManager.ChangeBonusItem(state, item.Type.ToBonus(), item, settings);
ApiHelpers.Lock(state, key, flags); ApiHelpers.Lock(state, key, flags);
} }
@ -138,7 +138,7 @@ public class ItemsApi(ApiHelpers helpers, ItemManager itemManager, StateManager
return item.Valid; return item.Valid;
} }
private bool ResolveBonusItem(ApiBonusSlot apiSlot, ulong itemId, out BonusItem item) private bool ResolveBonusItem(ApiBonusSlot apiSlot, ulong itemId, out EquipItem item)
{ {
var slot = apiSlot switch var slot = apiSlot switch
{ {

View file

@ -281,7 +281,7 @@ public class DesignBase
var item = _designData.BonusItem(slot); var item = _designData.BonusItem(slot);
ret[slot.ToString()] = new JObject() ret[slot.ToString()] = new JObject()
{ {
["BonusId"] = item.CustomId.Id, ["BonusId"] = item.Id.Id,
["Apply"] = DoApplyBonusItem(slot), ["Apply"] = DoApplyBonusItem(slot),
}; };
} }
@ -443,7 +443,7 @@ public class DesignBase
if (json[slot.ToString()] is not JObject itemJson) if (json[slot.ToString()] is not JObject itemJson)
{ {
design.Application.BonusItem &= ~slot; design.Application.BonusItem &= ~slot;
design.GetDesignDataRef().SetBonusItem(slot, BonusItem.Empty(slot)); design.GetDesignDataRef().SetBonusItem(slot, EquipItem.BonusItemNothing(slot));
continue; continue;
} }

View file

@ -108,12 +108,12 @@ public unsafe struct DesignData
} }
} }
public readonly BonusItem BonusItem(BonusItemFlag slot) public readonly EquipItem BonusItem(BonusItemFlag slot)
=> slot switch => slot switch
{ {
// @formatter:off // @formatter:off
BonusItemFlag.Glasses => Penumbra.GameData.Structs.BonusItem.FromIds(_bonusIds[0], _iconIds[12], _bonusModelIds[0], _bonusVariants[0], BonusItemFlag.Glasses, _nameGlasses), BonusItemFlag.Glasses => EquipItem.FromIds(_bonusIds[0], _iconIds[12], _bonusModelIds[0], 0, _bonusVariants[0], FullEquipType.Glasses, 0, _nameGlasses),
_ => Penumbra.GameData.Structs.BonusItem.Empty(slot), _ => EquipItem.BonusItemNothing(slot),
// @formatter:on // @formatter:on
}; };
@ -191,15 +191,15 @@ public unsafe struct DesignData
return true; return true;
} }
public bool SetBonusItem(BonusItemFlag slot, BonusItem item) public bool SetBonusItem(BonusItemFlag slot, EquipItem item)
{ {
var index = slot.ToIndex(); var index = slot.ToIndex();
if (index > NumBonusItems) if (index > NumBonusItems)
return false; return false;
_iconIds[NumEquipment + NumWeapons + index] = item.Icon.Id; _iconIds[NumEquipment + NumWeapons + index] = item.IconId.Id;
_bonusIds[index] = item.Id.Id; _bonusIds[index] = item.Id.BonusItem.Id;
_bonusModelIds[index] = item.ModelId.Id; _bonusModelIds[index] = item.PrimaryId.Id;
_bonusVariants[index] = item.Variant.Id; _bonusVariants[index] = item.Variant.Id;
switch (index) switch (index)
{ {
@ -330,7 +330,7 @@ public unsafe struct DesignData
public void SetDefaultBonusItems() public void SetDefaultBonusItems()
{ {
foreach (var slot in BonusExtensions.AllFlags) foreach (var slot in BonusExtensions.AllFlags)
SetBonusItem(slot, Penumbra.GameData.Structs.BonusItem.Empty(slot)); SetBonusItem(slot, EquipItem.BonusItemNothing(slot));
} }

View file

@ -172,10 +172,10 @@ public class DesignEditor(
} }
/// <inheritdoc/> /// <inheritdoc/>
public void ChangeBonusItem(object data, BonusItemFlag slot, BonusItem item, ApplySettings settings = default) public void ChangeBonusItem(object data, BonusItemFlag slot, EquipItem item, ApplySettings settings = default)
{ {
var design = (Design)data; var design = (Design)data;
if (item.Slot != slot) if (item.Type.ToBonus() != slot)
return; return;
var oldItem = design.DesignData.BonusItem(slot); var oldItem = design.DesignData.BonusItem(slot);

View file

@ -40,7 +40,7 @@ public readonly record struct EquipTransaction(EquipSlot Slot, EquipItem Old, Eq
=> editor.ChangeItem(data, Slot, Old, ApplySettings.Manual); => editor.ChangeItem(data, Slot, Old, ApplySettings.Manual);
} }
public readonly record struct BonusItemTransaction(BonusItemFlag Slot, BonusItem Old, BonusItem New) public readonly record struct BonusItemTransaction(BonusItemFlag Slot, EquipItem Old, EquipItem New)
: ITransaction : ITransaction
{ {
public ITransaction? Merge(ITransaction older) public ITransaction? Merge(ITransaction older)

View file

@ -65,7 +65,7 @@ public interface IDesignEditor
=> ChangeEquip(data, slot, item, null, settings); => ChangeEquip(data, slot, item, null, settings);
/// <summary> Change a bonus item. </summary> /// <summary> Change a bonus item. </summary>
public void ChangeBonusItem(object data, BonusItemFlag slot, BonusItem item, ApplySettings settings = default); public void ChangeBonusItem(object data, BonusItemFlag slot, EquipItem item, ApplySettings settings = default);
/// <summary> Change the stain for any equipment piece. </summary> /// <summary> Change the stain for any equipment piece. </summary>
public void ChangeStains(object data, EquipSlot slot, StainIds stains, ApplySettings settings = default) public void ChangeStains(object data, EquipSlot slot, StainIds stains, ApplySettings settings = default)

View file

@ -6,9 +6,12 @@ using Glamourer.Gui;
using Glamourer.Interop; using Glamourer.Interop;
using Glamourer.Services; using Glamourer.Services;
using Glamourer.State; using Glamourer.State;
using OtterGui;
using OtterGui.Classes; using OtterGui.Classes;
using OtterGui.Log; using OtterGui.Log;
using OtterGui.Services; using OtterGui.Services;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Files;
namespace Glamourer; namespace Glamourer;
@ -44,6 +47,15 @@ public class Glamourer : IDalamudPlugin
_services.GetService<CommandService>(); // initialize commands. _services.GetService<CommandService>(); // initialize commands.
_services.GetService<IpcProviders>(); // initialize IPC. _services.GetService<IpcProviders>(); // initialize IPC.
Log.Information($"Glamourer v{Version} loaded successfully."); Log.Information($"Glamourer v{Version} loaded successfully.");
//var text = File.ReadAllBytes(@"C:\FFXIVMods\PBDTest\files\human.pbd");
//var pbd = new PbdFile(text);
//var roundtrip = pbd.Write();
//File.WriteAllBytes(@"C:\FFXIVMods\PBDTest\files\Vanilla resaved save.pbd", roundtrip);
//var deformer = pbd.Deformers.FirstOrDefault(d => d.GenderRace is GenderRace.RoegadynFemale)!.RacialDeformer;
//deformer.DeformMatrices["ya_fukubu_phys"] = deformer.DeformMatrices["j_kosi"];
//var aleks = pbd.Write();
//File.WriteAllBytes(@"C:\FFXIVMods\PBDTest\files\rue.pbd", aleks);
} }
catch catch
{ {

View file

@ -20,7 +20,7 @@ public struct BonusDrawData(BonusItemFlag slot, in DesignData designData)
public readonly bool IsState public readonly bool IsState
=> _object is ActorState; => _object is ActorState;
public readonly void SetItem(BonusItem item) public readonly void SetItem(EquipItem item)
=> _editor.ChangeBonusItem(_object, Slot, item, ApplySettings.Manual); => _editor.ChangeBonusItem(_object, Slot, item, ApplySettings.Manual);
public readonly void SetApplyItem(bool value) public readonly void SetApplyItem(bool value)
@ -30,8 +30,8 @@ public struct BonusDrawData(BonusItemFlag slot, in DesignData designData)
manager.ChangeApplyBonusItem(design, Slot, value); manager.ChangeApplyBonusItem(design, Slot, value);
} }
public BonusItem CurrentItem = designData.BonusItem(slot); public EquipItem CurrentItem = designData.BonusItem(slot);
public BonusItem GameItem = default; public EquipItem GameItem = default;
public bool CurrentApply; public bool CurrentApply;
public static BonusDrawData FromDesign(DesignManager manager, Design design, BonusItemFlag slot) public static BonusDrawData FromDesign(DesignManager manager, Design design, BonusItemFlag slot)

View file

@ -13,11 +13,11 @@ using Penumbra.GameData.Structs;
namespace Glamourer.Gui.Equipment; namespace Glamourer.Gui.Equipment;
public sealed class BonusItemCombo : FilterComboCache<BonusItem> public sealed class BonusItemCombo : FilterComboCache<EquipItem>
{ {
private readonly FavoriteManager _favorites; private readonly FavoriteManager _favorites;
public readonly string Label; public readonly string Label;
private BonusItemId _currentItem; private CustomItemId _currentItem;
private float _innerWidth; private float _innerWidth;
public PrimaryId CustomSetId { get; private set; } public PrimaryId CustomSetId { get; private set; }
@ -75,14 +75,14 @@ public sealed class BonusItemCombo : FilterComboCache<BonusItem>
var ret = ImGui.Selectable(name, selected); var ret = ImGui.Selectable(name, selected);
ImGui.SameLine(); ImGui.SameLine();
using var color = ImRaii.PushColor(ImGuiCol.Text, 0xFF808080); using var color = ImRaii.PushColor(ImGuiCol.Text, 0xFF808080);
ImGuiUtil.RightAlign($"({obj.ModelId.Id}-{obj.Variant.Id})"); ImGuiUtil.RightAlign($"({obj.PrimaryId.Id}-{obj.Variant.Id})");
return ret; return ret;
} }
protected override bool IsVisible(int globalIndex, LowerString filter) 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(BonusItem obj) protected override string ToString(EquipItem obj)
=> obj.Name; => obj.Name;
private static string GetLabel(IDataManager gameData, BonusItemFlag slot) private static string GetLabel(IDataManager gameData, BonusItemFlag slot)
@ -98,13 +98,10 @@ public sealed class BonusItemCombo : FilterComboCache<BonusItem>
}; };
} }
private static List<BonusItem> GetItems(FavoriteManager favorites, ItemManager items, BonusItemFlag slot) private static List<EquipItem> GetItems(FavoriteManager favorites, ItemManager items, BonusItemFlag slot)
{ {
var nothing = BonusItem.Empty(slot); var nothing = EquipItem.BonusItemNothing(slot);
if (slot is not BonusItemFlag.Glasses) return items.ItemData.ByType[slot.ToEquipType()].OrderByDescending(favorites.Contains).ThenBy(i => i.Id.Id).Prepend(nothing).ToList();
return [nothing];
return items.DictBonusItems.Values.OrderByDescending(favorites.Contains).ThenBy(i => i.Id.Id).Prepend(nothing).ToList();
} }
protected override void OnClosePopup() protected override void OnClosePopup()

View file

@ -468,14 +468,14 @@ public class EquipmentDrawer
UiHelpers.OpenCombo($"##{combo.Label}"); UiHelpers.OpenCombo($"##{combo.Label}");
using var disabled = ImRaii.Disabled(data.Locked); using var disabled = ImRaii.Disabled(data.Locked);
var change = combo.Draw(data.CurrentItem.Name, data.CurrentItem.Id, small ? _comboLength - ImGui.GetFrameHeight() : _comboLength, var change = combo.Draw(data.CurrentItem.Name, data.CurrentItem.Id.BonusItem, small ? _comboLength - ImGui.GetFrameHeight() : _comboLength,
_requiredComboWidth); _requiredComboWidth);
if (change) if (change)
data.SetItem(combo.CurrentSelection); data.SetItem(combo.CurrentSelection);
else if (combo.CustomVariant.Id > 0) else if (combo.CustomVariant.Id > 0)
data.SetItem(_items.Identify(data.Slot, combo.CustomSetId, combo.CustomVariant)); data.SetItem(_items.Identify(data.Slot, combo.CustomSetId, combo.CustomVariant));
if (ResetOrClear(data.Locked, clear, data.AllowRevert, true, data.CurrentItem, data.GameItem, BonusItem.Empty(data.Slot), out var item)) if (ResetOrClear(data.Locked, clear, data.AllowRevert, true, data.CurrentItem, data.GameItem, EquipItem.BonusItemNothing(data.Slot), out var item))
data.SetItem(item); data.SetItem(item);
} }

View file

@ -305,7 +305,7 @@ public sealed unsafe class AdvancedDyePopup(
{ {
EquipSlot.MainHand => _state.ModelData.Weapon(EquipSlot.MainHand), EquipSlot.MainHand => _state.ModelData.Weapon(EquipSlot.MainHand),
EquipSlot.OffHand => _state.ModelData.Weapon(EquipSlot.OffHand), EquipSlot.OffHand => _state.ModelData.Weapon(EquipSlot.OffHand),
EquipSlot.Unknown => _state.ModelData.BonusItem((index.SlotIndex - 16u).ToBonusSlot()).ToArmor().ToWeapon(0), EquipSlot.Unknown => _state.ModelData.BonusItem((index.SlotIndex - 16u).ToBonusSlot()).Armor().ToWeapon(0), // TODO: Handle better
_ => _state.ModelData.Armor(slot).ToWeapon(0), _ => _state.ModelData.Armor(slot).ToWeapon(0),
}; };
value = new MaterialValueState(internalRow, internalRow, weapon, StateSource.Manual); value = new MaterialValueState(internalRow, internalRow, weapon, StateSource.Manual);

View file

@ -248,7 +248,7 @@ public unsafe class ModelEvaluationPanel(
{ {
var glassesId = actor.GetBonusItem(slot); var glassesId = actor.GetBonusItem(slot);
if (bonusItems.TryGetValue(glassesId, out var glasses)) if (bonusItems.TryGetValue(glassesId, out var glasses))
ImGuiUtil.DrawTableColumn($"{glasses.ModelId.Id},{glasses.Variant.Id} ({glassesId})"); ImGuiUtil.DrawTableColumn($"{glasses.PrimaryId.Id},{glasses.Variant.Id} ({glassesId})");
else else
ImGuiUtil.DrawTableColumn($"{glassesId}"); ImGuiUtil.DrawTableColumn($"{glassesId}");
} }

View file

@ -180,11 +180,11 @@ public class UnlockOverview(
if (remainder > 0) if (remainder > 0)
ImGuiClip.DrawEndDummy(remainder, iconSize.Y + spacing.Y); ImGuiClip.DrawEndDummy(remainder, iconSize.Y + spacing.Y);
void DrawItem(BonusItem item) void DrawItem(EquipItem item)
{ {
// TODO check unlocks // TODO check unlocks
var unlocked = true; var unlocked = true;
if (!textures.TryLoadIcon(item.Icon.Id, out var iconHandle)) if (!textures.TryLoadIcon(item.IconId.Id, out var iconHandle))
return; return;
var (icon, size) = (iconHandle.ImGuiHandle, new Vector2(iconHandle.Width, iconHandle.Height)); var (icon, size) = (iconHandle.ImGuiHandle, new Vector2(iconHandle.Width, iconHandle.Height));
@ -202,9 +202,9 @@ public class UnlockOverview(
if (size.X >= iconSize.X && size.Y >= iconSize.Y) if (size.X >= iconSize.X && size.Y >= iconSize.Y)
ImGui.Image(icon, size); ImGui.Image(icon, size);
ImUtf8.Text(item.Name); ImUtf8.Text(item.Name);
ImUtf8.Text($"{item.Slot.ToName()}"); ImUtf8.Text($"{item.Type.ToName()}");
ImUtf8.Text($"{item.Id.Id}"); ImUtf8.Text($"{item.Id.Id}");
ImUtf8.Text($"{item.ModelId.Id}-{item.Variant.Id}"); ImUtf8.Text($"{item.PrimaryId.Id}-{item.Variant.Id}");
// TODO // TODO
ImUtf8.Text("Always Unlocked"); // : $"Unlocked on {time:g}" : "Not Unlocked."); ImUtf8.Text("Always Unlocked"); // : $"Unlocked on {time:g}" : "Not Unlocked.");
// TODO // TODO

View file

@ -44,9 +44,9 @@ public static class UiHelpers
} }
} }
public static void DrawIcon(this BonusItem item, TextureService textures, Vector2 size, BonusItemFlag slot) public static void DrawIcon(this EquipItem item, TextureService textures, Vector2 size, BonusItemFlag slot)
{ {
var isEmpty = item.ModelId.Id == 0; var isEmpty = item.PrimaryId.Id == 0;
var (ptr, textureSize, empty) = textures.GetIcon(item, slot); var (ptr, textureSize, empty) = textures.GetIcon(item, slot);
if (empty) if (empty)
{ {
@ -149,27 +149,6 @@ public static class UiHelpers
} }
public static bool DrawFavoriteStar(FavoriteManager favorites, BonusItem item)
{
var favorite = favorites.Contains(item);
var hovering = ImGui.IsMouseHoveringRect(ImGui.GetCursorScreenPos(),
ImGui.GetCursorScreenPos() + new Vector2(ImGui.GetTextLineHeight()));
using var font = ImRaii.PushFont(UiBuilder.IconFont);
using var c = ImRaii.PushColor(ImGuiCol.Text,
hovering ? ColorId.FavoriteStarHovered.Value() : favorite ? ColorId.FavoriteStarOn.Value() : ColorId.FavoriteStarOff.Value());
ImGui.TextUnformatted(FontAwesomeIcon.Star.ToIconString());
if (!ImGui.IsItemClicked())
return false;
if (favorite)
favorites.Remove(item);
else
favorites.TryAdd(item);
return true;
}
public static bool DrawFavoriteStar(FavoriteManager favorites, StainId stain) public static bool DrawFavoriteStar(FavoriteManager favorites, StainId stain)
{ {
var favorite = favorites.Contains(stain); var favorite = favorites.Contains(stain);

View file

@ -104,11 +104,11 @@ public sealed class CharaFile
if (id is 0) if (id is 0)
{ {
data.SetBonusItem(slot, BonusItem.Empty(slot)); data.SetBonusItem(slot, EquipItem.BonusItemNothing(slot));
flags |= slot; flags |= slot;
} }
if (!items.DictBonusItems.TryGetValue((BonusItemId)id.Value, out var item) || item.Slot != slot) if (!items.DictBonusItems.TryGetValue((BonusItemId)id.Value, out var item) || item.Type.ToBonus() != slot)
return; return;
data.SetBonusItem(slot, item); data.SetBonusItem(slot, item);

View file

@ -58,7 +58,7 @@ public unsafe class UpdateSlotService : IDisposable
if (!_bonusItems.TryGetValue(id, out var glasses)) if (!_bonusItems.TryGetValue(id, out var glasses))
return; return;
var armor = new CharacterArmor(glasses.ModelId, glasses.Variant, StainIds.None); var armor = new CharacterArmor(glasses.PrimaryId, glasses.Variant, StainIds.None);
_flagBonusSlotForUpdateHook.Original(drawObject.Address, BonusItemFlag.Glasses.ToIndex(), &armor); _flagBonusSlotForUpdateHook.Original(drawObject.Address, BonusItemFlag.Glasses.ToIndex(), &armor);
} }

View file

@ -10,7 +10,7 @@ namespace Glamourer.Services;
public class ItemManager public class ItemManager
{ {
public const string Nothing = "Nothing"; public const string Nothing = EquipItem.Nothing;
public const string SmallClothesNpc = "Smallclothes (NPC)"; public const string SmallClothesNpc = "Smallclothes (NPC)";
public const ushort SmallClothesNpcModel = 9903; public const ushort SmallClothesNpcModel = 9903;
@ -126,31 +126,20 @@ public class ItemManager
} }
} }
public BonusItem Identify(BonusItemFlag slot, PrimaryId id, Variant variant) public EquipItem Identify(BonusItemFlag slot, PrimaryId id, Variant variant)
{ {
var index = slot.ToIndex(); var index = slot.ToIndex();
if (index == uint.MaxValue) if (index == uint.MaxValue)
return new BonusItem($"Invalid ({id.Id}-{variant})", 0, 0, id, variant, slot); return new EquipItem($"Invalid ({id.Id}-{variant})", 0, 0, id, 0, variant, slot.ToEquipType(), 0, 0, 0);
var item = ObjectIdentification.Identify(id, variant, slot) return ObjectIdentification.Identify(id, variant, slot)
.FirstOrDefault(BonusItem.FromIds(BonusItemId.Invalid, 0, id, variant, slot)); .FirstOrDefault(new EquipItem($"Invalid ({id.Id}-{variant})", 0, 0, id, 0, variant, slot.ToEquipType(), 0, 0, 0));
if (item.Id != BonusItemId.Invalid)
return item;
if (slot is BonusItemFlag.Glasses)
{
var headItem = ObjectIdentification.Identify(id, 0, variant, EquipSlot.Head).FirstOrDefault();
if (headItem.Valid)
return BonusItem.FromIds(BonusItemId.Invalid, headItem.IconId, id, variant, slot, $"{headItem.Name} ({EquipSlot.Head.ToName()}: {id}-{variant})");
}
return item;
} }
public BonusItem Resolve(BonusItemFlag slot, BonusItemId id) public EquipItem Resolve(BonusItemFlag slot, BonusItemId id)
=> IsBonusItemValid(slot, id, out var item) ? item : new BonusItem($"Invalid ({id.Id})", 0, id, 0, 0, slot); => IsBonusItemValid(slot, id, out var item) ? item : new EquipItem($"Invalid ({id.Id})", id, 0, 0, 0, 0, slot.ToEquipType(), 0, 0, 0);
public BonusItem Resolve(BonusItemFlag slot, CustomItemId id) public EquipItem Resolve(BonusItemFlag slot, CustomItemId id)
{ {
// Only from early designs as migration. // Only from early designs as migration.
if (!id.IsBonusItem) if (!id.IsBonusItem)
@ -164,12 +153,12 @@ public class ItemManager
if (IsBonusItemValid(slot, id.BonusItem, out var item)) if (IsBonusItemValid(slot, id.BonusItem, out var item))
return item; return item;
return BonusItem.Empty(slot); return EquipItem.BonusItemNothing(slot);
} }
var (model, variant, slot2) = id.SplitBonus; var (model, variant, slot2) = id.SplitBonus;
if (slot != slot2) if (slot != slot2)
return BonusItem.Empty(slot); return EquipItem.BonusItemNothing(slot);
return Identify(slot, model, variant); return Identify(slot, model, variant);
} }
@ -213,12 +202,12 @@ public class ItemManager
/// <summary> Returns whether a bonus item id represents a valid item for a slot and gives the item. </summary> /// <summary> Returns whether a bonus item id represents a valid item for a slot and gives the item. </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)] [MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public bool IsBonusItemValid(BonusItemFlag slot, BonusItemId itemId, out BonusItem item) public bool IsBonusItemValid(BonusItemFlag slot, BonusItemId itemId, out EquipItem item)
{ {
if (itemId.Id != 0) if (itemId.Id != 0)
return DictBonusItems.TryGetValue(itemId, out item) && slot == item.Slot; return DictBonusItems.TryGetValue(itemId, out item) && slot == item.Type.ToBonus();
item = BonusItem.Empty(slot); item = new EquipItem(Nothing, (BonusItemId)0, 0, 0, 0, 0, slot.ToEquipType(), 0, 0, 0);
return true; return true;
} }

View file

@ -23,9 +23,9 @@ public sealed class TextureService(IUiBuilder uiBuilder, IDataManager dataManage
: (nint.Zero, Vector2.Zero, true); : (nint.Zero, Vector2.Zero, true);
} }
public (nint, Vector2, bool) GetIcon(BonusItem item, BonusItemFlag slot) public (nint, Vector2, bool) GetIcon(EquipItem item, BonusItemFlag slot)
{ {
if (item.Icon.Id != 0 && TryLoadIcon(item.Icon.Id, out var ret)) if (item.IconId.Id != 0 && TryLoadIcon(item.IconId.Id, out var ret))
return (ret.ImGuiHandle, new Vector2(ret.Width, ret.Height), false); return (ret.ImGuiHandle, new Vector2(ret.Width, ret.Height), false);
var idx = slot.ToIndex(); var idx = slot.ToIndex();

View file

@ -152,7 +152,7 @@ public class InternalStateEditor(
} }
/// <summary> Change a single bonus item. </summary> /// <summary> Change a single bonus item. </summary>
public bool ChangeBonusItem(ActorState state, BonusItemFlag slot, BonusItem item, StateSource source, out BonusItem oldItem, uint key = 0) public bool ChangeBonusItem(ActorState state, BonusItemFlag slot, EquipItem item, StateSource source, out EquipItem oldItem, uint key = 0)
{ {
oldItem = state.ModelData.BonusItem(slot); oldItem = state.ModelData.BonusItem(slot);
if (!state.CanUnlock(key)) if (!state.CanUnlock(key))

View file

@ -146,7 +146,7 @@ public class StateApplier(
if (apply) if (apply)
{ {
var item = state.ModelData.BonusItem(slot); var item = state.ModelData.BonusItem(slot);
ChangeBonusItem(data, slot, item.ModelId, item.Variant); ChangeBonusItem(data, slot, item.PrimaryId, item.Variant);
} }
return data; return data;
@ -391,7 +391,7 @@ public class StateApplier(
foreach (var slot in BonusExtensions.AllFlags) foreach (var slot in BonusExtensions.AllFlags)
{ {
var item = state.ModelData.BonusItem(slot); var item = state.ModelData.BonusItem(slot);
ChangeBonusItem(actors, slot, item.ModelId, item.Variant); ChangeBonusItem(actors, slot, item.PrimaryId, item.Variant);
} }
var mainhandActors = state.ModelData.MainhandType != state.BaseData.MainhandType ? actors.OnlyGPose() : actors; var mainhandActors = state.ModelData.MainhandType != state.BaseData.MainhandType ? actors.OnlyGPose() : actors;

View file

@ -109,7 +109,7 @@ public class StateEditor(
} }
} }
public void ChangeBonusItem(object data, BonusItemFlag slot, BonusItem item, ApplySettings settings = default) public void ChangeBonusItem(object data, BonusItemFlag slot, EquipItem item, ApplySettings settings = default)
{ {
var state = (ActorState)data; var state = (ActorState)data;
if (!Editor.ChangeBonusItem(state, slot, item, settings.Source, out var old, settings.Key)) if (!Editor.ChangeBonusItem(state, slot, item, settings.Source, out var old, settings.Key))

View file

@ -250,11 +250,11 @@ public class StateListener : IDisposable
else else
apply = true; apply = true;
if (apply) if (apply)
item = state.ModelData.BonusItem(slot).ToArmor(); item = state.ModelData.BonusItem(slot).Armor();
break; break;
// Use current model data. // Use current model data.
case UpdateState.NoChange: case UpdateState.NoChange:
item = state.ModelData.BonusItem(slot).ToArmor(); item = state.ModelData.BonusItem(slot).Armor();
break; break;
case UpdateState.Transformed: break; case UpdateState.Transformed: break;
} }
@ -453,12 +453,12 @@ public class StateListener : IDisposable
return UpdateState.NoChange; return UpdateState.NoChange;
// The actor item does not correspond to the model item, thus the actor is transformed. // The actor item does not correspond to the model item, thus the actor is transformed.
if (actorItem.ModelId != item.Set || actorItem.Variant != item.Variant) if (actorItem.PrimaryId != item.Set || actorItem.Variant != item.Variant)
return UpdateState.Transformed; return UpdateState.Transformed;
var baseData = state.BaseData.BonusItem(slot); var baseData = state.BaseData.BonusItem(slot);
var change = UpdateState.NoChange; var change = UpdateState.NoChange;
if (baseData.Id != actorItem.Id || baseData.ModelId != item.Set || baseData.Variant != item.Variant) if (baseData.Id != actorItem.Id || baseData.PrimaryId != item.Set || baseData.Variant != item.Variant)
{ {
var identified = _items.Identify(slot, item.Set, item.Variant); var identified = _items.Identify(slot, item.Set, item.Variant);
state.BaseData.SetBonusItem(slot, identified); state.BaseData.SetBonusItem(slot, identified);

View file

@ -356,7 +356,7 @@ public sealed class StateManager(
foreach (var slot in BonusExtensions.AllFlags) foreach (var slot in BonusExtensions.AllFlags)
{ {
var item = state.ModelData.BonusItem(slot); var item = state.ModelData.BonusItem(slot);
Applier.ChangeBonusItem(actors, slot, item.ModelId, item.Variant); Applier.ChangeBonusItem(actors, slot, item.PrimaryId, item.Variant);
} }
var mainhandActors = state.ModelData.MainhandType != state.BaseData.MainhandType ? actors.OnlyGPose() : actors; var mainhandActors = state.ModelData.MainhandType != state.BaseData.MainhandType ? actors.OnlyGPose() : actors;

View file

@ -12,7 +12,7 @@ public class FavoriteManager : ISavable
private readonly record struct FavoriteHairStyle(Gender Gender, SubRace Race, CustomizeIndex Type, CustomizeValue Id) private readonly record struct FavoriteHairStyle(Gender Gender, SubRace Race, CustomizeIndex Type, CustomizeValue Id)
{ {
public uint ToValue() public uint ToValue()
=> (uint)Id.Value | ((uint)Type << 8) | ((uint)Race << 16) | ((uint)Gender << 24); => Id.Value | ((uint)Type << 8) | ((uint)Race << 16) | ((uint)Gender << 24);
public FavoriteHairStyle(uint value) public FavoriteHairStyle(uint value)
: this((Gender)((value >> 24) & 0xFF), (SubRace)((value >> 16) & 0xFF), (CustomizeIndex)((value >> 8) & 0xFF), : this((Gender)((value >> 24) & 0xFF), (SubRace)((value >> 16) & 0xFF), (CustomizeIndex)((value >> 8) & 0xFF),
@ -61,9 +61,9 @@ public class FavoriteManager : ISavable
{ {
case 1: case 1:
_favorites.UnionWith(load!.FavoriteItems.Select(i => (ItemId)i)); _favorites.UnionWith(load!.FavoriteItems.Select(i => (ItemId)i));
_favoriteColors.UnionWith(load!.FavoriteColors.Select(i => (StainId)i)); _favoriteColors.UnionWith(load.FavoriteColors.Select(i => (StainId)i));
_favoriteHairStyles.UnionWith(load!.FavoriteHairStyles.Select(t => new FavoriteHairStyle(t))); _favoriteHairStyles.UnionWith(load.FavoriteHairStyles.Select(t => new FavoriteHairStyle(t)));
_favoriteBonusItems.UnionWith(load!.FavoriteBonusItems.Select(b => new BonusItemId(b))); _favoriteBonusItems.UnionWith(load.FavoriteBonusItems.Select(b => new BonusItemId(b)));
break; break;
default: throw new Exception($"Unknown Version {load?.Version ?? 0}"); default: throw new Exception($"Unknown Version {load?.Version ?? 0}");
@ -126,7 +126,12 @@ public class FavoriteManager : ISavable
} }
public bool TryAdd(EquipItem item) public bool TryAdd(EquipItem item)
=> TryAdd(item.ItemId); {
if (item.Id.IsBonusItem)
return TryAdd(item.Id.BonusItem);
return TryAdd(item.ItemId);
}
public bool TryAdd(ItemId item) public bool TryAdd(ItemId item)
{ {
@ -137,18 +142,18 @@ public class FavoriteManager : ISavable
return true; return true;
} }
public bool TryAdd(StainId stain) public bool TryAdd(BonusItemId item)
{ {
if (stain.Id == 0 || !_favoriteColors.Add(stain)) if (item.Id == 0 || !_favoriteBonusItems.Add(item))
return false; return false;
Save(); Save();
return true; return true;
} }
public bool TryAdd(BonusItem bonusItem) public bool TryAdd(StainId stain)
{ {
if (bonusItem.Id == 0 || !_favoriteBonusItems.Add(bonusItem.Id)) if (stain.Id == 0 || !_favoriteColors.Add(stain))
return false; return false;
Save(); Save();
@ -165,7 +170,11 @@ public class FavoriteManager : ISavable
} }
public bool Remove(EquipItem item) public bool Remove(EquipItem item)
=> Remove(item.ItemId); {
if (item.Id.IsBonusItem)
Remove(item.Id.BonusItem);
return Remove(item.ItemId);
}
public bool Remove(ItemId item) public bool Remove(ItemId item)
{ {
@ -176,18 +185,18 @@ public class FavoriteManager : ISavable
return true; return true;
} }
public bool Remove(StainId stain) public bool Remove(BonusItemId item)
{ {
if (!_favoriteColors.Remove(stain)) if (!_favoriteBonusItems.Remove(item))
return false; return false;
Save(); Save();
return true; return true;
} }
public bool Remove(BonusItem bonusItem) public bool Remove(StainId stain)
{ {
if (!_favoriteBonusItems.Remove(bonusItem.Id)) if (!_favoriteColors.Remove(stain))
return false; return false;
Save(); Save();
@ -204,13 +213,21 @@ public class FavoriteManager : ISavable
} }
public bool Contains(EquipItem item) public bool Contains(EquipItem item)
=> _favorites.Contains(item.ItemId); {
if (item.Id.IsBonusItem)
return _favoriteBonusItems.Contains(item.Id.BonusItem);
return _favorites.Contains(item.ItemId);
}
public bool Contains(StainId stain) public bool Contains(StainId stain)
=> _favoriteColors.Contains(stain); => _favoriteColors.Contains(stain);
public bool Contains(BonusItem bonusItem) public bool Contains(ItemId itemId)
=> _favoriteBonusItems.Contains(bonusItem.Id); => _favorites.Contains(itemId);
public bool Contains(BonusItemId bonusItemId)
=> _favoriteBonusItems.Contains(bonusItemId);
public bool Contains(Gender gender, SubRace race, CustomizeIndex type, CustomizeValue value) public bool Contains(Gender gender, SubRace race, CustomizeIndex type, CustomizeValue value)
=> _favoriteHairStyles.Contains(new FavoriteHairStyle(gender, race, type, value)); => _favoriteHairStyles.Contains(new FavoriteHairStyle(gender, race, type, value));

@ -1 +1 @@
Subproject commit dd86dafb88ca4c7b662938bbc1310729ba7f788d Subproject commit 2f6acca678b71203763ac4404c3f054747c14f75