mirror of
https://github.com/Ottermandias/Glamourer.git
synced 2025-12-22 16:39:29 +01:00
Initial Update for multiple stains, some facewear support, and API X
This commit is contained in:
parent
c1d9af2dd0
commit
7caf6cc08a
90 changed files with 654 additions and 537 deletions
|
|
@ -1,5 +1,5 @@
|
|||
using Dalamud.Game.ClientState.Objects.Enums;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Designs.Special;
|
||||
using Glamourer.Events;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Interop;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
using Dalamud.Configuration;
|
||||
using Dalamud.Game.ClientState.Keys;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Gui;
|
||||
using Glamourer.Gui.Tabs.DesignTab;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Glamourer.Automation;
|
||||
using Glamourer.Designs.Links;
|
||||
using Glamourer.Interop.Material;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Glamourer.GameData;
|
||||
using Glamourer.Interop.Material;
|
||||
using Glamourer.Services;
|
||||
|
|
@ -257,10 +257,10 @@ public class DesignBase
|
|||
foreach (var slot in EquipSlotExtensions.EqdpSlots.Prepend(EquipSlot.OffHand).Prepend(EquipSlot.MainHand))
|
||||
{
|
||||
var item = _designData.Item(slot);
|
||||
var stain = _designData.Stain(slot);
|
||||
var stains = _designData.Stain(slot);
|
||||
var crestSlot = slot.ToCrestFlag();
|
||||
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");
|
||||
|
|
@ -274,16 +274,15 @@ public class DesignBase
|
|||
|
||||
return ret;
|
||||
|
||||
static JObject Serialize(CustomItemId id, StainId stain, bool crest, bool apply, bool applyStain, bool applyCrest)
|
||||
=> new()
|
||||
static JObject Serialize(CustomItemId id, StainIds stains, bool crest, bool apply, bool applyStain, bool applyCrest)
|
||||
=> stains.AddToObject(new JObject
|
||||
{
|
||||
["ItemId"] = id.Id,
|
||||
["Stain"] = stain.Id,
|
||||
["Crest"] = crest,
|
||||
["Apply"] = apply,
|
||||
["ApplyStain"] = applyStain,
|
||||
["ApplyCrest"] = applyCrest,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
protected JObject SerializeCustomize()
|
||||
|
|
@ -522,7 +521,7 @@ public class DesignBase
|
|||
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 stain = (StainId)(item?["Stain"]?.ToObject<byte>() ?? 0);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using OtterGui;
|
||||
using Penumbra.GameData.DataContainers;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
|
@ -117,7 +116,7 @@ public class DesignBase64Migration
|
|||
}
|
||||
|
||||
data.SetItem(slot, item);
|
||||
data.SetStain(slot, mdl.Stain);
|
||||
data.SetStain(slot, mdl.Stains);
|
||||
}
|
||||
|
||||
var main = cur[0].Skeleton.Id == 0
|
||||
|
|
@ -130,7 +129,7 @@ public class DesignBase64Migration
|
|||
}
|
||||
|
||||
data.SetItem(EquipSlot.MainHand, main);
|
||||
data.SetStain(EquipSlot.MainHand, cur[0].Stain);
|
||||
data.SetStain(EquipSlot.MainHand, cur[0].Stains);
|
||||
|
||||
EquipItem off;
|
||||
// Fist weapon hack
|
||||
|
|
@ -141,7 +140,7 @@ public class DesignBase64Migration
|
|||
if (gauntlet.Valid)
|
||||
{
|
||||
data.SetItem(EquipSlot.Hands, gauntlet);
|
||||
data.SetStain(EquipSlot.Hands, cur[0].Stain);
|
||||
data.SetStain(EquipSlot.Hands, cur[0].Stains);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -158,7 +157,7 @@ public class DesignBase64Migration
|
|||
}
|
||||
|
||||
data.SetItem(EquipSlot.OffHand, off);
|
||||
data.SetStain(EquipSlot.OffHand, cur[1].Stain);
|
||||
data.SetStain(EquipSlot.OffHand, cur[1].Stains);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Dalamud.Interface.Utility.Raii;
|
||||
using Glamourer.Gui;
|
||||
using Glamourer.Services;
|
||||
|
|
|
|||
|
|
@ -176,7 +176,7 @@ public class DesignConverter(
|
|||
return System.Convert.ToBase64String(compressed);
|
||||
}
|
||||
|
||||
public IEnumerable<(EquipSlot Slot, EquipItem Item, StainId Stain)> FromDrawData(IReadOnlyList<CharacterArmor> armors,
|
||||
public IEnumerable<(EquipSlot Slot, EquipItem Item, StainIds Stains)> FromDrawData(IReadOnlyList<CharacterArmor> armors,
|
||||
CharacterWeapon mainhand, CharacterWeapon offhand, bool skipWarnings)
|
||||
{
|
||||
if (armors.Count != 10)
|
||||
|
|
@ -194,7 +194,7 @@ public class DesignConverter(
|
|||
item = ItemManager.NothingItem(slot);
|
||||
}
|
||||
|
||||
yield return (slot, item, armor.Stain);
|
||||
yield return (slot, item, armor.Stains);
|
||||
}
|
||||
|
||||
var mh = _items.Identify(EquipSlot.MainHand, mainhand.Skeleton, mainhand.Weapon, mainhand.Variant);
|
||||
|
|
@ -204,7 +204,7 @@ public class DesignConverter(
|
|||
mh = _items.DefaultSword;
|
||||
}
|
||||
|
||||
yield return (EquipSlot.MainHand, mh, mainhand.Stain);
|
||||
yield return (EquipSlot.MainHand, mh, mainhand.Stains);
|
||||
|
||||
var oh = _items.Identify(EquipSlot.OffHand, offhand.Skeleton, offhand.Weapon, offhand.Variant, mh.Type);
|
||||
if (!skipWarnings && !oh.Valid)
|
||||
|
|
@ -215,7 +215,7 @@ public class DesignConverter(
|
|||
oh = ItemManager.NothingItem(FullEquipType.Shield);
|
||||
}
|
||||
|
||||
yield return (EquipSlot.OffHand, oh, offhand.Stain);
|
||||
yield return (EquipSlot.OffHand, oh, offhand.Stains);
|
||||
}
|
||||
|
||||
private static void ComputeMaterials(DesignMaterialManager manager, in StateMaterialManager materials,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ namespace Glamourer.Designs;
|
|||
|
||||
public unsafe struct DesignData
|
||||
{
|
||||
public const int EquipmentByteSize = 10 * CharacterArmor.Size;
|
||||
|
||||
private string _nameHead = string.Empty;
|
||||
private string _nameBody = string.Empty;
|
||||
private string _nameHands = string.Empty;
|
||||
|
|
@ -21,15 +23,14 @@ public unsafe struct DesignData
|
|||
private string _nameLFinger = string.Empty;
|
||||
private string _nameMainhand = string.Empty;
|
||||
private string _nameOffhand = string.Empty;
|
||||
private string _nameFaceWear = string.Empty;
|
||||
private fixed uint _itemIds[12];
|
||||
private fixed ushort _iconIds[12];
|
||||
private fixed byte _equipmentBytes[48];
|
||||
private fixed uint _iconIds[12];
|
||||
private fixed byte _equipmentBytes[EquipmentByteSize + 16];
|
||||
public CustomizeParameterData Parameters;
|
||||
public CustomizeArray Customize = CustomizeArray.Default;
|
||||
public uint ModelId;
|
||||
public CrestFlag CrestVisibility;
|
||||
private SecondaryId _secondaryMainhand;
|
||||
private SecondaryId _secondaryOffhand;
|
||||
private FullEquipType _typeMainhand;
|
||||
private FullEquipType _typeOffhand;
|
||||
private byte _states;
|
||||
|
|
@ -50,12 +51,19 @@ public unsafe struct DesignData
|
|||
|| name.IsContained(_nameRFinger)
|
||||
|| name.IsContained(_nameLFinger)
|
||||
|| name.IsContained(_nameMainhand)
|
||||
|| name.IsContained(_nameOffhand);
|
||||
|| name.IsContained(_nameOffhand)
|
||||
|| name.IsContained(_nameFaceWear);
|
||||
|
||||
public readonly StainId Stain(EquipSlot slot)
|
||||
public readonly StainIds Stain(EquipSlot slot)
|
||||
{
|
||||
var index = slot.ToIndex();
|
||||
return index > 11 ? (StainId)0 : _equipmentBytes[4 * index + 3];
|
||||
return index switch
|
||||
{
|
||||
< 10 => new StainIds(_equipmentBytes[CharacterArmor.Size * index + 3], _equipmentBytes[CharacterArmor.Size * index + 4]),
|
||||
10 => new StainIds(_equipmentBytes[EquipmentByteSize + 6], _equipmentBytes[EquipmentByteSize + 7]),
|
||||
11 => new StainIds(_equipmentBytes[EquipmentByteSize + 14], _equipmentBytes[EquipmentByteSize + 15]),
|
||||
_ => StainIds.None,
|
||||
};
|
||||
}
|
||||
|
||||
public readonly bool Crest(CrestFlag slot)
|
||||
|
|
@ -69,24 +77,29 @@ public unsafe struct DesignData
|
|||
=> _typeOffhand;
|
||||
|
||||
public readonly EquipItem Item(EquipSlot slot)
|
||||
=> slot.ToIndex() switch
|
||||
{
|
||||
fixed (byte* ptr = _equipmentBytes)
|
||||
{
|
||||
return slot.ToIndex() switch
|
||||
{
|
||||
// @formatter:off
|
||||
0 => EquipItem.FromIds((ItemId)_itemIds[ 0], (IconId)_iconIds[ 0], (PrimaryId)(_equipmentBytes[ 0] | (_equipmentBytes[ 1] << 8)), (SecondaryId)0, (Variant)_equipmentBytes[ 2], FullEquipType.Head, name: _nameHead ),
|
||||
1 => EquipItem.FromIds((ItemId)_itemIds[ 1], (IconId)_iconIds[ 1], (PrimaryId)(_equipmentBytes[ 4] | (_equipmentBytes[ 5] << 8)), (SecondaryId)0, (Variant)_equipmentBytes[ 6], FullEquipType.Body, name: _nameBody ),
|
||||
2 => EquipItem.FromIds((ItemId)_itemIds[ 2], (IconId)_iconIds[ 2], (PrimaryId)(_equipmentBytes[ 8] | (_equipmentBytes[ 9] << 8)), (SecondaryId)0, (Variant)_equipmentBytes[10], FullEquipType.Hands, name: _nameHands ),
|
||||
3 => EquipItem.FromIds((ItemId)_itemIds[ 3], (IconId)_iconIds[ 3], (PrimaryId)(_equipmentBytes[12] | (_equipmentBytes[13] << 8)), (SecondaryId)0, (Variant)_equipmentBytes[14], FullEquipType.Legs, name: _nameLegs ),
|
||||
4 => EquipItem.FromIds((ItemId)_itemIds[ 4], (IconId)_iconIds[ 4], (PrimaryId)(_equipmentBytes[16] | (_equipmentBytes[17] << 8)), (SecondaryId)0, (Variant)_equipmentBytes[18], FullEquipType.Feet, name: _nameFeet ),
|
||||
5 => EquipItem.FromIds((ItemId)_itemIds[ 5], (IconId)_iconIds[ 5], (PrimaryId)(_equipmentBytes[20] | (_equipmentBytes[21] << 8)), (SecondaryId)0, (Variant)_equipmentBytes[22], FullEquipType.Ears, name: _nameEars ),
|
||||
6 => EquipItem.FromIds((ItemId)_itemIds[ 6], (IconId)_iconIds[ 6], (PrimaryId)(_equipmentBytes[24] | (_equipmentBytes[25] << 8)), (SecondaryId)0, (Variant)_equipmentBytes[26], FullEquipType.Neck, name: _nameNeck ),
|
||||
7 => EquipItem.FromIds((ItemId)_itemIds[ 7], (IconId)_iconIds[ 7], (PrimaryId)(_equipmentBytes[28] | (_equipmentBytes[29] << 8)), (SecondaryId)0, (Variant)_equipmentBytes[30], FullEquipType.Wrists, name: _nameWrists ),
|
||||
8 => EquipItem.FromIds((ItemId)_itemIds[ 8], (IconId)_iconIds[ 8], (PrimaryId)(_equipmentBytes[32] | (_equipmentBytes[33] << 8)), (SecondaryId)0, (Variant)_equipmentBytes[34], FullEquipType.Finger, name: _nameRFinger ),
|
||||
9 => EquipItem.FromIds((ItemId)_itemIds[ 9], (IconId)_iconIds[ 9], (PrimaryId)(_equipmentBytes[36] | (_equipmentBytes[37] << 8)), (SecondaryId)0, (Variant)_equipmentBytes[38], FullEquipType.Finger, name: _nameLFinger ),
|
||||
10 => EquipItem.FromIds((ItemId)_itemIds[10], (IconId)_iconIds[10], (PrimaryId)(_equipmentBytes[40] | (_equipmentBytes[41] << 8)), _secondaryMainhand, (Variant)_equipmentBytes[42], _typeMainhand, name: _nameMainhand),
|
||||
11 => EquipItem.FromIds((ItemId)_itemIds[11], (IconId)_iconIds[11], (PrimaryId)(_equipmentBytes[44] | (_equipmentBytes[45] << 8)), _secondaryOffhand, (Variant)_equipmentBytes[46], _typeOffhand, name: _nameOffhand ),
|
||||
0 => EquipItem.FromIds(_itemIds[ 0], _iconIds[ 0], ((CharacterArmor*)ptr)[0].Set, 0, ((CharacterArmor*)ptr)[0].Variant, FullEquipType.Head, name: _nameHead ),
|
||||
1 => EquipItem.FromIds(_itemIds[ 1], _iconIds[ 1], ((CharacterArmor*)ptr)[1].Set, 0, ((CharacterArmor*)ptr)[1].Variant, FullEquipType.Body, name: _nameBody ),
|
||||
2 => EquipItem.FromIds(_itemIds[ 2], _iconIds[ 2], ((CharacterArmor*)ptr)[2].Set, 0, ((CharacterArmor*)ptr)[2].Variant, FullEquipType.Hands, name: _nameHands ),
|
||||
3 => EquipItem.FromIds(_itemIds[ 3], _iconIds[ 3], ((CharacterArmor*)ptr)[3].Set, 0, ((CharacterArmor*)ptr)[3].Variant, FullEquipType.Legs, name: _nameLegs ),
|
||||
4 => EquipItem.FromIds(_itemIds[ 4], _iconIds[ 4], ((CharacterArmor*)ptr)[4].Set, 0, ((CharacterArmor*)ptr)[4].Variant, FullEquipType.Feet, name: _nameFeet ),
|
||||
5 => EquipItem.FromIds(_itemIds[ 5], _iconIds[ 5], ((CharacterArmor*)ptr)[5].Set, 0, ((CharacterArmor*)ptr)[5].Variant, FullEquipType.Ears, name: _nameEars ),
|
||||
6 => EquipItem.FromIds(_itemIds[ 6], _iconIds[ 6], ((CharacterArmor*)ptr)[6].Set, 0, ((CharacterArmor*)ptr)[6].Variant, FullEquipType.Neck, name: _nameNeck ),
|
||||
7 => EquipItem.FromIds(_itemIds[ 7], _iconIds[ 7], ((CharacterArmor*)ptr)[7].Set, 0, ((CharacterArmor*)ptr)[7].Variant, FullEquipType.Wrists, name: _nameWrists ),
|
||||
8 => EquipItem.FromIds(_itemIds[ 8], _iconIds[ 8], ((CharacterArmor*)ptr)[8].Set, 0, ((CharacterArmor*)ptr)[8].Variant, FullEquipType.Finger, name: _nameRFinger ),
|
||||
9 => EquipItem.FromIds(_itemIds[ 9], _iconIds[ 9], ((CharacterArmor*)ptr)[9].Set, 0, ((CharacterArmor*)ptr)[9].Variant, FullEquipType.Finger, name: _nameLFinger ),
|
||||
10 => EquipItem.FromIds(_itemIds[10], _iconIds[10], *(PrimaryId*)(ptr + EquipmentByteSize + 0), *(SecondaryId*)(ptr + EquipmentByteSize + 2), *(Variant*)(ptr + EquipmentByteSize + 4), _typeMainhand, name: _nameMainhand),
|
||||
11 => EquipItem.FromIds(_itemIds[11], _iconIds[11], *(PrimaryId*)(ptr + EquipmentByteSize + 4), *(SecondaryId*)(ptr + EquipmentByteSize + 2), *(Variant*)(ptr + EquipmentByteSize + 4), _typeOffhand, name: _nameOffhand ),
|
||||
_ => new EquipItem(),
|
||||
// @formatter:on
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public readonly CharacterArmor Armor(EquipSlot slot)
|
||||
{
|
||||
|
|
@ -113,8 +126,8 @@ public unsafe struct DesignData
|
|||
{
|
||||
fixed (byte* ptr = _equipmentBytes)
|
||||
{
|
||||
var armorPtr = (CharacterArmor*)ptr;
|
||||
return slot is EquipSlot.MainHand ? armorPtr[10].ToWeapon(_secondaryMainhand) : armorPtr[11].ToWeapon(_secondaryOffhand);
|
||||
var weaponPtr = (CharacterWeapon*)(ptr + EquipmentByteSize);
|
||||
return weaponPtr[slot is EquipSlot.MainHand ? 0 : 1];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -126,9 +139,9 @@ public unsafe struct DesignData
|
|||
|
||||
_itemIds[index] = item.ItemId.Id;
|
||||
_iconIds[index] = item.IconId.Id;
|
||||
_equipmentBytes[4 * index + 0] = (byte)item.PrimaryId.Id;
|
||||
_equipmentBytes[4 * index + 1] = (byte)(item.PrimaryId.Id >> 8);
|
||||
_equipmentBytes[4 * index + 2] = item.Variant.Id;
|
||||
_equipmentBytes[CharacterArmor.Size * index + 0] = (byte)item.PrimaryId.Id;
|
||||
_equipmentBytes[CharacterArmor.Size * index + 1] = (byte)(item.PrimaryId.Id >> 8);
|
||||
_equipmentBytes[CharacterArmor.Size * index + 2] = item.Variant.Id;
|
||||
switch (index)
|
||||
{
|
||||
// @formatter:off
|
||||
|
|
@ -145,12 +158,14 @@ public unsafe struct DesignData
|
|||
// @formatter:on
|
||||
case 10:
|
||||
_nameMainhand = item.Name;
|
||||
_secondaryMainhand = item.SecondaryId;
|
||||
_equipmentBytes[EquipmentByteSize + 2] = (byte)item.SecondaryId.Id;
|
||||
_equipmentBytes[EquipmentByteSize + 3] = (byte)(item.SecondaryId.Id >> 8);
|
||||
_typeMainhand = item.Type;
|
||||
return true;
|
||||
case 11:
|
||||
_nameOffhand = item.Name;
|
||||
_secondaryOffhand = item.SecondaryId;
|
||||
_equipmentBytes[EquipmentByteSize + 2] = (byte)item.SecondaryId.Id;
|
||||
_equipmentBytes[EquipmentByteSize + 3] = (byte)(item.SecondaryId.Id >> 8);
|
||||
_typeOffhand = item.Type;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -158,22 +173,24 @@ public unsafe struct DesignData
|
|||
return true;
|
||||
}
|
||||
|
||||
public bool SetStain(EquipSlot slot, StainId stain)
|
||||
public bool SetStain(EquipSlot slot, StainIds stains)
|
||||
=> slot.ToIndex() switch
|
||||
{
|
||||
0 => SetIfDifferent(ref _equipmentBytes[3], stain.Id),
|
||||
1 => SetIfDifferent(ref _equipmentBytes[7], stain.Id),
|
||||
2 => SetIfDifferent(ref _equipmentBytes[11], stain.Id),
|
||||
3 => SetIfDifferent(ref _equipmentBytes[15], stain.Id),
|
||||
4 => SetIfDifferent(ref _equipmentBytes[19], stain.Id),
|
||||
5 => SetIfDifferent(ref _equipmentBytes[23], stain.Id),
|
||||
6 => SetIfDifferent(ref _equipmentBytes[27], stain.Id),
|
||||
7 => SetIfDifferent(ref _equipmentBytes[31], stain.Id),
|
||||
8 => SetIfDifferent(ref _equipmentBytes[35], stain.Id),
|
||||
9 => SetIfDifferent(ref _equipmentBytes[39], stain.Id),
|
||||
10 => SetIfDifferent(ref _equipmentBytes[43], stain.Id),
|
||||
11 => SetIfDifferent(ref _equipmentBytes[47], stain.Id),
|
||||
// @formatter:off
|
||||
0 => SetIfDifferent(ref _equipmentBytes[0 * CharacterArmor.Size + 3], stains.Stain1.Id) || SetIfDifferent(ref _equipmentBytes[0 * CharacterArmor.Size + 4], stains.Stain2.Id),
|
||||
1 => SetIfDifferent(ref _equipmentBytes[1 * CharacterArmor.Size + 3], stains.Stain1.Id) || SetIfDifferent(ref _equipmentBytes[1 * CharacterArmor.Size + 4], stains.Stain2.Id),
|
||||
2 => SetIfDifferent(ref _equipmentBytes[2 * CharacterArmor.Size + 3], stains.Stain1.Id) || SetIfDifferent(ref _equipmentBytes[2 * CharacterArmor.Size + 4], stains.Stain2.Id),
|
||||
3 => SetIfDifferent(ref _equipmentBytes[3 * CharacterArmor.Size + 3], stains.Stain1.Id) || SetIfDifferent(ref _equipmentBytes[3 * CharacterArmor.Size + 4], stains.Stain2.Id),
|
||||
4 => SetIfDifferent(ref _equipmentBytes[4 * CharacterArmor.Size + 3], stains.Stain1.Id) || SetIfDifferent(ref _equipmentBytes[4 * CharacterArmor.Size + 4], stains.Stain2.Id),
|
||||
5 => SetIfDifferent(ref _equipmentBytes[5 * CharacterArmor.Size + 3], stains.Stain1.Id) || SetIfDifferent(ref _equipmentBytes[5 * CharacterArmor.Size + 4], stains.Stain2.Id),
|
||||
6 => SetIfDifferent(ref _equipmentBytes[6 * CharacterArmor.Size + 3], stains.Stain1.Id) || SetIfDifferent(ref _equipmentBytes[6 * CharacterArmor.Size + 4], stains.Stain2.Id),
|
||||
7 => SetIfDifferent(ref _equipmentBytes[7 * CharacterArmor.Size + 3], stains.Stain1.Id) || SetIfDifferent(ref _equipmentBytes[7 * CharacterArmor.Size + 4], stains.Stain2.Id),
|
||||
8 => SetIfDifferent(ref _equipmentBytes[8 * CharacterArmor.Size + 3], stains.Stain1.Id) || SetIfDifferent(ref _equipmentBytes[8 * CharacterArmor.Size + 4], stains.Stain2.Id),
|
||||
9 => SetIfDifferent(ref _equipmentBytes[9 * CharacterArmor.Size + 3], stains.Stain1.Id) || SetIfDifferent(ref _equipmentBytes[9 * CharacterArmor.Size + 4], stains.Stain2.Id),
|
||||
10 => SetIfDifferent(ref _equipmentBytes[EquipmentByteSize + 6], stains.Stain1.Id) || SetIfDifferent(ref _equipmentBytes[EquipmentByteSize + 7], stains.Stain2.Id),
|
||||
11 => SetIfDifferent(ref _equipmentBytes[EquipmentByteSize + 14], stains.Stain1.Id) || SetIfDifferent(ref _equipmentBytes[EquipmentByteSize + 15], stains.Stain2.Id),
|
||||
_ => false,
|
||||
// @formatter:on
|
||||
};
|
||||
|
||||
public bool SetCrest(CrestFlag slot, bool visible)
|
||||
|
|
@ -260,15 +277,15 @@ public unsafe struct DesignData
|
|||
foreach (var slot in EquipSlotExtensions.EqdpSlots)
|
||||
{
|
||||
SetItem(slot, ItemManager.NothingItem(slot));
|
||||
SetStain(slot, 0);
|
||||
SetStain(slot, StainIds.None);
|
||||
SetCrest(slot.ToCrestFlag(), false);
|
||||
}
|
||||
|
||||
SetItem(EquipSlot.MainHand, items.DefaultSword);
|
||||
SetStain(EquipSlot.MainHand, 0);
|
||||
SetStain(EquipSlot.MainHand, StainIds.None);
|
||||
SetCrest(CrestFlag.MainHand, false);
|
||||
SetItem(EquipSlot.OffHand, ItemManager.NothingItem(FullEquipType.Shield));
|
||||
SetStain(EquipSlot.OffHand, 0);
|
||||
SetStain(EquipSlot.OffHand, StainIds.None);
|
||||
SetCrest(CrestFlag.OffHand, false);
|
||||
}
|
||||
|
||||
|
|
@ -291,7 +308,7 @@ public unsafe struct DesignData
|
|||
MemoryUtility.MemSet(ptr, 0, 10 * 4);
|
||||
}
|
||||
|
||||
fixed (ushort* ptr = _iconIds)
|
||||
fixed (uint* ptr = _iconIds)
|
||||
{
|
||||
MemoryUtility.MemSet(ptr, 0, 10 * 2);
|
||||
}
|
||||
|
|
@ -306,6 +323,7 @@ public unsafe struct DesignData
|
|||
_nameWrists = string.Empty;
|
||||
_nameRFinger = string.Empty;
|
||||
_nameLFinger = string.Empty;
|
||||
_nameFaceWear = string.Empty;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -322,7 +340,7 @@ public unsafe struct DesignData
|
|||
|
||||
public readonly byte[] GetEquipmentBytes()
|
||||
{
|
||||
var ret = new byte[40];
|
||||
var ret = new byte[80];
|
||||
fixed (byte* retPtr = ret, inPtr = _equipmentBytes)
|
||||
{
|
||||
MemoryUtility.MemCpyUnchecked(retPtr, inPtr, ret.Length);
|
||||
|
|
@ -343,8 +361,8 @@ public unsafe struct DesignData
|
|||
{
|
||||
fixed (byte* dataPtr = _equipmentBytes)
|
||||
{
|
||||
var data = new Span<byte>(dataPtr, 40);
|
||||
return Convert.TryFromBase64String(base64, data, out var written) && written == 40;
|
||||
var data = new Span<byte>(dataPtr, 80);
|
||||
return Convert.TryFromBase64String(base64, data, out var written) && written == 80;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@ using Glamourer.Events;
|
|||
using Glamourer.GameData;
|
||||
using Glamourer.Interop.Material;
|
||||
using Glamourer.Services;
|
||||
using Glamourer.State;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
|
|
@ -167,29 +166,29 @@ public class DesignEditor(
|
|||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void ChangeStain(object data, EquipSlot slot, StainId stain, ApplySettings _ = default)
|
||||
public void ChangeStains(object data, EquipSlot slot, StainIds stains, ApplySettings _ = default)
|
||||
{
|
||||
var design = (Design)data;
|
||||
if (Items.ValidateStain(stain, out var _, false).Length > 0)
|
||||
if (Items.ValidateStain(stains, out var _, false).Length > 0)
|
||||
return;
|
||||
|
||||
var oldStain = design.DesignData.Stain(slot);
|
||||
if (!design.GetDesignDataRef().SetStain(slot, stain))
|
||||
if (!design.GetDesignDataRef().SetStain(slot, stains))
|
||||
return;
|
||||
|
||||
design.LastEdit = DateTimeOffset.UtcNow;
|
||||
SaveService.QueueSave(design);
|
||||
Glamourer.Log.Debug($"Set stain of {slot} equipment piece to {stain.Id}.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.Stain, design, (oldStain, stain, slot));
|
||||
Glamourer.Log.Debug($"Set stain of {slot} equipment piece to {stains}.");
|
||||
DesignChanged.Invoke(DesignChanged.Type.Stain, design, (oldStain, stains, slot));
|
||||
}
|
||||
|
||||
/// <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)
|
||||
ChangeItem(data, slot, item.Value, _);
|
||||
if (stain.HasValue)
|
||||
ChangeStain(data, slot, stain.Value, _);
|
||||
if (stains.HasValue)
|
||||
ChangeStains(data, slot, stains.Value, _);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Services;
|
||||
using Newtonsoft.Json;
|
||||
|
|
|
|||
|
|
@ -65,11 +65,11 @@ public interface IDesignEditor
|
|||
=> ChangeEquip(data, slot, item, null, settings);
|
||||
|
||||
/// <summary> Change the stain for any equipment piece. </summary>
|
||||
public void ChangeStain(object data, EquipSlot slot, StainId stain, ApplySettings settings = default)
|
||||
=> ChangeEquip(data, slot, null, stain, settings);
|
||||
public void ChangeStains(object data, EquipSlot slot, StainIds stains, ApplySettings settings = default)
|
||||
=> ChangeEquip(data, slot, null, stains, settings);
|
||||
|
||||
/// <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>
|
||||
public void ChangeCrest(object data, CrestFlag slot, bool crest, ApplySettings settings = default);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using OtterGui;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Services;
|
||||
using Notification = OtterGui.Classes.Notification;
|
||||
|
||||
namespace Glamourer.Designs.Links;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Glamourer.Gui;
|
||||
using Glamourer.Services;
|
||||
using Newtonsoft.Json;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ namespace Glamourer.Events;
|
|||
/// </list>
|
||||
/// </summary>
|
||||
public sealed class MovedEquipment()
|
||||
: EventWrapper<(EquipSlot, uint, StainId)[], MovedEquipment.Priority>(nameof(MovedEquipment))
|
||||
: EventWrapper<(EquipSlot, uint, StainIds)[], MovedEquipment.Priority>(nameof(MovedEquipment))
|
||||
{
|
||||
public enum Priority
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.Interface.Textures;
|
||||
using Dalamud.Interface.Textures.TextureWraps;
|
||||
using Dalamud.Plugin.Services;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Services;
|
||||
|
|
@ -32,8 +33,8 @@ public class CustomizeManager : IAsyncDataContainer
|
|||
}
|
||||
|
||||
/// <summary> Get specific icons. </summary>
|
||||
public IDalamudTextureWrap GetIcon(uint id)
|
||||
=> _icons.LoadIcon(id)!;
|
||||
public ISharedImmediateTexture GetIcon(uint id)
|
||||
=> _icons.TextureProvider.GetFromGameIcon(id);
|
||||
|
||||
/// <summary> Iterate over all supported genders and clans. </summary>
|
||||
public static IEnumerable<(SubRace Clan, Gender Gender)> AllSets()
|
||||
|
|
@ -47,7 +48,7 @@ public class CustomizeManager : IAsyncDataContainer
|
|||
|
||||
public CustomizeManager(ITextureProvider textures, IDataManager gameData, IPluginLog log, NpcCustomizeSet npcCustomizeSet)
|
||||
{
|
||||
_icons = new IconStorage(textures, gameData);
|
||||
_icons = new TextureCache(gameData, textures);
|
||||
var stopwatch = new Stopwatch();
|
||||
var tmpTask = Task.Run(() =>
|
||||
{
|
||||
|
|
@ -72,7 +73,7 @@ public class CustomizeManager : IAsyncDataContainer
|
|||
public bool Finished
|
||||
=> Awaiter.IsCompletedSuccessfully;
|
||||
|
||||
private readonly IconStorage _icons;
|
||||
private readonly TextureCache _icons;
|
||||
private static readonly int ListSize = Clans.Count * Genders.Count;
|
||||
private readonly CustomizeSet[] _customizationSets = new CustomizeSet[ListSize];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Dalamud;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Dalamud.Utility;
|
||||
using Lumina.Excel;
|
||||
|
|
@ -13,11 +14,11 @@ namespace Glamourer.GameData;
|
|||
internal class CustomizeSetFactory(
|
||||
IDataManager _gameData,
|
||||
IPluginLog _log,
|
||||
IconStorage _icons,
|
||||
TextureCache _icons,
|
||||
NpcCustomizeSet _npcCustomizeSet,
|
||||
ColorParameters _colors)
|
||||
{
|
||||
public CustomizeSetFactory(IDataManager gameData, IPluginLog log, IconStorage icons, NpcCustomizeSet npcCustomizeSet)
|
||||
public CustomizeSetFactory(IDataManager gameData, IPluginLog log, TextureCache icons, NpcCustomizeSet npcCustomizeSet)
|
||||
: this(gameData, log, icons, npcCustomizeSet, new ColorParameters(gameData, log))
|
||||
{ }
|
||||
|
||||
|
|
@ -87,7 +88,8 @@ internal class CustomizeSetFactory(
|
|||
|
||||
var npcCustomizations = new HashSet<(CustomizeIndex, CustomizeValue)>();
|
||||
_npcCustomizeSet.Awaiter.Wait();
|
||||
foreach (var customize in _npcCustomizeSet.Select(s => s.Customize).Where(c => c.Clan == race && c.Gender == gender && c.BodyType.Value == 1))
|
||||
foreach (var customize in _npcCustomizeSet.Select(s => s.Customize)
|
||||
.Where(c => c.Clan == race && c.Gender == gender && c.BodyType.Value == 1))
|
||||
{
|
||||
foreach (var customizeIndex in customizeIndices)
|
||||
{
|
||||
|
|
@ -346,10 +348,6 @@ internal class CustomizeSetFactory(
|
|||
/// <summary> Set the availability of options according to actual availability. </summary>
|
||||
private static void SetAvailability(CustomizeSet set, CharaMakeParams row)
|
||||
{
|
||||
// TODO: Hrothgar female
|
||||
if (set is { Race: Race.Hrothgar, Gender: Gender.Female })
|
||||
return;
|
||||
|
||||
Set(true, CustomizeIndex.Height);
|
||||
Set(set.Faces.Count > 0, CustomizeIndex.Face);
|
||||
Set(true, CustomizeIndex.Hairstyle);
|
||||
|
|
|
|||
|
|
@ -80,25 +80,9 @@ public class NpcCustomizeSet : IAsyncDataContainer, IReadOnlyList<NpcData>
|
|||
// Event NPCs have a reference to NpcEquip but also contain the appearance in their own row.
|
||||
// Prefer the NpcEquip reference if it is set, otherwise use the own.
|
||||
if (row.NpcEquip.Row != 0 && row.NpcEquip.Value is { } equip)
|
||||
{
|
||||
ApplyNpcEquip(ref ret, equip);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.Set(0, row.ModelHead | (row.DyeHead.Row << 24));
|
||||
ret.Set(1, row.ModelBody | (row.DyeBody.Row << 24));
|
||||
ret.Set(2, row.ModelHands | (row.DyeHands.Row << 24));
|
||||
ret.Set(3, row.ModelLegs | (row.DyeLegs.Row << 24));
|
||||
ret.Set(4, row.ModelFeet | (row.DyeFeet.Row << 24));
|
||||
ret.Set(5, row.ModelEars | (row.DyeEars.Row << 24));
|
||||
ret.Set(6, row.ModelNeck | (row.DyeNeck.Row << 24));
|
||||
ret.Set(7, row.ModelWrists | (row.DyeWrists.Row << 24));
|
||||
ret.Set(8, row.ModelRightRing | (row.DyeRightRing.Row << 24));
|
||||
ret.Set(9, row.ModelLeftRing | (row.DyeLeftRing.Row << 24));
|
||||
ret.Mainhand = new CharacterWeapon(row.ModelMainHand | ((ulong)row.DyeMainHand.Row << 48));
|
||||
ret.Offhand = new CharacterWeapon(row.ModelOffHand | ((ulong)row.DyeOffHand.Row << 48));
|
||||
ret.VisorToggled = row.Visor;
|
||||
}
|
||||
ApplyNpcEquip(ref ret, row);
|
||||
|
||||
list.Add(ret);
|
||||
}
|
||||
|
|
@ -202,18 +186,36 @@ public class NpcCustomizeSet : IAsyncDataContainer, IReadOnlyList<NpcData>
|
|||
/// <summary> Apply equipment from a NpcEquip row. </summary>
|
||||
private static void ApplyNpcEquip(ref NpcData data, NpcEquip row)
|
||||
{
|
||||
data.Set(0, row.ModelHead | (row.DyeHead.Row << 24));
|
||||
data.Set(1, row.ModelBody | (row.DyeBody.Row << 24));
|
||||
data.Set(2, row.ModelHands | (row.DyeHands.Row << 24));
|
||||
data.Set(3, row.ModelLegs | (row.DyeLegs.Row << 24));
|
||||
data.Set(4, row.ModelFeet | (row.DyeFeet.Row << 24));
|
||||
data.Set(5, row.ModelEars | (row.DyeEars.Row << 24));
|
||||
data.Set(6, row.ModelNeck | (row.DyeNeck.Row << 24));
|
||||
data.Set(7, row.ModelWrists | (row.DyeWrists.Row << 24));
|
||||
data.Set(8, row.ModelRightRing | (row.DyeRightRing.Row << 24));
|
||||
data.Set(9, row.ModelLeftRing | (row.DyeLeftRing.Row << 24));
|
||||
data.Mainhand = new CharacterWeapon(row.ModelMainHand | ((ulong)row.DyeMainHand.Row << 48));
|
||||
data.Offhand = new CharacterWeapon(row.ModelOffHand | ((ulong)row.DyeOffHand.Row << 48));
|
||||
data.Set(0, row.ModelHead | (row.DyeHead.Row << 24) | ((ulong)row.Dye2Head.Row << 32));
|
||||
data.Set(1, row.ModelBody | (row.DyeBody.Row << 24) | ((ulong)row.Dye2Body.Row << 32));
|
||||
data.Set(2, row.ModelHands | (row.DyeHands.Row << 24) | ((ulong)row.Dye2Hands.Row << 32));
|
||||
data.Set(3, row.ModelLegs | (row.DyeLegs.Row << 24) | ((ulong)row.Dye2Legs.Row << 32));
|
||||
data.Set(4, row.ModelFeet | (row.DyeFeet.Row << 24) | ((ulong)row.Dye2Feet.Row << 32));
|
||||
data.Set(5, row.ModelEars | (row.DyeEars.Row << 24) | ((ulong)row.Dye2Ears.Row << 32));
|
||||
data.Set(6, row.ModelNeck | (row.DyeNeck.Row << 24) | ((ulong)row.Dye2Neck.Row << 32));
|
||||
data.Set(7, row.ModelWrists | (row.DyeWrists.Row << 24) | ((ulong)row.Dye2Wrists.Row << 32));
|
||||
data.Set(8, row.ModelRightRing | (row.DyeRightRing.Row << 24) | ((ulong)row.Dye2RightRing.Row << 32));
|
||||
data.Set(9, row.ModelLeftRing | (row.DyeLeftRing.Row << 24) | ((ulong)row.Dye2LeftRing.Row << 32));
|
||||
data.Mainhand = new CharacterWeapon(row.ModelMainHand | ((ulong)row.DyeMainHand.Row << 48) | ((ulong)row.Dye2MainHand.Row << 56));
|
||||
data.Offhand = new CharacterWeapon(row.ModelOffHand | ((ulong)row.DyeOffHand.Row << 48) | ((ulong)row.Dye2OffHand.Row << 56));
|
||||
data.VisorToggled = row.Visor;
|
||||
}
|
||||
|
||||
/// <summary> Apply equipment from a ENpcBase Row row. </summary>
|
||||
private static void ApplyNpcEquip(ref NpcData data, ENpcBase row)
|
||||
{
|
||||
data.Set(0, row.ModelHead | (row.DyeHead.Row << 24) | ((ulong)row.Dye2Head.Row << 32));
|
||||
data.Set(1, row.ModelBody | (row.DyeBody.Row << 24) | ((ulong)row.Dye2Body.Row << 32));
|
||||
data.Set(2, row.ModelHands | (row.DyeHands.Row << 24) | ((ulong)row.Dye2Hands.Row << 32));
|
||||
data.Set(3, row.ModelLegs | (row.DyeLegs.Row << 24) | ((ulong)row.Dye2Legs.Row << 32));
|
||||
data.Set(4, row.ModelFeet | (row.DyeFeet.Row << 24) | ((ulong)row.Dye2Feet.Row << 32));
|
||||
data.Set(5, row.ModelEars | (row.DyeEars.Row << 24) | ((ulong)row.Dye2Ears.Row << 32));
|
||||
data.Set(6, row.ModelNeck | (row.DyeNeck.Row << 24) | ((ulong)row.Dye2Neck.Row << 32));
|
||||
data.Set(7, row.ModelWrists | (row.DyeWrists.Row << 24) | ((ulong)row.Dye2Wrists.Row << 32));
|
||||
data.Set(8, row.ModelRightRing | (row.DyeRightRing.Row << 24) | ((ulong)row.Dye2RightRing.Row << 32));
|
||||
data.Set(9, row.ModelLeftRing | (row.DyeLeftRing.Row << 24) | ((ulong)row.Dye2LeftRing.Row << 32));
|
||||
data.Mainhand = new CharacterWeapon(row.ModelMainHand | ((ulong)row.DyeMainHand.Row << 48) | ((ulong)row.Dye2MainHand.Row << 56));
|
||||
data.Offhand = new CharacterWeapon(row.ModelOffHand | ((ulong)row.DyeOffHand.Row << 48) | ((ulong)row.Dye2OffHand.Row << 56));
|
||||
data.VisorToggled = row.Visor;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ public unsafe struct NpcData
|
|||
public CustomizeArray Customize;
|
||||
|
||||
/// <summary> The equipment appearance of the NPC, 10 * CharacterArmor. </summary>
|
||||
private fixed byte _equip[40];
|
||||
private fixed byte _equip[CharacterArmor.Size * 10];
|
||||
|
||||
/// <summary> The mainhand weapon appearance of the NPC. </summary>
|
||||
public CharacterWeapon Mainhand;
|
||||
|
|
@ -54,36 +54,35 @@ public unsafe struct NpcData
|
|||
{
|
||||
sb.Append(span[i].Set.Id.ToString("D4"))
|
||||
.Append('-')
|
||||
.Append(span[i].Variant.Id.ToString("D3"))
|
||||
.Append('-')
|
||||
.Append(span[i].Stain.Id.ToString("D3"))
|
||||
.Append(", ");
|
||||
.Append(span[i].Variant.Id.ToString("D3"));
|
||||
foreach (var stain in span[i].Stains)
|
||||
sb.Append('-').Append(stain.Id.ToString("D3"));
|
||||
}
|
||||
|
||||
sb.Append(Mainhand.Skeleton.Id.ToString("D4"))
|
||||
.Append('-')
|
||||
.Append(Mainhand.Weapon.Id.ToString("D4"))
|
||||
.Append('-')
|
||||
.Append(Mainhand.Variant.Id.ToString("D3"))
|
||||
.Append('-')
|
||||
.Append(Mainhand.Stain.Id.ToString("D4"))
|
||||
.Append(", ")
|
||||
.Append(Mainhand.Variant.Id.ToString("D3"));
|
||||
foreach (var stain in Mainhand.Stains)
|
||||
sb.Append('-').Append(stain.Id.ToString("D3"));
|
||||
sb.Append(", ")
|
||||
.Append(Offhand.Skeleton.Id.ToString("D4"))
|
||||
.Append('-')
|
||||
.Append(Offhand.Weapon.Id.ToString("D4"))
|
||||
.Append('-')
|
||||
.Append(Offhand.Variant.Id.ToString("D3"))
|
||||
.Append('-')
|
||||
.Append(Offhand.Stain.Id.ToString("D3"));
|
||||
.Append(Offhand.Variant.Id.ToString("D3"));
|
||||
foreach (var stain in Mainhand.Stains)
|
||||
sb.Append('-').Append(stain.Id.ToString("D3"));
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary> Set an equipment piece to a given value. </summary>
|
||||
internal void Set(int idx, uint value)
|
||||
internal void Set(int idx, ulong value)
|
||||
{
|
||||
fixed (byte* ptr = _equip)
|
||||
{
|
||||
((uint*)ptr)[idx] = value;
|
||||
((ulong*)ptr)[idx] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public class Glamourer : IDalamudPlugin
|
|||
|
||||
private readonly ServiceManager _services;
|
||||
|
||||
public Glamourer(DalamudPluginInterface pluginInterface)
|
||||
public Glamourer(IDalamudPluginInterface pluginInterface)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
|
@ -128,7 +128,7 @@ public class Glamourer : IDalamudPlugin
|
|||
[
|
||||
"Penumbra", "MareSynchronos", "CustomizePlus", "SimpleHeels", "VfxEditor", "heliosphere-plugin", "Ktisis", "Brio", "DynamicBridge",
|
||||
];
|
||||
var plugins = _services.GetService<DalamudPluginInterface>().InstalledPlugins
|
||||
var plugins = _services.GetService<IDalamudPluginInterface>().InstalledPlugins
|
||||
.GroupBy(p => p.InternalName)
|
||||
.ToDictionary(g => g.Key, g =>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<DalamudLibPath>$(AppData)\XIVLauncher\addon\Hooks\dev\</DalamudLibPath>
|
||||
<DalamudLibPath>H:\Projects\FFPlugins\Dalamud\bin\Release\</DalamudLibPath>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
"AssemblyVersion": "9.0.0.1",
|
||||
"RepoUrl": "https://github.com/Ottermandias/Glamourer",
|
||||
"ApplicableVersion": "any",
|
||||
"DalamudApiLevel": 9,
|
||||
"DalamudApiLevel": 10,
|
||||
"ImageUrls": null,
|
||||
"IconUrl": "https://raw.githubusercontent.com/Ottermandias/Glamourer/master/images/icon.png"
|
||||
}
|
||||
|
|
@ -34,11 +34,10 @@ public partial class CustomizationDrawer
|
|||
|
||||
private void DrawGenderSelector()
|
||||
{
|
||||
using (var disabled = ImRaii.Disabled(_locked || _lockedRedraw))
|
||||
using (ImRaii.Disabled(_locked || _lockedRedraw))
|
||||
{
|
||||
var icon = _customize.Gender switch
|
||||
{
|
||||
Gender.Male when _customize.Race is Race.Hrothgar => FontAwesomeIcon.MarsDouble,
|
||||
Gender.Male => FontAwesomeIcon.Mars,
|
||||
Gender.Female => FontAwesomeIcon.Venus,
|
||||
_ => FontAwesomeIcon.Question,
|
||||
|
|
@ -56,7 +55,7 @@ public partial class CustomizationDrawer
|
|||
|
||||
private void DrawRaceCombo()
|
||||
{
|
||||
using (var disabled = ImRaii.Disabled(_locked || _lockedRedraw))
|
||||
using (ImRaii.Disabled(_locked || _lockedRedraw))
|
||||
{
|
||||
ImGui.SetNextItemWidth(_raceSelectorWidth);
|
||||
using (var combo = ImRaii.Combo("##subRaceCombo", _service.ClanName(_customize.Clan, _customize.Gender)))
|
||||
|
|
|
|||
|
|
@ -30,9 +30,10 @@ public partial class CustomizationDrawer
|
|||
}
|
||||
|
||||
var icon = _service.Manager.GetIcon(custom!.Value.IconId);
|
||||
var hasIcon = icon.TryGetWrap(out var wrap, out _);
|
||||
using (_ = ImRaii.Disabled(_locked || _currentIndex is CustomizeIndex.Face && _lockedRedraw))
|
||||
{
|
||||
if (ImGui.ImageButton(icon.ImGuiHandle, _iconSize))
|
||||
if (ImGui.ImageButton(wrap?.ImGuiHandle ?? icon.GetWrapOrEmpty().ImGuiHandle, _iconSize))
|
||||
{
|
||||
ImGui.OpenPopup(IconSelectorPopup);
|
||||
}
|
||||
|
|
@ -43,7 +44,8 @@ public partial class CustomizationDrawer
|
|||
}
|
||||
}
|
||||
|
||||
ImGuiUtil.HoverIconTooltip(icon, _iconSize);
|
||||
if (hasIcon)
|
||||
ImGuiUtil.HoverIconTooltip(wrap!, _iconSize);
|
||||
|
||||
ImGui.SameLine();
|
||||
using (_ = ImRaii.Group())
|
||||
|
|
@ -83,8 +85,9 @@ public partial class CustomizationDrawer
|
|||
using var frameColor = current == i
|
||||
? ImRaii.PushColor(ImGuiCol.Button, Colors.SelectedRed)
|
||||
: ImRaii.PushColor(ImGuiCol.Button, ColorId.FavoriteStarOn.Value(), isFavorite);
|
||||
var hasIcon = icon.TryGetWrap(out var wrap, out var _);
|
||||
|
||||
if (ImGui.ImageButton(icon.ImGuiHandle, _iconSize))
|
||||
if (ImGui.ImageButton(wrap?.ImGuiHandle ?? icon.GetWrapOrEmpty().ImGuiHandle, _iconSize))
|
||||
{
|
||||
UpdateValue(custom.Value);
|
||||
ImGui.CloseCurrentPopup();
|
||||
|
|
@ -96,7 +99,8 @@ public partial class CustomizationDrawer
|
|||
else
|
||||
_favorites.TryAdd(_set.Gender, _set.Clan, _currentIndex, custom.Value);
|
||||
|
||||
ImGuiUtil.HoverIconTooltip(icon, _iconSize,
|
||||
if (hasIcon)
|
||||
ImGuiUtil.HoverIconTooltip(wrap!, _iconSize,
|
||||
FavoriteManager.TypeAllowed(_currentIndex) ? "Right-Click to toggle favorite." : string.Empty);
|
||||
|
||||
var text = custom.Value.ToString();
|
||||
|
|
@ -199,14 +203,17 @@ public partial class CustomizationDrawer
|
|||
var icon = featureIdx == CustomizeIndex.LegacyTattoo
|
||||
? _legacyTattoo ?? _service.Manager.GetIcon(feature.IconId)
|
||||
: _service.Manager.GetIcon(feature.IconId);
|
||||
if (ImGui.ImageButton(icon.ImGuiHandle, _iconSize, Vector2.Zero, Vector2.One, (int)ImGui.GetStyle().FramePadding.X,
|
||||
var hasIcon = icon.TryGetWrap(out var wrap, out _);
|
||||
if (ImGui.ImageButton(wrap?.ImGuiHandle ?? icon.GetWrapOrEmpty().ImGuiHandle, _iconSize, Vector2.Zero, Vector2.One,
|
||||
(int)ImGui.GetStyle().FramePadding.X,
|
||||
Vector4.Zero, enabled ? Vector4.One : _redTint))
|
||||
{
|
||||
_customize.Set(featureIdx, enabled ? CustomizeValue.Zero : CustomizeValue.Max);
|
||||
Changed |= _currentFlag;
|
||||
}
|
||||
|
||||
ImGuiUtil.HoverIconTooltip(icon, _iconSize);
|
||||
if (hasIcon)
|
||||
ImGuiUtil.HoverIconTooltip(wrap!, _iconSize);
|
||||
if (idx % 4 != 3)
|
||||
ImGui.SameLine();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.Interface.Textures;
|
||||
using Dalamud.Interface.Textures.TextureWraps;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Plugin;
|
||||
using Glamourer.GameData;
|
||||
|
|
@ -6,6 +8,7 @@ using Glamourer.Services;
|
|||
using Glamourer.Unlocks;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Raii;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
|
@ -13,16 +16,15 @@ using Penumbra.GameData.Structs;
|
|||
namespace Glamourer.Gui.Customization;
|
||||
|
||||
public partial class CustomizationDrawer(
|
||||
DalamudPluginInterface pi,
|
||||
TextureCache textureCache,
|
||||
CustomizeService _service,
|
||||
CodeService _codes,
|
||||
Configuration _config,
|
||||
FavoriteManager _favorites,
|
||||
HeightService _heightService)
|
||||
: IDisposable
|
||||
{
|
||||
private readonly Vector4 _redTint = new(0.6f, 0.3f, 0.3f, 1f);
|
||||
private readonly IDalamudTextureWrap? _legacyTattoo = GetLegacyTattooIcon(pi);
|
||||
private readonly ISharedImmediateTexture? _legacyTattoo = GetLegacyTattooIcon(textureCache);
|
||||
|
||||
private Exception? _terminate;
|
||||
|
||||
|
|
@ -47,9 +49,6 @@ public partial class CustomizationDrawer(
|
|||
private float _raceSelectorWidth;
|
||||
private bool _withApply;
|
||||
|
||||
public void Dispose()
|
||||
=> _legacyTattoo?.Dispose();
|
||||
|
||||
public bool Draw(CustomizeArray current, bool locked, bool lockedRedraw)
|
||||
{
|
||||
_withApply = false;
|
||||
|
|
@ -190,16 +189,6 @@ public partial class CustomizationDrawer(
|
|||
_raceSelectorWidth = _inputIntSize + _comboSelectorSize - _framedIconSize.X;
|
||||
}
|
||||
|
||||
private static IDalamudTextureWrap? GetLegacyTattooIcon(DalamudPluginInterface pi)
|
||||
{
|
||||
using var resource = Assembly.GetExecutingAssembly().GetManifestResourceStream("Glamourer.LegacyTattoo.raw");
|
||||
if (resource == null)
|
||||
return null;
|
||||
|
||||
var rawImage = new byte[resource.Length];
|
||||
var length = resource.Read(rawImage, 0, (int)resource.Length);
|
||||
return length == resource.Length
|
||||
? pi.UiBuilder.LoadImageRaw(rawImage, 192, 192, 4)
|
||||
: null;
|
||||
}
|
||||
private static ISharedImmediateTexture? GetLegacyTattooIcon(TextureCache icons)
|
||||
=> icons.TextureProvider.GetFromManifestResource(Assembly.GetExecutingAssembly(), "Glamourer.LegacyTattoo.raw");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@ public struct EquipDrawData(EquipSlot slot, in DesignData designData)
|
|||
public readonly void SetItem(EquipItem item)
|
||||
=> _editor.ChangeItem(_object, Slot, item, ApplySettings.Manual);
|
||||
|
||||
public readonly void SetStain(StainId stain)
|
||||
=> _editor.ChangeStain(_object, Slot, stain, ApplySettings.Manual);
|
||||
public readonly void SetStains(StainIds stains)
|
||||
=> _editor.ChangeStains(_object, Slot, stains, ApplySettings.Manual);
|
||||
|
||||
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 StainId CurrentStain = designData.Stain(slot);
|
||||
public StainIds CurrentStains = designData.Stain(slot);
|
||||
public EquipItem GameItem = default;
|
||||
public StainId GameStain = default;
|
||||
public StainIds GameStains = default;
|
||||
public bool CurrentApply;
|
||||
public bool CurrentApplyStain;
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ public struct EquipDrawData(EquipSlot slot, in DesignData designData)
|
|||
Locked = state.IsLocked,
|
||||
DisplayApplication = false,
|
||||
GameItem = state.BaseData.Item(slot),
|
||||
GameStain = state.BaseData.Stain(slot),
|
||||
GameStains = state.BaseData.Stain(slot),
|
||||
AllowRevert = true,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ using Glamourer.Unlocks;
|
|||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Text;
|
||||
using OtterGui.Widgets;
|
||||
using Penumbra.GameData.DataContainers;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
|
@ -236,14 +237,18 @@ public class EquipmentDrawer
|
|||
/// <summary> Draw an input for stain that can set arbitrary values instead of choosing valid stains. </summary>
|
||||
private static void DrawStainArtisan(EquipDrawData data)
|
||||
{
|
||||
int stainId = data.CurrentStain.Id;
|
||||
foreach (var (stain, index) in data.CurrentStains.WithIndex())
|
||||
{
|
||||
using var id = ImUtf8.PushId(index);
|
||||
int stainId = stain.Id;
|
||||
ImGui.SetNextItemWidth(40 * ImGuiHelpers.GlobalScale);
|
||||
if (!ImGui.InputInt("##stain", ref stainId, 0, 0))
|
||||
return;
|
||||
|
||||
var newStainId = (StainId)Math.Clamp(stainId, 0, byte.MaxValue);
|
||||
if (newStainId != data.CurrentStain.Id)
|
||||
data.SetStain(newStainId);
|
||||
if (newStainId != stain.Id)
|
||||
data.SetStains(data.CurrentStains.With(index, newStainId));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary> Draw an input for armor that can set arbitrary values instead of choosing items. </summary>
|
||||
|
|
@ -441,19 +446,27 @@ public class EquipmentDrawer
|
|||
|
||||
private void DrawStain(in EquipDrawData data, bool small)
|
||||
{
|
||||
var found = _stainData.TryGetValue(data.CurrentStain, out var stain);
|
||||
using var disabled = ImRaii.Disabled(data.Locked);
|
||||
var width = (_comboLength - ImUtf8.ItemInnerSpacing.X * (data.CurrentStains.Count - 1)) / data.CurrentStains.Count;
|
||||
foreach (var (stainId, index) in data.CurrentStains.WithIndex())
|
||||
{
|
||||
using var id = ImUtf8.PushId(index);
|
||||
var found = _stainData.TryGetValue(stainId, out var stain);
|
||||
var change = small
|
||||
? _stainCombo.Draw($"##stain{data.Slot}", stain.RgbaColor, stain.Name, found, stain.Gloss)
|
||||
: _stainCombo.Draw($"##stain{data.Slot}", stain.RgbaColor, stain.Name, found, stain.Gloss, _comboLength);
|
||||
: _stainCombo.Draw($"##stain{data.Slot}", stain.RgbaColor, stain.Name, found, stain.Gloss, width);
|
||||
if (index < data.CurrentStains.Count - 1)
|
||||
ImUtf8.SameLineInner();
|
||||
|
||||
if (change)
|
||||
if (_stainData.TryGetValue(_stainCombo.CurrentSelection.Key, out stain))
|
||||
data.SetStain(stain.RowIndex);
|
||||
data.SetStains(data.CurrentStains.With(index, stain.RowIndex));
|
||||
else if (_stainCombo.CurrentSelection.Key == Stain.None.RowIndex)
|
||||
data.SetStain(Stain.None.RowIndex);
|
||||
|
||||
if (ResetOrClear(data.Locked, false, data.AllowRevert, true, data.CurrentStain, data.GameStain, Stain.None.RowIndex, out var newStain))
|
||||
data.SetStain(newStain);
|
||||
data.SetStains(data.CurrentStains.With(index, Stain.None.RowIndex));
|
||||
if (ResetOrClear(data.Locked, false, data.AllowRevert, true, stainId, data.GameStains[index], Stain.None.RowIndex,
|
||||
out var newStain))
|
||||
data.SetStains(data.CurrentStains.With(index, newStain));
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawItem(in EquipDrawData data, out string label, bool small, bool clear, bool open)
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@ namespace Glamourer.Gui;
|
|||
public class GlamourerWindowSystem : IDisposable
|
||||
{
|
||||
private readonly WindowSystem _windowSystem = new("Glamourer");
|
||||
private readonly UiBuilder _uiBuilder;
|
||||
private readonly IUiBuilder _uiBuilder;
|
||||
private readonly MainWindow _ui;
|
||||
private readonly PenumbraChangedItemTooltip _penumbraTooltip;
|
||||
|
||||
public GlamourerWindowSystem(UiBuilder uiBuilder, MainWindow ui, GenericPopupWindow popups, PenumbraChangedItemTooltip penumbraTooltip,
|
||||
public GlamourerWindowSystem(IUiBuilder uiBuilder, MainWindow ui, GenericPopupWindow popups, PenumbraChangedItemTooltip penumbraTooltip,
|
||||
Configuration config, UnlocksTab unlocksTab, GlamourerChangelog changelog, DesignQuickBar quick)
|
||||
{
|
||||
_uiBuilder = uiBuilder;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Dalamud.Interface.Windowing;
|
||||
using Dalamud.Plugin;
|
||||
using Glamourer.Designs;
|
||||
|
|
@ -64,7 +64,7 @@ public class MainWindow : Window, IDisposable
|
|||
|
||||
public TabType SelectTab;
|
||||
|
||||
public MainWindow(DalamudPluginInterface pi, Configuration config, SettingsTab settings, ActorTab actors, DesignTab designs,
|
||||
public MainWindow(IDalamudPluginInterface pi, Configuration config, SettingsTab settings, ActorTab actors, DesignTab designs,
|
||||
DebugTab debugTab, AutomationTab automation, UnlocksTab unlocks, TabSelected @event, MessagesTab messages, DesignQuickBar quickBar,
|
||||
NpcTab npcs, MainWindowPosition position, PenumbraService penumbra)
|
||||
: base("GlamourerMainWindow")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
using Dalamud.Game.ClientState.Conditions;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Dalamud.Plugin.Services;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||
using Glamourer.Automation;
|
||||
|
|
@ -208,7 +208,7 @@ public class ActorPanel
|
|||
var data = EquipDrawData.FromState(_stateManager, _state!, slot);
|
||||
_equipmentDrawer.DrawEquip(data);
|
||||
if (usedAllStain)
|
||||
_stateManager.ChangeStain(_state, slot, newAllStain, ApplySettings.Manual);
|
||||
_stateManager.ChangeStains(_state, slot, newAllStain, ApplySettings.Manual);
|
||||
}
|
||||
|
||||
var mainhand = EquipDrawData.FromState(_stateManager, _state, EquipSlot.MainHand);
|
||||
|
|
|
|||
|
|
@ -87,8 +87,8 @@ public class ActiveStatePanel(StateManager _stateManager, ObjectManager _objectM
|
|||
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]);
|
||||
ImGuiUtil.DrawTableColumn(state.BaseData.Stain(slot).Id.ToString());
|
||||
ImGuiUtil.DrawTableColumn(state.ModelData.Stain(slot).Id.ToString());
|
||||
ImGuiUtil.DrawTableColumn(state.BaseData.Stain(slot).ToString());
|
||||
ImGuiUtil.DrawTableColumn(state.ModelData.Stain(slot).ToString());
|
||||
ImGuiUtil.DrawTableColumn(state.Sources[slot, true].ToString());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,10 @@ using Glamourer.Services;
|
|||
using Glamourer.State;
|
||||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using Penumbra.GameData;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Gui.Debug;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.DebugTab;
|
||||
|
||||
|
|
@ -51,7 +53,7 @@ public unsafe class GlamourPlatePanel : IGameDataDrawer
|
|||
using (ImRaii.Group())
|
||||
{
|
||||
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.SameLine();
|
||||
if (ImGui.SmallButton("Request Update"))
|
||||
|
|
@ -67,13 +69,13 @@ public unsafe class GlamourPlatePanel : IGameDataDrawer
|
|||
var (identifier, data) = _objects.PlayerData;
|
||||
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}");
|
||||
if (!tree)
|
||||
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))
|
||||
{
|
||||
var design = CreateDesign(plate);
|
||||
|
|
@ -90,12 +92,12 @@ public unsafe class GlamourPlatePanel : IGameDataDrawer
|
|||
using (ImRaii.Group())
|
||||
{
|
||||
foreach (var (_, index) in EquipSlotExtensions.FullSlots.WithIndex())
|
||||
ImGui.TextUnformatted($"{plate.ItemIds[index]:D6}, {plate.StainIds[index]:D3}");
|
||||
ImGui.TextUnformatted($"{plate.ItemIds[index]:D6}, {StainIds.FromGlamourPlate(plate, index)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Signature("E8 ?? ?? ?? ?? 32 C0 48 8B 5C 24 ?? 48 8B 6C 24 ?? 48 83 C4 ?? 5F")]
|
||||
[Signature(Sigs.RequestGlamourPlates)]
|
||||
private readonly delegate* unmanaged<MirageManager*, void> _requestUpdate = null!;
|
||||
|
||||
public void RequestGlamour()
|
||||
|
|
@ -126,7 +128,7 @@ public unsafe class GlamourPlatePanel : IGameDataDrawer
|
|||
continue;
|
||||
|
||||
design.GetDesignDataRef().SetItem(slot, item);
|
||||
design.GetDesignDataRef().SetStain(slot, plate.StainIds[index]);
|
||||
design.GetDesignDataRef().SetStain(slot, StainIds.FromGlamourPlate(plate, index));
|
||||
design.ApplyEquip |= slot.ToBothFlags();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -43,8 +43,8 @@ public unsafe class InventoryPanel : IGameDataDrawer
|
|||
}
|
||||
else
|
||||
{
|
||||
ImGuiUtil.DrawTableColumn(item->ItemID.ToString());
|
||||
ImGuiUtil.DrawTableColumn(item->GlamourID.ToString());
|
||||
ImGuiUtil.DrawTableColumn(item->ItemId.ToString());
|
||||
ImGuiUtil.DrawTableColumn(item->GlamourId.ToString());
|
||||
ImGui.TableNextColumn();
|
||||
ImGuiUtil.CopyOnClickSelectable($"0x{(ulong)item:X}");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ using OtterGui.Services;
|
|||
|
||||
namespace Glamourer.Gui.Tabs.DebugTab.IpcTester;
|
||||
|
||||
public class DesignIpcTester(DalamudPluginInterface pluginInterface) : IUiService
|
||||
public class DesignIpcTester(IDalamudPluginInterface pluginInterface) : IUiService
|
||||
{
|
||||
private Dictionary<Guid, string> _designs = [];
|
||||
private int _gameObjectIndex;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ using Penumbra.GameData.Gui.Debug;
|
|||
namespace Glamourer.Gui.Tabs.DebugTab.IpcTester;
|
||||
|
||||
public class IpcTesterPanel(
|
||||
DalamudPluginInterface pluginInterface,
|
||||
IDalamudPluginInterface pluginInterface,
|
||||
DesignIpcTester designs,
|
||||
ItemsIpcTester items,
|
||||
StateIpcTester state,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ using Penumbra.GameData.Structs;
|
|||
|
||||
namespace Glamourer.Gui.Tabs.DebugTab.IpcTester;
|
||||
|
||||
public class ItemsIpcTester(DalamudPluginInterface pluginInterface) : IUiService
|
||||
public class ItemsIpcTester(IDalamudPluginInterface pluginInterface) : IUiService
|
||||
{
|
||||
private int _gameObjectIndex;
|
||||
private string _gameObjectName = string.Empty;
|
||||
|
|
@ -40,12 +40,12 @@ public class ItemsIpcTester(DalamudPluginInterface pluginInterface) : IUiService
|
|||
|
||||
IpcTesterHelpers.DrawIntro(SetItem.Label);
|
||||
if (ImGui.Button("Set##Idx"))
|
||||
_lastError = new SetItem(pluginInterface).Invoke(_gameObjectIndex, (ApiEquipSlot)_slot, _customItemId.Id, _stainId.Id, _key,
|
||||
_lastError = new SetItem(pluginInterface).Invoke(_gameObjectIndex, (ApiEquipSlot)_slot, _customItemId.Id, [_stainId.Id], _key,
|
||||
_flags);
|
||||
|
||||
IpcTesterHelpers.DrawIntro(SetItemName.Label);
|
||||
if (ImGui.Button("Set##Name"))
|
||||
_lastError = new SetItemName(pluginInterface).Invoke(_gameObjectName, (ApiEquipSlot)_slot, _customItemId.Id, _stainId.Id, _key,
|
||||
_lastError = new SetItemName(pluginInterface).Invoke(_gameObjectName, (ApiEquipSlot)_slot, _customItemId.Id, [_stainId.Id], _key,
|
||||
_flags);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace Glamourer.Gui.Tabs.DebugTab.IpcTester;
|
|||
|
||||
public class StateIpcTester : IUiService, IDisposable
|
||||
{
|
||||
private readonly DalamudPluginInterface _pluginInterface;
|
||||
private readonly IDalamudPluginInterface _pluginInterface;
|
||||
|
||||
private int _gameObjectIndex;
|
||||
private string _gameObjectName = string.Empty;
|
||||
|
|
@ -41,7 +41,7 @@ public class StateIpcTester : IUiService, IDisposable
|
|||
|
||||
private int _numUnlocked;
|
||||
|
||||
public StateIpcTester(DalamudPluginInterface pluginInterface)
|
||||
public StateIpcTester(IDalamudPluginInterface pluginInterface)
|
||||
{
|
||||
_pluginInterface = pluginInterface;
|
||||
StateChanged = Api.IpcSubscribers.StateChangedWithType.Subscriber(_pluginInterface, OnStateChanged);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ using Glamourer.Interop.Structs;
|
|||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Text;
|
||||
using Penumbra.GameData.DataContainers;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Gui.Debug;
|
||||
using Penumbra.GameData.Interop;
|
||||
|
|
@ -18,7 +20,8 @@ public unsafe class ModelEvaluationPanel(
|
|||
VisorService _visorService,
|
||||
UpdateSlotService _updateSlotService,
|
||||
ChangeCustomizeService _changeCustomizeService,
|
||||
CrestService _crestService) : IGameDataDrawer
|
||||
CrestService _crestService,
|
||||
DictGlasses _glasses) : IGameDataDrawer
|
||||
{
|
||||
public string Label
|
||||
=> "Model Evaluation";
|
||||
|
|
@ -177,7 +180,7 @@ public unsafe class ModelEvaluationPanel(
|
|||
{
|
||||
using var id = ImRaii.PushId("Wetness");
|
||||
ImGuiUtil.DrawTableColumn("Wetness");
|
||||
ImGuiUtil.DrawTableColumn(actor.IsCharacter ? actor.AsCharacter->IsGPoseWet ? "GPose" : "None" : "No Character");
|
||||
ImGuiUtil.DrawTableColumn(actor.IsCharacter ? actor.IsGPoseWet ? "GPose" : "None" : "No Character");
|
||||
var modelString = model.IsCharacterBase
|
||||
? $"{model.AsCharacterBase->SwimmingWetness:F4} Swimming\n"
|
||||
+ $"{model.AsCharacterBase->WeatherWetness:F4} Weather\n"
|
||||
|
|
@ -190,13 +193,13 @@ public unsafe class ModelEvaluationPanel(
|
|||
return;
|
||||
|
||||
if (ImGui.SmallButton("GPose On"))
|
||||
actor.AsCharacter->IsGPoseWet = true;
|
||||
actor.IsGPoseWet = true;
|
||||
ImGui.SameLine();
|
||||
if (ImGui.SmallButton("GPose Off"))
|
||||
actor.AsCharacter->IsGPoseWet = false;
|
||||
actor.IsGPoseWet = false;
|
||||
ImGui.SameLine();
|
||||
if (ImGui.SmallButton("GPose Toggle"))
|
||||
actor.AsCharacter->IsGPoseWet = !actor.AsCharacter->IsGPoseWet;
|
||||
actor.IsGPoseWet = !actor.IsGPoseWet;
|
||||
}
|
||||
|
||||
private void DrawEquip(Actor actor, Model model)
|
||||
|
|
@ -214,14 +217,39 @@ public unsafe class ModelEvaluationPanel(
|
|||
|
||||
if (ImGui.SmallButton("Change Piece"))
|
||||
_updateSlotService.UpdateArmor(model, slot,
|
||||
new CharacterArmor((PrimaryId)(slot == EquipSlot.Hands ? 6064 : slot == EquipSlot.Head ? 6072 : 1), 1, 0));
|
||||
new CharacterArmor((PrimaryId)(slot == EquipSlot.Hands ? 6064 : slot == EquipSlot.Head ? 6072 : 1), 1, StainIds.None));
|
||||
ImGui.SameLine();
|
||||
if (ImGui.SmallButton("Change Stain"))
|
||||
_updateSlotService.UpdateStain(model, slot, 5);
|
||||
_updateSlotService.UpdateStain(model, slot, new StainIds(5, 7));
|
||||
ImGui.SameLine();
|
||||
if (ImGui.SmallButton("Reset"))
|
||||
_updateSlotService.UpdateSlot(model, slot, actor.GetArmor(slot));
|
||||
}
|
||||
|
||||
using (ImRaii.PushId((int)EquipSlot.FaceWear))
|
||||
{
|
||||
ImGuiUtil.DrawTableColumn(EquipSlot.FaceWear.ToName());
|
||||
if (!actor.IsCharacter)
|
||||
{
|
||||
ImGuiUtil.DrawTableColumn("No Character");
|
||||
}
|
||||
else
|
||||
{
|
||||
var glassesId = actor.AsCharacter->DrawData.GlassesIds[(int)EquipSlot.FaceWear.ToBonusIndex()];
|
||||
if (_glasses.TryGetValue(glassesId, out var glasses))
|
||||
ImGuiUtil.DrawTableColumn($"{glasses.Id.Id},{glasses.Variant.Id} ({glassesId})");
|
||||
else
|
||||
ImGuiUtil.DrawTableColumn($"{glassesId}");
|
||||
}
|
||||
|
||||
ImGuiUtil.DrawTableColumn(model.IsHuman ? model.GetArmor(EquipSlot.FaceWear).ToString() : "No Human");
|
||||
ImGui.TableNextColumn();
|
||||
if (ImUtf8.SmallButton("Change Piece"u8))
|
||||
{
|
||||
var data = model.GetArmor(EquipSlot.FaceWear);
|
||||
_updateSlotService.UpdateSlot(model, EquipSlot.FaceWear, data with { Variant = (Variant)((data.Variant.Id + 1) % 12) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void DrawCustomize(Actor actor, Model model)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Services;
|
||||
using ImGuiNET;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Events;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.ImGuiFileDialog;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
||||
using Glamourer.Automation;
|
||||
using Glamourer.Designs;
|
||||
|
|
@ -106,7 +106,7 @@ public class DesignPanel
|
|||
var data = EquipDrawData.FromDesign(_manager, _selector.Selected!, slot);
|
||||
_equipmentDrawer.DrawEquip(data);
|
||||
if (usedAllStain)
|
||||
_manager.ChangeStain(_selector.Selected, slot, newAllStain);
|
||||
_manager.ChangeStains(_selector.Selected, slot, newAllStain);
|
||||
}
|
||||
|
||||
var mainhand = EquipDrawData.FromDesign(_manager, _selector.Selected!, EquipSlot.MainHand);
|
||||
|
|
@ -453,7 +453,7 @@ public class DesignPanel
|
|||
}
|
||||
|
||||
private static unsafe string GetUserPath()
|
||||
=> Framework.Instance()->UserPath;
|
||||
=> Framework.Instance()->UserPathString;
|
||||
|
||||
|
||||
private sealed class LockButton(DesignPanel panel) : Button
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Interop;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Dalamud.Utility;
|
||||
using Glamourer.Designs;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Gui.Customization;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ using Glamourer.Interop.PalettePlus;
|
|||
using ImGuiNET;
|
||||
using OtterGui;
|
||||
using OtterGui.Raii;
|
||||
using OtterGui.Text;
|
||||
using OtterGui.Widgets;
|
||||
|
||||
namespace Glamourer.Gui.Tabs.SettingsTab;
|
||||
|
|
@ -19,7 +18,7 @@ public class SettingsTab(
|
|||
Configuration config,
|
||||
DesignFileSystemSelector selector,
|
||||
ContextMenuService contextMenuService,
|
||||
UiBuilder uiBuilder,
|
||||
IUiBuilder uiBuilder,
|
||||
GlamourerChangelog changelog,
|
||||
IKeyState keys,
|
||||
DesignColorUi designColorUi,
|
||||
|
|
|
|||
|
|
@ -116,20 +116,20 @@ public class UnlockOverview
|
|||
|
||||
var unlocked = _customizeUnlocks.IsUnlocked(customize, out var time);
|
||||
var icon = _customizations.Manager.GetIcon(customize.IconId);
|
||||
|
||||
ImGui.Image(icon.ImGuiHandle, iconSize, Vector2.Zero, Vector2.One,
|
||||
var hasIcon = icon.TryGetWrap(out var wrap, out _);
|
||||
ImGui.Image(wrap?.ImGuiHandle ?? icon.GetWrapOrEmpty().ImGuiHandle, iconSize, Vector2.Zero, Vector2.One,
|
||||
unlocked || _codes.Enabled(CodeService.CodeFlag.Shirts) ? Vector4.One : UnavailableTint);
|
||||
|
||||
if (_favorites.Contains(_selected3, _selected2, customize.Index, customize.Value))
|
||||
ImGui.GetWindowDrawList().AddRect(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), ColorId.FavoriteStarOn.Value(),
|
||||
12 * ImGuiHelpers.GlobalScale, ImDrawFlags.RoundCornersAll, 6 * ImGuiHelpers.GlobalScale);
|
||||
|
||||
if (ImGui.IsItemHovered())
|
||||
if (hasIcon && ImGui.IsItemHovered())
|
||||
{
|
||||
using var tt = ImRaii.Tooltip();
|
||||
var size = new Vector2(icon.Width, icon.Height);
|
||||
var size = new Vector2(wrap!.Width, wrap.Height);
|
||||
if (size.X >= iconSize.X && size.Y >= iconSize.Y)
|
||||
ImGui.Image(icon.ImGuiHandle, size);
|
||||
ImGui.Image(wrap.ImGuiHandle, size);
|
||||
ImGui.TextUnformatted(unlockData.Name);
|
||||
ImGui.TextUnformatted($"{customize.Index.ToDefaultName()} {customize.Value.Value}");
|
||||
ImGui.TextUnformatted(unlocked ? $"Unlocked on {time:g}" : "Not unlocked.");
|
||||
|
|
@ -191,7 +191,8 @@ public class UnlockOverview
|
|||
|
||||
var (icon, size) = (iconHandle.ImGuiHandle, new Vector2(iconHandle.Width, iconHandle.Height));
|
||||
|
||||
ImGui.Image(icon, iconSize, Vector2.Zero, Vector2.One, unlocked || _codes.Enabled(CodeService.CodeFlag.Shirts) ? Vector4.One : UnavailableTint);
|
||||
ImGui.Image(icon, iconSize, Vector2.Zero, Vector2.One,
|
||||
unlocked || _codes.Enabled(CodeService.CodeFlag.Shirts) ? Vector4.One : UnavailableTint);
|
||||
if (_favorites.Contains(item))
|
||||
ImGui.GetWindowDrawList().AddRect(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), ColorId.FavoriteStarOn.Value(),
|
||||
2 * ImGuiHelpers.GlobalScale, ImDrawFlags.RoundCornersAll, 4 * ImGuiHelpers.GlobalScale);
|
||||
|
|
@ -233,8 +234,8 @@ public class UnlockOverview
|
|||
ImGui.TextUnformatted($"For all {_jobs.AllJobGroups[item.JobRestrictions.Id].Name} of at least Level {item.Level}");
|
||||
}
|
||||
|
||||
if (item.Flags.HasFlag(ItemFlags.IsDyable))
|
||||
ImGui.TextUnformatted("Dyable");
|
||||
if (item.Flags.HasFlag(ItemFlags.IsDyable1))
|
||||
ImGui.TextUnformatted(item.Flags.HasFlag(ItemFlags.IsDyable2) ? "Dyable (2 Slots)" : "Dyable");
|
||||
if (item.Flags.HasFlag(ItemFlags.IsTradable))
|
||||
ImGui.TextUnformatted("Tradable");
|
||||
if (item.Flags.HasFlag(ItemFlags.IsCrestWorthy))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using Dalamud.Game.Text.SeStringHandling;
|
||||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Utility;
|
||||
using Glamourer.Events;
|
||||
using Glamourer.Interop;
|
||||
|
|
@ -249,7 +250,7 @@ public class UnlockTable : Table<EquipItem>, IDisposable
|
|||
=> 70 * ImGuiHelpers.GlobalScale;
|
||||
|
||||
public override int ToValue(EquipItem item)
|
||||
=> (int) item.Id.Id;
|
||||
=> (int)item.Id.Id;
|
||||
|
||||
public ItemIdColumn()
|
||||
: base(ComparisonMethod.Equal)
|
||||
|
|
@ -378,13 +379,68 @@ public class UnlockTable : Table<EquipItem>, IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
private sealed class DyableColumn : YesNoColumn<EquipItem>
|
||||
{
|
||||
public DyableColumn()
|
||||
=> Tooltip = "Whether the item is dyable.";
|
||||
|
||||
protected override bool GetValue(EquipItem item)
|
||||
=> item.Flags.HasFlag(ItemFlags.IsDyable);
|
||||
private sealed class DyableColumn : ColumnFlags<DyableColumn.Dyable, EquipItem>
|
||||
{
|
||||
[Flags]
|
||||
public enum Dyable : byte
|
||||
{
|
||||
No = 1,
|
||||
Yes = 2,
|
||||
Two = 4,
|
||||
}
|
||||
|
||||
private Dyable _filterValue;
|
||||
|
||||
public DyableColumn()
|
||||
{
|
||||
AllFlags = Dyable.No | Dyable.Yes | Dyable.Two;
|
||||
Flags &= ~ImGuiTableColumnFlags.NoResize;
|
||||
_filterValue = AllFlags;
|
||||
}
|
||||
|
||||
public override Dyable FilterValue
|
||||
=> _filterValue;
|
||||
|
||||
protected override void SetValue(Dyable value, bool enable)
|
||||
=> _filterValue = enable ? _filterValue | value : _filterValue & ~value;
|
||||
|
||||
public override float Width
|
||||
=> ImGui.GetFrameHeight() * 2;
|
||||
|
||||
public override bool FilterFunc(EquipItem item)
|
||||
=> GetValue(item) switch
|
||||
{
|
||||
0 => _filterValue.HasFlag(Dyable.No),
|
||||
ItemFlags.IsDyable2 => _filterValue.HasFlag(Dyable.Yes),
|
||||
ItemFlags.IsDyable1 => _filterValue.HasFlag(Dyable.Yes),
|
||||
_ => _filterValue.HasFlag(Dyable.Two),
|
||||
};
|
||||
|
||||
public override int Compare(EquipItem lhs, EquipItem rhs)
|
||||
=> GetValue(lhs).CompareTo(GetValue(rhs));
|
||||
|
||||
public override void DrawColumn(EquipItem item, int idx)
|
||||
{
|
||||
using (ImRaii.PushFont(UiBuilder.IconFont))
|
||||
{
|
||||
ImGuiUtil.Center(Icon(item));
|
||||
}
|
||||
|
||||
ImGuiUtil.HoverTooltip("Whether the item is dyable, and how many slots it has.");
|
||||
}
|
||||
|
||||
private static string Icon(EquipItem item)
|
||||
=> GetValue(item) switch
|
||||
{
|
||||
0 => FontAwesomeIcon.Times.ToIconString(),
|
||||
ItemFlags.IsDyable2 => FontAwesomeIcon.Check.ToIconString(),
|
||||
ItemFlags.IsDyable1 => FontAwesomeIcon.Check.ToIconString(),
|
||||
_ => FontAwesomeIcon.DiceTwo.ToIconString(),
|
||||
};
|
||||
|
||||
private static ItemFlags GetValue(EquipItem item)
|
||||
=> item.Flags & (ItemFlags.IsDyable1 | ItemFlags.IsDyable2);
|
||||
}
|
||||
|
||||
private sealed class TradableColumn : YesNoColumn<EquipItem>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ public unsafe class ChangeCustomizeService : EventWrapperRef2<Model, CustomizeAr
|
|||
{
|
||||
private readonly PenumbraReloaded _penumbraReloaded;
|
||||
private readonly IGameInteropProvider _interop;
|
||||
private readonly delegate* unmanaged[Stdcall]<Human*, byte*, bool, bool> _original;
|
||||
private readonly delegate* unmanaged<Human*, byte*, bool, bool> _original;
|
||||
private readonly Post _postEvent = new();
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ public sealed class CharaFile
|
|||
return;
|
||||
|
||||
data.SetItem(slot, item);
|
||||
data.SetStain(slot, dye);
|
||||
data.SetStain(slot, new StainIds(dye));
|
||||
flags |= slot.ToFlag();
|
||||
flags |= slot.ToStainFlag();
|
||||
}
|
||||
|
|
@ -79,7 +79,7 @@ public sealed class CharaFile
|
|||
return;
|
||||
|
||||
data.SetItem(slot, item);
|
||||
data.SetStain(slot, dye);
|
||||
data.SetStain(slot, new StainIds(dye));
|
||||
flags |= slot.ToFlag();
|
||||
flags |= slot.ToStainFlag();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ public sealed class CmaFile
|
|||
var armor = ((CharacterArmor*)ptr)[idx];
|
||||
var item = items.Identify(slot, armor.Set, armor.Variant);
|
||||
data.SetItem(slot, item);
|
||||
data.SetStain(slot, armor.Stain);
|
||||
data.SetStain(slot, armor.Stains);
|
||||
}
|
||||
|
||||
data.Customize.Read(ptr);
|
||||
|
|
@ -74,7 +74,7 @@ public sealed class CmaFile
|
|||
if (mainhand == null)
|
||||
{
|
||||
data.SetItem(EquipSlot.MainHand, items.DefaultSword);
|
||||
data.SetStain(EquipSlot.MainHand, 0);
|
||||
data.SetStain(EquipSlot.MainHand, StainIds.None);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -85,7 +85,7 @@ public sealed class CmaFile
|
|||
var item = items.Identify(EquipSlot.MainHand, set, type, variant);
|
||||
|
||||
data.SetItem(EquipSlot.MainHand, item.Valid ? item : items.DefaultSword);
|
||||
data.SetStain(EquipSlot.MainHand, stain);
|
||||
data.SetStain(EquipSlot.MainHand, new StainIds(stain));
|
||||
}
|
||||
|
||||
private static void ParseOffHand(ItemManager items, JObject jObj, ref DesignData data)
|
||||
|
|
@ -95,7 +95,7 @@ public sealed class CmaFile
|
|||
if (offhand == null)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -106,6 +106,6 @@ public sealed class CmaFile
|
|||
var item = items.Identify(EquipSlot.OffHand, set, type, variant, data.MainhandType);
|
||||
|
||||
data.SetItem(EquipSlot.OffHand, item.Valid ? item : defaultOffhand);
|
||||
data.SetStain(EquipSlot.OffHand, defaultOffhand.PrimaryId.Id == 0 ? 0 : (StainId)stain);
|
||||
data.SetStain(EquipSlot.OffHand, defaultOffhand.PrimaryId.Id == 0 ? StainIds.None : new StainIds(stain));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ public class ContextMenuService : IDisposable
|
|||
private readonly ObjectManager _objects;
|
||||
private readonly IGameGui _gameGui;
|
||||
private EquipItem _lastItem;
|
||||
private StainId _lastStain;
|
||||
private readonly StainId[] _lastStains = new StainId[StainId.NumStains];
|
||||
|
||||
private readonly MenuItem _inventoryItem;
|
||||
|
||||
|
|
@ -47,14 +47,15 @@ public class ContextMenuService : IDisposable
|
|||
};
|
||||
}
|
||||
|
||||
private unsafe void OnMenuOpened(MenuOpenedArgs args)
|
||||
private unsafe void OnMenuOpened(IMenuOpenedArgs args)
|
||||
{
|
||||
if (args.MenuType is ContextMenuType.Inventory)
|
||||
{
|
||||
var arg = (MenuTargetInventory)args.Target;
|
||||
if (arg.TargetItem.HasValue && HandleItem(arg.TargetItem.Value.ItemId))
|
||||
{
|
||||
_lastStain = arg.TargetItem.Value.Stain;
|
||||
for (var i = 0; i < arg.TargetItem.Value.Stains.Length; ++i)
|
||||
_lastStains[i] = (StainId)arg.TargetItem.Value.Stains[i];
|
||||
args.AddMenuItem(_inventoryItem);
|
||||
}
|
||||
}
|
||||
|
|
@ -77,7 +78,8 @@ public class ContextMenuService : IDisposable
|
|||
|
||||
if (HandleItem(*(ItemId*)(agent + ChatLogContextItemId)))
|
||||
{
|
||||
_lastStain = 0;
|
||||
for (var i = 0; i < _lastStains.Length; ++i)
|
||||
_lastStains[i] = 0;
|
||||
args.AddMenuItem(_inventoryItem);
|
||||
}
|
||||
|
||||
|
|
@ -96,7 +98,7 @@ public class ContextMenuService : IDisposable
|
|||
public void Dispose()
|
||||
=> Disable();
|
||||
|
||||
private void OnClick(MenuItemClickedArgs _)
|
||||
private void OnClick(IMenuItemClickedArgs _)
|
||||
{
|
||||
var (id, playerData) = _objects.PlayerData;
|
||||
if (!playerData.Valid)
|
||||
|
|
@ -106,15 +108,15 @@ public class ContextMenuService : IDisposable
|
|||
return;
|
||||
|
||||
var slot = _lastItem.Type.ToSlot();
|
||||
_state.ChangeEquip(state, slot, _lastItem, _lastStain, ApplySettings.Manual);
|
||||
_state.ChangeEquip(state, slot, _lastItem, _lastStains[0], ApplySettings.Manual);
|
||||
if (!_lastItem.Type.ValidOffhand().IsOffhandType())
|
||||
return;
|
||||
|
||||
if (_lastItem.PrimaryId.Id is > 1600 and < 1651
|
||||
&& _items.ItemData.TryGetValue(_lastItem.ItemId, EquipSlot.Hands, out var gauntlets))
|
||||
_state.ChangeEquip(state, EquipSlot.Hands, gauntlets, _lastStain, ApplySettings.Manual);
|
||||
_state.ChangeEquip(state, EquipSlot.Hands, gauntlets, _lastStains[0], ApplySettings.Manual);
|
||||
if (_items.ItemData.TryGetValue(_lastItem.ItemId, EquipSlot.OffHand, out var offhand))
|
||||
_state.ChangeEquip(state, EquipSlot.OffHand, offhand, _lastStain, ApplySettings.Manual);
|
||||
_state.ChangeEquip(state, EquipSlot.OffHand, offhand, _lastStains[0], ApplySettings.Manual);
|
||||
}
|
||||
|
||||
private bool HandleItem(ItemId id)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using Dalamud.Utility.Signatures;
|
|||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using OtterGui.Classes;
|
||||
using Penumbra.GameData;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Interop;
|
||||
|
||||
|
|
@ -30,9 +31,9 @@ public sealed unsafe class CrestService : EventWrapperRef3<Actor, CrestFlag, boo
|
|||
{
|
||||
interop.InitializeFromAttributes(this);
|
||||
_humanSetFreeCompanyCrestVisibleOnSlot =
|
||||
interop.HookFromAddress<SetCrestDelegateIntern>(_humanVTable[96], HumanSetFreeCompanyCrestVisibleOnSlotDetour);
|
||||
interop.HookFromAddress<SetCrestDelegateIntern>(_humanVTable[109], HumanSetFreeCompanyCrestVisibleOnSlotDetour);
|
||||
_weaponSetFreeCompanyCrestVisibleOnSlot =
|
||||
interop.HookFromAddress<SetCrestDelegateIntern>(_weaponVTable[96], WeaponSetFreeCompanyCrestVisibleOnSlotDetour);
|
||||
interop.HookFromAddress<SetCrestDelegateIntern>(_weaponVTable[109], WeaponSetFreeCompanyCrestVisibleOnSlotDetour);
|
||||
_humanSetFreeCompanyCrestVisibleOnSlot.Enable();
|
||||
_weaponSetFreeCompanyCrestVisibleOnSlot.Enable();
|
||||
_crestChangeHook.Enable();
|
||||
|
|
@ -63,7 +64,7 @@ public sealed unsafe class CrestService : EventWrapperRef3<Actor, CrestFlag, boo
|
|||
|
||||
private delegate void CrestChangeDelegate(Character* character, byte crestFlags);
|
||||
|
||||
[Signature("E8 ?? ?? ?? ?? 48 8B 55 ?? 49 8B CE E8", DetourName = nameof(CrestChangeDetour))]
|
||||
[Signature(Sigs.CrestChange, DetourName = nameof(CrestChangeDetour))]
|
||||
private readonly Hook<CrestChangeDelegate> _crestChangeHook = null!;
|
||||
|
||||
private void CrestChangeDetour(Character* character, byte crestFlags)
|
||||
|
|
@ -96,8 +97,7 @@ public sealed unsafe class CrestService : EventWrapperRef3<Actor, CrestFlag, boo
|
|||
if (!model.IsHuman)
|
||||
return false;
|
||||
|
||||
var getter = (delegate* unmanaged<Human*, byte, byte>)((nint*)model.AsCharacterBase->VTable)[95];
|
||||
return getter(model.AsHuman, index) != 0;
|
||||
return model.AsHuman->IsFreeCompanyCrestVisibleOnSlot(index) != 0;
|
||||
}
|
||||
case CrestType.Offhand:
|
||||
{
|
||||
|
|
@ -105,8 +105,7 @@ public sealed unsafe class CrestService : EventWrapperRef3<Actor, CrestFlag, boo
|
|||
if (!model.IsWeapon)
|
||||
return false;
|
||||
|
||||
var getter = (delegate* unmanaged<Weapon*, byte, byte>)((nint*)model.AsCharacterBase->VTable)[95];
|
||||
return getter(model.AsWeapon, index) != 0;
|
||||
return model.AsWeapon->IsFreeCompanyCrestVisibleOnSlot(index) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -117,10 +116,10 @@ public sealed unsafe class CrestService : EventWrapperRef3<Actor, CrestFlag, boo
|
|||
|
||||
private delegate void SetCrestDelegateIntern(DrawObject* drawObject, byte slot, byte visible);
|
||||
|
||||
[Signature(global::Penumbra.GameData.Sigs.HumanVTable, ScanType = ScanType.StaticAddress)]
|
||||
[Signature(Sigs.HumanVTable, ScanType = ScanType.StaticAddress)]
|
||||
private readonly nint* _humanVTable = null!;
|
||||
|
||||
[Signature(global::Penumbra.GameData.Sigs.WeaponVTable, ScanType = ScanType.StaticAddress)]
|
||||
[Signature(Sigs.WeaponVTable, ScanType = ScanType.StaticAddress)]
|
||||
private readonly nint* _weaponVTable = null!;
|
||||
|
||||
private readonly Hook<SetCrestDelegateIntern> _humanSetFreeCompanyCrestVisibleOnSlot;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
using Dalamud.Interface.DragDrop;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Interop.CharaFile;
|
||||
using Glamourer.Services;
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
|
|||
{
|
||||
private readonly MovedEquipment _movedItemsEvent;
|
||||
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)
|
||||
{
|
||||
|
|
@ -60,56 +60,56 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
|
|||
|
||||
if (glamourPlateId != 0)
|
||||
{
|
||||
void Add(EquipSlot slot, uint glamourId, StainId glamourStain, ref RaptureGearsetModule.GearsetItem item)
|
||||
void Add(EquipSlot slot, uint glamourId, StainIds glamourStain, ref RaptureGearsetModule.GearsetItem item)
|
||||
{
|
||||
if (item.ItemID == 0)
|
||||
_itemList.Add((slot, 0, 0));
|
||||
if (item.ItemId == 0)
|
||||
_itemList.Add((slot, 0, StainIds.None));
|
||||
else if (glamourId != 0)
|
||||
_itemList.Add((slot, glamourId, glamourStain));
|
||||
else if (item.GlamourId != 0)
|
||||
_itemList.Add((slot, item.GlamourId, item.Stain));
|
||||
_itemList.Add((slot, item.GlamourId, StainIds.FromGearsetItem(item)));
|
||||
else
|
||||
_itemList.Add((slot, FixId(item.ItemID), item.Stain));
|
||||
_itemList.Add((slot, FixId(item.ItemId), StainIds.FromGearsetItem(item)));
|
||||
}
|
||||
|
||||
var plate = MirageManager.Instance()->GlamourPlatesSpan[glamourPlateId - 1];
|
||||
Add(EquipSlot.MainHand, plate.ItemIds[0], plate.StainIds[0], ref entry->ItemsSpan[0]);
|
||||
Add(EquipSlot.OffHand, plate.ItemIds[1], plate.StainIds[1], ref entry->ItemsSpan[1]);
|
||||
Add(EquipSlot.Head, plate.ItemIds[2], plate.StainIds[2], ref entry->ItemsSpan[2]);
|
||||
Add(EquipSlot.Body, plate.ItemIds[3], plate.StainIds[3], ref entry->ItemsSpan[3]);
|
||||
Add(EquipSlot.Hands, plate.ItemIds[4], plate.StainIds[4], ref entry->ItemsSpan[5]);
|
||||
Add(EquipSlot.Legs, plate.ItemIds[5], plate.StainIds[5], ref entry->ItemsSpan[6]);
|
||||
Add(EquipSlot.Feet, plate.ItemIds[6], plate.StainIds[6], ref entry->ItemsSpan[7]);
|
||||
Add(EquipSlot.Ears, plate.ItemIds[7], plate.StainIds[7], ref entry->ItemsSpan[8]);
|
||||
Add(EquipSlot.Neck, plate.ItemIds[8], plate.StainIds[8], ref entry->ItemsSpan[9]);
|
||||
Add(EquipSlot.Wrists, plate.ItemIds[9], plate.StainIds[9], ref entry->ItemsSpan[10]);
|
||||
Add(EquipSlot.RFinger, plate.ItemIds[10], plate.StainIds[10], ref entry->ItemsSpan[11]);
|
||||
Add(EquipSlot.LFinger, plate.ItemIds[11], plate.StainIds[11], ref entry->ItemsSpan[12]);
|
||||
var plate = MirageManager.Instance()->GlamourPlates[glamourPlateId - 1];
|
||||
Add(EquipSlot.MainHand, plate.ItemIds[0], StainIds.FromGlamourPlate(plate, 0), ref entry->Items[0]);
|
||||
Add(EquipSlot.OffHand, plate.ItemIds[1], StainIds.FromGlamourPlate(plate, 1), ref entry->Items[1]);
|
||||
Add(EquipSlot.Head, plate.ItemIds[2], StainIds.FromGlamourPlate(plate, 2), ref entry->Items[2]);
|
||||
Add(EquipSlot.Body, plate.ItemIds[3], StainIds.FromGlamourPlate(plate, 3), ref entry->Items[3]);
|
||||
Add(EquipSlot.Hands, plate.ItemIds[4], StainIds.FromGlamourPlate(plate, 4), ref entry->Items[5]);
|
||||
Add(EquipSlot.Legs, plate.ItemIds[5], StainIds.FromGlamourPlate(plate, 5), ref entry->Items[6]);
|
||||
Add(EquipSlot.Feet, plate.ItemIds[6], StainIds.FromGlamourPlate(plate, 6), ref entry->Items[7]);
|
||||
Add(EquipSlot.Ears, plate.ItemIds[7], StainIds.FromGlamourPlate(plate, 7), ref entry->Items[8]);
|
||||
Add(EquipSlot.Neck, plate.ItemIds[8], StainIds.FromGlamourPlate(plate, 8), ref entry->Items[9]);
|
||||
Add(EquipSlot.Wrists, plate.ItemIds[9], StainIds.FromGlamourPlate(plate, 9), ref entry->Items[10]);
|
||||
Add(EquipSlot.RFinger, plate.ItemIds[10], StainIds.FromGlamourPlate(plate, 10), ref entry->Items[11]);
|
||||
Add(EquipSlot.LFinger, plate.ItemIds[11], StainIds.FromGlamourPlate(plate, 11), ref entry->Items[12]);
|
||||
}
|
||||
else
|
||||
{
|
||||
void Add(EquipSlot slot, ref RaptureGearsetModule.GearsetItem item)
|
||||
{
|
||||
if (item.ItemID == 0)
|
||||
_itemList.Add((slot, 0, 0));
|
||||
if (item.ItemId == 0)
|
||||
_itemList.Add((slot, 0, StainIds.None));
|
||||
else if (item.GlamourId != 0)
|
||||
_itemList.Add((slot, item.GlamourId, item.Stain));
|
||||
_itemList.Add((slot, item.GlamourId, StainIds.FromGearsetItem(item)));
|
||||
else
|
||||
_itemList.Add((slot, FixId(item.ItemID), item.Stain));
|
||||
_itemList.Add((slot, FixId(item.ItemId), StainIds.FromGearsetItem(item)));
|
||||
}
|
||||
|
||||
Add(EquipSlot.MainHand, ref entry->ItemsSpan[0]);
|
||||
Add(EquipSlot.OffHand, ref entry->ItemsSpan[1]);
|
||||
Add(EquipSlot.Head, ref entry->ItemsSpan[2]);
|
||||
Add(EquipSlot.Body, ref entry->ItemsSpan[3]);
|
||||
Add(EquipSlot.Hands, ref entry->ItemsSpan[5]);
|
||||
Add(EquipSlot.Legs, ref entry->ItemsSpan[6]);
|
||||
Add(EquipSlot.Feet, ref entry->ItemsSpan[7]);
|
||||
Add(EquipSlot.Ears, ref entry->ItemsSpan[8]);
|
||||
Add(EquipSlot.Neck, ref entry->ItemsSpan[9]);
|
||||
Add(EquipSlot.Wrists, ref entry->ItemsSpan[10]);
|
||||
Add(EquipSlot.RFinger, ref entry->ItemsSpan[11]);
|
||||
Add(EquipSlot.LFinger, ref entry->ItemsSpan[12]);
|
||||
Add(EquipSlot.MainHand, ref entry->Items[0]);
|
||||
Add(EquipSlot.OffHand, ref entry->Items[1]);
|
||||
Add(EquipSlot.Head, ref entry->Items[2]);
|
||||
Add(EquipSlot.Body, ref entry->Items[3]);
|
||||
Add(EquipSlot.Hands, ref entry->Items[5]);
|
||||
Add(EquipSlot.Legs, ref entry->Items[6]);
|
||||
Add(EquipSlot.Feet, ref entry->Items[7]);
|
||||
Add(EquipSlot.Ears, ref entry->Items[8]);
|
||||
Add(EquipSlot.Neck, ref entry->Items[9]);
|
||||
Add(EquipSlot.Wrists, ref entry->Items[10]);
|
||||
Add(EquipSlot.RFinger, ref entry->Items[11]);
|
||||
Add(EquipSlot.LFinger, ref entry->Items[12]);
|
||||
}
|
||||
|
||||
_movedItemsEvent.Invoke(_itemList.ToArray());
|
||||
|
|
@ -155,7 +155,7 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
|
|||
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;
|
||||
if (sourceContainer is not InventoryType.EquippedItems)
|
||||
|
|
@ -165,12 +165,12 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
|
|||
if (slot is EquipSlot.Unknown)
|
||||
return false;
|
||||
|
||||
tuple = (slot, 0u, 0);
|
||||
tuple = (slot, 0u, StainIds.None);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool InvokeTarget(InventoryManager* manager, InventoryType targetContainer, uint targetSlot,
|
||||
out (EquipSlot, uint, StainId) tuple)
|
||||
out (EquipSlot, uint, StainIds) tuple)
|
||||
{
|
||||
tuple = default;
|
||||
if (targetContainer is not InventoryType.EquippedItems)
|
||||
|
|
@ -189,7 +189,7 @@ public sealed unsafe class InventoryService : IDisposable, IRequiredService
|
|||
if (item == null)
|
||||
return false;
|
||||
|
||||
tuple = (slot, item->GlamourID != 0 ? item->GlamourID : item->ItemID, item->Stain);
|
||||
tuple = (slot, item->GlamourId != 0 ? item->GlamourId : item->ItemId, new StainIds(item->Stains));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -169,13 +169,13 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
|
|||
if (!actor.AsObject->IsCharacter())
|
||||
return false;
|
||||
|
||||
if (actor.AsCharacter->DrawData.WeaponDataSpan[0].DrawObject == characterBase)
|
||||
if (actor.AsCharacter->DrawData.WeaponData[0].DrawObject == characterBase)
|
||||
{
|
||||
type = MaterialValueIndex.DrawObjectType.Mainhand;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (actor.AsCharacter->DrawData.WeaponDataSpan[1].DrawObject == characterBase)
|
||||
if (actor.AsCharacter->DrawData.WeaponData[1].DrawObject == characterBase)
|
||||
{
|
||||
type = MaterialValueIndex.DrawObjectType.Offhand;
|
||||
return true;
|
||||
|
|
@ -199,10 +199,11 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
|
|||
/// </summary>
|
||||
private static CharacterWeapon GetTempSlot(Weapon* weapon)
|
||||
{
|
||||
// TODO: Fix offset
|
||||
var changedData = *(void**)((byte*)weapon + 0x918);
|
||||
if (changedData == null)
|
||||
return new CharacterWeapon(weapon->ModelSetId, weapon->SecondaryId, (Variant)weapon->Variant, (StainId)weapon->ModelUnknown);
|
||||
return new CharacterWeapon(weapon->ModelSetId, weapon->SecondaryId, (Variant)weapon->Variant, StainIds.FromWeapon(*weapon));
|
||||
|
||||
return new CharacterWeapon(weapon->ModelSetId, *(SecondaryId*)changedData, ((Variant*)changedData)[2], ((StainId*)changedData)[3]);
|
||||
return new CharacterWeapon(weapon->ModelSetId, *(SecondaryId*)changedData, ((Variant*)changedData)[2], new StainIds(((StainId*)changedData)[3], ((StainId*)changedData)[4]));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -62,8 +62,8 @@ public readonly record struct MaterialValueIndex(
|
|||
model = DrawObject switch
|
||||
{
|
||||
DrawObjectType.Human => actor.Model,
|
||||
DrawObjectType.Mainhand => actor.IsCharacter ? actor.AsCharacter->DrawData.WeaponDataSpan[0].DrawObject : Model.Null,
|
||||
DrawObjectType.Offhand => actor.IsCharacter ? actor.AsCharacter->DrawData.WeaponDataSpan[1].DrawObject : Model.Null,
|
||||
DrawObjectType.Mainhand => actor.IsCharacter ? actor.AsCharacter->DrawData.WeaponData[0].DrawObject : Model.Null,
|
||||
DrawObjectType.Offhand => actor.IsCharacter ? actor.AsCharacter->DrawData.WeaponData[1].DrawObject : Model.Null,
|
||||
_ => Model.Null,
|
||||
};
|
||||
return model.IsCharacterBase;
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ public struct MaterialValueState(
|
|||
=> DrawData.Skeleton == rhsData.Skeleton
|
||||
&& DrawData.Weapon == rhsData.Weapon
|
||||
&& DrawData.Variant == rhsData.Variant
|
||||
&& DrawData.Stain == rhsData.Stain
|
||||
&& DrawData.Stains == rhsData.Stains
|
||||
&& Game.NearEqual(rhsRow);
|
||||
|
||||
public readonly MaterialValueDesign Convert()
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
|||
using FFXIVClientStructs.FFXIV.Client.System.Resource.Handle;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.GameData;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Files.MaterialStructs;
|
||||
using Penumbra.GameData.Interop;
|
||||
|
|
@ -22,7 +23,7 @@ public sealed unsafe class PrepareColorSet
|
|||
|
||||
public PrepareColorSet(HookManager hooks)
|
||||
: base("Prepare Color Set ")
|
||||
=> _task = hooks.CreateHook<Delegate>(Name, "40 55 56 41 56 48 83 EC ?? 80 BA", Detour, true);
|
||||
=> _task = hooks.CreateHook<Delegate>(Name, Sigs.PrepareColorSet, Detour, true);
|
||||
|
||||
private readonly Task<Hook<Delegate>> _task;
|
||||
|
||||
|
|
@ -54,7 +55,7 @@ public sealed unsafe class PrepareColorSet
|
|||
return _task.Result.Original(characterBase, material, stainId);
|
||||
}
|
||||
|
||||
public static bool TryGetColorTable(CharacterBase* characterBase, MaterialResourceHandle* material, StainId stainId,
|
||||
public static bool TryGetColorTable(CharacterBase* characterBase, MaterialResourceHandle* material, StainIds stainIds,
|
||||
out LegacyColorTable table)
|
||||
{
|
||||
if (material->ColorTable == null)
|
||||
|
|
@ -64,8 +65,9 @@ public sealed unsafe class PrepareColorSet
|
|||
}
|
||||
|
||||
var newTable = *(LegacyColorTable*)material->ColorTable;
|
||||
if (stainId.Id != 0)
|
||||
characterBase->ReadStainingTemplate(material, stainId.Id, (Half*)(&newTable));
|
||||
// TODO
|
||||
//if (stainIds.Stain1.Id != 0 || stainIds.Stain2.Id != 0)
|
||||
// characterBase->ReadStainingTemplate(material, stainId.Id, (Half*)(&newTable));
|
||||
table = newTable;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -84,21 +86,21 @@ public sealed unsafe class PrepareColorSet
|
|||
return false;
|
||||
}
|
||||
|
||||
return TryGetColorTable(model.AsCharacterBase, handle, GetStain(), out table);
|
||||
return TryGetColorTable(model.AsCharacterBase, handle, GetStains(), out table);
|
||||
|
||||
StainId GetStain()
|
||||
StainIds GetStains()
|
||||
{
|
||||
switch (index.DrawObject)
|
||||
{
|
||||
case MaterialValueIndex.DrawObjectType.Human:
|
||||
return index.SlotIndex < 10 ? actor.Model.GetArmor(((uint)index.SlotIndex).ToEquipSlot()).Stain : 0;
|
||||
return index.SlotIndex < 10 ? actor.Model.GetArmor(((uint)index.SlotIndex).ToEquipSlot()).Stains : StainIds.None;
|
||||
case MaterialValueIndex.DrawObjectType.Mainhand:
|
||||
var mainhand = (Model)actor.AsCharacter->DrawData.WeaponDataSpan[1].DrawObject;
|
||||
return mainhand.IsWeapon ? (StainId)mainhand.AsWeapon->ModelUnknown : 0;
|
||||
var mainhand = (Model)actor.AsCharacter->DrawData.WeaponData[1].DrawObject;
|
||||
return mainhand.IsWeapon ? StainIds.FromWeapon(*mainhand.AsWeapon) : StainIds.None;
|
||||
case MaterialValueIndex.DrawObjectType.Offhand:
|
||||
var offhand = (Model)actor.AsCharacter->DrawData.WeaponDataSpan[1].DrawObject;
|
||||
return offhand.IsWeapon ? (StainId)offhand.AsWeapon->ModelUnknown : 0;
|
||||
default: return 0;
|
||||
var offhand = (Model)actor.AsCharacter->DrawData.WeaponData[1].DrawObject;
|
||||
return offhand.IsWeapon ? StainIds.FromWeapon(*offhand.AsWeapon) : StainIds.None;
|
||||
default: return StainIds.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,11 +49,11 @@ public unsafe class MetaService : IDisposable
|
|||
return;
|
||||
|
||||
// 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.Equipment(DrawDataContainer.EquipmentSlot.Head).Id;
|
||||
if (old == 0 && actor.AsCharacter->CharacterData.ModelCharaId == 0)
|
||||
actor.AsCharacter->DrawData.Head.Id = 1;
|
||||
actor.AsCharacter->DrawData.Equipment(DrawDataContainer.EquipmentSlot.Head).Id = 1;
|
||||
_hideHatGearHook.Original(&actor.AsCharacter->DrawData, 0, (byte)(value ? 0 : 1));
|
||||
actor.AsCharacter->DrawData.Head.Id = old;
|
||||
actor.AsCharacter->DrawData.Equipment(DrawDataContainer.EquipmentSlot.Head).Id = old;
|
||||
}
|
||||
|
||||
public void SetWeaponState(Actor actor, bool value)
|
||||
|
|
@ -72,7 +72,7 @@ public unsafe class MetaService : IDisposable
|
|||
return;
|
||||
}
|
||||
|
||||
Actor actor = drawData->Parent;
|
||||
Actor actor = drawData->OwnerObject;
|
||||
var v = value == 0;
|
||||
_headGearEvent.Invoke(actor, ref v);
|
||||
value = (byte)(v ? 0 : 1);
|
||||
|
|
@ -82,7 +82,7 @@ public unsafe class MetaService : IDisposable
|
|||
|
||||
private void HideWeaponsDetour(DrawDataContainer* drawData, bool value)
|
||||
{
|
||||
Actor actor = drawData->Parent;
|
||||
Actor actor = drawData->OwnerObject;
|
||||
value = !value;
|
||||
_weaponEvent.Invoke(actor, ref value);
|
||||
value = !value;
|
||||
|
|
@ -92,7 +92,7 @@ public unsafe class MetaService : IDisposable
|
|||
|
||||
private void ToggleVisorDetour(DrawDataContainer* drawData, bool value)
|
||||
{
|
||||
Actor actor = drawData->Parent;
|
||||
Actor actor = drawData->OwnerObject;
|
||||
_visorEvent.Invoke(actor.Model, true, ref value);
|
||||
Glamourer.Log.Verbose($"[MetaService] Toggle Visor triggered with 0x{(nint)drawData:X} {value} for {actor.Utf8Name}.");
|
||||
_toggleVisorHook.Original(drawData, value);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ public class ObjectManager(
|
|||
IFramework framework,
|
||||
IClientState clientState,
|
||||
IObjectTable objects,
|
||||
DalamudPluginInterface pi,
|
||||
IDalamudPluginInterface pi,
|
||||
Logger log,
|
||||
ActorManager actors,
|
||||
ITargetManager targets)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ using OtterGui.Services;
|
|||
|
||||
namespace Glamourer.Interop.PalettePlus;
|
||||
|
||||
public class PaletteImport(DalamudPluginInterface pluginInterface, DesignManager designManager, DesignFileSystem designFileSystem) : IService
|
||||
public class PaletteImport(IDalamudPluginInterface pluginInterface, DesignManager designManager, DesignFileSystem designFileSystem) : IService
|
||||
{
|
||||
private string ConfigFile
|
||||
=> Path.Combine(Path.GetDirectoryName(pluginInterface.GetPluginConfigDirectory())!, "PalettePlus.json");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Dalamud.Plugin;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Services;
|
||||
using Notification = OtterGui.Classes.Notification;
|
||||
|
||||
namespace Glamourer.Interop.PalettePlus;
|
||||
|
||||
|
|
@ -9,9 +9,9 @@ public sealed class PalettePlusChecker : IRequiredService, IDisposable
|
|||
{
|
||||
private readonly Timer _paletteTimer;
|
||||
private readonly Configuration _config;
|
||||
private readonly DalamudPluginInterface _pluginInterface;
|
||||
private readonly IDalamudPluginInterface _pluginInterface;
|
||||
|
||||
public PalettePlusChecker(Configuration config, DalamudPluginInterface pluginInterface)
|
||||
public PalettePlusChecker(Configuration config, IDalamudPluginInterface pluginInterface)
|
||||
{
|
||||
_config = config;
|
||||
_pluginInterface = pluginInterface;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Dalamud.Plugin;
|
||||
using Glamourer.Events;
|
||||
using OtterGui.Classes;
|
||||
|
|
@ -36,7 +36,7 @@ public unsafe class PenumbraService : IDisposable
|
|||
public const int RequiredPenumbraBreakingVersion = 5;
|
||||
public const int RequiredPenumbraFeatureVersion = 0;
|
||||
|
||||
private readonly DalamudPluginInterface _pluginInterface;
|
||||
private readonly IDalamudPluginInterface _pluginInterface;
|
||||
private readonly EventSubscriber<ChangedItemType, uint> _tooltipSubscriber;
|
||||
private readonly EventSubscriber<MouseButton, ChangedItemType, uint> _clickSubscriber;
|
||||
private readonly EventSubscriber<nint, Guid, nint, nint, nint> _creatingCharacterBase;
|
||||
|
|
@ -68,7 +68,7 @@ public unsafe class PenumbraService : IDisposable
|
|||
public int CurrentMinor { get; private set; }
|
||||
public DateTime AttachTime { get; private set; }
|
||||
|
||||
public PenumbraService(DalamudPluginInterface pi, PenumbraReloaded penumbraReloaded)
|
||||
public PenumbraService(IDalamudPluginInterface pi, PenumbraReloaded penumbraReloaded)
|
||||
{
|
||||
_pluginInterface = pi;
|
||||
_penumbraReloaded = penumbraReloaded;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using Dalamud.Hooking;
|
|||
using Dalamud.Plugin.Services;
|
||||
using Dalamud.Utility.Signatures;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Character;
|
||||
using Penumbra.GameData;
|
||||
using Penumbra.GameData.Interop;
|
||||
using Character = FFXIVClientStructs.FFXIV.Client.Game.Character.Character;
|
||||
|
||||
|
|
@ -14,7 +15,7 @@ public unsafe class ScalingService : IDisposable
|
|||
{
|
||||
interop.InitializeFromAttributes(this);
|
||||
_setupMountHook =
|
||||
interop.HookFromAddress<SetupMount>((nint)Character.MountContainer.MemberFunctionPointers.SetupMount, SetupMountDetour);
|
||||
interop.HookFromAddress<SetupMount>((nint)MountContainer.MemberFunctionPointers.SetupMount, SetupMountDetour);
|
||||
_setupOrnamentHook = interop.HookFromAddress<SetupOrnament>((nint)Ornament.MemberFunctionPointers.SetupOrnament, SetupOrnamentDetour);
|
||||
_calculateHeightHook =
|
||||
interop.HookFromAddress<CalculateHeight>((nint)Character.MemberFunctionPointers.CalculateHeight, CalculateHeightDetour);
|
||||
|
|
@ -33,7 +34,7 @@ public unsafe class ScalingService : IDisposable
|
|||
_calculateHeightHook.Dispose();
|
||||
}
|
||||
|
||||
private delegate void SetupMount(Character.MountContainer* container, short mountId, uint unk1, uint unk2, uint unk3, byte unk4);
|
||||
private delegate void SetupMount(MountContainer* container, short mountId, uint unk1, uint unk2, uint unk3, byte unk4);
|
||||
private delegate void SetupOrnament(Ornament* ornament, uint* unk1, float* unk2);
|
||||
private delegate void PlaceMinion(Companion* character);
|
||||
private delegate float CalculateHeight(Character* character);
|
||||
|
|
@ -45,15 +46,15 @@ public unsafe class ScalingService : IDisposable
|
|||
private readonly Hook<CalculateHeight> _calculateHeightHook;
|
||||
|
||||
// TODO: Use client structs sig.
|
||||
[Signature("48 89 5C 24 ?? 55 57 41 57 48 8D 6C 24", DetourName = nameof(PlaceMinionDetour))]
|
||||
[Signature(Sigs.PlaceMinion, DetourName = nameof(PlaceMinionDetour))]
|
||||
private readonly Hook<PlaceMinion> _placeMinionHook = null!;
|
||||
|
||||
private void SetupMountDetour(Character.MountContainer* container, short mountId, uint unk1, uint unk2, uint unk3, byte unk4)
|
||||
private void SetupMountDetour(MountContainer* container, short mountId, uint unk1, uint unk2, uint unk3, byte unk4)
|
||||
{
|
||||
var (race, clan, gender) = GetScaleRelevantCustomize(&container->OwnerObject->Character);
|
||||
SetScaleCustomize(&container->OwnerObject->Character, container->OwnerObject->Character.GameObject.DrawObject);
|
||||
var (race, clan, gender) = GetScaleRelevantCustomize(container->OwnerObject);
|
||||
SetScaleCustomize(container->OwnerObject, container->OwnerObject->DrawObject);
|
||||
_setupMountHook.Original(container, mountId, unk1, unk2, unk3, unk4);
|
||||
SetScaleCustomize(&container->OwnerObject->Character, race, clan, gender);
|
||||
SetScaleCustomize(container->OwnerObject, race, clan, gender);
|
||||
}
|
||||
|
||||
private void SetupOrnamentDetour(Ornament* ornament, uint* unk1, float* unk2)
|
||||
|
|
|
|||
|
|
@ -18,33 +18,44 @@ public unsafe class UpdateSlotService : IDisposable
|
|||
SlotUpdatingEvent = slotUpdating;
|
||||
interop.InitializeFromAttributes(this);
|
||||
_flagSlotForUpdateHook.Enable();
|
||||
_flagBonusSlotForUpdateHook.Enable();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
=> _flagSlotForUpdateHook.Dispose();
|
||||
{
|
||||
_flagSlotForUpdateHook.Dispose();
|
||||
_flagBonusSlotForUpdateHook.Dispose();
|
||||
}
|
||||
|
||||
public void UpdateSlot(Model drawObject, EquipSlot slot, CharacterArmor data)
|
||||
{
|
||||
if (!drawObject.IsCharacterBase)
|
||||
return;
|
||||
|
||||
var bonusSlot = slot.ToBonusIndex();
|
||||
if (bonusSlot == uint.MaxValue)
|
||||
FlagSlotForUpdateInterop(drawObject, slot, data);
|
||||
else
|
||||
_flagBonusSlotForUpdateHook.Original(drawObject.Address, bonusSlot, &data);
|
||||
}
|
||||
|
||||
public void UpdateArmor(Model drawObject, EquipSlot slot, CharacterArmor armor, StainId stain)
|
||||
=> UpdateSlot(drawObject, slot, armor.With(stain));
|
||||
public void UpdateArmor(Model drawObject, EquipSlot slot, CharacterArmor armor, StainIds stains)
|
||||
=> UpdateSlot(drawObject, slot, armor.With(stains));
|
||||
|
||||
public void UpdateArmor(Model drawObject, EquipSlot slot, CharacterArmor armor)
|
||||
=> UpdateArmor(drawObject, slot, armor, drawObject.GetArmor(slot).Stain);
|
||||
=> UpdateArmor(drawObject, slot, armor, drawObject.GetArmor(slot).Stains);
|
||||
|
||||
public void UpdateStain(Model drawObject, EquipSlot slot, StainId stain)
|
||||
=> UpdateArmor(drawObject, slot, drawObject.GetArmor(slot), stain);
|
||||
public void UpdateStain(Model drawObject, EquipSlot slot, StainIds stains)
|
||||
=> UpdateArmor(drawObject, slot, drawObject.GetArmor(slot), stains);
|
||||
|
||||
private delegate ulong FlagSlotForUpdateDelegateIntern(nint drawObject, uint slot, CharacterArmor* data);
|
||||
|
||||
[Signature(Sigs.FlagSlotForUpdate, DetourName = nameof(FlagSlotForUpdateDetour))]
|
||||
private readonly Hook<FlagSlotForUpdateDelegateIntern> _flagSlotForUpdateHook = null!;
|
||||
|
||||
[Signature(Sigs.FlagBonusSlotForUpdate, DetourName = nameof(FlagBonusSlotForUpdateDetour))]
|
||||
private readonly Hook<FlagSlotForUpdateDelegateIntern> _flagBonusSlotForUpdateHook = null!;
|
||||
|
||||
private ulong FlagSlotForUpdateDetour(nint drawObject, uint slotIdx, CharacterArmor* data)
|
||||
{
|
||||
var slot = slotIdx.ToEquipSlot();
|
||||
|
|
@ -54,6 +65,15 @@ public unsafe class UpdateSlotService : IDisposable
|
|||
return returnValue == ulong.MaxValue ? _flagSlotForUpdateHook.Original(drawObject, slotIdx, data) : returnValue;
|
||||
}
|
||||
|
||||
private ulong FlagBonusSlotForUpdateDetour(nint drawObject, uint slotIdx, CharacterArmor* data)
|
||||
{
|
||||
var slot = slotIdx.ToBonusSlot();
|
||||
var returnValue = ulong.MaxValue;
|
||||
SlotUpdatingEvent.Invoke(drawObject, slot, ref *data, ref returnValue);
|
||||
Glamourer.Log.Excessive($"[FlagBonusSlotForUpdate] Called with 0x{drawObject:X} for slot {slot} with {*data} ({returnValue:X}).");
|
||||
return returnValue == ulong.MaxValue ? _flagBonusSlotForUpdateHook.Original(drawObject, slotIdx, data) : returnValue;
|
||||
}
|
||||
|
||||
private ulong FlagSlotForUpdateInterop(Model drawObject, EquipSlot slot, CharacterArmor armor)
|
||||
=> _flagSlotForUpdateHook.Original(drawObject.Address, slot.ToIndex(), &armor);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ public unsafe class WeaponService : IDisposable
|
|||
if (tmpWeapon.Value != weapon.Value)
|
||||
{
|
||||
if (tmpWeapon.Skeleton.Id == 0)
|
||||
tmpWeapon.Stain = 0;
|
||||
tmpWeapon.Stains = StainIds.None;
|
||||
_loadWeaponHook.Original(drawData, slot, tmpWeapon.Value, 1, unk2, 1, unk4);
|
||||
}
|
||||
|
||||
|
|
@ -107,12 +107,12 @@ public unsafe class WeaponService : IDisposable
|
|||
}
|
||||
}
|
||||
|
||||
public void LoadStain(Actor character, EquipSlot slot, StainId stain)
|
||||
public void LoadStain(Actor character, EquipSlot slot, StainIds stains)
|
||||
{
|
||||
var mdl = character.Model;
|
||||
var (_, _, mh, oh) = mdl.GetWeapons(character);
|
||||
var value = slot == EquipSlot.OffHand ? oh : mh;
|
||||
var weapon = value.With(value.Skeleton.Id == 0 ? 0 : stain);
|
||||
var weapon = value.With(value.Skeleton.Id == 0 ? StainIds.None : stains);
|
||||
LoadWeapon(character, slot, weapon);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Glamourer.Interop.Penumbra;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using OtterGui;
|
||||
using OtterGui.Classes;
|
||||
using OtterGui.Filesystem;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.GameData.Actors;
|
||||
using Penumbra.GameData.Interop;
|
||||
using Notification = OtterGui.Classes.Notification;
|
||||
|
||||
namespace Glamourer.Services;
|
||||
|
||||
|
|
|
|||
|
|
@ -161,13 +161,6 @@ public sealed class CustomizeService(
|
|||
return $"The gender {gender.ToName()} is unknown, reset to {Gender.Male.ToName()}.";
|
||||
}
|
||||
|
||||
// TODO: Female Hrothgar
|
||||
if (gender is Gender.Female && race is Race.Hrothgar)
|
||||
{
|
||||
actualGender = Gender.Male;
|
||||
return $"{Race.Hrothgar.ToName()} do not currently support {Gender.Female.ToName()} characters, reset to {Gender.Male.ToName()}.";
|
||||
}
|
||||
|
||||
actualGender = gender;
|
||||
return string.Empty;
|
||||
}
|
||||
|
|
@ -225,13 +218,6 @@ public sealed class CustomizeService(
|
|||
customize.Race = newRace;
|
||||
customize.Clan = newClan;
|
||||
|
||||
// TODO Female Hrothgar
|
||||
if (newRace == Race.Hrothgar)
|
||||
{
|
||||
customize.Gender = Gender.Male;
|
||||
flags |= CustomizeFlag.Gender;
|
||||
}
|
||||
|
||||
var set = Manager.GetSet(customize.Clan, customize.Gender);
|
||||
return FixValues(set, ref customize) | flags;
|
||||
}
|
||||
|
|
@ -242,10 +228,6 @@ public sealed class CustomizeService(
|
|||
if (customize.Gender == newGender)
|
||||
return 0;
|
||||
|
||||
// TODO Female Hrothgar
|
||||
if (customize.Race is Race.Hrothgar)
|
||||
return 0;
|
||||
|
||||
if (ValidateGender(customize.Race, newGender, out newGender).Length > 0)
|
||||
return 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ namespace Glamourer.Services;
|
|||
|
||||
public class DalamudServices
|
||||
{
|
||||
public static void AddServices(ServiceManager services, DalamudPluginInterface pi)
|
||||
public static void AddServices(ServiceManager services, IDalamudPluginInterface pi)
|
||||
{
|
||||
services.AddExistingService(pi);
|
||||
services.AddExistingService(pi.UiBuilder);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public class FilenameService
|
|||
public readonly string NpcAppearanceFile;
|
||||
public readonly string CollectionOverrideFile;
|
||||
|
||||
public FilenameService(DalamudPluginInterface pi)
|
||||
public FilenameService(IDalamudPluginInterface pi)
|
||||
{
|
||||
ConfigDirectory = pi.ConfigDirectory.FullName;
|
||||
ConfigFile = pi.ConfigFile.FullName;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ using Dalamud.Plugin.Services;
|
|||
using Dalamud.Utility.Signatures;
|
||||
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
|
||||
using OtterGui.Services;
|
||||
using Penumbra.GameData;
|
||||
using Penumbra.GameData.Enums;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
|
|
@ -9,7 +10,7 @@ namespace Glamourer.Services;
|
|||
|
||||
public unsafe class HeightService : IService
|
||||
{
|
||||
[Signature("E8 ?? ?? ?? FF 48 8B 0D ?? ?? ?? ?? 0F 28 F0")]
|
||||
[Signature(Sigs.CalculateHeight)]
|
||||
private readonly delegate* unmanaged[Stdcall]<CharacterUtility*, byte, byte, byte, byte, float> _calculateHeight = null!;
|
||||
|
||||
public HeightService(IGameInteropProvider interop)
|
||||
|
|
|
|||
|
|
@ -195,16 +195,16 @@ public class ItemManager
|
|||
/// 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.
|
||||
/// </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 || stains.All(IsStainValid))
|
||||
{
|
||||
ret = stain;
|
||||
ret = stains;
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
ret = 0;
|
||||
return $"The Stain {stain} does not exist, reset to unstained.";
|
||||
ret = StainIds.None;
|
||||
return $"The Stain {stains} does not exist, reset to unstained.";
|
||||
}
|
||||
|
||||
/// <summary> Returns whether an offhand is valid given the required offhand type. </summary>
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ namespace Glamourer.Services;
|
|||
|
||||
public static class StaticServiceManager
|
||||
{
|
||||
public static ServiceManager CreateProvider(DalamudPluginInterface pi, Logger log, Glamourer glamourer)
|
||||
public static ServiceManager CreateProvider(IDalamudPluginInterface pi, Logger log, Glamourer glamourer)
|
||||
{
|
||||
EventWrapperBase.ChangeLogger(log);
|
||||
var services = new ServiceManager(log)
|
||||
|
|
@ -165,5 +165,6 @@ public static class StaticServiceManager
|
|||
.AddSingleton<GlamourerChangelog>()
|
||||
.AddSingleton<DesignQuickBar>()
|
||||
.AddSingleton<DesignColorUi>()
|
||||
.AddSingleton<NpcCombo>();
|
||||
.AddSingleton<NpcCombo>()
|
||||
.AddSingleton<TextureCache>();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
using Dalamud.Interface;
|
||||
using Dalamud.Interface.Internal;
|
||||
using Dalamud.Interface.Textures.TextureWraps;
|
||||
using Dalamud.Plugin.Services;
|
||||
using OtterGui.Classes;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
|
@ -7,7 +7,7 @@ using Penumbra.GameData.Structs;
|
|||
|
||||
namespace Glamourer.Services;
|
||||
|
||||
public sealed class TextureService(UiBuilder uiBuilder, IDataManager dataManager, ITextureProvider textureProvider)
|
||||
public sealed class TextureService(IUiBuilder uiBuilder, IDataManager dataManager, ITextureProvider textureProvider)
|
||||
: TextureCache(dataManager, textureProvider), IDisposable
|
||||
{
|
||||
private readonly IDalamudTextureWrap?[] _slotIcons = CreateSlotIcons(uiBuilder);
|
||||
|
|
@ -32,7 +32,7 @@ public sealed class TextureService(UiBuilder uiBuilder, IDataManager dataManager
|
|||
}
|
||||
}
|
||||
|
||||
private static IDalamudTextureWrap?[] CreateSlotIcons(UiBuilder uiBuilder)
|
||||
private static IDalamudTextureWrap?[] CreateSlotIcons(IUiBuilder uiBuilder)
|
||||
{
|
||||
var ret = new IDalamudTextureWrap?[12];
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
using Glamourer.Interop.Structs;
|
||||
using Penumbra.GameData.Structs;
|
||||
using Penumbra.GameData.Structs;
|
||||
|
||||
namespace Glamourer.State;
|
||||
|
||||
|
|
@ -21,8 +20,8 @@ internal class FunEquipSet
|
|||
{
|
||||
public Group(ushort headS, byte headV, ushort bodyS, byte bodyV, ushort handsS, byte handsV, ushort legsS, byte legsV, ushort feetS,
|
||||
byte feetV, StainId[]? stains = null)
|
||||
: this(new CharacterArmor(headS, headV, 0), new CharacterArmor(bodyS, bodyV, 0), new CharacterArmor(handsS, handsV, 0),
|
||||
new CharacterArmor(legsS, legsV, 0), new CharacterArmor(feetS, feetV, 0), stains)
|
||||
: this(new CharacterArmor(headS, headV, StainIds.None), new CharacterArmor(bodyS, bodyV, StainIds.None), new CharacterArmor(handsS, handsV, StainIds.None),
|
||||
new CharacterArmor(legsS, legsV, StainIds.None), new CharacterArmor(feetS, feetV, StainIds.None), stains)
|
||||
{ }
|
||||
|
||||
public static Group FullSetWithoutHat(ushort modelSet, byte variant, StainId[]? stains = null)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
using Dalamud.Game.ClientState.Objects.Enums;
|
||||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||
using Glamourer.Designs;
|
||||
using Glamourer.Gui;
|
||||
using Glamourer.Services;
|
||||
|
|
@ -106,7 +106,7 @@ public unsafe class FunModule : IDisposable
|
|||
&& actor.OnlineStatus is OnlineStatus.PvEMentor or OnlineStatus.PvPMentor or OnlineStatus.TradeMentor
|
||||
&& slot.IsEquipment())
|
||||
{
|
||||
armor = new CharacterArmor(6117, 1, 0);
|
||||
armor = new CharacterArmor(6117, 1, StainIds.None);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -198,7 +198,7 @@ public unsafe class FunModule : IDisposable
|
|||
|
||||
private static bool ValidFunTarget(Actor actor)
|
||||
=> actor.IsCharacter
|
||||
&& actor.AsObject->ObjectKind is (byte)ObjectKind.Player
|
||||
&& actor.AsObject->ObjectKind is ObjectKind.Pc
|
||||
&& !actor.IsTransformed
|
||||
&& actor.AsCharacter->CharacterData.ModelCharaId == 0;
|
||||
|
||||
|
|
@ -208,7 +208,7 @@ public unsafe class FunModule : IDisposable
|
|||
private void SetRandomDye(ref CharacterArmor armor)
|
||||
{
|
||||
var stainIdx = _rng.Next(0, _stains.Length - 1);
|
||||
armor.Stain = _stains[stainIdx];
|
||||
armor.Stains = _stains[stainIdx];
|
||||
}
|
||||
|
||||
private void SetRandomItem(EquipSlot slot, ref CharacterArmor armor)
|
||||
|
|
@ -235,17 +235,17 @@ public unsafe class FunModule : IDisposable
|
|||
private static IReadOnlyList<CharacterArmor> DolphinBodies
|
||||
=>
|
||||
[
|
||||
new CharacterArmor(6089, 1, 4), // Toad
|
||||
new CharacterArmor(6089, 1, 4), // Toad
|
||||
new CharacterArmor(6089, 1, 4), // Toad
|
||||
new CharacterArmor(6023, 1, 4), // Swine
|
||||
new CharacterArmor(6023, 1, 4), // Swine
|
||||
new CharacterArmor(6023, 1, 4), // Swine
|
||||
new CharacterArmor(6133, 1, 4), // Gaja
|
||||
new CharacterArmor(6182, 1, 3), // Imp
|
||||
new CharacterArmor(6182, 1, 3), // Imp
|
||||
new CharacterArmor(6182, 1, 4), // Imp
|
||||
new CharacterArmor(6182, 1, 4), // Imp
|
||||
new CharacterArmor(6089, 1, new StainIds(4)), // Toad
|
||||
new CharacterArmor(6089, 1, new StainIds(4)), // Toad
|
||||
new CharacterArmor(6089, 1, new StainIds(4)), // Toad
|
||||
new CharacterArmor(6023, 1, new StainIds(4)), // Swine
|
||||
new CharacterArmor(6023, 1, new StainIds(4)), // Swine
|
||||
new CharacterArmor(6023, 1, new StainIds(4)), // Swine
|
||||
new CharacterArmor(6133, 1, new StainIds(4)), // Gaja
|
||||
new CharacterArmor(6182, 1, new StainIds(3)), // Imp
|
||||
new CharacterArmor(6182, 1, new StainIds(3)), // Imp
|
||||
new CharacterArmor(6182, 1, new StainIds(4)), // Imp
|
||||
new CharacterArmor(6182, 1, new StainIds(4)), // Imp
|
||||
];
|
||||
|
||||
private void SetDolphin(EquipSlot slot, ref CharacterArmor armor)
|
||||
|
|
@ -253,7 +253,7 @@ public unsafe class FunModule : IDisposable
|
|||
armor = slot switch
|
||||
{
|
||||
EquipSlot.Body => DolphinBodies[_rng.Next(0, DolphinBodies.Count - 1)],
|
||||
EquipSlot.Head => new CharacterArmor(5040, 1, 0),
|
||||
EquipSlot.Head => new CharacterArmor(5040, 1, StainIds.None),
|
||||
_ => armor,
|
||||
};
|
||||
}
|
||||
|
|
@ -270,7 +270,7 @@ public unsafe class FunModule : IDisposable
|
|||
|
||||
private static void SetCrown(Span<CharacterArmor> armor)
|
||||
{
|
||||
var clown = new CharacterArmor(6117, 1, 0);
|
||||
var clown = new CharacterArmor(6117, 1, StainIds.None);
|
||||
armor[0] = clown;
|
||||
armor[1] = clown;
|
||||
armor[2] = clown;
|
||||
|
|
@ -285,15 +285,12 @@ public unsafe class FunModule : IDisposable
|
|||
return;
|
||||
|
||||
var targetClan = (SubRace)((int)race * 2 - (int)customize.Clan % 2);
|
||||
// TODO Female Hrothgar
|
||||
if (race is Race.Hrothgar && customize.Gender is Gender.Female)
|
||||
targetClan = targetClan is SubRace.Lost ? SubRace.Seawolf : SubRace.Hellsguard;
|
||||
_customizations.ChangeClan(ref customize, targetClan);
|
||||
}
|
||||
|
||||
private void SetGender(ref CustomizeArray customize)
|
||||
{
|
||||
if (!_codes.Enabled(CodeService.CodeFlag.SixtyThree) || customize.Race is Race.Hrothgar) // TODO Female Hrothgar
|
||||
if (!_codes.Enabled(CodeService.CodeFlag.SixtyThree))
|
||||
return;
|
||||
|
||||
_customizations.ChangeGender(ref customize, customize.Gender is Gender.Male ? Gender.Female : Gender.Male);
|
||||
|
|
|
|||
|
|
@ -152,11 +152,11 @@ public class InternalStateEditor(
|
|||
}
|
||||
|
||||
/// <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,
|
||||
out StainId oldStain, uint key = 0)
|
||||
public bool ChangeEquip(ActorState state, EquipSlot slot, EquipItem item, StainIds stains, StateSource source, out EquipItem oldItem,
|
||||
out StainIds oldStains, uint key = 0)
|
||||
{
|
||||
oldItem = state.ModelData.Item(slot);
|
||||
oldStain = state.ModelData.Stain(slot);
|
||||
oldStains = state.ModelData.Stain(slot);
|
||||
if (!state.CanUnlock(key))
|
||||
return false;
|
||||
|
||||
|
|
@ -168,7 +168,7 @@ public class InternalStateEditor(
|
|||
return false;
|
||||
|
||||
var old = oldItem;
|
||||
var oldS = oldStain;
|
||||
var oldS = oldStains;
|
||||
gPose.AddActionOnLeave(() =>
|
||||
{
|
||||
if (old.Type == state.BaseData.Item(slot).Type)
|
||||
|
|
@ -177,20 +177,20 @@ public class InternalStateEditor(
|
|||
}
|
||||
|
||||
state.ModelData.SetItem(slot, item);
|
||||
state.ModelData.SetStain(slot, stain);
|
||||
state.ModelData.SetStain(slot, stains);
|
||||
state.Sources[slot, false] = source;
|
||||
state.Sources[slot, true] = source;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary> Change only the stain of an equipment piece. </summary>
|
||||
public bool ChangeStain(ActorState state, EquipSlot slot, StainId stain, StateSource source, out StainId oldStain, uint key = 0)
|
||||
public bool ChangeStains(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))
|
||||
return false;
|
||||
|
||||
state.ModelData.SetStain(slot, stain);
|
||||
state.ModelData.SetStain(slot, stains);
|
||||
state.Sources[slot, true] = source;
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,22 +130,22 @@ public class StateApplier(
|
|||
/// Change the stain of a single piece of armor or weapon.
|
||||
/// If the offhand is empty, the stain will be fixed to 0 to prevent crashes.
|
||||
/// </summary>
|
||||
public void ChangeStain(ActorData data, EquipSlot slot, StainId stain)
|
||||
public void ChangeStain(ActorData data, EquipSlot slot, StainIds stains)
|
||||
{
|
||||
var idx = slot.ToIndex();
|
||||
switch (idx)
|
||||
{
|
||||
case < 10:
|
||||
foreach (var actor in data.Objects.Where(a => a.Model.IsHuman))
|
||||
_updateSlot.UpdateStain(actor.Model, slot, stain);
|
||||
_updateSlot.UpdateStain(actor.Model, slot, stains);
|
||||
break;
|
||||
case 10:
|
||||
foreach (var actor in data.Objects.Where(a => a.Model.IsHuman))
|
||||
_weapon.LoadStain(actor, EquipSlot.MainHand, stain);
|
||||
_weapon.LoadStain(actor, EquipSlot.MainHand, stains);
|
||||
break;
|
||||
case 11:
|
||||
foreach (var actor in data.Objects.Where(a => a.Model.IsHuman))
|
||||
_weapon.LoadStain(actor, EquipSlot.OffHand, stain);
|
||||
_weapon.LoadStain(actor, EquipSlot.OffHand, stains);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -162,12 +162,12 @@ public class StateApplier(
|
|||
|
||||
|
||||
/// <summary> Apply a weapon to the appropriate slot. </summary>
|
||||
public void ChangeWeapon(ActorData data, EquipSlot slot, EquipItem item, StainId stain)
|
||||
public void ChangeWeapon(ActorData data, EquipSlot slot, EquipItem item, StainIds stains)
|
||||
{
|
||||
if (slot is EquipSlot.MainHand)
|
||||
ChangeMainhand(data, item, stain);
|
||||
ChangeMainhand(data, item, stains);
|
||||
else
|
||||
ChangeOffhand(data, item, stain);
|
||||
ChangeOffhand(data, item, stains);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ChangeWeapon(ActorData,EquipSlot,EquipItem,StainId)"/>
|
||||
|
|
@ -186,19 +186,19 @@ public class StateApplier(
|
|||
/// <summary>
|
||||
/// Apply a weapon to the mainhand. If the weapon type has no associated offhand type, apply both.
|
||||
/// </summary>
|
||||
public void ChangeMainhand(ActorData data, EquipItem weapon, StainId stain)
|
||||
public void ChangeMainhand(ActorData data, EquipItem weapon, StainIds stains)
|
||||
{
|
||||
var slot = weapon.Type.ValidOffhand() == FullEquipType.Unknown ? EquipSlot.BothHand : EquipSlot.MainHand;
|
||||
foreach (var actor in data.Objects.Where(a => a.Model.IsHuman))
|
||||
_weapon.LoadWeapon(actor, slot, weapon.Weapon().With(stain));
|
||||
_weapon.LoadWeapon(actor, slot, weapon.Weapon().With(stains));
|
||||
}
|
||||
|
||||
/// <summary> Apply a weapon to the offhand. </summary>
|
||||
public void ChangeOffhand(ActorData data, EquipItem weapon, StainId stain)
|
||||
public void ChangeOffhand(ActorData data, EquipItem weapon, StainIds stains)
|
||||
{
|
||||
stain = weapon.PrimaryId.Id == 0 ? 0 : stain;
|
||||
stains = weapon.PrimaryId.Id == 0 ? StainIds.None : stains;
|
||||
foreach (var actor in data.Objects.Where(a => a.Model.IsHuman))
|
||||
_weapon.LoadWeapon(actor, EquipSlot.OffHand, weapon.Weapon().With(stain));
|
||||
_weapon.LoadWeapon(actor, EquipSlot.OffHand, weapon.Weapon().With(stains));
|
||||
}
|
||||
|
||||
/// <summary> Change a meta state. </summary>
|
||||
|
|
@ -209,7 +209,7 @@ public class StateApplier(
|
|||
case MetaIndex.Wetness:
|
||||
{
|
||||
foreach (var actor in data.Objects.Where(a => a.IsCharacter))
|
||||
actor.AsCharacter->IsGPoseWet = value;
|
||||
actor.IsGPoseWet = value;
|
||||
return;
|
||||
}
|
||||
case MetaIndex.HatState:
|
||||
|
|
|
|||
|
|
@ -90,22 +90,22 @@ public class StateEditor(
|
|||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void ChangeEquip(object data, EquipSlot slot, EquipItem? item, StainId? stain, ApplySettings settings)
|
||||
public void ChangeEquip(object data, EquipSlot slot, EquipItem? item, StainIds? stains, ApplySettings settings)
|
||||
{
|
||||
switch (item.HasValue, stain.HasValue)
|
||||
switch (item.HasValue, stains.HasValue)
|
||||
{
|
||||
case (false, false): return;
|
||||
case (true, false):
|
||||
ChangeItem(data, slot, item!.Value, settings);
|
||||
return;
|
||||
case (false, true):
|
||||
ChangeStain(data, slot, stain!.Value, settings);
|
||||
ChangeStains(data, slot, stains!.Value, settings);
|
||||
return;
|
||||
}
|
||||
|
||||
var state = (ActorState)data;
|
||||
if (!Editor.ChangeEquip(state, slot, item ?? state.ModelData.Item(slot), stain ?? state.ModelData.Stain(slot), settings.Source,
|
||||
out var old, out var oldStain, settings.Key))
|
||||
if (!Editor.ChangeEquip(state, slot, item ?? state.ModelData.Item(slot), stains ?? state.ModelData.Stain(slot), settings.Source,
|
||||
out var old, out var oldStains, settings.Key))
|
||||
return;
|
||||
|
||||
var type = slot.ToIndex() < 10 ? StateChangeType.Equip : StateChangeType.Weapon;
|
||||
|
|
@ -115,25 +115,25 @@ public class StateEditor(
|
|||
item!.Value.Type != (slot is EquipSlot.MainHand ? state.BaseData.MainhandType : state.BaseData.OffhandType));
|
||||
|
||||
if (slot is EquipSlot.MainHand)
|
||||
ApplyMainhandPeriphery(state, item, stain, settings);
|
||||
ApplyMainhandPeriphery(state, item, stains, settings);
|
||||
|
||||
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 stain from {oldStains} to {stains!.Value}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
StateChanged.Invoke(type, settings.Source, state, actors, (old, item!.Value, slot));
|
||||
StateChanged.Invoke(StateChangeType.Stain, settings.Source, state, actors, (oldStain, stain!.Value, slot));
|
||||
StateChanged.Invoke(StateChangeType.Stains, settings.Source, state, actors, (oldStains, stains!.Value, slot));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void ChangeStain(object data, EquipSlot slot, StainId stain, ApplySettings settings)
|
||||
public void ChangeStains(object data, EquipSlot slot, StainIds stains, ApplySettings settings)
|
||||
{
|
||||
var state = (ActorState)data;
|
||||
if (!Editor.ChangeStain(state, slot, stain, settings.Source, out var old, settings.Key))
|
||||
if (!Editor.ChangeStains(state, slot, stains, settings.Source, out var old, settings.Key))
|
||||
return;
|
||||
|
||||
var actors = Applier.ChangeStain(state, slot, settings.Source.RequiresChange());
|
||||
Glamourer.Log.Verbose(
|
||||
$"Set {slot.ToName()} stain in state {state.Identifier.Incognito(null)} from {old.Id} to {stain.Id}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
StateChanged.Invoke(StateChangeType.Stain, settings.Source, state, actors, (old, stain, slot));
|
||||
$"Set {slot.ToName()} stain in state {state.Identifier.Incognito(null)} from {old} to {stains}. [Affecting {actors.ToLazyString("nothing")}.]");
|
||||
StateChanged.Invoke(StateChangeType.Stains, settings.Source, state, actors, (old, stains, slot));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
@ -269,7 +269,7 @@ public class StateEditor(
|
|||
|
||||
if (mergedDesign.Design.DoApplyStain(slot))
|
||||
if (!settings.RespectManual || !state.Sources[slot, true].IsManual())
|
||||
Editor.ChangeStain(state, slot, mergedDesign.Design.DesignData.Stain(slot),
|
||||
Editor.ChangeStains(state, slot, mergedDesign.Design.DesignData.Stain(slot),
|
||||
Source(slot.ToState(true)), out _, settings.Key);
|
||||
}
|
||||
|
||||
|
|
@ -277,7 +277,7 @@ public class StateEditor(
|
|||
{
|
||||
if (mergedDesign.Design.DoApplyStain(weaponSlot))
|
||||
if (!settings.RespectManual || !state.Sources[weaponSlot, true].IsManual())
|
||||
Editor.ChangeStain(state, weaponSlot, mergedDesign.Design.DesignData.Stain(weaponSlot),
|
||||
Editor.ChangeStains(state, weaponSlot, mergedDesign.Design.DesignData.Stain(weaponSlot),
|
||||
Source(weaponSlot.ToState(true)), out _, settings.Key);
|
||||
|
||||
if (!mergedDesign.Design.DoApplyEquip(weaponSlot))
|
||||
|
|
@ -392,19 +392,19 @@ public class StateEditor(
|
|||
|
||||
|
||||
/// <summary> Apply offhand item and potentially gauntlets if configured. </summary>
|
||||
private void ApplyMainhandPeriphery(ActorState state, EquipItem? newMainhand, StainId? newStain, ApplySettings settings)
|
||||
private void ApplyMainhandPeriphery(ActorState state, EquipItem? newMainhand, StainIds? newStains, ApplySettings settings)
|
||||
{
|
||||
if (!Config.ChangeEntireItem || !settings.Source.IsManual())
|
||||
return;
|
||||
|
||||
var mh = newMainhand ?? state.ModelData.Item(EquipSlot.MainHand);
|
||||
var offhand = newMainhand != null ? Items.GetDefaultOffhand(mh) : state.ModelData.Item(EquipSlot.OffHand);
|
||||
var stain = newStain ?? state.ModelData.Stain(EquipSlot.MainHand);
|
||||
var stains = newStains ?? state.ModelData.Stain(EquipSlot.MainHand);
|
||||
if (offhand.Valid)
|
||||
ChangeEquip(state, EquipSlot.OffHand, offhand, stain, settings);
|
||||
ChangeEquip(state, EquipSlot.OffHand, offhand, stains, settings);
|
||||
|
||||
if (mh is { Type: FullEquipType.Fists } && Items.ItemData.Tertiary.TryGetValue(mh.ItemId, out var gauntlets))
|
||||
ChangeEquip(state, EquipSlot.Hands, newMainhand != null ? gauntlets : state.ModelData.Item(EquipSlot.Hands),
|
||||
stain, settings);
|
||||
stains, settings);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ public class StateListener : IDisposable
|
|||
(_, 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();
|
||||
var (identifier, objects) = _objects.PlayerData;
|
||||
|
|
@ -250,14 +250,14 @@ public class StateListener : IDisposable
|
|||
&& current.Weapon == changed.Weapon
|
||||
&& !state.Sources[slot, false].IsFixed();
|
||||
|
||||
var stainChanged = current.Stain == changed.Stain && !state.Sources[slot, true].IsFixed();
|
||||
var stainChanged = current.Stains == changed.Stains && !state.Sources[slot, true].IsFixed();
|
||||
|
||||
switch ((itemChanged, stainChanged))
|
||||
{
|
||||
case (true, true):
|
||||
_manager.ChangeEquip(state, slot, currentItem, current.Stain, ApplySettings.Game);
|
||||
_manager.ChangeEquip(state, slot, currentItem, current.Stains, ApplySettings.Game);
|
||||
if (slot is EquipSlot.MainHand or EquipSlot.OffHand)
|
||||
_applier.ChangeWeapon(objects, slot, currentItem, current.Stain);
|
||||
_applier.ChangeWeapon(objects, slot, currentItem, current.Stains);
|
||||
else
|
||||
_applier.ChangeArmor(objects, slot, current.ToArmor(), !state.Sources[slot, false].IsFixed(),
|
||||
state.ModelData.IsHatVisible());
|
||||
|
|
@ -265,14 +265,14 @@ public class StateListener : IDisposable
|
|||
case (true, false):
|
||||
_manager.ChangeItem(state, slot, currentItem, ApplySettings.Game);
|
||||
if (slot is EquipSlot.MainHand or EquipSlot.OffHand)
|
||||
_applier.ChangeWeapon(objects, slot, currentItem, model.Stain);
|
||||
_applier.ChangeWeapon(objects, slot, currentItem, model.Stains);
|
||||
else
|
||||
_applier.ChangeArmor(objects, slot, current.ToArmor(model.Stain), !state.Sources[slot, false].IsFixed(),
|
||||
_applier.ChangeArmor(objects, slot, current.ToArmor(model.Stains), !state.Sources[slot, false].IsFixed(),
|
||||
state.ModelData.IsHatVisible());
|
||||
break;
|
||||
case (false, true):
|
||||
_manager.ChangeStain(state, slot, current.Stain, ApplySettings.Game);
|
||||
_applier.ChangeStain(objects, slot, current.Stain);
|
||||
_manager.ChangeStains(state, slot, current.Stains, ApplySettings.Game);
|
||||
_applier.ChangeStain(objects, slot, current.Stains);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -308,7 +308,7 @@ public class StateListener : IDisposable
|
|||
apply = true;
|
||||
|
||||
if (!state.Sources[slot, true].IsFixed())
|
||||
_manager.ChangeStain(state, slot, state.BaseData.Stain(slot), ApplySettings.Game);
|
||||
_manager.ChangeStains(state, slot, state.BaseData.Stain(slot), ApplySettings.Game);
|
||||
else
|
||||
apply = true;
|
||||
break;
|
||||
|
|
@ -332,7 +332,7 @@ public class StateListener : IDisposable
|
|||
else
|
||||
{
|
||||
if (weapon.Skeleton.Id != 0)
|
||||
weapon = weapon.With(newWeapon.Stain);
|
||||
weapon = weapon.With(newWeapon.Stains);
|
||||
// Force unlock if necessary.
|
||||
_manager.ChangeItem(state, slot, state.BaseData.Item(slot), ApplySettings.Game with { Key = state.Combination });
|
||||
}
|
||||
|
|
@ -341,7 +341,7 @@ public class StateListener : IDisposable
|
|||
// Fist Weapon Offhand hack.
|
||||
if (slot is EquipSlot.MainHand && weapon.Skeleton.Id is > 1600 and < 1651)
|
||||
_lastFistOffhand = new CharacterWeapon((PrimaryId)(weapon.Skeleton.Id + 50), weapon.Weapon, weapon.Variant,
|
||||
weapon.Stain);
|
||||
weapon.Stains);
|
||||
|
||||
_funModule.ApplyFunToWeapon(actor, ref weapon, slot);
|
||||
}
|
||||
|
|
@ -365,7 +365,7 @@ public class StateListener : IDisposable
|
|||
{
|
||||
var item = _items.Identify(slot, actorArmor.Set, actorArmor.Variant);
|
||||
state.BaseData.SetItem(EquipSlot.Head, item);
|
||||
state.BaseData.SetStain(EquipSlot.Head, actorArmor.Stain);
|
||||
state.BaseData.SetStain(EquipSlot.Head, actorArmor.Stains);
|
||||
return UpdateState.Change;
|
||||
}
|
||||
|
||||
|
|
@ -378,9 +378,9 @@ public class StateListener : IDisposable
|
|||
|
||||
var baseData = state.BaseData.Armor(slot);
|
||||
var change = UpdateState.NoChange;
|
||||
if (baseData.Stain != armor.Stain)
|
||||
if (baseData.Stains != armor.Stains)
|
||||
{
|
||||
state.BaseData.SetStain(slot, armor.Stain);
|
||||
state.BaseData.SetStain(slot, armor.Stains);
|
||||
change = UpdateState.Change;
|
||||
}
|
||||
|
||||
|
|
@ -418,7 +418,7 @@ public class StateListener : IDisposable
|
|||
apply = true;
|
||||
|
||||
if (!state.Sources[slot, true].IsFixed())
|
||||
_manager.ChangeStain(state, slot, state.BaseData.Stain(slot), ApplySettings.Game);
|
||||
_manager.ChangeStains(state, slot, state.BaseData.Stain(slot), ApplySettings.Game);
|
||||
else
|
||||
apply = true;
|
||||
|
||||
|
|
@ -503,9 +503,9 @@ public class StateListener : IDisposable
|
|||
if (slot is EquipSlot.OffHand && weapon.Value == 0 && actor.GetMainhand().Skeleton.Id is > 1600 and < 1651)
|
||||
return UpdateState.NoChange;
|
||||
|
||||
if (baseData.Stain != weapon.Stain)
|
||||
if (baseData.Stains != weapon.Stains)
|
||||
{
|
||||
state.BaseData.SetStain(slot, weapon.Stain);
|
||||
state.BaseData.SetStain(slot, weapon.Stains);
|
||||
change = UpdateState.Change;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ public sealed class StateManager(
|
|||
if (!_humans.IsHuman((uint)actor.AsCharacter->CharacterData.ModelCharaId))
|
||||
{
|
||||
ret.LoadNonHuman((uint)actor.AsCharacter->CharacterData.ModelCharaId, *(CustomizeArray*)&actor.AsCharacter->DrawData.CustomizeData,
|
||||
(nint)(&actor.AsCharacter->DrawData.Head));
|
||||
(nint)Unsafe.AsPointer(ref actor.AsCharacter->DrawData.EquipmentModelIds[0]));
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -141,7 +141,7 @@ public sealed class StateManager(
|
|||
var head = ret.IsHatVisible() || ignoreHatState ? model.GetArmor(EquipSlot.Head) : actor.GetArmor(EquipSlot.Head);
|
||||
var headItem = Items.Identify(EquipSlot.Head, head.Set, head.Variant);
|
||||
ret.SetItem(EquipSlot.Head, headItem);
|
||||
ret.SetStain(EquipSlot.Head, head.Stain);
|
||||
ret.SetStain(EquipSlot.Head, head.Stains);
|
||||
|
||||
// The other slots can be used from the draw object.
|
||||
foreach (var slot in EquipSlotExtensions.EqdpSlots.Skip(1))
|
||||
|
|
@ -149,7 +149,7 @@ public sealed class StateManager(
|
|||
var armor = model.GetArmor(slot);
|
||||
var item = Items.Identify(slot, armor.Set, armor.Variant);
|
||||
ret.SetItem(slot, item);
|
||||
ret.SetStain(slot, armor.Stain);
|
||||
ret.SetStain(slot, armor.Stains);
|
||||
}
|
||||
|
||||
// Weapons use the draw objects of the weapons, but require the game object either way.
|
||||
|
|
@ -171,7 +171,7 @@ public sealed class StateManager(
|
|||
var armor = actor.GetArmor(slot);
|
||||
var item = Items.Identify(slot, armor.Set, armor.Variant);
|
||||
ret.SetItem(slot, item);
|
||||
ret.SetStain(slot, armor.Stain);
|
||||
ret.SetStain(slot, armor.Stains);
|
||||
}
|
||||
|
||||
main = actor.GetMainhand();
|
||||
|
|
@ -187,13 +187,13 @@ public sealed class StateManager(
|
|||
var mainItem = Items.Identify(EquipSlot.MainHand, main.Skeleton, main.Weapon, main.Variant);
|
||||
var offItem = Items.Identify(EquipSlot.OffHand, off.Skeleton, off.Weapon, off.Variant, mainItem.Type);
|
||||
ret.SetItem(EquipSlot.MainHand, mainItem);
|
||||
ret.SetStain(EquipSlot.MainHand, main.Stain);
|
||||
ret.SetStain(EquipSlot.MainHand, main.Stains);
|
||||
ret.SetItem(EquipSlot.OffHand, offItem);
|
||||
ret.SetStain(EquipSlot.OffHand, off.Stain);
|
||||
ret.SetStain(EquipSlot.OffHand, off.Stains);
|
||||
|
||||
// Wetness can technically only be set in GPose or via external tools.
|
||||
// It is only available in the game object.
|
||||
ret.SetIsWet(actor.AsCharacter->IsGPoseWet);
|
||||
ret.SetIsWet(actor.IsGPoseWet);
|
||||
|
||||
// Weapon visibility could technically be inferred from the weapon draw objects,
|
||||
// but since we use hat visibility from the game object we can also use weapon visibility from it.
|
||||
|
|
@ -214,7 +214,7 @@ public sealed class StateManager(
|
|||
offhand.Variant = mainhand.Variant;
|
||||
offhand.Weapon = mainhand.Weapon;
|
||||
ret.SetItem(EquipSlot.Hands, gauntlets);
|
||||
ret.SetStain(EquipSlot.Hands, mainhand.Stain);
|
||||
ret.SetStain(EquipSlot.Hands, mainhand.Stains);
|
||||
}
|
||||
|
||||
/// <summary> Turn an actor human. </summary>
|
||||
|
|
@ -414,7 +414,9 @@ public sealed class StateManager(
|
|||
public void ReapplyState(Actor actor, ActorState state, bool forceRedraw, StateSource source)
|
||||
{
|
||||
var data = Applier.ApplyAll(state,
|
||||
forceRedraw || !actor.Model.IsHuman || CustomizeArray.Compare(actor.Model.GetCustomize(), state.ModelData.Customize).RequiresRedraw(), false);
|
||||
forceRedraw
|
||||
|| !actor.Model.IsHuman
|
||||
|| CustomizeArray.Compare(actor.Model.GetCustomize(), state.ModelData.Customize).RequiresRedraw(), false);
|
||||
StateChanged.Invoke(StateChangeType.Reapply, source, state, data, null);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public class WorldSets
|
|||
[(Gender.Male, Race.AuRa)] = FunEquipSet.Group.FullSetWithoutHat(0257, 2),
|
||||
[(Gender.Female, Race.AuRa)] = FunEquipSet.Group.FullSetWithoutHat(0258, 2),
|
||||
[(Gender.Male, Race.Hrothgar)] = FunEquipSet.Group.FullSetWithoutHat(0597, 1),
|
||||
[(Gender.Female, Race.Hrothgar)] = FunEquipSet.Group.FullSetWithoutHat(0000, 0), // TODO Hrothgar Female
|
||||
[(Gender.Female, Race.Hrothgar)] = FunEquipSet.Group.FullSetWithoutHat(0829, 1),
|
||||
[(Gender.Male, Race.Viera)] = FunEquipSet.Group.FullSetWithoutHat(0744, 1),
|
||||
[(Gender.Female, Race.Viera)] = FunEquipSet.Group.FullSetWithoutHat(0581, 1),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using Dalamud;
|
||||
using Dalamud.Game;
|
||||
using Dalamud.Hooking;
|
||||
using Dalamud.Plugin.Services;
|
||||
using Dalamud.Utility;
|
||||
|
|
@ -8,6 +8,7 @@ using Glamourer.GameData;
|
|||
using Glamourer.Events;
|
||||
using Glamourer.Services;
|
||||
using Lumina.Excel.GeneratedSheets;
|
||||
using Penumbra.GameData;
|
||||
using Penumbra.GameData.Enums;
|
||||
|
||||
namespace Glamourer.Unlocks;
|
||||
|
|
@ -128,7 +129,7 @@ public class CustomizeUnlockManager : IDisposable, ISavable
|
|||
|
||||
private delegate void SetUnlockLinkValueDelegate(nint uiState, uint data, byte value);
|
||||
|
||||
[Signature("48 83 EC ?? 8B C2 44 8B D2", DetourName = nameof(SetUnlockLinkValueDetour))]
|
||||
[Signature(Sigs.SetUnlockLinkValue, DetourName = nameof(SetUnlockLinkValueDetour))]
|
||||
private readonly Hook<SetUnlockLinkValueDelegate> _setUnlockLinkValueHook = null!;
|
||||
|
||||
private void SetUnlockLinkValueDetour(nint uiState, uint data, byte value)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using Glamourer.Services;
|
||||
using Newtonsoft.Json;
|
||||
using OtterGui.Classes;
|
||||
|
|
|
|||
|
|
@ -144,8 +144,7 @@ public class ItemUnlockManager : ISavable, IDisposable, IReadOnlyDictionary<Item
|
|||
if (newGlamourState != _lastGlamourState)
|
||||
{
|
||||
_lastGlamourState = newGlamourState;
|
||||
// TODO: Make independent from hardcoded value
|
||||
var span = new ReadOnlySpan<uint>(mirageManager->PrismBoxItemIds, 800);
|
||||
var span = mirageManager->PrismBoxItemIds;
|
||||
foreach (var item in span)
|
||||
changes |= AddItem(item, time);
|
||||
}
|
||||
|
|
@ -154,10 +153,9 @@ public class ItemUnlockManager : ISavable, IDisposable, IReadOnlyDictionary<Item
|
|||
if (newPlateState != _lastPlateState)
|
||||
{
|
||||
_lastPlateState = newPlateState;
|
||||
foreach (var plate in mirageManager->GlamourPlatesSpan)
|
||||
foreach (var plate in mirageManager->GlamourPlates)
|
||||
{
|
||||
// TODO: Make independent from hardcoded value
|
||||
var span = new ReadOnlySpan<uint>(plate.ItemIds, 12);
|
||||
var span = plate.ItemIds;
|
||||
foreach (var item in span)
|
||||
changes |= AddItem(item, time);
|
||||
}
|
||||
|
|
@ -176,8 +174,8 @@ public class ItemUnlockManager : ISavable, IDisposable, IReadOnlyDictionary<Item
|
|||
var item = container->GetInventorySlot(_currentInventoryIndex++);
|
||||
if (item != null)
|
||||
{
|
||||
changes |= AddItem(item->ItemID, time);
|
||||
changes |= AddItem(item->GlamourID, time);
|
||||
changes |= AddItem(item->ItemId, time);
|
||||
changes |= AddItem(item->GlamourId, time);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
using Dalamud.Interface.Internal.Notifications;
|
||||
using Dalamud.Interface.ImGuiNotification;
|
||||
using OtterGui.Classes;
|
||||
|
||||
namespace Glamourer.Unlocks;
|
||||
|
|
|
|||
2
OtterGui
2
OtterGui
|
|
@ -1 +1 @@
|
|||
Subproject commit fd791285606d49a7644762ea0b4dc2bbb1368eac
|
||||
Subproject commit c2738e1d42974cddbe5a31238c6ed236a831d17d
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 6aeae346332a255b7575ccfca554afb0f3cf1494
|
||||
Subproject commit 8ec296d1f8113ae2ba509527749cd3e8f54debbf
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit caa58c5c92710e69ce07b9d736ebe2d228cb4488
|
||||
Subproject commit f04abbabedf5e757c5cbb970f3e513fef23e53cf
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
"TestingAssemblyVersion": "1.2.3.3",
|
||||
"RepoUrl": "https://github.com/Ottermandias/Glamourer",
|
||||
"ApplicableVersion": "any",
|
||||
"DalamudApiLevel": 9,
|
||||
"DalamudApiLevel": 10,
|
||||
"IsHide": "False",
|
||||
"IsTestingExclusive": "False",
|
||||
"DownloadCount": 1,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue