More stains, more misc fixes

I made my best to try to fix as much as I could. I have no idea if it is correct but I hope it is and I hope this will be helpful for ApiX / Dawntrail update.
This commit is contained in:
Aspher0 2024-07-11 09:21:26 +02:00
parent 8a954e6e09
commit e9d1efccdc
25 changed files with 117 additions and 110 deletions

View file

@ -257,10 +257,10 @@ public class DesignBase
foreach (var slot in EquipSlotExtensions.EqdpSlots.Prepend(EquipSlot.OffHand).Prepend(EquipSlot.MainHand)) foreach (var slot in EquipSlotExtensions.EqdpSlots.Prepend(EquipSlot.OffHand).Prepend(EquipSlot.MainHand))
{ {
var item = _designData.Item(slot); var item = _designData.Item(slot);
var stain = _designData.Stain(slot); var stains = _designData.Stain(slot);
var crestSlot = slot.ToCrestFlag(); var crestSlot = slot.ToCrestFlag();
var crest = _designData.Crest(crestSlot); var crest = _designData.Crest(crestSlot);
ret[slot.ToString()] = Serialize(item.Id, stain, crest, DoApplyEquip(slot), DoApplyStain(slot), DoApplyCrest(crestSlot)); ret[slot.ToString()] = Serialize(item.Id, stains, crest, DoApplyEquip(slot), DoApplyStain(slot), DoApplyCrest(crestSlot));
} }
ret["Hat"] = new QuadBool(_designData.IsHatVisible(), DoApplyMeta(MetaIndex.HatState)).ToJObject("Show", "Apply"); ret["Hat"] = new QuadBool(_designData.IsHatVisible(), DoApplyMeta(MetaIndex.HatState)).ToJObject("Show", "Apply");
@ -274,11 +274,11 @@ public class DesignBase
return ret; return ret;
static JObject Serialize(CustomItemId id, StainId stain, bool crest, bool apply, bool applyStain, bool applyCrest) static JObject Serialize(CustomItemId id, StainIds stains, bool crest, bool apply, bool applyStain, bool applyCrest)
=> new() => new()
{ {
["ItemId"] = id.Id, ["ItemId"] = id.Id,
["Stain"] = stain.Id, ["Stain"] = stains.ToString(),
["Crest"] = crest, ["Crest"] = crest,
["Apply"] = apply, ["Apply"] = apply,
["ApplyStain"] = applyStain, ["ApplyStain"] = applyStain,
@ -522,15 +522,15 @@ public class DesignBase
return; return;
} }
static (CustomItemId, StainId, bool, bool, bool, bool) ParseItem(EquipSlot slot, JToken? item) static (CustomItemId, StainIds, bool, bool, bool, bool) ParseItem(EquipSlot slot, JToken? item)
{ {
var id = item?["ItemId"]?.ToObject<ulong>() ?? ItemManager.NothingId(slot).Id; var id = item?["ItemId"]?.ToObject<ulong>() ?? ItemManager.NothingId(slot).Id;
var stain = (StainId)(item?["Stain"]?.ToObject<byte>() ?? 0); var stains = (item?["Stain"]?.ToObject<StainIds>() ?? StainIds.None);
var crest = item?["Crest"]?.ToObject<bool>() ?? false; var crest = item?["Crest"]?.ToObject<bool>() ?? false;
var apply = item?["Apply"]?.ToObject<bool>() ?? false; var apply = item?["Apply"]?.ToObject<bool>() ?? false;
var applyStain = item?["ApplyStain"]?.ToObject<bool>() ?? false; var applyStain = item?["ApplyStain"]?.ToObject<bool>() ?? false;
var applyCrest = item?["ApplyCrest"]?.ToObject<bool>() ?? false; var applyCrest = item?["ApplyCrest"]?.ToObject<bool>() ?? false;
return (id, stain, crest, apply, applyStain, applyCrest); return (id, stains, crest, apply, applyStain, applyCrest);
} }
void PrintWarning(string msg) void PrintWarning(string msg)
@ -541,13 +541,13 @@ public class DesignBase
foreach (var slot in EquipSlotExtensions.EqdpSlots) foreach (var slot in EquipSlotExtensions.EqdpSlots)
{ {
var (id, stain, crest, apply, applyStain, applyCrest) = ParseItem(slot, equip[slot.ToString()]); var (id, stains, crest, apply, applyStain, applyCrest) = ParseItem(slot, equip[slot.ToString()]);
PrintWarning(items.ValidateItem(slot, id, out var item, allowUnknown)); PrintWarning(items.ValidateItem(slot, id, out var item, allowUnknown));
PrintWarning(items.ValidateStain(stain, out stain, allowUnknown)); PrintWarning(items.ValidateStain(stains, out stains, allowUnknown));
var crestSlot = slot.ToCrestFlag(); var crestSlot = slot.ToCrestFlag();
design._designData.SetItem(slot, item); design._designData.SetItem(slot, item);
design._designData.SetStain(slot, stain); design._designData.SetStain(slot, stains);
design._designData.SetCrest(crestSlot, crest); design._designData.SetCrest(crestSlot, crest);
design.SetApplyEquip(slot, apply); design.SetApplyEquip(slot, apply);
design.SetApplyStain(slot, applyStain); design.SetApplyStain(slot, applyStain);

View file

@ -52,10 +52,10 @@ public unsafe struct DesignData
|| name.IsContained(_nameMainhand) || name.IsContained(_nameMainhand)
|| name.IsContained(_nameOffhand); || name.IsContained(_nameOffhand);
public readonly StainId Stain(EquipSlot slot) public readonly StainIds Stain(EquipSlot slot)
{ {
var index = slot.ToIndex(); var index = slot.ToIndex();
return index > 11 ? (StainId)0 : _equipmentBytes[4 * index + 3]; return index > 11 ? StainIds.None : new(_equipmentBytes[4 * index + 3], _equipmentBytes[4 * index + 3]);
} }
public readonly bool Crest(CrestFlag slot) public readonly bool Crest(CrestFlag slot)

View file

@ -167,29 +167,29 @@ public class DesignEditor(
} }
/// <inheritdoc/> /// <inheritdoc/>
public void ChangeStain(object data, EquipSlot slot, StainId stain, ApplySettings _ = default) public void ChangeStain(object data, EquipSlot slot, StainIds stains, ApplySettings _ = default)
{ {
var design = (Design)data; var design = (Design)data;
if (Items.ValidateStain(stain, out var _, false).Length > 0) if (Items.ValidateStain(stains, out var _, false).Length > 0)
return; return;
var oldStain = design.DesignData.Stain(slot); var oldStain = design.DesignData.Stain(slot);
if (!design.GetDesignDataRef().SetStain(slot, stain)) if (!design.GetDesignDataRef().SetStain(slot, stains))
return; return;
design.LastEdit = DateTimeOffset.UtcNow; design.LastEdit = DateTimeOffset.UtcNow;
SaveService.QueueSave(design); SaveService.QueueSave(design);
Glamourer.Log.Debug($"Set stain of {slot} equipment piece to {stain.Id}."); Glamourer.Log.Debug($"Set stain of {slot} equipment piece to {stains}.");
DesignChanged.Invoke(DesignChanged.Type.Stain, design, (oldStain, stain, slot)); DesignChanged.Invoke(DesignChanged.Type.Stain, design, (oldStain, stains, slot));
} }
/// <inheritdoc/> /// <inheritdoc/>
public void ChangeEquip(object data, EquipSlot slot, EquipItem? item, StainId? stain, ApplySettings _ = default) public void ChangeEquip(object data, EquipSlot slot, EquipItem? item, StainIds? stains, ApplySettings _ = default)
{ {
if (item.HasValue) if (item.HasValue)
ChangeItem(data, slot, item.Value, _); ChangeItem(data, slot, item.Value, _);
if (stain.HasValue) if (stains.HasValue)
ChangeStain(data, slot, stain.Value, _); ChangeStain(data, slot, stains.Value, _);
} }
/// <inheritdoc/> /// <inheritdoc/>
@ -366,4 +366,9 @@ public class DesignEditor(
return true; return true;
} }
public void ChangeEquip(object data, EquipSlot slot, EquipItem? item, StainId? stain, ApplySettings settings = default)
{
throw new NotImplementedException();
}
} }

View file

@ -65,11 +65,11 @@ public interface IDesignEditor
=> ChangeEquip(data, slot, item, null, settings); => ChangeEquip(data, slot, item, null, settings);
/// <summary> Change the stain for any equipment piece. </summary> /// <summary> Change the stain for any equipment piece. </summary>
public void ChangeStain(object data, EquipSlot slot, StainId stain, ApplySettings settings = default) public void ChangeStain(object data, EquipSlot slot, StainIds stains, ApplySettings settings = default)
=> ChangeEquip(data, slot, null, stain, settings); => ChangeEquip(data, slot, null, stains, settings);
/// <summary> Change an equipment piece and its stain at the same time. </summary> /// <summary> Change an equipment piece and its stain at the same time. </summary>
public void ChangeEquip(object data, EquipSlot slot, EquipItem? item, StainId? stain, ApplySettings settings = default); public void ChangeEquip(object data, EquipSlot slot, EquipItem? item, StainIds? stains, ApplySettings settings = default);
/// <summary> Change the crest visibility for any equipment piece. </summary> /// <summary> Change the crest visibility for any equipment piece. </summary>
public void ChangeCrest(object data, CrestFlag slot, bool crest, ApplySettings settings = default); public void ChangeCrest(object data, CrestFlag slot, bool crest, ApplySettings settings = default);

View file

@ -11,7 +11,7 @@ namespace Glamourer.Events;
/// </list> /// </list>
/// </summary> /// </summary>
public sealed class MovedEquipment() public sealed class MovedEquipment()
: EventWrapper<(EquipSlot, uint, StainId)[], MovedEquipment.Priority>(nameof(MovedEquipment)) : EventWrapper<(EquipSlot, uint, StainIds)[], MovedEquipment.Priority>(nameof(MovedEquipment))
{ {
public enum Priority public enum Priority
{ {

View file

@ -23,8 +23,8 @@ public struct EquipDrawData(EquipSlot slot, in DesignData designData)
public readonly void SetItem(EquipItem item) public readonly void SetItem(EquipItem item)
=> _editor.ChangeItem(_object, Slot, item, ApplySettings.Manual); => _editor.ChangeItem(_object, Slot, item, ApplySettings.Manual);
public readonly void SetStain(StainId stain) public readonly void SetStain(StainIds stains)
=> _editor.ChangeStain(_object, Slot, stain, ApplySettings.Manual); => _editor.ChangeStain(_object, Slot, stains, ApplySettings.Manual);
public readonly void SetApplyItem(bool value) public readonly void SetApplyItem(bool value)
{ {
@ -41,9 +41,9 @@ public struct EquipDrawData(EquipSlot slot, in DesignData designData)
} }
public EquipItem CurrentItem = designData.Item(slot); public EquipItem CurrentItem = designData.Item(slot);
public StainId CurrentStain = designData.Stain(slot); public StainIds CurrentStain = designData.Stain(slot);
public EquipItem GameItem = default; public EquipItem GameItem = default;
public StainId GameStain = default; public StainIds GameStain = default;
public bool CurrentApply; public bool CurrentApply;
public bool CurrentApplyStain; public bool CurrentApplyStain;

View file

@ -87,8 +87,8 @@ public class ActiveStatePanel(StateManager _stateManager, ObjectManager _objectM
foreach (var slot in EquipSlotExtensions.EqdpSlots.Prepend(EquipSlot.OffHand).Prepend(EquipSlot.MainHand)) foreach (var slot in EquipSlotExtensions.EqdpSlots.Prepend(EquipSlot.OffHand).Prepend(EquipSlot.MainHand))
{ {
PrintRow(slot.ToName(), ItemString(state.BaseData, slot), ItemString(state.ModelData, slot), state.Sources[slot, false]); PrintRow(slot.ToName(), ItemString(state.BaseData, slot), ItemString(state.ModelData, slot), state.Sources[slot, false]);
ImGuiUtil.DrawTableColumn(state.BaseData.Stain(slot).Id.ToString()); ImGuiUtil.DrawTableColumn($"{state.BaseData.Stain(slot)[0]}, {state.BaseData.Stain(slot)[1]}");
ImGuiUtil.DrawTableColumn(state.ModelData.Stain(slot).Id.ToString()); ImGuiUtil.DrawTableColumn($"{state.ModelData.Stain(slot)[0]}, {state.ModelData.Stain(slot)[1]}");
ImGuiUtil.DrawTableColumn(state.Sources[slot, true].ToString()); ImGuiUtil.DrawTableColumn(state.Sources[slot, true].ToString());
} }

View file

@ -51,7 +51,7 @@ public unsafe class GlamourPlatePanel : IGameDataDrawer
using (ImRaii.Group()) using (ImRaii.Group())
{ {
ImGuiUtil.CopyOnClickSelectable($"0x{(ulong)manager:X}"); ImGuiUtil.CopyOnClickSelectable($"0x{(ulong)manager:X}");
ImGui.TextUnformatted(manager == null ? "-" : manager->GlamourPlatesSpan.Length.ToString()); ImGui.TextUnformatted(manager == null ? "-" : manager->GlamourPlates.Length.ToString());
ImGui.TextUnformatted(manager == null ? "-" : manager->GlamourPlatesRequested.ToString()); ImGui.TextUnformatted(manager == null ? "-" : manager->GlamourPlatesRequested.ToString());
ImGui.SameLine(); ImGui.SameLine();
if (ImGui.SmallButton("Request Update")) if (ImGui.SmallButton("Request Update"))
@ -67,13 +67,13 @@ public unsafe class GlamourPlatePanel : IGameDataDrawer
var (identifier, data) = _objects.PlayerData; var (identifier, data) = _objects.PlayerData;
var enabled = data.Valid && _state.GetOrCreate(identifier, data.Objects[0], out state); var enabled = data.Valid && _state.GetOrCreate(identifier, data.Objects[0], out state);
for (var i = 0; i < manager->GlamourPlatesSpan.Length; ++i) for (var i = 0; i < manager->GlamourPlates.Length; ++i)
{ {
using var tree = ImRaii.TreeNode($"Plate #{i + 1:D2}"); using var tree = ImRaii.TreeNode($"Plate #{i + 1:D2}");
if (!tree) if (!tree)
continue; continue;
ref var plate = ref manager->GlamourPlatesSpan[i]; ref var plate = ref manager->GlamourPlates[i];
if (ImGuiUtil.DrawDisabledButton("Apply to Player", Vector2.Zero, string.Empty, !enabled)) if (ImGuiUtil.DrawDisabledButton("Apply to Player", Vector2.Zero, string.Empty, !enabled))
{ {
var design = CreateDesign(plate); var design = CreateDesign(plate);
@ -90,7 +90,7 @@ public unsafe class GlamourPlatePanel : IGameDataDrawer
using (ImRaii.Group()) using (ImRaii.Group())
{ {
foreach (var (_, index) in EquipSlotExtensions.FullSlots.WithIndex()) foreach (var (_, index) in EquipSlotExtensions.FullSlots.WithIndex())
ImGui.TextUnformatted($"{plate.ItemIds[index]:D6}, {plate.StainIds[index]:D3}"); ImGui.TextUnformatted($"{plate.ItemIds[index]:D6}, {plate.Stain0Ids[index]:D3}, {plate.Stain1Ids[index]:D3}");
} }
} }
} }
@ -126,7 +126,7 @@ public unsafe class GlamourPlatePanel : IGameDataDrawer
continue; continue;
design.GetDesignDataRef().SetItem(slot, item); design.GetDesignDataRef().SetItem(slot, item);
design.GetDesignDataRef().SetStain(slot, plate.StainIds[index]); design.GetDesignDataRef().SetStain(slot, new(plate.Stain0Ids[index], plate.Stain1Ids[index]));
design.ApplyEquip |= slot.ToBothFlags(); design.ApplyEquip |= slot.ToBothFlags();
} }

View file

@ -43,8 +43,8 @@ public unsafe class InventoryPanel : IGameDataDrawer
} }
else else
{ {
ImGuiUtil.DrawTableColumn(item->ItemID.ToString()); ImGuiUtil.DrawTableColumn(item->ItemId.ToString());
ImGuiUtil.DrawTableColumn(item->GlamourID.ToString()); ImGuiUtil.DrawTableColumn(item->GlamourId.ToString());
ImGui.TableNextColumn(); ImGui.TableNextColumn();
ImGuiUtil.CopyOnClickSelectable($"0x{(ulong)item:X}"); ImGuiUtil.CopyOnClickSelectable($"0x{(ulong)item:X}");
} }

View file

@ -453,7 +453,7 @@ public class DesignPanel
} }
private static unsafe string GetUserPath() private static unsafe string GetUserPath()
=> Framework.Instance()->UserPath; => Framework.Instance()->UserPathString;
private sealed class LockButton(DesignPanel panel) : Button private sealed class LockButton(DesignPanel panel) : Button

View file

@ -233,7 +233,7 @@ public class UnlockOverview
ImGui.TextUnformatted($"For all {_jobs.AllJobGroups[item.JobRestrictions.Id].Name} of at least Level {item.Level}"); ImGui.TextUnformatted($"For all {_jobs.AllJobGroups[item.JobRestrictions.Id].Name} of at least Level {item.Level}");
} }
if (item.Flags.HasFlag(ItemFlags.IsDyable)) if (item.Flags.HasFlag(ItemFlags.IsDyable1))
ImGui.TextUnformatted("Dyable"); ImGui.TextUnformatted("Dyable");
if (item.Flags.HasFlag(ItemFlags.IsTradable)) if (item.Flags.HasFlag(ItemFlags.IsTradable))
ImGui.TextUnformatted("Tradable"); ImGui.TextUnformatted("Tradable");

View file

@ -384,7 +384,7 @@ public class UnlockTable : Table<EquipItem>, IDisposable
=> Tooltip = "Whether the item is dyable."; => Tooltip = "Whether the item is dyable.";
protected override bool GetValue(EquipItem item) protected override bool GetValue(EquipItem item)
=> item.Flags.HasFlag(ItemFlags.IsDyable); => item.Flags.HasFlag(ItemFlags.IsDyable1);
} }
private sealed class TradableColumn : YesNoColumn<EquipItem> private sealed class TradableColumn : YesNoColumn<EquipItem>

View file

@ -36,7 +36,7 @@ public unsafe class ChangeCustomizeService : EventWrapperRef2<Model, CustomizeAr
_penumbraReloaded = penumbraReloaded; _penumbraReloaded = penumbraReloaded;
_interop = interop; _interop = interop;
_changeCustomizeHook = Create(); _changeCustomizeHook = Create();
_original = Human.MemberFunctionPointers.UpdateDrawData; _original = (delegate* unmanaged[Stdcall]<Human*, byte*, bool, bool>)Human.MemberFunctionPointers.UpdateDrawData;
interop.InitializeFromAttributes(this); interop.InitializeFromAttributes(this);
_penumbraReloaded.Subscribe(Restore, PenumbraReloaded.Priority.ChangeCustomizeService); _penumbraReloaded.Subscribe(Restore, PenumbraReloaded.Priority.ChangeCustomizeService);
} }

View file

@ -60,7 +60,7 @@ public sealed class CharaFile
return; return;
data.SetItem(slot, item); data.SetItem(slot, item);
data.SetStain(slot, dye); data.SetStain(slot, new(dye, dye));
flags |= slot.ToFlag(); flags |= slot.ToFlag();
flags |= slot.ToStainFlag(); flags |= slot.ToStainFlag();
} }
@ -79,7 +79,7 @@ public sealed class CharaFile
return; return;
data.SetItem(slot, item); data.SetItem(slot, item);
data.SetStain(slot, dye); data.SetStain(slot, new(dye, dye));
flags |= slot.ToFlag(); flags |= slot.ToFlag();
flags |= slot.ToStainFlag(); flags |= slot.ToStainFlag();
} }

View file

@ -96,7 +96,7 @@ public sealed class CmaFile
if (offhand == null) if (offhand == null)
{ {
data.SetItem(EquipSlot.MainHand, defaultOffhand); data.SetItem(EquipSlot.MainHand, defaultOffhand);
data.SetStain(EquipSlot.MainHand, defaultOffhand.PrimaryId.Id == 0 ? 0 : data.Stain(EquipSlot.MainHand)); data.SetStain(EquipSlot.MainHand, defaultOffhand.PrimaryId.Id == 0 ? StainIds.None : data.Stain(EquipSlot.MainHand));
return; return;
} }

View file

@ -96,7 +96,7 @@ public sealed unsafe class CrestService : EventWrapperRef3<Actor, CrestFlag, boo
if (!model.IsHuman) if (!model.IsHuman)
return false; return false;
var getter = (delegate* unmanaged<Human*, byte, byte>)((nint*)model.AsCharacterBase->VTable)[95]; var getter = (delegate* unmanaged<Human*, byte, byte>)((nint*)model.AsCharacterBase->VirtualTable)[95];
return getter(model.AsHuman, index) != 0; return getter(model.AsHuman, index) != 0;
} }
case CrestType.Offhand: case CrestType.Offhand:
@ -105,7 +105,7 @@ public sealed unsafe class CrestService : EventWrapperRef3<Actor, CrestFlag, boo
if (!model.IsWeapon) if (!model.IsWeapon)
return false; return false;
var getter = (delegate* unmanaged<Weapon*, byte, byte>)((nint*)model.AsCharacterBase->VTable)[95]; var getter = (delegate* unmanaged<Weapon*, byte, byte>)((nint*)model.AsCharacterBase->VirtualTable)[95];
return getter(model.AsWeapon, index) != 0; return getter(model.AsWeapon, index) != 0;
} }
} }

View file

@ -14,7 +14,7 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
{ {
private readonly MovedEquipment _movedItemsEvent; private readonly MovedEquipment _movedItemsEvent;
private readonly EquippedGearset _gearsetEvent; private readonly EquippedGearset _gearsetEvent;
private readonly List<(EquipSlot, uint, StainId)> _itemList = new(12); private readonly List<(EquipSlot, uint, StainIds)> _itemList = new(12);
public InventoryService(MovedEquipment movedItemsEvent, IGameInteropProvider interop, EquippedGearset gearsetEvent) public InventoryService(MovedEquipment movedItemsEvent, IGameInteropProvider interop, EquippedGearset gearsetEvent)
{ {
@ -60,56 +60,56 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
if (glamourPlateId != 0) if (glamourPlateId != 0)
{ {
void Add(EquipSlot slot, uint glamourId, StainId glamourStain, ref RaptureGearsetModule.GearsetItem item) void Add(EquipSlot slot, uint glamourId, StainId glamourStain1, StainId glamourStain2, ref RaptureGearsetModule.GearsetItem item)
{ {
if (item.ItemID == 0) if (item.ItemId == 0)
_itemList.Add((slot, 0, 0)); _itemList.Add((slot, 0, new(0, 0)));
else if (glamourId != 0) else if (glamourId != 0)
_itemList.Add((slot, glamourId, glamourStain)); _itemList.Add((slot, glamourId, new(glamourStain1, glamourStain2)));
else if (item.GlamourId != 0) else if (item.GlamourId != 0)
_itemList.Add((slot, item.GlamourId, item.Stain)); _itemList.Add((slot, item.GlamourId, new(item.Stain0Id, item.Stain1Id)));
else else
_itemList.Add((slot, FixId(item.ItemID), item.Stain)); _itemList.Add((slot, FixId(item.ItemId), new(item.Stain0Id, item.Stain1Id)));
} }
var plate = MirageManager.Instance()->GlamourPlatesSpan[glamourPlateId - 1]; var plate = MirageManager.Instance()->GlamourPlates[glamourPlateId - 1];
Add(EquipSlot.MainHand, plate.ItemIds[0], plate.StainIds[0], ref entry->ItemsSpan[0]); Add(EquipSlot.MainHand, plate.ItemIds[0], plate.Stain0Ids[0], plate.Stain1Ids[0], ref entry->Items[0]);
Add(EquipSlot.OffHand, plate.ItemIds[1], plate.StainIds[1], ref entry->ItemsSpan[1]); Add(EquipSlot.OffHand, plate.ItemIds[1], plate.Stain0Ids[1], plate.Stain1Ids[1], ref entry->Items[1]);
Add(EquipSlot.Head, plate.ItemIds[2], plate.StainIds[2], ref entry->ItemsSpan[2]); Add(EquipSlot.Head, plate.ItemIds[2], plate.Stain0Ids[2], plate.Stain1Ids[2], ref entry->Items[2]);
Add(EquipSlot.Body, plate.ItemIds[3], plate.StainIds[3], ref entry->ItemsSpan[3]); Add(EquipSlot.Body, plate.ItemIds[3], plate.Stain0Ids[3], plate.Stain1Ids[3], ref entry->Items[3]);
Add(EquipSlot.Hands, plate.ItemIds[4], plate.StainIds[4], ref entry->ItemsSpan[5]); Add(EquipSlot.Hands, plate.ItemIds[4], plate.Stain0Ids[4], plate.Stain1Ids[4], ref entry->Items[5]);
Add(EquipSlot.Legs, plate.ItemIds[5], plate.StainIds[5], ref entry->ItemsSpan[6]); Add(EquipSlot.Legs, plate.ItemIds[5], plate.Stain0Ids[5], plate.Stain1Ids[5], ref entry->Items[6]);
Add(EquipSlot.Feet, plate.ItemIds[6], plate.StainIds[6], ref entry->ItemsSpan[7]); Add(EquipSlot.Feet, plate.ItemIds[6], plate.Stain0Ids[6], plate.Stain1Ids[6], ref entry->Items[7]);
Add(EquipSlot.Ears, plate.ItemIds[7], plate.StainIds[7], ref entry->ItemsSpan[8]); Add(EquipSlot.Ears, plate.ItemIds[7], plate.Stain0Ids[7], plate.Stain1Ids[7], ref entry->Items[8]);
Add(EquipSlot.Neck, plate.ItemIds[8], plate.StainIds[8], ref entry->ItemsSpan[9]); Add(EquipSlot.Neck, plate.ItemIds[8], plate.Stain0Ids[8], plate.Stain1Ids[8], ref entry->Items[9]);
Add(EquipSlot.Wrists, plate.ItemIds[9], plate.StainIds[9], ref entry->ItemsSpan[10]); Add(EquipSlot.Wrists, plate.ItemIds[9], plate.Stain0Ids[9], plate.Stain1Ids[9], ref entry->Items[10]);
Add(EquipSlot.RFinger, plate.ItemIds[10], plate.StainIds[10], ref entry->ItemsSpan[11]); Add(EquipSlot.RFinger, plate.ItemIds[10], plate.Stain0Ids[10], plate.Stain1Ids[10], ref entry->Items[11]);
Add(EquipSlot.LFinger, plate.ItemIds[11], plate.StainIds[11], ref entry->ItemsSpan[12]); Add(EquipSlot.LFinger, plate.ItemIds[11], plate.Stain0Ids[11], plate.Stain1Ids[11], ref entry->Items[12]);
} }
else else
{ {
void Add(EquipSlot slot, ref RaptureGearsetModule.GearsetItem item) void Add(EquipSlot slot, ref RaptureGearsetModule.GearsetItem item)
{ {
if (item.ItemID == 0) if (item.ItemId == 0)
_itemList.Add((slot, 0, 0)); _itemList.Add((slot, 0, new(0, 0)));
else if (item.GlamourId != 0) else if (item.GlamourId != 0)
_itemList.Add((slot, item.GlamourId, item.Stain)); _itemList.Add((slot, item.GlamourId, new(item.Stain0Id, item.Stain1Id)));
else else
_itemList.Add((slot, FixId(item.ItemID), item.Stain)); _itemList.Add((slot, FixId(item.ItemId), new(item.Stain0Id, item.Stain1Id)));
} }
Add(EquipSlot.MainHand, ref entry->ItemsSpan[0]); Add(EquipSlot.MainHand, ref entry->Items[0]);
Add(EquipSlot.OffHand, ref entry->ItemsSpan[1]); Add(EquipSlot.OffHand, ref entry->Items[1]);
Add(EquipSlot.Head, ref entry->ItemsSpan[2]); Add(EquipSlot.Head, ref entry->Items[2]);
Add(EquipSlot.Body, ref entry->ItemsSpan[3]); Add(EquipSlot.Body, ref entry->Items[3]);
Add(EquipSlot.Hands, ref entry->ItemsSpan[5]); Add(EquipSlot.Hands, ref entry->Items[5]);
Add(EquipSlot.Legs, ref entry->ItemsSpan[6]); Add(EquipSlot.Legs, ref entry->Items[6]);
Add(EquipSlot.Feet, ref entry->ItemsSpan[7]); Add(EquipSlot.Feet, ref entry->Items[7]);
Add(EquipSlot.Ears, ref entry->ItemsSpan[8]); Add(EquipSlot.Ears, ref entry->Items[8]);
Add(EquipSlot.Neck, ref entry->ItemsSpan[9]); Add(EquipSlot.Neck, ref entry->Items[9]);
Add(EquipSlot.Wrists, ref entry->ItemsSpan[10]); Add(EquipSlot.Wrists, ref entry->Items[10]);
Add(EquipSlot.RFinger, ref entry->ItemsSpan[11]); Add(EquipSlot.RFinger, ref entry->Items[11]);
Add(EquipSlot.LFinger, ref entry->ItemsSpan[12]); Add(EquipSlot.LFinger, ref entry->Items[12]);
} }
_movedItemsEvent.Invoke(_itemList.ToArray()); _movedItemsEvent.Invoke(_itemList.ToArray());
@ -155,7 +155,7 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
return ret; return ret;
} }
private static bool InvokeSource(InventoryType sourceContainer, uint sourceSlot, out (EquipSlot, uint, StainId) tuple) private static bool InvokeSource(InventoryType sourceContainer, uint sourceSlot, out (EquipSlot, uint, StainIds) tuple)
{ {
tuple = default; tuple = default;
if (sourceContainer is not InventoryType.EquippedItems) if (sourceContainer is not InventoryType.EquippedItems)
@ -165,12 +165,12 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
if (slot is EquipSlot.Unknown) if (slot is EquipSlot.Unknown)
return false; return false;
tuple = (slot, 0u, 0); tuple = (slot, 0u, StainIds.None);
return true; return true;
} }
private static bool InvokeTarget(InventoryManager* manager, InventoryType targetContainer, uint targetSlot, private static bool InvokeTarget(InventoryManager* manager, InventoryType targetContainer, uint targetSlot,
out (EquipSlot, uint, StainId) tuple) out (EquipSlot, uint, StainIds) tuple)
{ {
tuple = default; tuple = default;
if (targetContainer is not InventoryType.EquippedItems) if (targetContainer is not InventoryType.EquippedItems)
@ -189,7 +189,7 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
if (item == null) if (item == null)
return false; return false;
tuple = (slot, item->GlamourID != 0 ? item->GlamourID : item->ItemID, item->Stain); tuple = (slot, item->GlamourId != 0 ? item->GlamourId : item->ItemId, new(item->Stains[0], item->Stains[1]));
return true; return true;
} }

View file

@ -48,12 +48,14 @@ public unsafe class MetaService : IDisposable
if (!actor.IsCharacter) if (!actor.IsCharacter)
return; return;
actor.AsCharacter->DrawData.SetVisor(value);
// The function seems to not do anything if the head is 0, but also breaks for carbuncles turned human, sometimes? // The function seems to not do anything if the head is 0, but also breaks for carbuncles turned human, sometimes?
var old = actor.AsCharacter->DrawData.Head.Id; /* var old = actor.AsCharacter->DrawData.Head.Id;
if (old == 0 && actor.AsCharacter->CharacterData.ModelCharaId == 0) if (old == 0 && actor.AsCharacter->CharacterData.ModelCharaId == 0)
actor.AsCharacter->DrawData.Head.Id = 1; actor.AsCharacter->DrawData.Head.Id = 1;
_hideHatGearHook.Original(&actor.AsCharacter->DrawData, 0, (byte)(value ? 0 : 1)); _hideHatGearHook.Original(&actor.AsCharacter->DrawData, 0, (byte)(value ? 0 : 1));
actor.AsCharacter->DrawData.Head.Id = old; actor.AsCharacter->DrawData.Head.Id = old; */
} }
public void SetWeaponState(Actor actor, bool value) public void SetWeaponState(Actor actor, bool value)

View file

@ -164,7 +164,7 @@ public sealed class CollectionOverrideService : IService, ISavable
var collection = _penumbra.CollectionByIdentifier(collectionIdentifier); var collection = _penumbra.CollectionByIdentifier(collectionIdentifier);
if (collection == null) if (collection == null)
{ {
Glamourer.Messager.AddMessage(new Notification( Glamourer.Messager.AddMessage(new OtterGui.Classes.Notification(
$"The overridden collection for identifier {identifier.Incognito(null)} with name {collectionIdentifier} could not be found by Penumbra for migration.", $"The overridden collection for identifier {identifier.Incognito(null)} with name {collectionIdentifier} could not be found by Penumbra for migration.",
NotificationType.Warning)); NotificationType.Warning));
continue; continue;

View file

@ -195,16 +195,16 @@ public class ItemManager
/// The returned stain id is either the input or 0. /// The returned stain id is either the input or 0.
/// The return value is an empty string if there was no problem and a warning otherwise. /// The return value is an empty string if there was no problem and a warning otherwise.
/// </summary> /// </summary>
public string ValidateStain(StainId stain, out StainId ret, bool allowUnknown) public string ValidateStain(StainIds stains, out StainIds ret, bool allowUnknown)
{ {
if (allowUnknown || IsStainValid(stain)) if (allowUnknown || IsStainValid(stains[0]) && IsStainValid(stains[1]))
{ {
ret = stain; ret = stains;
return string.Empty; return string.Empty;
} }
ret = 0; ret = StainIds.None;
return $"The Stain {stain} does not exist, reset to unstained."; return $"The Stains {stains} does not exist, reset to unstained.";
} }
/// <summary> Returns whether an offhand is valid given the required offhand type. </summary> /// <summary> Returns whether an offhand is valid given the required offhand type. </summary>

View file

@ -152,11 +152,11 @@ public class InternalStateEditor(
} }
/// <summary> Change a single piece of equipment including stain. </summary> /// <summary> Change a single piece of equipment including stain. </summary>
public bool ChangeEquip(ActorState state, EquipSlot slot, EquipItem item, StainId stain, StateSource source, out EquipItem oldItem, public bool ChangeEquip(ActorState state, EquipSlot slot, EquipItem item, StainIds stains, StateSource source, out EquipItem oldItem,
out StainId oldStain, uint key = 0) out StainIds oldStains, uint key = 0)
{ {
oldItem = state.ModelData.Item(slot); oldItem = state.ModelData.Item(slot);
oldStain = state.ModelData.Stain(slot); oldStains = state.ModelData.Stain(slot);
if (!state.CanUnlock(key)) if (!state.CanUnlock(key))
return false; return false;
@ -168,7 +168,7 @@ public class InternalStateEditor(
return false; return false;
var old = oldItem; var old = oldItem;
var oldS = oldStain; var oldS = oldStains;
gPose.AddActionOnLeave(() => gPose.AddActionOnLeave(() =>
{ {
if (old.Type == state.BaseData.Item(slot).Type) if (old.Type == state.BaseData.Item(slot).Type)
@ -177,16 +177,16 @@ public class InternalStateEditor(
} }
state.ModelData.SetItem(slot, item); state.ModelData.SetItem(slot, item);
state.ModelData.SetStain(slot, stain); state.ModelData.SetStain(slot, stains);
state.Sources[slot, false] = source; state.Sources[slot, false] = source;
state.Sources[slot, true] = source; state.Sources[slot, true] = source;
return true; return true;
} }
/// <summary> Change only the stain of an equipment piece. </summary> /// <summary> Change only the stain of an equipment piece. </summary>
public bool ChangeStain(ActorState state, EquipSlot slot, StainIds stains, StateSource source, out StainId oldStain, uint key = 0) public bool ChangeStain(ActorState state, EquipSlot slot, StainIds stains, StateSource source, out StainIds oldStains, uint key = 0)
{ {
oldStain = state.ModelData.Stain(slot); oldStains = state.ModelData.Stain(slot);
if (!state.CanUnlock(key)) if (!state.CanUnlock(key))
return false; return false;

View file

@ -118,7 +118,7 @@ public class StateEditor(
ApplyMainhandPeriphery(state, item, stains, settings); ApplyMainhandPeriphery(state, item, stains, settings);
Glamourer.Log.Verbose( Glamourer.Log.Verbose(
$"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item!.Value.Name} ({item.Value.ItemId}) and its stain from {oldStain.Id} to {stain!.Value.Id}. [Affecting {actors.ToLazyString("nothing")}.]"); $"Set {slot.ToName()} in state {state.Identifier.Incognito(null)} from {old.Name} ({old.ItemId}) to {item!.Value.Name} ({item.Value.ItemId}) and its stains from {oldStain[0]},{oldStain[1]} to {stains!.Value[0]}, {stains!.Value[1]}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(type, settings.Source, state, actors, (old, item!.Value, slot)); StateChanged.Invoke(type, settings.Source, state, actors, (old, item!.Value, slot));
StateChanged.Invoke(StateChangeType.Stain, settings.Source, state, actors, (oldStain, stains!.Value, slot)); StateChanged.Invoke(StateChangeType.Stain, settings.Source, state, actors, (oldStain, stains!.Value, slot));
} }
@ -132,7 +132,7 @@ public class StateEditor(
var actors = Applier.ChangeStain(state, slot, settings.Source.RequiresChange()); var actors = Applier.ChangeStain(state, slot, settings.Source.RequiresChange());
Glamourer.Log.Verbose( Glamourer.Log.Verbose(
$"Set {slot.ToName()} stain in state {state.Identifier.Incognito(null)} from {old.Id} to {stains.ToString()}. [Affecting {actors.ToLazyString("nothing")}.]"); $"Set {slot.ToName()} stain in state {state.Identifier.Incognito(null)} from {old[0]},{old[1]} to {stains.ToString()}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChangeType.Stain, settings.Source, state, actors, (old, stains, slot)); StateChanged.Invoke(StateChangeType.Stain, settings.Source, state, actors, (old, stains, slot));
} }

View file

@ -227,14 +227,14 @@ public class StateListener : IDisposable
(_, armor) = _items.RestrictedGear.ResolveRestricted(armor, slot, customize.Race, customize.Gender); (_, armor) = _items.RestrictedGear.ResolveRestricted(armor, slot, customize.Race, customize.Gender);
} }
private void OnMovedEquipment((EquipSlot, uint, StainId)[] items) private void OnMovedEquipment((EquipSlot, uint, StainIds)[] items)
{ {
_objects.Update(); _objects.Update();
var (identifier, objects) = _objects.PlayerData; var (identifier, objects) = _objects.PlayerData;
if (!identifier.IsValid || !_manager.TryGetValue(identifier, out var state)) if (!identifier.IsValid || !_manager.TryGetValue(identifier, out var state))
return; return;
foreach (var (slot, item, stain) in items) foreach (var (slot, item, stains) in items)
{ {
var currentItem = state.BaseData.Item(slot); var currentItem = state.BaseData.Item(slot);
var model = slot is EquipSlot.MainHand or EquipSlot.OffHand var model = slot is EquipSlot.MainHand or EquipSlot.OffHand
@ -244,7 +244,7 @@ public class StateListener : IDisposable
if (model.Value == current.Value || !_items.ItemData.TryGetValue(item, EquipSlot.MainHand, out var changedItem)) if (model.Value == current.Value || !_items.ItemData.TryGetValue(item, EquipSlot.MainHand, out var changedItem))
continue; continue;
var changed = changedItem.Weapon(stain); var changed = changedItem.Weapon(stains);
var itemChanged = current.Skeleton == changed.Skeleton var itemChanged = current.Skeleton == changed.Skeleton
&& current.Variant == changed.Variant && current.Variant == changed.Variant
&& current.Weapon == changed.Weapon && current.Weapon == changed.Weapon

View file

@ -174,7 +174,7 @@ public class CustomizeUnlockManager : IDisposable, ISavable
IDataManager gameData) IDataManager gameData)
{ {
var ret = new Dictionary<CustomizeData, (uint Data, string Name)>(); var ret = new Dictionary<CustomizeData, (uint Data, string Name)>();
var sheet = gameData.GetExcelSheet<CharaMakeCustomize>(ClientLanguage.English)!; var sheet = gameData.GetExcelSheet<CharaMakeCustomize>(Dalamud.Game.ClientLanguage.English)!;
foreach (var (clan, gender) in CustomizeManager.AllSets()) foreach (var (clan, gender) in CustomizeManager.AllSets())
{ {
var list = customizations.Manager.GetSet(clan, gender); var list = customizations.Manager.GetSet(clan, gender);

View file

@ -154,7 +154,7 @@ public class ItemUnlockManager : ISavable, IDisposable, IReadOnlyDictionary<Item
if (newPlateState != _lastPlateState) if (newPlateState != _lastPlateState)
{ {
_lastPlateState = newPlateState; _lastPlateState = newPlateState;
foreach (var plate in mirageManager->GlamourPlatesSpan) foreach (var plate in mirageManager->GlamourPlates)
{ {
// TODO: Make independent from hardcoded value // TODO: Make independent from hardcoded value
var span = new ReadOnlySpan<uint>(plate.ItemIds, 12); var span = new ReadOnlySpan<uint>(plate.ItemIds, 12);
@ -176,8 +176,8 @@ public class ItemUnlockManager : ISavable, IDisposable, IReadOnlyDictionary<Item
var item = container->GetInventorySlot(_currentInventoryIndex++); var item = container->GetInventorySlot(_currentInventoryIndex++);
if (item != null) if (item != null)
{ {
changes |= AddItem(item->ItemID, time); changes |= AddItem(item->ItemId, time);
changes |= AddItem(item->GlamourID, time); changes |= AddItem(item->GlamourId, time);
} }
} }
else else