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);
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);
return GlamourerApiEc.Success;
}
@ -111,7 +111,7 @@ public class ItemsApi(ApiHelpers helpers, ItemManager itemManager, StateManager
continue;
anyUnlocked = true;
stateManager.ChangeBonusItem(state, item.Slot, item, settings);
stateManager.ChangeBonusItem(state, item.Type.ToBonus(), item, settings);
ApiHelpers.Lock(state, key, flags);
}
@ -138,7 +138,7 @@ public class ItemsApi(ApiHelpers helpers, ItemManager itemManager, StateManager
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
{

View file

@ -281,7 +281,7 @@ public class DesignBase
var item = _designData.BonusItem(slot);
ret[slot.ToString()] = new JObject()
{
["BonusId"] = item.CustomId.Id,
["BonusId"] = item.Id.Id,
["Apply"] = DoApplyBonusItem(slot),
};
}
@ -443,7 +443,7 @@ public class DesignBase
if (json[slot.ToString()] is not JObject itemJson)
{
design.Application.BonusItem &= ~slot;
design.GetDesignDataRef().SetBonusItem(slot, BonusItem.Empty(slot));
design.GetDesignDataRef().SetBonusItem(slot, EquipItem.BonusItemNothing(slot));
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
{
// @formatter:off
BonusItemFlag.Glasses => Penumbra.GameData.Structs.BonusItem.FromIds(_bonusIds[0], _iconIds[12], _bonusModelIds[0], _bonusVariants[0], BonusItemFlag.Glasses, _nameGlasses),
_ => Penumbra.GameData.Structs.BonusItem.Empty(slot),
BonusItemFlag.Glasses => EquipItem.FromIds(_bonusIds[0], _iconIds[12], _bonusModelIds[0], 0, _bonusVariants[0], FullEquipType.Glasses, 0, _nameGlasses),
_ => EquipItem.BonusItemNothing(slot),
// @formatter:on
};
@ -191,15 +191,15 @@ public unsafe struct DesignData
return true;
}
public bool SetBonusItem(BonusItemFlag slot, BonusItem item)
public bool SetBonusItem(BonusItemFlag slot, EquipItem item)
{
var index = slot.ToIndex();
if (index > NumBonusItems)
return false;
_iconIds[NumEquipment + NumWeapons + index] = item.Icon.Id;
_bonusIds[index] = item.Id.Id;
_bonusModelIds[index] = item.ModelId.Id;
_iconIds[NumEquipment + NumWeapons + index] = item.IconId.Id;
_bonusIds[index] = item.Id.BonusItem.Id;
_bonusModelIds[index] = item.PrimaryId.Id;
_bonusVariants[index] = item.Variant.Id;
switch (index)
{
@ -330,7 +330,7 @@ public unsafe struct DesignData
public void SetDefaultBonusItems()
{
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/>
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;
if (item.Slot != slot)
if (item.Type.ToBonus() != slot)
return;
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);
}
public readonly record struct BonusItemTransaction(BonusItemFlag Slot, BonusItem Old, BonusItem New)
public readonly record struct BonusItemTransaction(BonusItemFlag Slot, EquipItem Old, EquipItem New)
: ITransaction
{
public ITransaction? Merge(ITransaction older)

View file

@ -65,7 +65,7 @@ public interface IDesignEditor
=> ChangeEquip(data, slot, item, null, settings);
/// <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>
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.Services;
using Glamourer.State;
using OtterGui;
using OtterGui.Classes;
using OtterGui.Log;
using OtterGui.Services;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Files;
namespace Glamourer;
@ -44,6 +47,15 @@ public class Glamourer : IDalamudPlugin
_services.GetService<CommandService>(); // initialize commands.
_services.GetService<IpcProviders>(); // initialize IPC.
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
{

View file

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

View file

@ -13,11 +13,11 @@ using Penumbra.GameData.Structs;
namespace Glamourer.Gui.Equipment;
public sealed class BonusItemCombo : FilterComboCache<BonusItem>
public sealed class BonusItemCombo : FilterComboCache<EquipItem>
{
private readonly FavoriteManager _favorites;
public readonly string Label;
private BonusItemId _currentItem;
private CustomItemId _currentItem;
private float _innerWidth;
public PrimaryId CustomSetId { get; private set; }
@ -75,14 +75,14 @@ public sealed class BonusItemCombo : FilterComboCache<BonusItem>
var ret = ImGui.Selectable(name, selected);
ImGui.SameLine();
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;
}
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;
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);
if (slot is not BonusItemFlag.Glasses)
return [nothing];
return items.DictBonusItems.Values.OrderByDescending(favorites.Contains).ThenBy(i => i.Id.Id).Prepend(nothing).ToList();
var nothing = EquipItem.BonusItemNothing(slot);
return items.ItemData.ByType[slot.ToEquipType()].OrderByDescending(favorites.Contains).ThenBy(i => i.Id.Id).Prepend(nothing).ToList();
}
protected override void OnClosePopup()

View file

@ -468,14 +468,14 @@ public class EquipmentDrawer
UiHelpers.OpenCombo($"##{combo.Label}");
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);
if (change)
data.SetItem(combo.CurrentSelection);
else if (combo.CustomVariant.Id > 0)
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);
}

View file

@ -305,7 +305,7 @@ public sealed unsafe class AdvancedDyePopup(
{
EquipSlot.MainHand => _state.ModelData.Weapon(EquipSlot.MainHand),
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),
};
value = new MaterialValueState(internalRow, internalRow, weapon, StateSource.Manual);

View file

@ -248,7 +248,7 @@ public unsafe class ModelEvaluationPanel(
{
var glassesId = actor.GetBonusItem(slot);
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
ImGuiUtil.DrawTableColumn($"{glassesId}");
}

View file

@ -180,11 +180,11 @@ public class UnlockOverview(
if (remainder > 0)
ImGuiClip.DrawEndDummy(remainder, iconSize.Y + spacing.Y);
void DrawItem(BonusItem item)
void DrawItem(EquipItem item)
{
// TODO check unlocks
var unlocked = true;
if (!textures.TryLoadIcon(item.Icon.Id, out var iconHandle))
if (!textures.TryLoadIcon(item.IconId.Id, out var iconHandle))
return;
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)
ImGui.Image(icon, size);
ImUtf8.Text(item.Name);
ImUtf8.Text($"{item.Slot.ToName()}");
ImUtf8.Text($"{item.Type.ToName()}");
ImUtf8.Text($"{item.Id.Id}");
ImUtf8.Text($"{item.ModelId.Id}-{item.Variant.Id}");
ImUtf8.Text($"{item.PrimaryId.Id}-{item.Variant.Id}");
// TODO
ImUtf8.Text("Always Unlocked"); // : $"Unlocked on {time:g}" : "Not Unlocked.");
// 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);
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)
{
var favorite = favorites.Contains(stain);

View file

@ -104,11 +104,11 @@ public sealed class CharaFile
if (id is 0)
{
data.SetBonusItem(slot, BonusItem.Empty(slot));
data.SetBonusItem(slot, EquipItem.BonusItemNothing(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;
data.SetBonusItem(slot, item);

View file

@ -58,7 +58,7 @@ public unsafe class UpdateSlotService : IDisposable
if (!_bonusItems.TryGetValue(id, out var glasses))
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);
}

View file

@ -10,7 +10,7 @@ namespace Glamourer.Services;
public class ItemManager
{
public const string Nothing = "Nothing";
public const string Nothing = EquipItem.Nothing;
public const string SmallClothesNpc = "Smallclothes (NPC)";
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();
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)
.FirstOrDefault(BonusItem.FromIds(BonusItemId.Invalid, 0, id, variant, slot));
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 ObjectIdentification.Identify(id, variant, slot)
.FirstOrDefault(new EquipItem($"Invalid ({id.Id}-{variant})", 0, 0, id, 0, variant, slot.ToEquipType(), 0, 0, 0));
}
return item;
}
public EquipItem Resolve(BonusItemFlag slot, BonusItemId id)
=> 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, BonusItemId id)
=> IsBonusItemValid(slot, id, out var item) ? item : new BonusItem($"Invalid ({id.Id})", 0, id, 0, 0, slot);
public BonusItem Resolve(BonusItemFlag slot, CustomItemId id)
public EquipItem Resolve(BonusItemFlag slot, CustomItemId id)
{
// Only from early designs as migration.
if (!id.IsBonusItem)
@ -164,12 +153,12 @@ public class ItemManager
if (IsBonusItemValid(slot, id.BonusItem, out var item))
return item;
return BonusItem.Empty(slot);
return EquipItem.BonusItemNothing(slot);
}
var (model, variant, slot2) = id.SplitBonus;
if (slot != slot2)
return BonusItem.Empty(slot);
return EquipItem.BonusItemNothing(slot);
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>
[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)
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;
}

View file

@ -23,9 +23,9 @@ public sealed class TextureService(IUiBuilder uiBuilder, IDataManager dataManage
: (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);
var idx = slot.ToIndex();

View file

@ -152,7 +152,7 @@ public class InternalStateEditor(
}
/// <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);
if (!state.CanUnlock(key))

View file

@ -146,7 +146,7 @@ public class StateApplier(
if (apply)
{
var item = state.ModelData.BonusItem(slot);
ChangeBonusItem(data, slot, item.ModelId, item.Variant);
ChangeBonusItem(data, slot, item.PrimaryId, item.Variant);
}
return data;
@ -391,7 +391,7 @@ public class StateApplier(
foreach (var slot in BonusExtensions.AllFlags)
{
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;

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;
if (!Editor.ChangeBonusItem(state, slot, item, settings.Source, out var old, settings.Key))

View file

@ -250,11 +250,11 @@ public class StateListener : IDisposable
else
apply = true;
if (apply)
item = state.ModelData.BonusItem(slot).ToArmor();
item = state.ModelData.BonusItem(slot).Armor();
break;
// Use current model data.
case UpdateState.NoChange:
item = state.ModelData.BonusItem(slot).ToArmor();
item = state.ModelData.BonusItem(slot).Armor();
break;
case UpdateState.Transformed: break;
}
@ -453,12 +453,12 @@ public class StateListener : IDisposable
return UpdateState.NoChange;
// 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;
var baseData = state.BaseData.BonusItem(slot);
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);
state.BaseData.SetBonusItem(slot, identified);

View file

@ -356,7 +356,7 @@ public sealed class StateManager(
foreach (var slot in BonusExtensions.AllFlags)
{
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;

View file

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

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