Skip to content

Commit

Permalink
ShortcutPanel: Add support for all /macroicon formats
Browse files Browse the repository at this point in the history
  • Loading branch information
haroldiedema committed Aug 30, 2024
1 parent efb3289 commit 9e76e83
Showing 1 changed file with 114 additions and 26 deletions.
140 changes: 114 additions & 26 deletions Umbra.Game/src/Macro/MacroIconProvider.cs
Original file line number Diff line number Diff line change
@@ -1,60 +1,148 @@
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
using Dalamud.Plugin.Services;
using Dalamud.Utility.Signatures;
using FFXIVClientStructs.FFXIV.Client.UI;
using FFXIVClientStructs.FFXIV.Client.UI.Misc;
using FFXIVClientStructs.FFXIV.Client.UI.Shell;
using System.Runtime.InteropServices;
using Lumina.Excel.GeneratedSheets;
using Umbra.Common;

namespace Umbra.Game.Macro;

#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value

[Service]
internal sealed class MacroIconProvider : IMacroIconProvider
internal sealed unsafe class MacroIconProvider : IMacroIconProvider
{
[StructLayout(LayoutKind.Explicit, Size = 0x120)]
private struct MacroIconTextCommand {
[FieldOffset(0x00)] public ushort TextCommandId;
[FieldOffset(0x08)] public int Id;
[FieldOffset(0x0C)] public int Category;
// Resolves the proper icon for a macro when using the "/macroicon" or
// "/micon" command inside the macro text. Courtesy of Haselnussbomber.
[Signature("E8 ?? ?? ?? ?? 0F B6 BE ?? ?? ?? ?? 8B 9E")]
private ResolveMacroIconDelegate? _resolveMacroIcon;

private delegate byte ResolveMacroIconDelegate(
RaptureMacroModule* thisPtr,
UIModule* uiModule,
RaptureHotbarModule.HotbarSlotType* outType,
uint* outRowId,
int setId,
uint macroId,
uint* outItemId
);

private IDataManager DataManager { get; }

public MacroIconProvider(IDataManager dataManager, IGameInteropProvider interopProvider)
{
DataManager = dataManager;
interopProvider.InitializeFromAttributes(this);
}

/// <inheritdoc/>
public unsafe uint GetIconIdForMacro(byte set, uint macroIndex)
public uint GetIconIdForMacro(byte set, uint macroIndex)
{
RaptureShellModule* rsm = RaptureShellModule.Instance();
RaptureMacroModule* rmm = RaptureMacroModule.Instance();

if (rsm == null || rmm == null || (macroIndex > 99)) return 0;

RaptureMacroModule.Macro* macro = rmm->GetMacro(set, macroIndex);
if (!macro->IsNotEmpty()) return macro->IconId;

MacroIconTextCommand* result = stackalloc MacroIconTextCommand[1];

return rsm->TryGetMacroIconCommand(macro, result)
? GetIconIdFromCategory(result, macro->IconId)
return macro->IsNotEmpty()
? GetMacroIconId(set, macroIndex, macro->IconId)
: macro->IconId;
}

private static unsafe uint GetIconIdFromCategory(MacroIconTextCommand* cmd, uint originalId)
private uint GetMacroIconId(byte set, uint macroIndex, uint originalId)
{
return cmd->Category switch {
// Support for SimpleTweaks' Extended Macro Icon tweak.
270 or 271 when cmd->TextCommandId == 207 => (uint)cmd->Id,
if (_resolveMacroIcon == null) return originalId;

RaptureMacroModule* rmm = RaptureMacroModule.Instance();
UIModule* ui = UIModule.Instance();

RaptureHotbarModule.HotbarSlotType type;

uint rowId;
uint itemId; // can be collectible etc.

// Gearset.
352 => GetIconIdForGearset(cmd->Id),
if (_resolveMacroIcon?.Invoke(rmm, ui, &type, &rowId, set, macroIndex, &itemId) != 1) {
return originalId;
}

// "Action" (350) category is unsupported.
_ => originalId
if ((byte)type == 0xFF) {
// Support SimpleTweak's Extended Macro Icon tweak.
return rowId;
}

if ((byte)type == 19) {
// Support for the "classjob" type that isn't in the enum.
return rowId + 62800;
}

return type switch {
RaptureHotbarModule.HotbarSlotType.Empty => originalId,
RaptureHotbarModule.HotbarSlotType.Action => GetIconIdForAction(rowId, originalId),
RaptureHotbarModule.HotbarSlotType.Item => GetIconIdForItem(rowId, originalId),
RaptureHotbarModule.HotbarSlotType.Emote => GetIconIdForEmote(rowId, originalId),
RaptureHotbarModule.HotbarSlotType.Marker => GetIconIdForMarker(rowId, originalId),
RaptureHotbarModule.HotbarSlotType.BuddyAction => GetIconIdForBuddyAction(rowId, originalId),
RaptureHotbarModule.HotbarSlotType.Companion => GetIconIdForCompanion(rowId, originalId),
RaptureHotbarModule.HotbarSlotType.GearSet => GetIconIdForGearset((int)rowId + 1),
RaptureHotbarModule.HotbarSlotType.PetAction => GetIconIdForPetAction(rowId, originalId),
RaptureHotbarModule.HotbarSlotType.Mount => GetIconIdForMount(rowId, originalId),
RaptureHotbarModule.HotbarSlotType.FieldMarker => GetIconIdForFieldMarker(rowId, originalId),
_ => originalId,
};
}

/// <summary>
/// Returns an icon ID of a gearset.
/// </summary>
private static unsafe uint GetIconIdForGearset(int id)
private static uint GetIconIdForGearset(int id)
{
RaptureGearsetModule* rgm = RaptureGearsetModule.Instance();
if (rgm == null) return 0;

return (uint)(rgm->GetGearset(id - 1)->ClassJob + 62800);
}

private uint GetIconIdForAction(uint rowId, uint fallbackId)
{
return DataManager.GetExcelSheet<Action>()!.GetRow(rowId)?.Icon ?? fallbackId;
}

private uint GetIconIdForItem(uint rowId, uint fallbackId)
{
return DataManager.GetExcelSheet<Item>()!.GetRow(rowId)?.Icon ?? fallbackId;
}

private uint GetIconIdForEmote(uint rowId, uint fallbackId)
{
return DataManager.GetExcelSheet<Emote>()!.GetRow(rowId)?.Icon ?? fallbackId;
}

private uint GetIconIdForMarker(uint rowId, uint fallbackId)
{
return ((uint?)DataManager.GetExcelSheet<Marker>()!.GetRow(rowId)?.Icon) ?? fallbackId;
}

private uint GetIconIdForBuddyAction(uint rowId, uint fallbackId)
{
return ((uint?)DataManager.GetExcelSheet<BuddyAction>()!.GetRow(rowId)?.Icon) ?? fallbackId;
}

private uint GetIconIdForCompanion(uint rowId, uint fallbackId)
{
return ((uint?)DataManager.GetExcelSheet<Companion>()!.GetRow(rowId)?.Icon) ?? fallbackId;
}

private uint GetIconIdForPetAction(uint rowId, uint fallbackId)
{
return ((uint?)DataManager.GetExcelSheet<PetAction>()!.GetRow(rowId)?.Icon) ?? fallbackId;
}

private uint GetIconIdForMount(uint rowId, uint fallbackId)
{
return ((uint?)DataManager.GetExcelSheet<Mount>()!.GetRow(rowId)?.Icon) ?? fallbackId;
}

private uint GetIconIdForFieldMarker(uint rowId, uint fallbackId)
{
return ((uint?)DataManager.GetExcelSheet<FieldMarker>()!.GetRow(rowId)?.MapIcon) ?? fallbackId;
}
}

0 comments on commit 9e76e83

Please sign in to comment.