mirror of
https://github.com/xivdev/Penumbra.git
synced 2025-12-12 10:17:22 +01:00
Add support for imc-toggle attributes to accessories, and fix up attributes when item swapping models.
This commit is contained in:
parent
c0aa2e36ea
commit
a953febfba
2 changed files with 112 additions and 1 deletions
|
|
@ -1,3 +1,4 @@
|
|||
using System.Collections.Frozen;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.Collections;
|
||||
using Penumbra.Collections.Cache;
|
||||
|
|
@ -5,6 +6,8 @@ using Penumbra.GameData.Enums;
|
|||
using Penumbra.GameData.Interop;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.Interop.Hooks.PostProcessing;
|
||||
using Penumbra.Interop.Structs;
|
||||
using Penumbra.Meta.Files;
|
||||
using Penumbra.Meta.Manipulations;
|
||||
|
||||
namespace Penumbra.Meta;
|
||||
|
|
@ -58,11 +61,72 @@ public unsafe class ShapeAttributeManager : IRequiredService, IDisposable
|
|||
_ids[(int)_modelIndex] = model.GetModelId(_modelIndex);
|
||||
CheckShapes(collection.MetaCache!.Shp);
|
||||
CheckAttributes(collection.MetaCache!.Atr);
|
||||
if (_modelIndex is <= HumanSlot.LFinger and >= HumanSlot.Ears)
|
||||
AccessoryImcCheck(model);
|
||||
}
|
||||
|
||||
UpdateDefaultMasks(model, collection.MetaCache!.Shp);
|
||||
}
|
||||
|
||||
private void AccessoryImcCheck(Model model)
|
||||
{
|
||||
var imcMask = (ushort)(0x03FF & *(ushort*)(model.Address + 0xAAC + 6 * (int)_modelIndex));
|
||||
|
||||
Span<byte> attr =
|
||||
[
|
||||
(byte)'a',
|
||||
(byte)'t',
|
||||
(byte)'r',
|
||||
(byte)'_',
|
||||
AccessoryByte(_modelIndex),
|
||||
(byte)'v',
|
||||
(byte)'_',
|
||||
(byte)'a',
|
||||
0,
|
||||
];
|
||||
for (var i = 1; i < 10; ++i)
|
||||
{
|
||||
var flag = (ushort)(1 << i);
|
||||
if ((imcMask & flag) is not 0)
|
||||
continue;
|
||||
|
||||
attr[^2] = (byte)('a' + i);
|
||||
|
||||
foreach (var (attribute, index) in _model->ModelResourceHandle->Attributes)
|
||||
{
|
||||
if (!EqualAttribute(attr, attribute.Value))
|
||||
continue;
|
||||
|
||||
_model->EnabledAttributeIndexMask &= ~(1u << index);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
|
||||
private static bool EqualAttribute(Span<byte> needle, byte* haystack)
|
||||
{
|
||||
foreach (var character in needle)
|
||||
{
|
||||
if (*haystack++ != character)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static byte AccessoryByte(HumanSlot slot)
|
||||
=> slot switch
|
||||
{
|
||||
HumanSlot.Head => (byte)'m',
|
||||
HumanSlot.Ears => (byte)'e',
|
||||
HumanSlot.Neck => (byte)'n',
|
||||
HumanSlot.Wrists => (byte)'w',
|
||||
HumanSlot.RFinger => (byte)'r',
|
||||
HumanSlot.LFinger => (byte)'r',
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
private void CheckAttributes(AtrCache attributeCache)
|
||||
{
|
||||
if (attributeCache.DisabledCount is 0)
|
||||
|
|
|
|||
|
|
@ -234,9 +234,56 @@ public static class EquipmentSwap
|
|||
mdl.ChildSwaps.Add(mtrl);
|
||||
}
|
||||
|
||||
FixAttributes(mdl, slotFrom, slotTo);
|
||||
|
||||
return mdl;
|
||||
}
|
||||
|
||||
private static void FixAttributes(FileSwap swap, EquipSlot slotFrom, EquipSlot slotTo)
|
||||
{
|
||||
if (slotFrom == slotTo)
|
||||
return;
|
||||
|
||||
var needle = slotTo switch
|
||||
{
|
||||
EquipSlot.Head => "atr_mv_",
|
||||
EquipSlot.Ears => "atr_ev_",
|
||||
EquipSlot.Neck => "atr_nv_",
|
||||
EquipSlot.Wrists => "atr_wv_",
|
||||
EquipSlot.RFinger or EquipSlot.LFinger => "atr_rv_",
|
||||
_ => string.Empty,
|
||||
};
|
||||
|
||||
var replacement = slotFrom switch
|
||||
{
|
||||
EquipSlot.Head => 'm',
|
||||
EquipSlot.Ears => 'e',
|
||||
EquipSlot.Neck => 'n',
|
||||
EquipSlot.Wrists => 'w',
|
||||
EquipSlot.RFinger or EquipSlot.LFinger => 'r',
|
||||
_ => 'm',
|
||||
};
|
||||
|
||||
var attributes = swap.AsMdl()!.Attributes;
|
||||
for (var i = 0; i < attributes.Length; ++i)
|
||||
{
|
||||
if (FixAttribute(ref attributes[i], needle, replacement))
|
||||
swap.DataWasChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe bool FixAttribute(ref string attribute, string from, char to)
|
||||
{
|
||||
if (!attribute.StartsWith(from) || attribute.Length != from.Length + 1 || attribute[^1] is < 'a' or > 'j')
|
||||
return false;
|
||||
|
||||
Span<char> stack = stackalloc char[attribute.Length];
|
||||
attribute.CopyTo(stack);
|
||||
stack[4] = to;
|
||||
attribute = new string(stack);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void LookupItem(EquipItem i, out EquipSlot slot, out PrimaryId modelId, out Variant variant)
|
||||
{
|
||||
slot = i.Type.ToSlot();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue