Update other things.

This commit is contained in:
Ottermandias 2024-07-16 18:19:34 +02:00
parent 81059411e5
commit 9529963aa2
31 changed files with 575 additions and 294 deletions

View file

@ -106,6 +106,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn
["Tags"] = JArray.FromObject(Tags),
["WriteProtected"] = WriteProtected(),
["Equipment"] = SerializeEquipment(),
["Bonus"] = SerializeBonusItems(),
["Customize"] = SerializeCustomize(),
["Parameters"] = SerializeParameters(),
["Materials"] = SerializeMaterials(),
@ -171,6 +172,7 @@ public sealed class Design : DesignBase, ISavable, IDesignStandIn
design.SetWriteProtected(json["WriteProtected"]?.ToObject<bool>() ?? false);
LoadCustomize(customizations, json["Customize"], design, design.Name, true, false);
LoadEquip(items, json["Equipment"], design, design.Name, true);
LoadBonus(items, design, json["Bonus"]);
LoadMods(json["Mods"], design);
LoadParameters(json["Parameters"], design, design.Name);
LoadMaterials(json["Materials"], design, design.Name);

View file

@ -228,6 +228,7 @@ public class DesignBase
{
["FileVersion"] = FileVersion,
["Equipment"] = SerializeEquipment(),
["Bonus"] = SerializeBonusItems(),
["Customize"] = SerializeCustomize(),
["Parameters"] = SerializeParameters(),
["Materials"] = SerializeMaterials(),
@ -279,7 +280,7 @@ public class DesignBase
var item = _designData.BonusItem(slot);
ret[slot.ToString()] = new JObject()
{
["BonusId"] = item.ModelId.Id,
["BonusId"] = item.Id.Id,
["Apply"] = DoApplyBonusItem(slot),
};
}
@ -424,19 +425,44 @@ public class DesignBase
LoadEquip(items, json["Equipment"], ret, "Temporary Design", true);
LoadParameters(json["Parameters"], ret, "Temporary Design");
LoadMaterials(json["Materials"], ret, "Temporary Design");
LoadBonus(items, ret, json["Bonus"]);
return ret;
}
protected static void LoadBonus(ItemManager items, DesignBase design, JToken? json)
{
if (json is not JObject obj)
{
design.Application.BonusItem = 0;
return;
}
foreach (var slot in BonusExtensions.AllFlags)
{
var itemJson = json[slot.ToString()] as JObject;
if (itemJson == null)
{
design.Application.BonusItem &= ~slot;
design.GetDesignDataRef().SetBonusItem(slot, BonusItem.Empty(slot));
continue;
}
design.SetApplyBonusItem(slot, itemJson["Apply"]?.ToObject<bool>() ?? false);
var id = itemJson["BonusId"]?.ToObject<ushort>() ?? 0;
var item = items.Resolve(slot, id);
design.GetDesignDataRef().SetBonusItem(slot, item);
}
}
protected static void LoadParameters(JToken? parameters, DesignBase design, string name)
{
if (parameters == null)
{
design.Application.Parameters = 0;
design.Application.Parameters = 0;
design.GetDesignDataRef().Parameters = default;
return;
}
foreach (var flag in CustomizeParameterExtensions.ValueFlags)
{
if (!TryGetToken(flag, out var token))
@ -492,7 +518,7 @@ public class DesignBase
return true;
}
design.Application.Parameters &= ~flag;
design.Application.Parameters &= ~flag;
design.GetDesignDataRef().Parameters[flag] = CustomizeParameterValue.Zero;
return false;
}
@ -523,32 +549,15 @@ public class DesignBase
return;
}
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);
var crest = item?["Crest"]?.ToObject<bool>() ?? false;
var apply = item?["Apply"]?.ToObject<bool>() ?? false;
var applyStain = item?["ApplyStain"]?.ToObject<bool>() ?? false;
var applyCrest = item?["ApplyCrest"]?.ToObject<bool>() ?? false;
return (id, stain, crest, apply, applyStain, applyCrest);
}
void PrintWarning(string msg)
{
if (msg.Length > 0 && name != "Temporary Design")
Glamourer.Messager.NotificationMessage($"{msg} ({name})", NotificationType.Warning);
}
foreach (var slot in EquipSlotExtensions.EqdpSlots)
{
var (id, stain, crest, apply, applyStain, applyCrest) = ParseItem(slot, equip[slot.ToString()]);
var (id, stains, crest, apply, applyStain, applyCrest) = ParseItem(slot, equip[slot.ToString()]);
PrintWarning(items.ValidateItem(slot, id, out var item, allowUnknown));
PrintWarning(items.ValidateStain(stain, out stain, allowUnknown));
PrintWarning(items.ValidateStain(stains, out stains, allowUnknown));
var crestSlot = slot.ToCrestFlag();
design._designData.SetItem(slot, item);
design._designData.SetStain(slot, stain);
design._designData.SetStain(slot, stains);
design._designData.SetCrest(crestSlot, crest);
design.SetApplyEquip(slot, apply);
design.SetApplyStain(slot, applyStain);
@ -556,21 +565,21 @@ public class DesignBase
}
{
var (id, stain, crest, apply, applyStain, applyCrest) = ParseItem(EquipSlot.MainHand, equip[EquipSlot.MainHand.ToString()]);
var (id, stains, crest, apply, applyStain, applyCrest) = ParseItem(EquipSlot.MainHand, equip[EquipSlot.MainHand.ToString()]);
if (id == ItemManager.NothingId(EquipSlot.MainHand))
id = items.DefaultSword.ItemId;
var (idOff, stainOff, crestOff, applyOff, applyStainOff, applyCrestOff) =
var (idOff, stainsOff, crestOff, applyOff, applyStainOff, applyCrestOff) =
ParseItem(EquipSlot.OffHand, equip[EquipSlot.OffHand.ToString()]);
if (id == ItemManager.NothingId(EquipSlot.OffHand))
id = ItemManager.NothingId(FullEquipType.Shield);
PrintWarning(items.ValidateWeapons(id, idOff, out var main, out var off, allowUnknown));
PrintWarning(items.ValidateStain(stain, out stain, allowUnknown));
PrintWarning(items.ValidateStain(stainOff, out stainOff, allowUnknown));
PrintWarning(items.ValidateStain(stains, out stains, allowUnknown));
PrintWarning(items.ValidateStain(stainsOff, out stainsOff, allowUnknown));
design._designData.SetItem(EquipSlot.MainHand, main);
design._designData.SetItem(EquipSlot.OffHand, off);
design._designData.SetStain(EquipSlot.MainHand, stain);
design._designData.SetStain(EquipSlot.OffHand, stainOff);
design._designData.SetStain(EquipSlot.MainHand, stains);
design._designData.SetStain(EquipSlot.OffHand, stainsOff);
design._designData.SetCrest(CrestFlag.MainHand, crest);
design._designData.SetCrest(CrestFlag.OffHand, crestOff);
design.SetApplyEquip(EquipSlot.MainHand, apply);
@ -591,6 +600,24 @@ public class DesignBase
metaValue = QuadBool.FromJObject(equip["Visor"], "IsToggled", "Apply", QuadBool.NullFalse);
design.SetApplyMeta(MetaIndex.VisorState, metaValue.Enabled);
design._designData.SetVisor(metaValue.ForcedValue);
return;
void PrintWarning(string msg)
{
if (msg.Length > 0 && name != "Temporary Design")
Glamourer.Messager.NotificationMessage($"{msg} ({name})", NotificationType.Warning);
}
static (CustomItemId, StainIds, bool, bool, bool, bool) ParseItem(EquipSlot slot, JToken? item)
{
var id = item?["ItemId"]?.ToObject<ulong>() ?? ItemManager.NothingId(slot).Id;
var stains = StainIds.ParseFromObject(item as JObject);
var crest = item?["Crest"]?.ToObject<bool>() ?? false;
var apply = item?["Apply"]?.ToObject<bool>() ?? false;
var applyStain = item?["ApplyStain"]?.ToObject<bool>() ?? false;
var applyCrest = item?["ApplyCrest"]?.ToObject<bool>() ?? false;
return (id, stains, crest, apply, applyStain, applyCrest);
}
}
protected static void LoadCustomize(CustomizeService customizations, JToken? json, DesignBase design, string name, bool forbidNonHuman,
@ -671,12 +698,12 @@ public class DesignBase
{
_designData = DesignBase64Migration.MigrateBase64(items, humans, base64, out var equipFlags, out var customizeFlags,
out var writeProtected, out var applyMeta);
Application.Equip = equipFlags;
ApplyCustomize = customizeFlags;
Application.Equip = equipFlags;
ApplyCustomize = customizeFlags;
Application.Parameters = 0;
Application.Crest = 0;
Application.Meta = applyMeta;
Application.BonusItem = 0;
Application.Crest = 0;
Application.Meta = applyMeta;
Application.BonusItem = 0;
SetWriteProtected(writeProtected);
CustomizeSet = SetCustomizationSet(customize);
}

View file

@ -214,7 +214,7 @@ public class DesignConverter(
foreach (var (key, value) in materials.Values)
{
var idx = MaterialValueIndex.FromKey(key);
if (idx.RowIndex >= LegacyColorTable.NumUsedRows)
if (idx.RowIndex >= ColorTable.NumUsedRows)
continue;
if (idx.MaterialIndex >= MaterialService.MaterialsPerModel)
continue;

View file

@ -101,7 +101,7 @@ public unsafe struct DesignData
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 ),
11 => EquipItem.FromIds(_itemIds[11], _iconIds[11], *(PrimaryId*)(ptr + EquipmentByteSize + 8), *(SecondaryId*)(ptr + EquipmentByteSize + 10), *(Variant*)(ptr + EquipmentByteSize + 12), _typeOffhand, name: _nameOffhand ),
_ => new EquipItem(),
// @formatter:on
};
@ -176,13 +176,15 @@ public unsafe struct DesignData
_nameMainhand = item.Name;
_equipmentBytes[EquipmentByteSize + 2] = (byte)item.SecondaryId.Id;
_equipmentBytes[EquipmentByteSize + 3] = (byte)(item.SecondaryId.Id >> 8);
_equipmentBytes[EquipmentByteSize + 4] = item.Variant.Id;
_typeMainhand = item.Type;
return true;
case 11:
_nameOffhand = item.Name;
_equipmentBytes[EquipmentByteSize + 2] = (byte)item.SecondaryId.Id;
_equipmentBytes[EquipmentByteSize + 3] = (byte)(item.SecondaryId.Id >> 8);
_typeOffhand = item.Type;
_nameOffhand = item.Name;
_equipmentBytes[EquipmentByteSize + 10] = (byte)item.SecondaryId.Id;
_equipmentBytes[EquipmentByteSize + 11] = (byte)(item.SecondaryId.Id >> 8);
_equipmentBytes[EquipmentByteSize + 12] = item.Variant.Id;
_typeOffhand = item.Type;
return true;
}
@ -212,18 +214,18 @@ public unsafe struct DesignData
=> slot.ToIndex() switch
{
// @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),
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
};
@ -322,6 +324,13 @@ public unsafe struct DesignData
SetItem(EquipSlot.OffHand, ItemManager.NothingItem(FullEquipType.Shield));
SetStain(EquipSlot.OffHand, StainIds.None);
SetCrest(CrestFlag.OffHand, false);
SetDefaultBonusItems();
}
public void SetDefaultBonusItems()
{
foreach (var slot in BonusExtensions.AllFlags)
SetBonusItem(slot, Penumbra.GameData.Structs.BonusItem.Empty(slot));
}

View file

@ -165,9 +165,21 @@ public class DesignEditor(
}
}
/// <inheritdoc/>
public void ChangeBonusItem(object data, BonusItemFlag slot, BonusItem item, ApplySettings settings = default)
{
var design = (Design)data;
if (!Items.IsBonusItemValid(slot, item.Id, out item))
return;
var oldItem = design.DesignData.BonusItem(slot);
if (!design.GetDesignDataRef().SetBonusItem(slot, item))
return;
design.LastEdit = DateTimeOffset.UtcNow;
SaveService.QueueSave(design);
Glamourer.Log.Debug($"Set {slot} bonus item to {item}.");
DesignChanged.Invoke(DesignChanged.Type.BonusItem, design, (oldItem, item, slot));
}
/// <inheritdoc/>

View file

@ -356,7 +356,7 @@ public sealed class DesignManager : DesignEditor
design.LastEdit = DateTimeOffset.UtcNow;
SaveService.QueueSave(design);
Glamourer.Log.Debug($"Set applying of {slot} bonus item to {value}.");
DesignChanged.Invoke(DesignChanged.Type.ApplyBonus, design, slot);
DesignChanged.Invoke(DesignChanged.Type.ApplyBonusItem, design, slot);
}
/// <summary> Change whether to apply a specific stain. </summary>

View file

@ -62,6 +62,9 @@ public sealed class DesignChanged()
/// <summary> An existing design had an equipment piece changed. Data is the old value, the new value and the slot [(EquipItem, EquipItem, EquipSlot)]. </summary>
Equip,
/// <summary> An existing design had a bonus item changed. Data is the old value, the new value and the slot [(BonusItem, BonusItem, BonusItemFlag)]. </summary>
BonusItem,
/// <summary> An existing design had its weapons changed. Data is the old mainhand, the old offhand, the new mainhand, the new offhand (if any) and the new gauntlets (if any). [(EquipItem, EquipItem, EquipItem, EquipItem?, EquipItem?)]. </summary>
Weapon,
@ -90,7 +93,7 @@ public sealed class DesignChanged()
ApplyEquip,
/// <summary> An existing design changed whether a specific bonus item is applied. Data is the slot of the item [BonusItemFlag]. </summary>
ApplyBonus,
ApplyBonusItem,
/// <summary> An existing design changed whether a specific stain is applied. Data is the slot of the equipment [EquipSlot]. </summary>
ApplyStain,

View file

@ -12,7 +12,9 @@ public struct CustomizeParameterData
public Vector3 HairSpecular;
public Vector3 HairHighlight;
public Vector3 LeftEye;
public float LeftScleraIntensity;
public Vector3 RightEye;
public float RightScleraIntensity;
public Vector3 FeatureColor;
public float FacePaintUvMultiplier;
public float FacePaintUvOffset;
@ -33,7 +35,9 @@ public struct CustomizeParameterData
CustomizeParameterFlag.HairSpecular => new CustomizeParameterValue(HairSpecular),
CustomizeParameterFlag.HairHighlight => new CustomizeParameterValue(HairHighlight),
CustomizeParameterFlag.LeftEye => new CustomizeParameterValue(LeftEye),
CustomizeParameterFlag.LeftScleraIntensity => new CustomizeParameterValue(LeftScleraIntensity),
CustomizeParameterFlag.RightEye => new CustomizeParameterValue(RightEye),
CustomizeParameterFlag.RightScleraIntensity => new CustomizeParameterValue(RightScleraIntensity),
CustomizeParameterFlag.FeatureColor => new CustomizeParameterValue(FeatureColor),
CustomizeParameterFlag.DecalColor => new CustomizeParameterValue(DecalColor),
CustomizeParameterFlag.FacePaintUvMultiplier => new CustomizeParameterValue(FacePaintUvMultiplier),
@ -57,7 +61,9 @@ public struct CustomizeParameterData
CustomizeParameterFlag.HairSpecular => SetIfDifferent(ref HairSpecular, value.InternalTriple),
CustomizeParameterFlag.HairHighlight => SetIfDifferent(ref HairHighlight, value.InternalTriple),
CustomizeParameterFlag.LeftEye => SetIfDifferent(ref LeftEye, value.InternalTriple),
CustomizeParameterFlag.LeftScleraIntensity => SetIfDifferent(ref LeftScleraIntensity, value.Single),
CustomizeParameterFlag.RightEye => SetIfDifferent(ref RightEye, value.InternalTriple),
CustomizeParameterFlag.RightScleraIntensity => SetIfDifferent(ref RightScleraIntensity, value.Single),
CustomizeParameterFlag.FeatureColor => SetIfDifferent(ref FeatureColor, value.InternalTriple),
CustomizeParameterFlag.DecalColor => SetIfDifferent(ref DecalColor, value.InternalQuadruple),
CustomizeParameterFlag.FacePaintUvMultiplier => SetIfDifferent(ref FacePaintUvMultiplier, value.Single),
@ -67,7 +73,7 @@ public struct CustomizeParameterData
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public readonly void Apply(ref CustomizeParameter parameters, CustomizeParameterFlag flags = CustomizeParameterExtensions.All)
public readonly unsafe void Apply(ref CustomizeParameter parameters, CustomizeParameterFlag flags = CustomizeParameterExtensions.All)
{
parameters.SkinColor = (flags & (CustomizeParameterFlag.SkinDiffuse | CustomizeParameterFlag.MuscleTone)) switch
{
@ -77,20 +83,20 @@ public struct CustomizeParameterData
_ => new CustomizeParameterValue(SkinDiffuse, MuscleTone).XivQuadruple,
};
parameters.LeftColor = (flags & (CustomizeParameterFlag.LeftEye | CustomizeParameterFlag.FacePaintUvMultiplier)) switch
parameters.LeftColor = (flags & (CustomizeParameterFlag.LeftEye | CustomizeParameterFlag.LeftScleraIntensity)) switch
{
0 => parameters.LeftColor,
CustomizeParameterFlag.LeftEye => new CustomizeParameterValue(LeftEye, parameters.LeftColor.W).XivQuadruple,
CustomizeParameterFlag.FacePaintUvMultiplier => parameters.LeftColor with { W = FacePaintUvMultiplier },
_ => new CustomizeParameterValue(LeftEye, FacePaintUvMultiplier).XivQuadruple,
0 => parameters.LeftColor,
CustomizeParameterFlag.LeftEye => new CustomizeParameterValue(LeftEye, parameters.LeftColor.W).XivQuadruple,
CustomizeParameterFlag.LeftScleraIntensity => parameters.LeftColor with { W = LeftScleraIntensity },
_ => new CustomizeParameterValue(LeftEye, LeftScleraIntensity).XivQuadruple,
};
parameters.RightColor = (flags & (CustomizeParameterFlag.RightEye | CustomizeParameterFlag.FacePaintUvOffset)) switch
parameters.RightColor = (flags & (CustomizeParameterFlag.RightEye | CustomizeParameterFlag.RightScleraIntensity)) switch
{
0 => parameters.RightColor,
CustomizeParameterFlag.RightEye => new CustomizeParameterValue(RightEye, parameters.RightColor.W).XivQuadruple,
CustomizeParameterFlag.FacePaintUvOffset => parameters.RightColor with { W = FacePaintUvOffset },
_ => new CustomizeParameterValue(RightEye, FacePaintUvOffset).XivQuadruple,
0 => parameters.RightColor,
CustomizeParameterFlag.RightEye => new CustomizeParameterValue(RightEye, parameters.RightColor.W).XivQuadruple,
CustomizeParameterFlag.RightScleraIntensity => parameters.RightColor with { W = RightScleraIntensity },
_ => new CustomizeParameterValue(RightEye, RightScleraIntensity).XivQuadruple,
};
if (flags.HasFlag(CustomizeParameterFlag.SkinSpecular))
@ -101,6 +107,10 @@ public struct CustomizeParameterData
parameters.HairFresnelValue0 = new CustomizeParameterValue(HairSpecular).XivTriple;
if (flags.HasFlag(CustomizeParameterFlag.HairHighlight))
parameters.MeshColor = new CustomizeParameterValue(HairHighlight).XivTriple;
if (flags.HasFlag(CustomizeParameterFlag.FacePaintUvMultiplier))
GetUvMultiplierWrite(ref parameters) = FacePaintUvMultiplier;
if (flags.HasFlag(CustomizeParameterFlag.FacePaintUvOffset))
GetUvOffsetWrite(ref parameters) = FacePaintUvOffset;
if (flags.HasFlag(CustomizeParameterFlag.LipDiffuse))
parameters.LipColor = new CustomizeParameterValue(LipDiffuse).XivQuadruple;
if (flags.HasFlag(CustomizeParameterFlag.FeatureColor))
@ -115,7 +125,7 @@ public struct CustomizeParameterData
}
[MethodImpl(MethodImplOptions.AggressiveInlining | MethodImplOptions.AggressiveOptimization)]
public readonly void ApplySingle(ref CustomizeParameter parameters, CustomizeParameterFlag flag)
public readonly unsafe void ApplySingle(ref CustomizeParameter parameters, CustomizeParameterFlag flag)
{
switch (flag)
{
@ -150,19 +160,25 @@ public struct CustomizeParameterData
parameters.OptionColor = new CustomizeParameterValue(FeatureColor).XivTriple;
break;
case CustomizeParameterFlag.FacePaintUvMultiplier:
parameters.LeftColor.W = FacePaintUvMultiplier;
GetUvMultiplierWrite(ref parameters) = FacePaintUvMultiplier;
break;
case CustomizeParameterFlag.FacePaintUvOffset:
parameters.RightColor.W = FacePaintUvOffset;
GetUvOffsetWrite(ref parameters) = FacePaintUvOffset;
break;
case CustomizeParameterFlag.LeftScleraIntensity:
parameters.LeftColor.W = LeftScleraIntensity;
break;
case CustomizeParameterFlag.RightScleraIntensity:
parameters.RightColor.W = RightScleraIntensity;
break;
}
}
public static CustomizeParameterData FromParameters(in CustomizeParameter parameter, in DecalParameters decal)
public static unsafe CustomizeParameterData FromParameters(in CustomizeParameter parameter, in DecalParameters decal)
=> new()
{
FacePaintUvOffset = parameter.RightColor.W,
FacePaintUvMultiplier = parameter.LeftColor.W,
FacePaintUvOffset = GetUvOffset(parameter),
FacePaintUvMultiplier = GetUvMultiplier(parameter),
MuscleTone = parameter.SkinColor.W,
SkinDiffuse = new CustomizeParameterValue(parameter.SkinColor).InternalTriple,
SkinSpecular = new CustomizeParameterValue(parameter.SkinFresnelValue0).InternalTriple,
@ -171,12 +187,14 @@ public struct CustomizeParameterData
HairSpecular = new CustomizeParameterValue(parameter.HairFresnelValue0).InternalTriple,
HairHighlight = new CustomizeParameterValue(parameter.MeshColor).InternalTriple,
LeftEye = new CustomizeParameterValue(parameter.LeftColor).InternalTriple,
LeftScleraIntensity = new CustomizeParameterValue(parameter.LeftColor.W).Single,
RightEye = new CustomizeParameterValue(parameter.RightColor).InternalTriple,
RightScleraIntensity = new CustomizeParameterValue(parameter.RightColor.W).Single,
FeatureColor = new CustomizeParameterValue(parameter.OptionColor).InternalTriple,
DecalColor = FromParameter(decal),
};
public static CustomizeParameterValue FromParameter(in CustomizeParameter parameter, CustomizeParameterFlag flag)
public static unsafe CustomizeParameterValue FromParameter(in CustomizeParameter parameter, CustomizeParameterFlag flag)
=> flag switch
{
CustomizeParameterFlag.SkinDiffuse => new CustomizeParameterValue(parameter.SkinColor),
@ -189,8 +207,8 @@ public struct CustomizeParameterData
CustomizeParameterFlag.LeftEye => new CustomizeParameterValue(parameter.LeftColor),
CustomizeParameterFlag.RightEye => new CustomizeParameterValue(parameter.RightColor),
CustomizeParameterFlag.FeatureColor => new CustomizeParameterValue(parameter.OptionColor),
CustomizeParameterFlag.FacePaintUvMultiplier => new CustomizeParameterValue(parameter.LeftColor.W),
CustomizeParameterFlag.FacePaintUvOffset => new CustomizeParameterValue(parameter.RightColor.W),
CustomizeParameterFlag.FacePaintUvMultiplier => new CustomizeParameterValue(GetUvMultiplier(parameter)),
CustomizeParameterFlag.FacePaintUvOffset => new CustomizeParameterValue(GetUvOffset(parameter)),
_ => CustomizeParameterValue.Zero,
};
@ -223,4 +241,41 @@ public struct CustomizeParameterData
val = @new;
return true;
}
private static unsafe float GetUvOffset(in CustomizeParameter parameter)
{
// TODO CS Update
fixed (CustomizeParameter* ptr = &parameter)
{
return ((float*)ptr)[23];
}
}
private static unsafe ref float GetUvOffsetWrite(ref CustomizeParameter parameter)
{
// TODO CS Update
fixed (CustomizeParameter* ptr = &parameter)
{
return ref ((float*)ptr)[23];
}
}
private static unsafe float GetUvMultiplier(in CustomizeParameter parameter)
{
// TODO CS Update
fixed (CustomizeParameter* ptr = &parameter)
{
return ((float*)ptr)[15];
}
}
private static unsafe ref float GetUvMultiplierWrite(ref CustomizeParameter parameter)
{
// TODO CS Update
fixed (CustomizeParameter* ptr = &parameter)
{
return ref ((float*)ptr)[15];
}
}
}

View file

@ -16,17 +16,24 @@ public enum CustomizeParameterFlag : ushort
FacePaintUvMultiplier = 0x0400,
FacePaintUvOffset = 0x0800,
DecalColor = 0x1000,
LeftScleraIntensity = 0x2000,
RightScleraIntensity = 0x4000,
}
public static class CustomizeParameterExtensions
{
public const CustomizeParameterFlag All = (CustomizeParameterFlag)0x1FFF;
// Speculars are not available anymore.
public const CustomizeParameterFlag All = (CustomizeParameterFlag)0x1FDB;
public const CustomizeParameterFlag RgbTriples = All
& ~(RgbaQuadruples | Percentages | Values);
public const CustomizeParameterFlag RgbaQuadruples = CustomizeParameterFlag.DecalColor | CustomizeParameterFlag.LipDiffuse;
public const CustomizeParameterFlag Percentages = CustomizeParameterFlag.MuscleTone;
public const CustomizeParameterFlag Percentages = CustomizeParameterFlag.MuscleTone
| CustomizeParameterFlag.LeftScleraIntensity
| CustomizeParameterFlag.RightScleraIntensity;
public const CustomizeParameterFlag Values = CustomizeParameterFlag.FacePaintUvOffset | CustomizeParameterFlag.FacePaintUvMultiplier;
public static readonly IReadOnlyList<CustomizeParameterFlag> AllFlags = [.. Enum.GetValues<CustomizeParameterFlag>()];
@ -60,6 +67,8 @@ public static class CustomizeParameterExtensions
CustomizeParameterFlag.FacePaintUvMultiplier => "Multiplier for Face Paint",
CustomizeParameterFlag.FacePaintUvOffset => "Offset of Face Paint",
CustomizeParameterFlag.DecalColor => "Face Paint Color",
CustomizeParameterFlag.LeftScleraIntensity => "Left Sclera Intensity",
CustomizeParameterFlag.RightScleraIntensity => "Right Sclera Intensity",
_ => string.Empty,
};
}

View file

@ -158,22 +158,22 @@ public class EquipmentDrawer
}
}
public bool DrawAllStain(out StainId ret, bool locked)
public bool DrawAllStain(out StainIds ret, bool locked)
{
using var disabled = ImRaii.Disabled(locked);
var change = _stainCombo.Draw("Dye All Slots", Stain.None.RgbaColor, string.Empty, false, false, MouseWheelType.None);
ret = Stain.None.RowIndex;
ret = StainIds.None;
if (change)
if (_stainData.TryGetValue(_stainCombo.CurrentSelection.Key, out var stain))
ret = stain.RowIndex;
ret = StainIds.All(stain.RowIndex);
else if (_stainCombo.CurrentSelection.Key == Stain.None.RowIndex)
ret = Stain.None.RowIndex;
ret = StainIds.None;
if (!locked)
{
if (ImGui.IsItemClicked(ImGuiMouseButton.Right) && _config.DeleteDesignModifier.IsActive())
{
ret = Stain.None.RowIndex;
ret = StainIds.None;
change = true;
}
@ -420,7 +420,7 @@ public class EquipmentDrawer
private void DrawBonusItemNormal(in BonusDrawData bonusDrawData)
{
ImGui.Dummy(_iconSize with { Y = ImUtf8.FrameHeight });
bonusDrawData.CurrentItem.DrawIcon(_textures, _iconSize, bonusDrawData.Slot);
var right = ImGui.IsItemClicked(ImGuiMouseButton.Right);
var left = ImGui.IsItemClicked(ImGuiMouseButton.Left);
ImGui.SameLine();

View file

@ -51,6 +51,8 @@ public sealed unsafe class AdvancedDyePopup(
private void DrawButton(MaterialValueIndex index)
{
// TODO fix when working
return;
if (!config.UseAdvancedDyes)
return;
@ -193,11 +195,11 @@ public sealed unsafe class AdvancedDyePopup(
DrawWindow(textures);
}
private void DrawTable(MaterialValueIndex materialIndex, in LegacyColorTable table)
private void DrawTable(MaterialValueIndex materialIndex, in ColorTable table)
{
using var disabled = ImRaii.Disabled(_state.IsLocked);
_anyChanged = false;
for (byte i = 0; i < LegacyColorTable.NumUsedRows; ++i)
for (byte i = 0; i < ColorTable.NumUsedRows; ++i)
{
var index = materialIndex with { RowIndex = i };
ref var row = ref table[i];
@ -208,7 +210,7 @@ public sealed unsafe class AdvancedDyePopup(
DrawAllRow(materialIndex, table);
}
private void DrawAllRow(MaterialValueIndex materialIndex, in LegacyColorTable table)
private void DrawAllRow(MaterialValueIndex materialIndex, in ColorTable table)
{
using var id = ImRaii.PushId(100);
var buttonSize = new Vector2(ImGui.GetFrameHeight());
@ -245,11 +247,11 @@ public sealed unsafe class AdvancedDyePopup(
ImGui.SameLine(0, spacing);
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.UndoAlt.ToIconString(), buttonSize, "Reset this table to game state.", !_anyChanged,
true))
for (byte i = 0; i < LegacyColorTable.NumUsedRows; ++i)
for (byte i = 0; i < ColorTable.NumUsedRows; ++i)
stateManager.ResetMaterialValue(_state, materialIndex with { RowIndex = i }, ApplySettings.Game);
}
private void DrawRow(ref LegacyColorTable.Row row, MaterialValueIndex index, in LegacyColorTable table)
private void DrawRow(ref ColorTable.Row row, MaterialValueIndex index, in ColorTable table)
{
using var id = ImRaii.PushId(index.RowIndex);
var changed = _state.Materials.TryGetValue(index, out var value);

View file

@ -5,14 +5,14 @@ namespace Glamourer.Gui.Materials;
public static class ColorRowClipboard
{
private static ColorRow _row;
private static LegacyColorTable _table;
private static ColorRow _row;
private static ColorTable _table;
public static bool IsSet { get; private set; }
public static bool IsTableSet { get; private set; }
public static LegacyColorTable Table
public static ColorTable Table
{
get => _table;
set

View file

@ -175,9 +175,9 @@ public class MaterialDrawer(DesignManager _designManager, Configuration _config)
{
_newRowIdx += 1;
ImGui.SetNextItemWidth(ImGui.CalcTextSize("Row #0000").X);
if (ImGui.DragInt("##Row", ref _newRowIdx, 0.01f, 1, LegacyColorTable.NumUsedRows, "Row #%i"))
if (ImGui.DragInt("##Row", ref _newRowIdx, 0.01f, 1, ColorTable.NumUsedRows, "Row #%i"))
{
_newRowIdx = Math.Clamp(_newRowIdx, 1, LegacyColorTable.NumUsedRows);
_newRowIdx = Math.Clamp(_newRowIdx, 1, ColorTable.NumUsedRows);
_newKey = _newKey with { RowIndex = (byte)(_newRowIdx - 1) };
}

View file

@ -9,6 +9,7 @@ using OtterGui;
using OtterGui.Raii;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Gui.Debug;
using FFXIVClientStructs.FFXIV.Shader;
namespace Glamourer.Gui.Tabs.DebugTab;
@ -94,7 +95,8 @@ public class ActiveStatePanel(StateManager _stateManager, ObjectManager _objectM
foreach (var type in Enum.GetValues<CustomizeIndex>())
{
PrintRow(type.ToDefaultName(), state.BaseData.Customize[type].Value, state.ModelData.Customize[type].Value, state.Sources[type]);
PrintRow(type.ToDefaultName(), state.BaseData.Customize[type].Value, state.ModelData.Customize[type].Value,
state.Sources[type]);
ImGui.TableNextRow();
}

View file

@ -0,0 +1,135 @@
using FFXIVClientStructs.FFXIV.Client.Graphics.Kernel;
using Glamourer.Interop;
using ImGuiNET;
using OtterGui.Raii;
using OtterGui.Text;
using Penumbra.GameData.Gui.Debug;
namespace Glamourer.Gui.Tabs.DebugTab;
public unsafe class AdvancedCustomizationDrawer(ObjectManager objects) : IGameDataDrawer
{
public string Label
=> "Advanced Customizations";
public bool Disabled
=> false;
public void Draw()
{
var (player, data) = objects.PlayerData;
if (!data.Valid)
{
ImUtf8.Text("Invalid player."u8);
return;
}
var model = data.Objects[0].Model;
if (!model.IsHuman)
{
ImUtf8.Text("Invalid model."u8);
return;
}
DrawCBuffer("Customize"u8, model.AsHuman->CustomizeParameterCBuffer, 0);
DrawCBuffer("Decal"u8, model.AsHuman->DecalColorCBuffer, 1);
DrawCBuffer("Unk1"u8, *(ConstantBuffer**)((byte*)model.AsHuman + 0xBA0), 2);
DrawCBuffer("Unk2"u8, *(ConstantBuffer**)((byte*)model.AsHuman + 0xBA8), 3);
}
private static void DrawCBuffer(ReadOnlySpan<byte> label, ConstantBuffer* cBuffer, int type)
{
using var tree = ImUtf8.TreeNode(label);
if (!tree)
return;
if (cBuffer == null)
{
ImUtf8.Text("Invalid CBuffer."u8);
return;
}
ImUtf8.Text($"{cBuffer->ByteSize / 4}");
ImUtf8.Text($"{cBuffer->Flags}");
ImUtf8.Text($"0x{(ulong)cBuffer:X}");
var parameters = (float*)cBuffer->UnsafeSourcePointer;
if (parameters == null)
{
ImUtf8.Text("No Parameters."u8);
return;
}
var start = parameters;
using (ImUtf8.Group())
{
for (var end = start + cBuffer->ByteSize / 4; parameters < end; parameters += 2)
DrawParameters(parameters, type, (int)(parameters - start));
}
ImGui.SameLine(0, 50 * ImUtf8.GlobalScale);
parameters = start + 1;
using (ImUtf8.Group())
{
for (var end = start + cBuffer->ByteSize / 4; parameters < end; parameters += 2)
DrawParameters(parameters, type, (int)(parameters - start));
}
}
private static void DrawParameters(float* param, int type, int idx)
{
using var id = ImUtf8.PushId((nint)param);
ImUtf8.TextFrameAligned($"{idx:D2}: ");
ImUtf8.SameLineInner();
ImGui.SetNextItemWidth(200 * ImUtf8.GlobalScale);
if (TryGetKnown(type, idx, out var known))
{
ImUtf8.DragScalar(known, ref *param, float.MinValue, float.MaxValue, 0.01f);
}
else
{
using var color = ImRaii.PushColor(ImGuiCol.Text, ImGui.GetColorU32(ImGuiCol.TextDisabled));
ImUtf8.DragScalar($"+0x{idx * 4:X2}", ref *param, float.MinValue, float.MaxValue, 0.01f);
}
}
private static bool TryGetKnown(int type, int idx, out ReadOnlySpan<byte> text)
{
if (type == 0)
text = idx switch
{
0 => "Diffuse.R"u8,
1 => "Diffuse.G"u8,
2 => "Diffuse.B"u8,
3 => "Muscle Tone"u8,
8 => "Lipstick.R"u8,
9 => "Lipstick.G"u8,
10 => "Lipstick.B"u8,
11 => "Lipstick.Opacity"u8,
12 => "Hair.R"u8,
13 => "Hair.G"u8,
14 => "Hair.B"u8,
15 => "Facepaint.Offset"u8,
20 => "Highlight.R"u8,
21 => "Highlight.G"u8,
22 => "Highlight.B"u8,
23 => "Facepaint.Multiplier"u8,
24 => "LeftEye.R"u8,
25 => "LeftEye.G"u8,
26 => "LeftEye.B"u8,
27 => "LeftSclera"u8,
28 => "RightEye.R"u8,
29 => "RightEye.G"u8,
30 => "RightEye.B"u8,
31 => "RightSclera"u8,
32 => "Feature.R"u8,
33 => "Feature.G"u8,
34 => "Feature.B"u8,
_ => [],
};
else
text = [];
return text.Length > 0;
}
}

View file

@ -38,7 +38,8 @@ public class DebugTabHeader(string label, params IGameDataDrawer[] subTrees)
provider.GetRequiredService<PenumbraPanel>(),
provider.GetRequiredService<IpcTesterPanel>(),
provider.GetRequiredService<DatFilePanel>(),
provider.GetRequiredService<GlamourPlatePanel>()
provider.GetRequiredService<GlamourPlatePanel>(),
provider.GetRequiredService<AdvancedCustomizationDrawer>()
);
public static DebugTabHeader CreateGameData(IServiceProvider provider)

View file

@ -1,5 +1,6 @@
using Dalamud.Game.Text.SeStringHandling;
using Dalamud.Interface.Utility;
using Glamourer.Designs;
using Glamourer.GameData;
using Glamourer.Interop;
using Glamourer.Services;
@ -12,23 +13,23 @@ using ImGuiClip = OtterGui.ImGuiClip;
namespace Glamourer.Gui.Tabs.UnlocksTab;
public class UnlockOverview
public class UnlockOverview(
ItemManager items,
CustomizeService customizations,
ItemUnlockManager itemUnlocks,
CustomizeUnlockManager customizeUnlocks,
PenumbraChangedItemTooltip tooltip,
TextureService textures,
CodeService codes,
JobService jobs,
FavoriteManager favorites)
{
private readonly ItemManager _items;
private readonly ItemUnlockManager _itemUnlocks;
private readonly CustomizeService _customizations;
private readonly CustomizeUnlockManager _customizeUnlocks;
private readonly PenumbraChangedItemTooltip _tooltip;
private readonly TextureService _textures;
private readonly CodeService _codes;
private readonly JobService _jobs;
private readonly FavoriteManager _favorites;
private static readonly Vector4 UnavailableTint = new(0.3f, 0.3f, 0.3f, 1.0f);
private FullEquipType _selected1 = FullEquipType.Unknown;
private SubRace _selected2 = SubRace.Unknown;
private Gender _selected3 = Gender.Unknown;
private BonusItemFlag _selected4 = BonusItemFlag.Unknown;
private void DrawSelector()
{
@ -38,7 +39,7 @@ public class UnlockOverview
foreach (var type in Enum.GetValues<FullEquipType>())
{
if (type.IsOffhandType() || !_items.ItemData.ByType.TryGetValue(type, out var items) || items.Count == 0)
if (type.IsOffhandType() || !items.ItemData.ByType.TryGetValue(type, out var value) || value.Count == 0)
continue;
if (ImGui.Selectable(type.ToName(), _selected1 == type))
@ -46,12 +47,21 @@ public class UnlockOverview
_selected1 = type;
_selected2 = SubRace.Unknown;
_selected3 = Gender.Unknown;
_selected4 = BonusItemFlag.Unknown;
}
}
if (ImGui.Selectable("Bonus Items", _selected4 == BonusItemFlag.Glasses))
{
_selected1 = FullEquipType.Unknown;
_selected2 = SubRace.Unknown;
_selected3 = Gender.Unknown;
_selected4 = BonusItemFlag.Glasses;
}
foreach (var (clan, gender) in CustomizeManager.AllSets())
{
if (_customizations.Manager.GetSet(clan, gender).HairStyles.Count == 0)
if (customizations.Manager.GetSet(clan, gender).HairStyles.Count == 0)
continue;
if (ImGui.Selectable($"{(gender is Gender.Male ? '♂' : '♀')} {clan.ToShortName()} Hair & Paint",
@ -60,25 +70,11 @@ public class UnlockOverview
_selected1 = FullEquipType.Unknown;
_selected2 = clan;
_selected3 = gender;
_selected4 = BonusItemFlag.Unknown;
}
}
}
public UnlockOverview(ItemManager items, CustomizeService customizations, ItemUnlockManager itemUnlocks,
CustomizeUnlockManager customizeUnlocks, PenumbraChangedItemTooltip tooltip, TextureService textures, CodeService codes,
JobService jobs, FavoriteManager favorites)
{
_items = items;
_customizations = customizations;
_itemUnlocks = itemUnlocks;
_customizeUnlocks = customizeUnlocks;
_tooltip = tooltip;
_textures = textures;
_codes = codes;
_jobs = jobs;
_favorites = favorites;
}
public void Draw()
{
using var color = ImRaii.PushColor(ImGuiCol.Border, ImGui.GetColorU32(ImGuiCol.TableBorderStrong));
@ -97,11 +93,13 @@ public class UnlockOverview
DrawItems();
else if (_selected2 is not SubRace.Unknown && _selected3 is not Gender.Unknown)
DrawCustomizations();
else if (_selected4 is not BonusItemFlag.Unknown)
DrawBonusItems();
}
private void DrawCustomizations()
{
var set = _customizations.Manager.GetSet(_selected2, _selected3);
var set = customizations.Manager.GetSet(_selected2, _selected3);
var spacing = IconSpacing;
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing);
@ -111,16 +109,16 @@ public class UnlockOverview
var counter = 0;
foreach (var customize in set.HairStyles.Concat(set.FacePaints))
{
if (!_customizeUnlocks.Unlockable.TryGetValue(customize, out var unlockData))
if (!customizeUnlocks.Unlockable.TryGetValue(customize, out var unlockData))
continue;
var unlocked = _customizeUnlocks.IsUnlocked(customize, out var time);
var icon = _customizations.Manager.GetIcon(customize.IconId);
var unlocked = customizeUnlocks.IsUnlocked(customize, out var time);
var icon = customizations.Manager.GetIcon(customize.IconId);
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);
unlocked || codes.Enabled(CodeService.CodeFlag.Shirts) ? Vector4.One : UnavailableTint);
if (_favorites.Contains(_selected3, _selected2, customize.Index, customize.Value))
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);
@ -147,24 +145,90 @@ public class UnlockOverview
}
}
private void DrawBonusItems()
{
var spacing = IconSpacing;
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing);
var iconSize = ImGuiHelpers.ScaledVector2(64);
var iconsPerRow = IconsPerRow(iconSize.X, spacing.X);
var numRows = (items.DictBonusItems.Count + iconsPerRow - 1) / iconsPerRow;
var numVisibleRows = (int)(Math.Ceiling(ImGui.GetContentRegionAvail().Y / (iconSize.Y + spacing.Y)) + 0.5f) + 1;
var skips = ImGuiClip.GetNecessarySkips(iconSize.Y + spacing.Y);
var start = skips * iconsPerRow;
var end = Math.Min(numVisibleRows * iconsPerRow + skips * iconsPerRow, items.DictBonusItems.Count);
var counter = 0;
foreach (var item in items.DictBonusItems.Values.Skip(start).Take(end - start))
{
DrawItem(item);
if (counter != iconsPerRow - 1)
{
ImGui.SameLine();
++counter;
}
else
{
counter = 0;
}
}
if (ImGui.GetCursorPosX() != 0)
ImGui.NewLine();
var remainder = numRows - numVisibleRows - skips;
if (remainder > 0)
ImGuiClip.DrawEndDummy(remainder, iconSize.Y + spacing.Y);
void DrawItem(BonusItem item)
{
// TODO check unlocks
var unlocked = true;
if (!textures.TryLoadIcon(item.Icon.Id, out var iconHandle))
return;
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);
if (favorites.Contains(item))
ImGui.GetWindowDrawList().AddRect(ImGui.GetItemRectMin(), ImGui.GetItemRectMax(), ColorId.FavoriteStarOn.Value(),
2 * ImGuiHelpers.GlobalScale, ImDrawFlags.RoundCornersAll, 4 * ImGuiHelpers.GlobalScale);
// TODO handle clicking
if (ImGui.IsItemHovered())
{
using var tt = ImRaii.Tooltip();
if (size.X >= iconSize.X && size.Y >= iconSize.Y)
ImGui.Image(icon, size);
ImGui.TextUnformatted(item.Name);
ImGui.TextUnformatted($"{item.Slot.ToName()}");
ImGui.TextUnformatted($"{item.ModelId.Id}-{item.Variant.Id}");
// TODO
ImGui.TextUnformatted("Always Unlocked"); // : $"Unlocked on {time:g}" : "Not Unlocked.");
// TODO
//tooltip.CreateTooltip(item, string.Empty, false);
}
}
}
private void DrawItems()
{
if (!_items.ItemData.ByType.TryGetValue(_selected1, out var items))
if (!items.ItemData.ByType.TryGetValue(_selected1, out var value))
return;
var spacing = IconSpacing;
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing);
var iconSize = ImGuiHelpers.ScaledVector2(64);
var iconsPerRow = IconsPerRow(iconSize.X, spacing.X);
var numRows = (items.Count + iconsPerRow - 1) / iconsPerRow;
var numRows = (value.Count + iconsPerRow - 1) / iconsPerRow;
var numVisibleRows = (int)(Math.Ceiling(ImGui.GetContentRegionAvail().Y / (iconSize.Y + spacing.Y)) + 0.5f) + 1;
var skips = ImGuiClip.GetNecessarySkips(iconSize.Y + spacing.Y);
var end = Math.Min(numVisibleRows * iconsPerRow + skips * iconsPerRow, items.Count);
var end = Math.Min(numVisibleRows * iconsPerRow + skips * iconsPerRow, value.Count);
var counter = 0;
for (var idx = skips * iconsPerRow; idx < end; ++idx)
{
DrawItem(items[idx]);
DrawItem(value[idx]);
if (counter != iconsPerRow - 1)
{
ImGui.SameLine();
@ -185,23 +249,23 @@ public class UnlockOverview
void DrawItem(EquipItem item)
{
var unlocked = _itemUnlocks.IsUnlocked(item.Id, out var time);
if (!_textures.TryLoadIcon(item.IconId.Id, out var iconHandle))
var unlocked = itemUnlocks.IsUnlocked(item.Id, out var time);
if (!textures.TryLoadIcon(item.IconId.Id, out var iconHandle))
return;
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);
if (_favorites.Contains(item))
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);
if (ImGui.IsItemClicked())
Glamourer.Messager.Chat.Print(new SeStringBuilder().AddItemLink(item.ItemId.Id, false).BuiltString);
if (ImGui.IsItemClicked(ImGuiMouseButton.Right) && _tooltip.Player(out var state))
_tooltip.ApplyItem(state, item);
if (ImGui.IsItemClicked(ImGuiMouseButton.Right) && tooltip.Player(out var state))
tooltip.ApplyItem(state, item);
if (ImGui.IsItemHovered())
{
@ -213,7 +277,7 @@ public class UnlockOverview
ImGui.TextUnformatted($"{item.Type.ToName()} ({slot.ToName()})");
if (item.Type.ValidOffhand().IsOffhandType())
ImGui.TextUnformatted(
$"{item.Weapon()}{(_items.ItemData.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand) ? $" | {offhand.Weapon()}" : string.Empty)}");
$"{item.Weapon()}{(items.ItemData.TryGetValue(item.ItemId, EquipSlot.OffHand, out var offhand) ? $" | {offhand.Weapon()}" : string.Empty)}");
else
ImGui.TextUnformatted(slot is EquipSlot.MainHand ? $"{item.Weapon()}" : $"{item.Armor()}");
ImGui.TextUnformatted(
@ -221,17 +285,17 @@ public class UnlockOverview
if (item.Level.Value <= 1)
{
if (item.JobRestrictions.Id <= 1 || item.JobRestrictions.Id >= _jobs.AllJobGroups.Count)
if (item.JobRestrictions.Id <= 1 || item.JobRestrictions.Id >= jobs.AllJobGroups.Count)
ImGui.TextUnformatted("For Everyone");
else
ImGui.TextUnformatted($"For all {_jobs.AllJobGroups[item.JobRestrictions.Id].Name}");
ImGui.TextUnformatted($"For all {jobs.AllJobGroups[item.JobRestrictions.Id].Name}");
}
else
{
if (item.JobRestrictions.Id <= 1 || item.JobRestrictions.Id >= _jobs.AllJobGroups.Count)
if (item.JobRestrictions.Id <= 1 || item.JobRestrictions.Id >= jobs.AllJobGroups.Count)
ImGui.TextUnformatted($"For Everyone of at least Level {item.Level}");
else
ImGui.TextUnformatted($"For all {_jobs.AllJobGroups[item.JobRestrictions.Id].Name} of at least Level {item.Level}");
ImGui.TextUnformatted($"For all {jobs.AllJobGroups[item.JobRestrictions.Id].Name} of at least Level {item.Level}");
}
if (item.Flags.HasFlag(ItemFlags.IsDyable1))
@ -240,7 +304,7 @@ public class UnlockOverview
ImGui.TextUnformatted("Tradable");
if (item.Flags.HasFlag(ItemFlags.IsCrestWorthy))
ImGui.TextUnformatted("Can apply Crest");
_tooltip.CreateTooltip(item, string.Empty, false);
tooltip.CreateTooltip(item, string.Empty, false);
}
}
}

View file

@ -29,8 +29,30 @@ public static class UiHelpers
if (empty)
{
var (bgColor, tint) = isEmpty
? (ImGui.GetColorU32(ImGuiCol.FrameBg), new Vector4(0.1f, 0.1f, 0.1f, 0.5f))
: (ImGui.GetColorU32(ImGuiCol.FrameBgActive), new Vector4(0.3f, 0.3f, 0.3f, 0.8f));
? (ImGui.GetColorU32(ImGuiCol.FrameBg), Vector4.One)
: (ImGui.GetColorU32(ImGuiCol.FrameBgActive), new Vector4(0.3f, 0.3f, 0.3f, 1f));
var pos = ImGui.GetCursorScreenPos();
ImGui.GetWindowDrawList().AddRectFilled(pos, pos + size, bgColor, 5 * ImGuiHelpers.GlobalScale);
if (ptr != nint.Zero)
ImGui.Image(ptr, size, Vector2.Zero, Vector2.One, tint);
else
ImGui.Dummy(size);
}
else
{
ImGuiUtil.HoverIcon(ptr, textureSize, size);
}
}
public static void DrawIcon(this BonusItem item, TextureService textures, Vector2 size, BonusItemFlag slot)
{
var isEmpty = item.ModelId.Id == 0;
var (ptr, textureSize, empty) = textures.GetIcon(item, slot);
if (empty)
{
var (bgColor, tint) = isEmpty
? (ImGui.GetColorU32(ImGuiCol.FrameBg), Vector4.One)
: (ImGui.GetColorU32(ImGuiCol.FrameBgActive), new Vector4(0.3f, 0.3f, 0.3f, 1f));
var pos = ImGui.GetCursorScreenPos();
ImGui.GetWindowDrawList().AddRectFilled(pos, pos + size, bgColor, 5 * ImGuiHelpers.GlobalScale);
if (ptr != nint.Zero)

View file

@ -15,13 +15,13 @@ namespace Glamourer.Interop.Material;
public unsafe class DirectXService(IFramework framework) : IService
{
private readonly object _lock = new();
private readonly ConcurrentDictionary<nint, (DateTime Update, LegacyColorTable Table)> _textures = [];
private readonly ConcurrentDictionary<nint, (DateTime Update, ColorTable Table)> _textures = [];
/// <summary> Generate a color table the way the game does inside the original texture, and release the original. </summary>
/// <param name="original"> The original texture that will be replaced with a new one. </param>
/// <param name="colorTable"> The input color table. </param>
/// <returns> Success or failure. </returns>
public bool ReplaceColorTable(Texture** original, in LegacyColorTable colorTable)
public bool ReplaceColorTable(Texture** original, in ColorTable colorTable)
{
if (original == null)
return false;
@ -38,7 +38,7 @@ public unsafe class DirectXService(IFramework framework) : IService
if (texture.IsInvalid)
return false;
fixed (LegacyColorTable* ptr = &colorTable)
fixed (ColorTable* ptr = &colorTable)
{
if (!texture.Texture->InitializeContents(ptr))
return false;
@ -51,7 +51,7 @@ public unsafe class DirectXService(IFramework framework) : IService
return true;
}
public bool TryGetColorTable(Texture* texture, out LegacyColorTable table)
public bool TryGetColorTable(Texture* texture, out ColorTable table)
{
if (_textures.TryGetValue((nint)texture, out var p) && framework.LastUpdateUTC == p.Update)
{
@ -73,7 +73,7 @@ public unsafe class DirectXService(IFramework framework) : IService
/// <param name="texture"> A pointer to the internal texture struct containing the GPU handle. </param>
/// <param name="table"> The returned color table. </param>
/// <returns> Whether the table could be fetched. </returns>
private static bool TextureColorTable(Texture* texture, out LegacyColorTable table)
private static bool TextureColorTable(Texture* texture, out ColorTable table)
{
if (texture == null)
{
@ -114,7 +114,7 @@ public unsafe class DirectXService(IFramework framework) : IService
}
/// <summary> Turn a mapped texture into a color table. </summary>
private static LegacyColorTable GetTextureData(ID3D11Texture2D1 resource, MappedSubresource map)
private static ColorTable GetTextureData(ID3D11Texture2D1 resource, MappedSubresource map)
{
var desc = resource.Description1;
@ -133,14 +133,14 @@ public unsafe class DirectXService(IFramework framework) : IService
/// <param name="height"> The height of the texture. (Needs to be 16).</param>
/// <param name="pitch"> The stride in the texture data. </param>
/// <returns></returns>
private static LegacyColorTable ReadTexture(nint data, int length, int height, int pitch)
private static ColorTable ReadTexture(nint data, int length, int height, int pitch)
{
// Check that the data has sufficient dimension and size.
var expectedSize = sizeof(Half) * MaterialService.TextureWidth * height * 4;
if (length < expectedSize || sizeof(LegacyColorTable) != expectedSize || height != MaterialService.TextureHeight)
if (length < expectedSize || sizeof(ColorTable) != expectedSize || height != MaterialService.TextureHeight)
return default;
var ret = new LegacyColorTable();
var ret = new ColorTable();
var target = (byte*)&ret;
// If the stride is the same as in the table, just copy.
if (pitch == MaterialService.TextureWidth)

View file

@ -13,11 +13,11 @@ public sealed unsafe class LiveColorTablePreviewer : IService, IDisposable
private readonly DirectXService _directXService;
public MaterialValueIndex LastValueIndex { get; private set; } = MaterialValueIndex.Invalid;
public LegacyColorTable LastOriginalColorTable { get; private set; }
public ColorTable LastOriginalColorTable { get; private set; }
private MaterialValueIndex _valueIndex = MaterialValueIndex.Invalid;
private ObjectIndex _lastObjectIndex = ObjectIndex.AnyIndex;
private ObjectIndex _objectIndex = ObjectIndex.AnyIndex;
private LegacyColorTable _originalColorTable;
private ColorTable _originalColorTable;
public LiveColorTablePreviewer(global::Penumbra.GameData.Interop.ObjectManager objects, IFramework framework, DirectXService directXService)
{
@ -78,7 +78,7 @@ public sealed unsafe class LiveColorTablePreviewer : IService, IDisposable
}
else
{
for (var i = 0; i < LegacyColorTable.NumUsedRows; ++i)
for (var i = 0; i < ColorTable.NumUsedRows; ++i)
{
table[i].Diffuse = diffuse;
table[i].Emissive = emissive;
@ -92,7 +92,7 @@ public sealed unsafe class LiveColorTablePreviewer : IService, IDisposable
_objectIndex = ObjectIndex.AnyIndex;
}
public void OnHover(MaterialValueIndex index, ObjectIndex objectIndex, LegacyColorTable table)
public void OnHover(MaterialValueIndex index, ObjectIndex objectIndex, ColorTable table)
{
if (_valueIndex.DrawObject is not MaterialValueIndex.DrawObjectType.Invalid)
return;

View file

@ -40,6 +40,8 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
private void OnPrepareColorSet(CharacterBase* characterBase, MaterialResourceHandle* material, ref StainId stain, ref nint ret)
{
// TODO fix when working
return;
if (!_config.UseAdvancedDyes)
return;
@ -76,7 +78,7 @@ public sealed unsafe class MaterialManager : IRequiredService, IDisposable
/// <summary> Update and apply the glamourer state of an actor according to the application sources when updated by the game. </summary>
private void UpdateMaterialValues(ActorState state, ReadOnlySpan<(uint Key, MaterialValueState Value)> values, CharacterWeapon drawData,
ref LegacyColorTable colorTable)
ref ColorTable colorTable)
{
var deleteList = _deleteList.Value!;
deleteList.Clear();

View file

@ -9,11 +9,11 @@ namespace Glamourer.Interop.Material;
public static unsafe class MaterialService
{
public const int TextureWidth = 4;
public const int TextureHeight = LegacyColorTable.NumUsedRows;
public const int MaterialsPerModel = 4;
public const int TextureWidth = 8;
public const int TextureHeight = ColorTable.NumUsedRows;
public const int MaterialsPerModel = 10;
public static bool GenerateNewColorTable(in LegacyColorTable colorTable, out Texture* texture)
public static bool GenerateNewColorTable(in ColorTable colorTable, out Texture* texture)
{
var textureSize = stackalloc int[2];
textureSize[0] = TextureWidth;
@ -24,7 +24,7 @@ public static unsafe class MaterialService
if (texture == null)
return false;
fixed (LegacyColorTable* ptr = &colorTable)
fixed (ColorTable* ptr = &colorTable)
{
return texture->InitializeContents(ptr);
}
@ -53,7 +53,7 @@ public static unsafe class MaterialService
/// <param name="modelSlot"> The model slot. </param>
/// <param name="materialSlot"> The material slot in the model. </param>
/// <returns> A pointer to the color table or null. </returns>
public static LegacyColorTable* GetMaterialColorTable(Model model, int modelSlot, byte materialSlot)
public static ColorTable* GetMaterialColorTable(Model model, int modelSlot, byte materialSlot)
{
if (!model.IsCharacterBase)
return null;
@ -66,6 +66,6 @@ public static unsafe class MaterialService
if (material == null || material->ColorTable == null)
return null;
return (LegacyColorTable*)material->ColorTable;
return (ColorTable*)material->ColorTable;
}
}

View file

@ -149,7 +149,7 @@ public readonly record struct MaterialValueIndex(
=> materialIndex < MaterialService.MaterialsPerModel;
public static bool ValidateRow(byte rowIndex)
=> rowIndex < LegacyColorTable.NumUsedRows;
=> rowIndex < ColorTable.NumUsedRows;
private static uint ToKey(DrawObjectType type, byte slotIndex, byte materialIndex, byte rowIndex)
{

View file

@ -21,7 +21,7 @@ public struct ColorRow(Vector3 diffuse, Vector3 specular, Vector3 emissive, floa
public float SpecularStrength = specularStrength;
public float GlossStrength = glossStrength;
public ColorRow(in LegacyColorTable.Row row)
public ColorRow(in ColorTable.Row row)
: this(Root(row.Diffuse), Root(row.Specular), Root(row.Emissive), row.SpecularStrength, row.GlossStrength)
{ }
@ -44,7 +44,7 @@ public struct ColorRow(Vector3 diffuse, Vector3 specular, Vector3 emissive, floa
private static float Root(float value)
=> value < 0 ? MathF.Sqrt(-value) : MathF.Sqrt(value);
public readonly bool Apply(ref LegacyColorTable.Row row)
public readonly bool Apply(ref ColorTable.Row row)
{
var ret = false;
var d = Square(Diffuse);

View file

@ -56,7 +56,7 @@ public sealed unsafe class PrepareColorSet
}
public static bool TryGetColorTable(CharacterBase* characterBase, MaterialResourceHandle* material, StainIds stainIds,
out LegacyColorTable table)
out ColorTable table)
{
if (material->ColorTable == null)
{
@ -64,7 +64,7 @@ public sealed unsafe class PrepareColorSet
return false;
}
var newTable = *(LegacyColorTable*)material->ColorTable;
var newTable = *(ColorTable*)material->ColorTable;
// TODO
//if (stainIds.Stain1.Id != 0 || stainIds.Stain2.Id != 0)
// characterBase->ReadStainingTemplate(material, stainId.Id, (Half*)(&newTable));
@ -73,7 +73,7 @@ public sealed unsafe class PrepareColorSet
}
/// <summary> Assumes the actor is valid. </summary>
public static bool TryGetColorTable(Actor actor, MaterialValueIndex index, out LegacyColorTable table)
public static bool TryGetColorTable(Actor actor, MaterialValueIndex index, out ColorTable table)
{
var idx = index.SlotIndex * MaterialService.MaterialsPerModel + index.MaterialIndex;
if (!index.TryGetModel(actor, out var model))

View file

@ -13,7 +13,6 @@ public unsafe class WeaponService : IDisposable
private readonly WeaponLoading _event;
private readonly ThreadLocal<bool> _inUpdate = new(() => false);
private readonly delegate* unmanaged[Stdcall]<DrawDataContainer*, uint, ulong, byte, byte, byte, byte, void>
_original;

View file

@ -23,6 +23,21 @@ public sealed class TextureService(IUiBuilder uiBuilder, IDataManager dataManage
: (nint.Zero, Vector2.Zero, true);
}
public (nint, Vector2, bool) GetIcon(BonusItem item, BonusItemFlag slot)
{
if (item.Icon.Id != 0 && TryLoadIcon(item.Icon.Id, out var ret))
return (ret.ImGuiHandle, new Vector2(ret.Width, ret.Height), false);
var idx = slot.ToIndex();
if (idx == uint.MaxValue)
return (nint.Zero, Vector2.Zero, true);
idx += 12;
return idx < 13 && _slotIcons[idx] != null
? (_slotIcons[idx]!.ImGuiHandle, new Vector2(_slotIcons[idx]!.Width, _slotIcons[idx]!.Height), true)
: (nint.Zero, Vector2.Zero, true);
}
public void Dispose()
{
for (var i = 0; i < _slotIcons.Length; ++i)
@ -34,9 +49,9 @@ public sealed class TextureService(IUiBuilder uiBuilder, IDataManager dataManage
private static IDalamudTextureWrap?[] CreateSlotIcons(IUiBuilder uiBuilder)
{
var ret = new IDalamudTextureWrap?[12];
var ret = new IDalamudTextureWrap?[13];
using var uldWrapper = uiBuilder.LoadUld("ui/uld/ArmouryBoard.uld");
using var uldWrapper = uiBuilder.LoadUld("ui/uld/Character.uld");
if (!uldWrapper.Valid)
{
@ -44,33 +59,37 @@ public sealed class TextureService(IUiBuilder uiBuilder, IDataManager dataManage
return ret;
}
SetIcon(EquipSlot.Head, 1);
SetIcon(EquipSlot.Body, 2);
SetIcon(EquipSlot.Hands, 3);
SetIcon(EquipSlot.Legs, 5);
SetIcon(EquipSlot.Feet, 6);
SetIcon(EquipSlot.Ears, 8);
SetIcon(EquipSlot.Neck, 9);
SetIcon(EquipSlot.Wrists, 10);
SetIcon(EquipSlot.RFinger, 11);
SetIcon(EquipSlot.MainHand, 0);
SetIcon(EquipSlot.OffHand, 7);
SetIcon(EquipSlot.Head, 19);
SetIcon(EquipSlot.Body, 20);
SetIcon(EquipSlot.Hands, 21);
SetIcon(EquipSlot.Legs, 23);
SetIcon(EquipSlot.Feet, 24);
SetIcon(EquipSlot.Ears, 25);
SetIcon(EquipSlot.Neck, 26);
SetIcon(EquipSlot.Wrists, 27);
SetIcon(EquipSlot.RFinger, 28);
SetIcon(EquipSlot.MainHand, 17);
SetIcon(EquipSlot.OffHand, 18);
Set(BonusItemFlag.Glasses.ToName(), (int) BonusItemFlag.Glasses.ToIndex() + 12, 55);
ret[EquipSlot.LFinger.ToIndex()] = ret[EquipSlot.RFinger.ToIndex()];
return ret;
void SetIcon(EquipSlot slot, int index)
void Set(string name, int slot, int index)
{
try
{
ret[slot.ToIndex()] = uldWrapper.LoadTexturePart("ui/uld/ArmouryBoard_hr1.tex", index)!;
ret[slot] = uldWrapper.LoadTexturePart("ui/uld/Character_hr1.tex", index)!;
}
catch (Exception ex)
{
Glamourer.Log.Error($"Could not get empty slot texture for {slot.ToName()}, icon will be left empty. "
Glamourer.Log.Error($"Could not get empty slot texture for {name}, icon will be left empty. "
+ $"This may be because of incompatible mods affecting your character screen interface:\n{ex}");
ret[slot.ToIndex()] = null;
ret[slot] = null;
}
}
void SetIcon(EquipSlot slot, int index)
=> Set(slot.ToName(), (int)slot.ToIndex(), index);
}
}

View file

@ -306,6 +306,8 @@ public class StateApplier(
public unsafe void ChangeMaterialValue(ActorData data, MaterialValueIndex index, ColorRow? value, bool force)
{
// TODO fix when working
return;
if (!force && !_config.UseAdvancedDyes)
return;
@ -338,6 +340,8 @@ public class StateApplier(
public unsafe void ChangeMaterialValues(ActorData data, in StateMaterialManager materials, bool force)
{
// TODO: fix when working
return;
if (!force && !_config.UseAdvancedDyes)
return;
@ -383,6 +387,11 @@ public class StateApplier(
ChangeCustomize(actors, state.ModelData.Customize);
foreach (var slot in EquipSlotExtensions.EqdpSlots)
ChangeArmor(actors, slot, state.ModelData.Armor(slot), !state.Sources[slot, false].IsIpc(), state.ModelData.IsHatVisible());
foreach (var slot in BonusExtensions.AllFlags)
{
var item = state.ModelData.BonusItem(slot);
ChangeBonusItem(actors, slot, item.ModelId, item.Variant);
}
var mainhandActors = state.ModelData.MainhandType != state.BaseData.MainhandType ? actors.OnlyGPose() : actors;
ChangeMainhand(mainhandActors, state.ModelData.Item(EquipSlot.MainHand), state.ModelData.Stain(EquipSlot.MainHand));

View file

@ -175,7 +175,7 @@ public class StateEditor(
var @new = state.ModelData.Parameters[flag];
var actors = Applier.ChangeParameters(state, flag, settings.Source.RequiresChange());
Glamourer.Log.Verbose(
$"Set {flag} crest in state {state.Identifier.Incognito(null)} from {old} to {@new}. [Affecting {actors.ToLazyString("nothing")}.]");
$"Set {flag} in state {state.Identifier.Incognito(null)} from {old} to {@new}. [Affecting {actors.ToLazyString("nothing")}.]");
StateChanged.Invoke(StateChangeType.Parameter, settings.Source, state, actors, (old, @new, flag));
}

View file

@ -110,7 +110,9 @@ public readonly record struct StateIndex(int Value) : IEqualityOperators<StateIn
CustomizeParameterFlag.HairSpecular => new StateIndex(ParamHairSpecular),
CustomizeParameterFlag.HairHighlight => new StateIndex(ParamHairHighlight),
CustomizeParameterFlag.LeftEye => new StateIndex(ParamLeftEye),
CustomizeParameterFlag.LeftScleraIntensity => new StateIndex(ParamLeftScleraIntensity),
CustomizeParameterFlag.RightEye => new StateIndex(ParamRightEye),
CustomizeParameterFlag.RightScleraIntensity => new StateIndex(ParamRightScleraIntensity),
CustomizeParameterFlag.FeatureColor => new StateIndex(ParamFeatureColor),
CustomizeParameterFlag.FacePaintUvMultiplier => new StateIndex(ParamFacePaintUvMultiplier),
CustomizeParameterFlag.FacePaintUvOffset => new StateIndex(ParamFacePaintUvOffset),
@ -199,8 +201,10 @@ public readonly record struct StateIndex(int Value) : IEqualityOperators<StateIn
public const int ParamHairSpecular = ParamHairDiffuse + 1;
public const int ParamHairHighlight = ParamHairSpecular + 1;
public const int ParamLeftEye = ParamHairHighlight + 1;
public const int ParamRightEye = ParamLeftEye + 1;
public const int ParamFeatureColor = ParamRightEye + 1;
public const int ParamLeftScleraIntensity = ParamLeftEye + 1;
public const int ParamRightEye = ParamLeftScleraIntensity + 1;
public const int ParamRightScleraIntensity = ParamRightEye + 1;
public const int ParamFeatureColor = ParamRightScleraIntensity + 1;
public const int ParamFacePaintUvMultiplier = ParamFeatureColor + 1;
public const int ParamFacePaintUvOffset = ParamFacePaintUvMultiplier + 1;
public const int ParamDecalColor = ParamFacePaintUvOffset + 1;
@ -309,7 +313,9 @@ public readonly record struct StateIndex(int Value) : IEqualityOperators<StateIn
ParamHairSpecular => CustomizeParameterFlag.HairSpecular,
ParamHairHighlight => CustomizeParameterFlag.HairHighlight,
ParamLeftEye => CustomizeParameterFlag.LeftEye,
ParamLeftScleraIntensity => CustomizeParameterFlag.LeftScleraIntensity,
ParamRightEye => CustomizeParameterFlag.RightEye,
ParamRightScleraIntensity => CustomizeParameterFlag.RightScleraIntensity,
ParamFeatureColor => CustomizeParameterFlag.FeatureColor,
ParamFacePaintUvMultiplier => CustomizeParameterFlag.FacePaintUvMultiplier,
ParamFacePaintUvOffset => CustomizeParameterFlag.FacePaintUvOffset,
@ -320,103 +326,6 @@ public readonly record struct StateIndex(int Value) : IEqualityOperators<StateIn
_ => -1,
};
public object? GetValue(in DesignData data)
{
return Value switch
{
EquipHead => data.Item(EquipSlot.Head),
EquipBody => data.Item(EquipSlot.Body),
EquipHands => data.Item(EquipSlot.Hands),
EquipLegs => data.Item(EquipSlot.Legs),
EquipFeet => data.Item(EquipSlot.Feet),
EquipEars => data.Item(EquipSlot.Ears),
EquipNeck => data.Item(EquipSlot.Neck),
EquipWrist => data.Item(EquipSlot.Wrists),
EquipRFinger => data.Item(EquipSlot.RFinger),
EquipLFinger => data.Item(EquipSlot.LFinger),
EquipMainhand => data.Item(EquipSlot.MainHand),
EquipOffhand => data.Item(EquipSlot.OffHand),
StainHead => data.Stain(EquipSlot.Head),
StainBody => data.Stain(EquipSlot.Body),
StainHands => data.Stain(EquipSlot.Hands),
StainLegs => data.Stain(EquipSlot.Legs),
StainFeet => data.Stain(EquipSlot.Feet),
StainEars => data.Stain(EquipSlot.Ears),
StainNeck => data.Stain(EquipSlot.Neck),
StainWrist => data.Stain(EquipSlot.Wrists),
StainRFinger => data.Stain(EquipSlot.RFinger),
StainLFinger => data.Stain(EquipSlot.LFinger),
StainMainhand => data.Stain(EquipSlot.MainHand),
StainOffhand => data.Stain(EquipSlot.OffHand),
CustomizeRace => data.Customize[CustomizeIndex.Race],
CustomizeGender => data.Customize[CustomizeIndex.Gender],
CustomizeBodyType => data.Customize[CustomizeIndex.BodyType],
CustomizeHeight => data.Customize[CustomizeIndex.Height],
CustomizeClan => data.Customize[CustomizeIndex.Clan],
CustomizeFace => data.Customize[CustomizeIndex.Face],
CustomizeHairstyle => data.Customize[CustomizeIndex.Hairstyle],
CustomizeHighlights => data.Customize[CustomizeIndex.Highlights],
CustomizeSkinColor => data.Customize[CustomizeIndex.SkinColor],
CustomizeEyeColorRight => data.Customize[CustomizeIndex.EyeColorRight],
CustomizeHairColor => data.Customize[CustomizeIndex.HairColor],
CustomizeHighlightsColor => data.Customize[CustomizeIndex.HighlightsColor],
CustomizeFacialFeature1 => data.Customize[CustomizeIndex.FacialFeature1],
CustomizeFacialFeature2 => data.Customize[CustomizeIndex.FacialFeature2],
CustomizeFacialFeature3 => data.Customize[CustomizeIndex.FacialFeature3],
CustomizeFacialFeature4 => data.Customize[CustomizeIndex.FacialFeature4],
CustomizeFacialFeature5 => data.Customize[CustomizeIndex.FacialFeature5],
CustomizeFacialFeature6 => data.Customize[CustomizeIndex.FacialFeature6],
CustomizeFacialFeature7 => data.Customize[CustomizeIndex.FacialFeature7],
CustomizeLegacyTattoo => data.Customize[CustomizeIndex.LegacyTattoo],
CustomizeTattooColor => data.Customize[CustomizeIndex.TattooColor],
CustomizeEyebrows => data.Customize[CustomizeIndex.Eyebrows],
CustomizeEyeColorLeft => data.Customize[CustomizeIndex.EyeColorLeft],
CustomizeEyeShape => data.Customize[CustomizeIndex.EyeShape],
CustomizeSmallIris => data.Customize[CustomizeIndex.SmallIris],
CustomizeNose => data.Customize[CustomizeIndex.Nose],
CustomizeJaw => data.Customize[CustomizeIndex.Jaw],
CustomizeMouth => data.Customize[CustomizeIndex.Mouth],
CustomizeLipstick => data.Customize[CustomizeIndex.Lipstick],
CustomizeLipColor => data.Customize[CustomizeIndex.LipColor],
CustomizeMuscleMass => data.Customize[CustomizeIndex.MuscleMass],
CustomizeTailShape => data.Customize[CustomizeIndex.TailShape],
CustomizeBustSize => data.Customize[CustomizeIndex.BustSize],
CustomizeFacePaint => data.Customize[CustomizeIndex.FacePaint],
CustomizeFacePaintReversed => data.Customize[CustomizeIndex.FacePaintReversed],
CustomizeFacePaintColor => data.Customize[CustomizeIndex.FacePaintColor],
MetaWetness => data.GetMeta(MetaIndex.Wetness),
MetaHatState => data.GetMeta(MetaIndex.HatState),
MetaVisorState => data.GetMeta(MetaIndex.VisorState),
MetaWeaponState => data.GetMeta(MetaIndex.WeaponState),
MetaModelId => data.ModelId,
CrestHead => data.Crest(CrestFlag.Head),
CrestBody => data.Crest(CrestFlag.Body),
CrestOffhand => data.Crest(CrestFlag.OffHand),
ParamSkinDiffuse => data.Parameters[CustomizeParameterFlag.SkinDiffuse],
ParamMuscleTone => data.Parameters[CustomizeParameterFlag.MuscleTone],
ParamSkinSpecular => data.Parameters[CustomizeParameterFlag.SkinSpecular],
ParamLipDiffuse => data.Parameters[CustomizeParameterFlag.LipDiffuse],
ParamHairDiffuse => data.Parameters[CustomizeParameterFlag.HairDiffuse],
ParamHairSpecular => data.Parameters[CustomizeParameterFlag.HairSpecular],
ParamHairHighlight => data.Parameters[CustomizeParameterFlag.HairHighlight],
ParamLeftEye => data.Parameters[CustomizeParameterFlag.LeftEye],
ParamRightEye => data.Parameters[CustomizeParameterFlag.RightEye],
ParamFeatureColor => data.Parameters[CustomizeParameterFlag.FeatureColor],
ParamFacePaintUvMultiplier => data.Parameters[CustomizeParameterFlag.FacePaintUvMultiplier],
ParamFacePaintUvOffset => data.Parameters[CustomizeParameterFlag.FacePaintUvOffset],
ParamDecalColor => data.Parameters[CustomizeParameterFlag.DecalColor],
BonusItemGlasses => data.BonusItem(BonusItemFlag.Glasses),
_ => null,
};
}
private static string GetName(EquipFlag flag)
{
var slot = flag.ToSlot(out var stain);

@ -1 +1 @@
Subproject commit 8928015f38f951810a9a6fbb44fb4a0cb9a712dd
Subproject commit 491b61916951b7192bb2354d725363340ea153b5