Highlight existing advanced dyes in state and design.

This commit is contained in:
Ottermandias 2025-02-21 00:10:30 +01:00
parent 8a7ec45bbf
commit 0c8110e15e
6 changed files with 106 additions and 60 deletions

View file

@ -32,6 +32,7 @@ public enum ColorId
ModdedItemMarker,
ContainsItemsEnabled,
ContainsItemsDisabled,
AdvancedDyeActive,
}
public static class Colors
@ -70,6 +71,7 @@ public static class Colors
ColorId.ModdedItemMarker => (0xFFFF20FF, "Modded Item Marker", "The color of dot in the unlocks overview tab signaling that the item is modded in the currently selected Penumbra collection." ),
ColorId.ContainsItemsEnabled => (0xFFA0F0A0, "Enabled Mod Contains Design Items", "The color of enabled mods in the associated mod dropdown menu when they contain items used in this design." ),
ColorId.ContainsItemsDisabled => (0x80A0F0A0, "Disabled Mod Contains Design Items", "The color of disabled mods in the associated mod dropdown menu when they contain items used in this design." ),
ColorId.AdvancedDyeActive => (0xFF58DDFF, "Advanced Dyes Active", "The highlight color for the advanced dye button and marker if any advanced dyes are active for this slot." ),
_ => (0x00000000, string.Empty, string.Empty ),
// @formatter:on
};

View file

@ -1,4 +1,5 @@
using Glamourer.Designs;
using Glamourer.Interop.Material;
using Glamourer.State;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
@ -9,10 +10,11 @@ public struct BonusDrawData(BonusItemFlag slot, in DesignData designData)
{
private IDesignEditor _editor = null!;
private object _object = null!;
public readonly BonusItemFlag Slot = slot;
public readonly BonusItemFlag Slot = slot;
public bool Locked;
public bool DisplayApplication;
public bool AllowRevert;
public bool HasAdvancedDyes;
public readonly bool IsDesign
=> _object is Design;
@ -42,6 +44,7 @@ public struct BonusDrawData(BonusItemFlag slot, in DesignData designData)
CurrentApply = design.DoApplyBonusItem(slot),
Locked = design.WriteProtected(),
DisplayApplication = true,
HasAdvancedDyes = design.GetMaterialDataRef().CheckExistence(MaterialValueIndex.FromSlot(slot)),
};
public static BonusDrawData FromState(StateManager manager, ActorState state, BonusItemFlag slot)
@ -53,5 +56,6 @@ public struct BonusDrawData(BonusItemFlag slot, in DesignData designData)
DisplayApplication = false,
GameItem = state.BaseData.BonusItem(slot),
AllowRevert = true,
HasAdvancedDyes = state.Materials.CheckExistence(MaterialValueIndex.FromSlot(slot)),
};
}

View file

@ -1,4 +1,5 @@
using Glamourer.Designs;
using Glamourer.Interop.Material;
using Glamourer.State;
using Penumbra.GameData.Enums;
using Penumbra.GameData.Structs;
@ -9,7 +10,7 @@ public struct EquipDrawData(EquipSlot slot, in DesignData designData)
{
private IDesignEditor _editor = null!;
private object _object = null!;
public readonly EquipSlot Slot = slot;
public readonly EquipSlot Slot = slot;
public bool Locked;
public bool DisplayApplication;
public bool AllowRevert;
@ -29,15 +30,13 @@ public struct EquipDrawData(EquipSlot slot, in DesignData designData)
public readonly void SetApplyItem(bool value)
{
var manager = (DesignManager)_editor;
var design = (Design)_object;
manager.ChangeApplyItem(design, Slot, value);
manager.ChangeApplyItem((Design)_object, Slot, value);
}
public readonly void SetApplyStain(bool value)
{
var manager = (DesignManager)_editor;
var design = (Design)_object;
manager.ChangeApplyStains(design, Slot, value);
manager.ChangeApplyStains((Design)_object, Slot, value);
}
public EquipItem CurrentItem = designData.Item(slot);
@ -46,6 +45,7 @@ public struct EquipDrawData(EquipSlot slot, in DesignData designData)
public StainIds GameStains = default;
public bool CurrentApply;
public bool CurrentApplyStain;
public bool HasAdvancedDyes;
public readonly Gender CurrentGender = designData.Customize.Gender;
public readonly Race CurrentRace = designData.Customize.Race;
@ -58,6 +58,7 @@ public struct EquipDrawData(EquipSlot slot, in DesignData designData)
CurrentApply = design.DoApplyEquip(slot),
CurrentApplyStain = design.DoApplyStain(slot),
Locked = design.WriteProtected(),
HasAdvancedDyes = design.GetMaterialDataRef().CheckExistence(MaterialValueIndex.FromSlot(slot)),
DisplayApplication = true,
};
@ -70,6 +71,7 @@ public struct EquipDrawData(EquipSlot slot, in DesignData designData)
DisplayApplication = false,
GameItem = state.BaseData.Item(slot),
GameStains = state.BaseData.Stain(slot),
HasAdvancedDyes = state.Materials.CheckExistence(MaterialValueIndex.FromSlot(slot)),
AllowRevert = true,
};
}

View file

@ -3,6 +3,7 @@ using Dalamud.Interface.Utility;
using Dalamud.Plugin.Services;
using Glamourer.Events;
using Glamourer.Gui.Materials;
using Glamourer.Interop.Material;
using Glamourer.Services;
using Glamourer.Unlocks;
using ImGuiNET;
@ -63,6 +64,7 @@ public class EquipmentDrawer
private Vector2 _iconSize;
private float _comboLength;
private uint _advancedMaterialColor;
public void Prepare()
{
@ -74,7 +76,8 @@ public class EquipmentDrawer
.Max(i => ImGui.CalcTextSize($"{i.Item2.Name} ({i.Item2.ModelString})").X)
/ ImGuiHelpers.GlobalScale;
_requiredComboWidth = _requiredComboWidthUnscaled * ImGuiHelpers.GlobalScale;
_requiredComboWidth = _requiredComboWidthUnscaled * ImGuiHelpers.GlobalScale;
_advancedMaterialColor = ColorId.AdvancedDyeActive.Value();
}
private bool VerifyRestrictedGear(EquipDrawData data)
@ -91,7 +94,7 @@ public class EquipmentDrawer
if (_config.HideApplyCheckmarks)
equipDrawData.DisplayApplication = false;
using var id = ImRaii.PushId((int)equipDrawData.Slot);
using var id = ImUtf8.PushId((int)equipDrawData.Slot);
var spacing = ImGui.GetStyle().ItemInnerSpacing with { Y = ImGui.GetStyle().ItemSpacing.Y };
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing);
@ -106,7 +109,7 @@ public class EquipmentDrawer
if (_config.HideApplyCheckmarks)
bonusDrawData.DisplayApplication = false;
using var id = ImRaii.PushId(100 + (int)bonusDrawData.Slot);
using var id = ImUtf8.PushId(100 + (int)bonusDrawData.Slot);
var spacing = ImGui.GetStyle().ItemInnerSpacing with { Y = ImGui.GetStyle().ItemSpacing.Y };
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing);
@ -127,7 +130,7 @@ public class EquipmentDrawer
offhand.DisplayApplication = false;
}
using var id = ImRaii.PushId("Weapons");
using var id = ImUtf8.PushId("Weapons"u8);
var spacing = ImGui.GetStyle().ItemInnerSpacing with { Y = ImGui.GetStyle().ItemSpacing.Y };
using var style = ImRaii.PushStyle(ImGuiStyleVar.ItemSpacing, spacing);
@ -174,7 +177,7 @@ public class EquipmentDrawer
change = true;
}
ImGuiUtil.HoverTooltip($"{_config.DeleteDesignModifier.ToString()} and Right-click to clear.");
ImUtf8.HoverTooltip($"{_config.DeleteDesignModifier.ToString()} and Right-click to clear.");
}
return change;
@ -196,14 +199,13 @@ public class EquipmentDrawer
}
else if (equipDrawData.IsState)
{
_advancedDyes.DrawButton(equipDrawData.Slot);
_advancedDyes.DrawButton(equipDrawData.Slot, equipDrawData.HasAdvancedDyes ? _advancedMaterialColor : 0u);
}
if (VerifyRestrictedGear(equipDrawData))
label += " (Restricted)";
ImGui.SameLine();
ImGui.TextUnformatted(label);
DrawEquipLabel(equipDrawData is { IsDesign: true, HasAdvancedDyes: true }, label);
}
private void DrawBonusItemSmall(in BonusDrawData bonusDrawData)
@ -218,11 +220,10 @@ public class EquipmentDrawer
}
else if (bonusDrawData.IsState)
{
_advancedDyes.DrawButton(bonusDrawData.Slot);
_advancedDyes.DrawButton(bonusDrawData.Slot, bonusDrawData.HasAdvancedDyes ? _advancedMaterialColor : 0u);
}
ImGui.SameLine();
ImGui.TextUnformatted(label);
DrawEquipLabel(bonusDrawData is { IsDesign: true, HasAdvancedDyes: true }, label);
}
private void DrawWeaponsSmall(EquipDrawData mainhand, EquipDrawData offhand, bool allWeapons)
@ -239,12 +240,12 @@ public class EquipmentDrawer
}
else if (mainhand.IsState)
{
_advancedDyes.DrawButton(EquipSlot.MainHand);
_advancedDyes.DrawButton(EquipSlot.MainHand, mainhand.HasAdvancedDyes ? _advancedMaterialColor : 0u);
}
if (allWeapons)
mainhandLabel += $" ({mainhand.CurrentItem.Type.ToName()})";
WeaponHelpMarker(mainhandLabel);
WeaponHelpMarker(mainhand is { IsDesign: true, HasAdvancedDyes: true }, mainhandLabel);
if (offhand.CurrentItem.Type is FullEquipType.Unknown)
return;
@ -261,10 +262,10 @@ public class EquipmentDrawer
}
else if (offhand.IsState)
{
_advancedDyes.DrawButton(EquipSlot.OffHand);
_advancedDyes.DrawButton(EquipSlot.OffHand, offhand.HasAdvancedDyes ? _advancedMaterialColor : 0u);
}
WeaponHelpMarker(offhandLabel);
WeaponHelpMarker(offhand is { IsDesign: true, HasAdvancedDyes: true }, offhandLabel);
}
#endregion
@ -285,8 +286,8 @@ public class EquipmentDrawer
DrawApply(equipDrawData);
}
ImGui.SameLine();
ImGui.TextUnformatted(label);
DrawEquipLabel(equipDrawData is { IsDesign: true, HasAdvancedDyes: true }, label);
DrawStain(equipDrawData, false);
if (equipDrawData.DisplayApplication)
{
@ -295,13 +296,13 @@ public class EquipmentDrawer
}
else if (equipDrawData.IsState)
{
_advancedDyes.DrawButton(equipDrawData.Slot);
_advancedDyes.DrawButton(equipDrawData.Slot, equipDrawData.HasAdvancedDyes ? _advancedMaterialColor : 0u);
}
if (VerifyRestrictedGear(equipDrawData))
{
ImGui.SameLine();
ImGui.TextUnformatted("(Restricted)");
ImUtf8.Text("(Restricted)"u8);
}
}
@ -319,11 +320,10 @@ public class EquipmentDrawer
}
else if (bonusDrawData.IsState)
{
_advancedDyes.DrawButton(bonusDrawData.Slot);
_advancedDyes.DrawButton(bonusDrawData.Slot, bonusDrawData.HasAdvancedDyes ? _advancedMaterialColor : 0u);
}
ImGui.SameLine();
ImGui.TextUnformatted(label);
DrawEquipLabel(bonusDrawData is { IsDesign: true, HasAdvancedDyes: true }, label);
}
private void DrawWeaponsNormal(EquipDrawData mainhand, EquipDrawData offhand, bool allWeapons)
@ -334,7 +334,7 @@ public class EquipmentDrawer
mainhand.CurrentItem.DrawIcon(_textures, _iconSize, EquipSlot.MainHand);
var left = ImGui.IsItemClicked(ImGuiMouseButton.Left);
ImGui.SameLine();
using (ImRaii.Group())
using (ImUtf8.Group())
{
DrawMainhand(ref mainhand, ref offhand, out var mainhandLabel, allWeapons, false, left);
if (mainhand.DisplayApplication)
@ -343,7 +343,8 @@ public class EquipmentDrawer
DrawApply(mainhand);
}
WeaponHelpMarker(mainhandLabel, allWeapons ? mainhand.CurrentItem.Type.ToName() : null);
WeaponHelpMarker(mainhand is { IsDesign: true, HasAdvancedDyes: true }, mainhandLabel,
allWeapons ? mainhand.CurrentItem.Type.ToName() : null);
DrawStain(mainhand, false);
if (mainhand.DisplayApplication)
@ -353,7 +354,7 @@ public class EquipmentDrawer
}
else if (mainhand.IsState)
{
_advancedDyes.DrawButton(EquipSlot.MainHand);
_advancedDyes.DrawButton(EquipSlot.MainHand, mainhand.HasAdvancedDyes ? _advancedMaterialColor : 0u);
}
}
@ -364,7 +365,7 @@ public class EquipmentDrawer
var right = ImGui.IsItemClicked(ImGuiMouseButton.Right);
left = ImGui.IsItemClicked(ImGuiMouseButton.Left);
ImGui.SameLine();
using (ImRaii.Group())
using (ImUtf8.Group())
{
DrawOffhand(mainhand, offhand, out var offhandLabel, false, right, left);
if (offhand.DisplayApplication)
@ -373,7 +374,7 @@ public class EquipmentDrawer
DrawApply(offhand);
}
WeaponHelpMarker(offhandLabel);
WeaponHelpMarker(offhand is { IsDesign: true, HasAdvancedDyes: true }, offhandLabel);
DrawStain(offhand, false);
if (offhand.DisplayApplication)
@ -381,9 +382,9 @@ public class EquipmentDrawer
ImGui.SameLine();
DrawApplyStain(offhand);
}
else if (mainhand.IsState)
else if (offhand.IsState)
{
_advancedDyes.DrawButton(EquipSlot.OffHand);
_advancedDyes.DrawButton(EquipSlot.OffHand, offhand.HasAdvancedDyes ? _advancedMaterialColor : 0u);
}
}
}
@ -468,14 +469,16 @@ public class EquipmentDrawer
UiHelpers.OpenCombo($"##{combo.Label}");
using var disabled = ImRaii.Disabled(data.Locked);
var change = combo.Draw(data.CurrentItem.Name, data.CurrentItem.Id.BonusItem, small ? _comboLength - ImGui.GetFrameHeight() : _comboLength,
var change = combo.Draw(data.CurrentItem.Name, data.CurrentItem.Id.BonusItem,
small ? _comboLength - ImGui.GetFrameHeight() : _comboLength,
_requiredComboWidth);
if (change)
data.SetItem(combo.CurrentSelection);
else if (combo.CustomVariant.Id > 0)
data.SetItem(_items.Identify(data.Slot, combo.CustomSetId, combo.CustomVariant));
if (ResetOrClear(data.Locked, clear, data.AllowRevert, true, data.CurrentItem, data.GameItem, EquipItem.BonusItemNothing(data.Slot), out var item))
if (ResetOrClear(data.Locked, clear, data.AllowRevert, true, data.CurrentItem, data.GameItem, EquipItem.BonusItemNothing(data.Slot),
out var item))
data.SetItem(item);
}
@ -502,7 +505,7 @@ public class EquipmentDrawer
(false, true, _) => ("Right-click to clear.\nControl and mouse wheel to scroll.", clearItem, true),
(false, false, _) => ("Control and mouse wheel to scroll.", default, false),
};
ImGuiUtil.HoverTooltip(tt);
ImUtf8.HoverTooltip(tt);
return clicked && valid;
}
@ -544,8 +547,8 @@ public class EquipmentDrawer
}
}
if (unknown && ImGui.IsItemHovered(ImGuiHoveredFlags.AllowWhenDisabled))
ImGui.SetTooltip("The weapon type could not be identified, thus changing it to other weapons of that type is not possible.");
if (unknown)
ImUtf8.HoverTooltip(ImGuiHoveredFlags.AllowWhenDisabled, "The weapon type could not be identified, thus changing it to other weapons of that type is not possible."u8);
}
private void DrawOffhand(in EquipDrawData mainhand, in EquipDrawData offhand, out string label, bool small, bool clear, bool open)
@ -595,14 +598,14 @@ public class EquipmentDrawer
#endregion
private static void WeaponHelpMarker(string label, string? type = null)
private void WeaponHelpMarker(bool hasAdvancedDyes, string label, string? type = null)
{
ImGui.SameLine();
ImGuiComponents.HelpMarker(
"Changing weapons to weapons of different types can cause crashes, freezes, soft- and hard locks and cheating, "
+ "thus it is only allowed to change weapons to other weapons of the same type.");
ImGui.SameLine();
ImGui.TextUnformatted(label);
DrawEquipLabel(hasAdvancedDyes, label);
if (type == null)
return;
@ -610,4 +613,16 @@ public class EquipmentDrawer
pos.Y += ImGui.GetFrameHeightWithSpacing();
ImGui.GetWindowDrawList().AddText(pos, ImGui.GetColorU32(ImGuiCol.Text), $"({type})");
}
[MethodImpl(MethodImplOptions.AggressiveOptimization | MethodImplOptions.AggressiveInlining)]
private void DrawEquipLabel(bool hasAdvancedDyes, string label)
{
ImGui.SameLine();
using (ImRaii.PushColor(ImGuiCol.Text, _advancedMaterialColor, hasAdvancedDyes))
{
ImUtf8.Text(label);
}
if (hasAdvancedDyes)
ImUtf8.HoverTooltip("This design has advanced dyes setup for this slot."u8);
}
}

View file

@ -51,28 +51,29 @@ public sealed unsafe class AdvancedDyePopup(
return true;
}
public void DrawButton(EquipSlot slot)
=> DrawButton(MaterialValueIndex.FromSlot(slot));
public void DrawButton(EquipSlot slot, uint color)
=> DrawButton(MaterialValueIndex.FromSlot(slot), color);
public void DrawButton(BonusItemFlag slot)
=> DrawButton(MaterialValueIndex.FromSlot(slot));
public void DrawButton(BonusItemFlag slot, uint color)
=> DrawButton(MaterialValueIndex.FromSlot(slot), color);
private void DrawButton(MaterialValueIndex index)
private void DrawButton(MaterialValueIndex index, uint color)
{
if (!config.UseAdvancedDyes)
return;
ImGui.SameLine();
using var id = ImRaii.PushId(index.SlotIndex | ((int)index.DrawObject << 8));
using var id = ImUtf8.PushId(index.SlotIndex | ((int)index.DrawObject << 8));
var isOpen = index == _drawIndex;
using (ImRaii.PushColor(ImGuiCol.Button, ImGui.GetColorU32(ImGuiCol.ButtonActive), isOpen)
.Push(ImGuiCol.Text, ColorId.HeaderButtons.Value(), isOpen)
.Push(ImGuiCol.Border, ColorId.HeaderButtons.Value(), isOpen))
var (textColor, buttonColor) = isOpen
? (ColorId.HeaderButtons.Value(), ImGui.GetColorU32(ImGuiCol.ButtonActive))
: (color, 0u);
using (ImRaii.PushColor(ImGuiCol.Border, textColor, isOpen))
{
using var frame = ImRaii.PushStyle(ImGuiStyleVar.FrameBorderSize, 2 * ImGuiHelpers.GlobalScale, isOpen);
if (ImGuiUtil.DrawDisabledButton(FontAwesomeIcon.Palette.ToIconString(), new Vector2(ImGui.GetFrameHeight()),
string.Empty, false, true))
if (ImUtf8.IconButton(FontAwesomeIcon.Palette, ""u8, default, false, textColor, buttonColor))
{
_forceFocus = true;
_selectedMaterial = byte.MaxValue;
@ -80,7 +81,7 @@ public sealed unsafe class AdvancedDyePopup(
}
}
ImGuiUtil.HoverTooltip("Open advanced dyes for this slot.");
ImUtf8.HoverTooltip("Open advanced dyes for this slot."u8);
}
private (string Path, string GamePath) ResourceName(MaterialValueIndex index)
@ -197,7 +198,7 @@ public sealed unsafe class AdvancedDyePopup(
var width = 7 * ImGui.GetFrameHeight() // Buttons
+ 3 * ImGui.GetStyle().ItemSpacing.X // around text
+ 7 * ImGui.GetStyle().ItemInnerSpacing.X
+ 200 * ImGuiHelpers.GlobalScale // Drags
+ 200 * ImGuiHelpers.GlobalScale // Drags
+ 7 * UiBuilder.MonoFont.GetCharAdvance(' ') * ImGuiHelpers.GlobalScale // Row
+ 2 * ImGui.GetStyle().WindowPadding.X;
var height = 19 * ImGui.GetFrameHeightWithSpacing() + ImGui.GetStyle().WindowPadding.Y + 3 * ImGui.GetStyle().ItemSpacing.Y;
@ -305,8 +306,9 @@ public sealed unsafe class AdvancedDyePopup(
{
EquipSlot.MainHand => _state.ModelData.Weapon(EquipSlot.MainHand),
EquipSlot.OffHand => _state.ModelData.Weapon(EquipSlot.OffHand),
EquipSlot.Unknown => _state.ModelData.BonusItem((index.SlotIndex - 16u).ToBonusSlot()).Armor().ToWeapon(0), // TODO: Handle better
_ => _state.ModelData.Armor(slot).ToWeapon(0),
EquipSlot.Unknown =>
_state.ModelData.BonusItem((index.SlotIndex - 16u).ToBonusSlot()).Armor().ToWeapon(0), // TODO: Handle better
_ => _state.ModelData.Armor(slot).ToWeapon(0),
};
value = new MaterialValueState(internalRow, internalRow, weapon, StateSource.Manual);
}
@ -392,7 +394,8 @@ public sealed unsafe class AdvancedDyePopup(
{
var tmp = value;
var minValue = ImGui.GetIO().KeyCtrl ? 0f : (float)Half.Epsilon;
if (!ImUtf8.DragScalar("##Gloss"u8, ref tmp, "%.1f G"u8, 0.001f, minValue, Math.Max(0.01f, 0.005f * value), ImGuiSliderFlags.AlwaysClamp))
if (!ImUtf8.DragScalar("##Gloss"u8, ref tmp, "%.1f G"u8, 0.001f, minValue, Math.Max(0.01f, 0.005f * value),
ImGuiSliderFlags.AlwaysClamp))
return false;
var tmp2 = Math.Clamp(tmp, minValue, (float)Half.MaxValue);

View file

@ -6,6 +6,8 @@ using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Penumbra.GameData.Files.MaterialStructs;
using Penumbra.GameData.Structs;
using FFXIVClientStructs.FFXIV.Client.Graphics.Scene;
using static Penumbra.GameData.Files.ShpkFile;
namespace Glamourer.Interop.Material;
@ -280,6 +282,24 @@ public readonly struct MaterialValueManager<T>
return true;
}
public bool CheckExistence(MaterialValueIndex index)
{
if (_values.Count == 0)
return false;
var key = index.Key;
var idx = Search(key);
if (idx >= 0)
return true;
idx = ~idx;
if (idx >= _values.Count)
return false;
var nextItem = MaterialValueIndex.FromKey(_values[idx].Key);
return nextItem.DrawObject == index.DrawObject && nextItem.SlotIndex == index.SlotIndex;
}
public bool RemoveValue(MaterialValueIndex index)
=> RemoveValue(index.Key);