-
-
Notifications
You must be signed in to change notification settings - Fork 35
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ShortcutPanel: Add support for all /macroicon formats
- Loading branch information
1 parent
efb3289
commit 9e76e83
Showing
1 changed file
with
114 additions
and
26 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |