refactor and add SetLocalRaidTargetIndex + GetRaidTargets

This commit is contained in:
avitasia
2026-04-07 11:27:39 -07:00
parent 5560036cdc
commit a86e257de5
16 changed files with 13153 additions and 13086 deletions
+13
View File
@@ -176,6 +176,18 @@ Examples:
/run print(GetUnitGUID("0xF5300000000000A5target"))
```
### Local Raid Marker Helpers
Nampower adds Lua helpers for reading and updating the client's local raid-marker table without requiring a server-side raid marker update.
- `GetRaidTargets()` - Returns a table indexed `1..8` containing GUID strings for the current local raid markers. Empty slots return `nil`.
- `SetLocalRaidTargetIndex(unitToken, raidTargetIndex)` - Sets a local raid marker slot using any supported unit token / GUID string accepted by `GetUnitGUID`. `raidTargetIndex` must be in the `0..8` range, matching the current client-side helper implementation.
Examples:
```
/run local marks = GetRaidTargets(); print(marks[1])
/run SetLocalRaidTargetIndex("target", 0)
/run SetLocalRaidTargetIndex("mark1target", 7)
```
### CSimpleFrame:GetName() Enhancement
The built-in `CSimpleFrame:GetName()` method is hooked to support an optional argument for retrieving the GUID of the unit associated with a nameplate frame.
@@ -202,6 +214,7 @@ This includes functions for:
- Spell duration lookup (GetSpellDuration) - returns channel duration for channeling spells and the first aura effect duration for non-channeling spells
- Spell range lookup (GetSpellRangeData) - returns minRange, maxRange, flags, name for a SpellRange DBC entry by rangeIndex
- Player movement state queries (PlayerIsMoving, PlayerIsRooted, PlayerIsSwimming)
- Local raid marker helpers (GetRaidTargets, SetLocalRaidTargetIndex)
- File and script helpers (WriteCustomFile, ReadCustomFile, CustomFileExists, ImportFile, ExportFile, ExecuteCustomLuaFile)
- Encrypted login helpers (EncryptPassword, EncryptedServerLogin)
- Talent helpers (LearnTalentRank)
+12026 -12017
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -51,6 +51,8 @@ set(SOURCE_FILES
autoattack.cpp
player_scripts.hpp
player_scripts.cpp
unit_scripts.hpp
unit_scripts.cpp
pet.hpp
pet.cpp
unit.hpp
+22
View File
@@ -54,6 +54,28 @@ namespace Nampower {
static char textureKey[] = "texture";
static char itemLevelKey[] = "itemLevel";
uint32_t Script_GetItemIconTexture(uintptr_t *luaState) {
luaState = GetLuaStatePtr(); // pcall leads to corrupted lua state pointer on added scripts, not sure why
if (!lua_isnumber(luaState, 1)) {
lua_error(luaState, "Usage: GetItemIconTexture(displayInfoId)");
return 0;
}
uint32_t displayInfoId = static_cast<uint32_t>(lua_tonumber(luaState, 1));
auto const getInventoryArt = reinterpret_cast<GetInventoryArtT>(Offsets::CGItem_C_GetInventoryArt);
char *texturePath = getInventoryArt(displayInfoId);
if (texturePath && texturePath[0] != '\0' && !strstr(texturePath, "QuestionMark")) {
lua_pushstring(luaState, texturePath);
} else {
lua_pushnil(luaState);
}
return 1;
}
int32_t NormalizeSpellChargeValue(int32_t chargeValue) {
return chargeValue < 0 ? -chargeValue : chargeValue;
}
+1
View File
@@ -7,6 +7,7 @@
#include "main.hpp"
namespace Nampower {
uint32_t Script_GetItemIconTexture(uintptr_t *luaState);
uint32_t Script_GetItemLevel(uintptr_t *luaState);
uint32_t Script_GetItemStats(uintptr_t *luaState);
uint32_t Script_GetItemStatsField(uintptr_t *luaState);
+7
View File
@@ -38,6 +38,7 @@
#include "file_scripts.hpp"
#include "spell_scripts.hpp"
#include "player_scripts.hpp"
#include "unit_scripts.hpp"
#include "spellchannel.hpp"
#include "helper.hpp"
#include "items.hpp"
@@ -1920,6 +1921,9 @@ namespace Nampower {
char getSpellRecField[] = "GetSpellRecField";
RegisterLuaFunction(getSpellRecField, reinterpret_cast<uintptr_t *>(Script_GetSpellRecField));
char getRaidTargets[] = "GetRaidTargets";
RegisterLuaFunction(getRaidTargets, reinterpret_cast<uintptr_t *>(Script_GetRaidTargets));
char getUnitData[] = "GetUnitData";
RegisterLuaFunction(getUnitData, reinterpret_cast<uintptr_t *>(Script_GetUnitData));
@@ -2013,6 +2017,9 @@ namespace Nampower {
char setMouseoverUnit[] = "SetMouseoverUnit";
RegisterLuaFunction(setMouseoverUnit, reinterpret_cast<uintptr_t *>(Script_SetMouseoverUnit));
char setLocalRaidTargetIndex[] = "SetLocalRaidTargetIndex";
RegisterLuaFunction(setLocalRaidTargetIndex, reinterpret_cast<uintptr_t *>(Script_SetLocalRaidTargetIndex));
char getUnitGUID[] = "GetUnitGUID";
RegisterLuaFunction(getUnitGUID, reinterpret_cast<uintptr_t *>(Script_GetUnitGUID));
+2 -2
View File
@@ -26,8 +26,8 @@ namespace Nampower {
constexpr uint32_t DISENCHANT_QUALITY_PURPLE = 0x04; // Epic
constexpr uint32_t MAJOR_VERSION = 4;
constexpr uint32_t MINOR_VERSION = 3;
constexpr uint32_t PATCH_VERSION = 1;
constexpr uint32_t MINOR_VERSION = 4;
constexpr uint32_t PATCH_VERSION = 0;
constexpr int32_t LUA_REGISTRYINDEX = -10000;
constexpr int32_t LUA_GLOBALSINDEX = -10001;
File diff suppressed because it is too large Load Diff
-28
View File
@@ -8,42 +8,14 @@
#include "main.hpp"
namespace Nampower {
uint32_t Script_GetCurrentCastingInfo(uintptr_t *luaState);
uint32_t Script_GetCastInfo(uintptr_t *luaState);
uint32_t Script_ChannelStopCastingNextTick(uintptr_t *luaState);
uint32_t Script_GetNampowerVersion(uintptr_t *luaState);
uint32_t Script_QueueScript(uintptr_t *luaState);
uint32_t Script_GetUnitData(uintptr_t *luaState);
uint32_t Script_GetUnitField(uintptr_t *luaState);
bool TryDisenchant();
uint32_t Script_DisenchantAll(uintptr_t *luaState);
uint32_t Script_GetItemIconTexture(uintptr_t *luaState);
uint32_t Script_GetSpellIconTexture(uintptr_t *luaState);
uint32_t Script_LearnTalentRank(uintptr_t *luaState);
bool RunQueuedScript(int priority);
uint32_t Script_GetPlayerAuraDuration(uintptr_t *luaState);
uint32_t Script_CancelPlayerAuraSlot(uintptr_t *luaState);
uint32_t Script_CancelPlayerAuraSpellId(uintptr_t *luaState);
uint32_t Script_SetMouseoverUnit(uintptr_t *luaState);
uint32_t Script_GetUnitGUID(uintptr_t *luaState);
uint32_t Script_IsAuraHidden(uintptr_t *luaState);
uint32_t Script_CombatLogFlush(uintptr_t *luaState);
uint32_t CSimpleFrame_GetNameHook(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
uint64_t GetGUIDFromNameHook(hadesmem::PatchDetourBase *detour, const char *nameStr);
bool CSimpleTop_OnKeyDownHook(hadesmem::PatchDetourBase *detour, EVENT_DATA_KEY *keyData, int param_2);
bool CSimpleTop_OnKeyUpHook(hadesmem::PatchDetourBase *detour, EVENT_DATA_KEY *keyData, int param_2);
}
+3 -1
View File
@@ -244,6 +244,8 @@ enum class Offsets : std::uint32_t {
CGBag_C_GetItemAtSlot = 0x006228a0,
GetContainerGuid = 0x004f93e0,
CanInspectUnit = 0x004944a0,
CGUnit_C_CanInteract = 0x00606ba0,
CGUnit_C_NamePlateUpdateRaidTarget = 0x00608a90,
CGUnit_C_OnAuraRemoved = 0x00612320,
CGUnit_C_OnAuraAdded = 0x006123f0,
CGUnit_C_OnAuraStacksChanged = 0x00612450,
@@ -283,7 +285,7 @@ enum class Offsets : std::uint32_t {
XMLNode_GetAttributeByName = 0x006f2cf0,
RaidTargetMarkGuidBase = 0xB71368,
CGRaidInfo_m_raidtargets = 0xB71368,
CGPetInfo_SetPet = 0x004bc7e0,
TogglePetSlotAutocast = 0x004bcbb0,
+379
View File
@@ -3,8 +3,11 @@
//
#include "player_scripts.hpp"
#include "auras.hpp"
#include "helper.hpp"
#include "items.hpp"
#include "offsets.hpp"
#include <string>
namespace Nampower {
static uint32_t GetPlayerMovementFlags() {
@@ -60,4 +63,380 @@ namespace Nampower {
}
return 1;
}
uint32_t Script_LearnTalentRank(uintptr_t *luaState) {
luaState = GetLuaStatePtr();
if (!lua_isnumber(luaState, 1) || !lua_isnumber(luaState, 2) || !lua_isnumber(luaState, 3)) {
lua_error(luaState, "Usage: LearnTalentRank(talentPage, talentIndex, rank)");
return 0;
}
constexpr uintptr_t CGTalentInfo_m_talentTabs = 0x00BDCD24;
constexpr uintptr_t CGTalentInfo_m_talents = 0x00BDCD28;
constexpr uint32_t CMSG_LEARN_TALENT = 0x0251;
auto talentPage = static_cast<int32_t>(lua_tonumber(luaState, 1));
auto talentIndex = static_cast<int32_t>(lua_tonumber(luaState, 2));
auto requestedRank = static_cast<int32_t>(lua_tonumber(luaState, 3));
if (talentPage < 1 || talentPage > 3) {
lua_error(luaState, "LearnTalentRank: talentPage must be in range 1-3");
return 0;
}
if (talentIndex < 1 || talentIndex > 32) {
lua_error(luaState, "LearnTalentRank: talentIndex must be in range 1-32");
return 0;
}
if (requestedRank < 1 || requestedRank > 5) {
lua_error(luaState, "LearnTalentRank: rank must be in range 1-5");
return 0;
}
auto const playerGuid = game::ClntObjMgrGetActivePlayerGuid();
auto *playerUnit = game::ClntObjMgrObjectPtr(game::TYPEMASK_PLAYER, playerGuid);
if (!playerUnit) {
lua_error(luaState, "LearnTalentRank: active player not available");
return 0;
}
auto talentPage0Index = talentPage - 1;
auto talentIndex0 = talentIndex - 1;
int32_t talentTabCount = 0;
if (!SafeRead(reinterpret_cast<void *>(CGTalentInfo_m_talentTabs), talentTabCount)) {
lua_error(luaState, "LearnTalentRank: unable to read talent tab count");
return 0;
}
if (talentPage0Index < 0 || talentPage0Index >= talentTabCount || talentIndex0 < 0) {
lua_error(luaState, "LearnTalentRank: talent page/index out of bounds");
return 0;
}
uintptr_t talentsBase = 0;
if (!SafeRead(reinterpret_cast<void *>(CGTalentInfo_m_talents), talentsBase)) {
lua_error(luaState, "LearnTalentRank: unable to read talents base pointer");
return 0;
}
if (talentsBase == 0) {
lua_error(luaState, "LearnTalentRank: talents base pointer is null");
return 0;
}
auto talentTabSlotAddr = talentsBase + static_cast<uintptr_t>(talentPage0Index) * sizeof(uint32_t);
uintptr_t talentTabPtr = 0;
if (!SafeRead(reinterpret_cast<void *>(talentTabSlotAddr), talentTabPtr)) {
lua_error(luaState, "LearnTalentRank: unable to read talent tab pointer");
return 0;
}
if (talentTabPtr == 0) {
lua_error(luaState, "LearnTalentRank: talent tab pointer is null");
return 0;
}
uint32_t talentCountInTab = 0;
uintptr_t talentListBase = 0;
if (!SafeRead(reinterpret_cast<void *>(talentTabPtr + 0x8), talentCountInTab) ||
!SafeRead(reinterpret_cast<void *>(talentTabPtr + 0xC), talentListBase)) {
lua_error(luaState, "LearnTalentRank: unable to read talent tab metadata");
return 0;
}
if (talentCountInTab == 0 || talentCountInTab >= 512 || talentListBase == 0) {
lua_error(luaState, "LearnTalentRank: invalid talent tab metadata");
return 0;
}
if (talentIndex0 >= static_cast<int32_t>(talentCountInTab)) {
lua_error(luaState, "LearnTalentRank: talent index not present in selected tab");
return 0;
}
auto talentEntryPtr = talentListBase + static_cast<uintptr_t>(talentIndex0) * 0x54;
int32_t talentId = 0;
if (!SafeRead(reinterpret_cast<void *>(talentEntryPtr), talentId)) {
lua_error(luaState, "LearnTalentRank: unable to read talent entry");
return 0;
}
if (talentId <= 0 || talentId >= 200000) {
lua_error(luaState, "LearnTalentRank: resolved invalid talentId");
return 0;
}
auto requestedRank0Index = requestedRank - 1;
auto dataStore = CDataStore();
dataStore.Put<uint32_t>(CMSG_LEARN_TALENT);
dataStore.Put<uint32_t>(static_cast<uint32_t>(talentId));
dataStore.Put<uint32_t>(static_cast<uint32_t>(requestedRank0Index));
dataStore.Finalize();
auto const clientServicesSend = reinterpret_cast<ClientServices_SendT>(Offsets::ClientServices_Send);
clientServicesSend(&dataStore);
lua_pushnumber(luaState, 1);
return 1;
}
uint32_t Script_DisenchantAll(uintptr_t *luaState) {
luaState = GetLuaStatePtr();
if (!lua_isnumber(luaState, 1) && !lua_isstring(luaState, 1)) {
lua_error(luaState, "Usage: DisenchantAll(itemIdOrName, [includeSoulbound]) or DisenchantAll(quality, [includeSoulbound])\n"
"quality: \"greens\", \"blues\", \"purples\", or combinations like \"greens|blues\" or \"greens|blues|purples\"\n"
"itemIdOrName: item ID (number) or item name (string)\n"
"includeSoulbound: optional number - pass 1 to include soulbound items (defaults to 0)");
return 0;
}
constexpr uint32_t DISENCHANT_SPELL_ID = 13262;
bool hasDisenchant = false;
for (uint32_t slot = 0; slot < 1024; slot++) {
uint32_t spellId = *reinterpret_cast<uint32_t *>(static_cast<uint32_t>(Offsets::CGSpellBook_mKnownSpells) + slot * 4);
if (spellId == DISENCHANT_SPELL_ID) {
hasDisenchant = true;
break;
}
}
if (!hasDisenchant) {
lua_error(luaState, "You do not have the Disenchant spell");
return 0;
}
ResetDisenchantState();
if (lua_isnumber(luaState, 1)) {
gDisenchantItemId = static_cast<uint32_t>(lua_tonumber(luaState, 1));
} else if (lua_isstring(luaState, 1)) {
const char *param = lua_tostring(luaState, 1);
bool isQualityString = false;
uint32_t qualityBitmask = 0;
std::string paramStr(param);
size_t start = 0;
size_t end = 0;
while (end != std::string::npos) {
end = paramStr.find('|', start);
std::string token = paramStr.substr(start, (end == std::string::npos) ? std::string::npos : end - start);
if (_stricmp(token.c_str(), "greens") == 0) {
qualityBitmask |= DISENCHANT_QUALITY_GREEN;
isQualityString = true;
} else if (_stricmp(token.c_str(), "blues") == 0) {
qualityBitmask |= DISENCHANT_QUALITY_BLUE;
isQualityString = true;
} else if (_stricmp(token.c_str(), "purples") == 0) {
qualityBitmask |= DISENCHANT_QUALITY_PURPLE;
isQualityString = true;
}
start = (end == std::string::npos) ? std::string::npos : end + 1;
}
if (isQualityString) {
gDisenchantQuality = qualityBitmask;
} else {
uint32_t cachedItemId = GetItemIdFromCache(param);
if (cachedItemId != 0) {
gDisenchantItemId = cachedItemId;
} else {
auto itemSearchResult = FindPlayerItem(0, param);
if (itemSearchResult.found()) {
gDisenchantItemId = game::GetItemId(itemSearchResult.item);
CacheItemNameToId(param, gDisenchantItemId);
} else {
lua_pushnumber(luaState, 0);
return 1;
}
}
}
}
if (lua_isnumber(luaState, 2)) {
gDisenchantIncludeSoulbound = (lua_tonumber(luaState, 2) != 0);
}
bool success = TryDisenchant();
lua_pushnumber(luaState, success ? 1 : 0);
return 1;
}
bool TryDisenchant() {
DEBUG_LOG("Trying disenchant with quality filter " << gDisenchantQuality << " itemId " << gDisenchantItemId);
if (gDisenchantQuality == 0 && gDisenchantItemId == 0) {
ResetDisenchantState();
return false;
}
PlayerItemSearchResult itemSearchResult;
if (gDisenchantItemId != 0) {
itemSearchResult = FindPlayerItem(gDisenchantItemId, nullptr);
} else {
itemSearchResult = FindPlayerDisenchantItem(gDisenchantQuality, gDisenchantIncludeSoulbound);
}
if (!itemSearchResult.found()) {
DEBUG_LOG("No disenchantable item found, stopping disenchant");
LuaCall("DEFAULT_CHAT_FRAME:AddMessage(\"No more items to disenchant.\")");
ResetDisenchantState();
return false;
}
auto item = itemSearchResult.item;
if (!item || !item->object.m_obj) {
DEBUG_LOG("Invalid item, stopping disenchant");
ResetDisenchantState();
return false;
}
uint64_t itemGuid = item->object.m_obj->m_guid;
auto playerGuid = game::ClntObjMgrGetActivePlayerGuid();
auto playerUnit = game::GetObjectPtr(playerGuid);
if (!playerUnit) {
DEBUG_LOG("No player unit, stopping disenchant");
ResetDisenchantState();
return false;
}
char luaCode[256];
snprintf(luaCode, sizeof(luaCode),
"local itemLink = GetContainerItemLink(%d, %d); "
"if itemLink then DEFAULT_CHAT_FRAME:AddMessage(\"Disenchanting \" .. itemLink .. \" move during cast to cancel.\") end",
itemSearchResult.bagIndex, itemSearchResult.slot + 1);
LuaCall(luaCode);
auto currentTime = GetTime();
gNextDisenchantTimeMs = currentTime + 5000;
auto const castSpell = reinterpret_cast<Spell_C_CastSpellT>(Offsets::Spell_C_CastSpell);
bool success = castSpell(playerUnit, 13262, nullptr, itemGuid);
if (!success) {
DEBUG_LOG("Cast failed, stopping disenchant");
LuaCall("DEFAULT_CHAT_FRAME:AddMessage(\"Disenchant interrupted or failed.\")");
ResetDisenchantState();
return false;
}
return true;
}
uint32_t Script_GetPlayerAuraDuration(uintptr_t *luaState) {
luaState = GetLuaStatePtr();
if (!lua_isnumber(luaState, 1)) {
lua_error(luaState, "Usage: GetPlayerAuraDuration(auraSlot)");
return 0;
}
auto auraSlot = static_cast<uint32_t>(lua_tonumber(luaState, 1));
if (auraSlot >= MAX_AURA_SLOTS) {
lua_pushnil(luaState);
return 1;
}
auto *unitFields = game::GetActivePlayerUnitFields();
if (!unitFields) {
lua_pushnil(luaState);
return 1;
}
uint32_t spellId = unitFields->aura[auraSlot];
uint32_t expirationTime = gAuraExpirationTime[auraSlot];
auto now = static_cast<uint32_t>(GetWowTimeMs() & 0xFFFFFFFF);
int remainingDurationMs = expirationTime > now ? static_cast<int>(expirationTime - now) : 0;
lua_pushnumber(luaState, spellId);
lua_pushnumber(luaState, remainingDurationMs);
lua_pushnumber(luaState, expirationTime);
return 3;
}
uint32_t Script_CancelPlayerAuraSlot(uintptr_t *luaState) {
constexpr uint32_t kMaxCancelAuraSlots = 32;
luaState = GetLuaStatePtr();
if (!lua_isnumber(luaState, 1)) {
lua_error(luaState, "Usage: CancelPlayerAuraSlot(auraSlot)");
return 0;
}
auto auraSlot = static_cast<uint32_t>(lua_tonumber(luaState, 1));
if (auraSlot >= kMaxCancelAuraSlots) {
lua_pushnumber(luaState, 0);
return 1;
}
auto *unitFields = game::GetActivePlayerUnitFields();
if (!unitFields) {
lua_pushnumber(luaState, 0);
return 1;
}
auto spellId = static_cast<int>(unitFields->aura[auraSlot]);
if (spellId == 0) {
lua_pushnumber(luaState, 0);
return 1;
}
auto const cancelAura = reinterpret_cast<void(__fastcall *)(int)>(Offsets::Spell_C_CancelAura);
cancelAura(spellId);
lua_pushnumber(luaState, 1);
return 1;
}
uint32_t Script_CancelPlayerAuraSpellId(uintptr_t *luaState) {
constexpr uint32_t kMaxCancelAuraSlots = 32;
luaState = GetLuaStatePtr();
if (!lua_isnumber(luaState, 1)) {
lua_error(luaState, "Usage: CancelPlayerAuraSpellId(spellId, [ignoreMissing])");
return 0;
}
auto spellId = static_cast<uint32_t>(lua_tonumber(luaState, 1));
if (spellId == 0) {
lua_pushnumber(luaState, 0);
return 1;
}
bool ignoreMissing = false;
if (lua_isnumber(luaState, 2)) {
ignoreMissing = (static_cast<int>(lua_tonumber(luaState, 2)) == 1);
}
if (!ignoreMissing) {
auto *unitFields = game::GetActivePlayerUnitFields();
if (!unitFields) {
lua_pushnumber(luaState, 0);
return 1;
}
bool foundSpell = false;
for (uint32_t auraSlot = 0; auraSlot < kMaxCancelAuraSlots; ++auraSlot) {
if (unitFields->aura[auraSlot] == spellId) {
foundSpell = true;
break;
}
}
if (!foundSpell) {
lua_pushnumber(luaState, 0);
return 1;
}
}
auto const cancelAura = reinterpret_cast<void(__fastcall *)(uint32_t)>(Offsets::Spell_C_CancelAura);
cancelAura(spellId);
lua_pushnumber(luaState, 1);
return 1;
}
}
+6
View File
@@ -11,4 +11,10 @@ namespace Nampower {
uint32_t Script_PlayerIsMoving(uintptr_t *luaState);
uint32_t Script_PlayerIsRooted(uintptr_t *luaState);
uint32_t Script_PlayerIsSwimming(uintptr_t *luaState);
bool TryDisenchant();
uint32_t Script_DisenchantAll(uintptr_t *luaState);
uint32_t Script_GetPlayerAuraDuration(uintptr_t *luaState);
uint32_t Script_CancelPlayerAuraSlot(uintptr_t *luaState);
uint32_t Script_CancelPlayerAuraSpellId(uintptr_t *luaState);
uint32_t Script_LearnTalentRank(uintptr_t *luaState);
}
+162
View File
@@ -10,7 +10,21 @@
#include <cstring>
namespace Nampower {
namespace LuaFields {
static char castId[] = "castId";
static char spellId[] = "spellId";
static char guid[] = "guid";
static char castType[] = "castType";
static char castStartS[] = "castStartS";
static char castEndS[] = "castEndS";
static char castRemainingMs[] = "castRemainingMs";
static char castDurationMs[] = "castDurationMs";
static char gcdEndS[] = "gcdEndS";
static char gcdRemainingMs[] = "gcdRemainingMs";
}
// Reusable table references to reduce memory allocations
static int castInfoTableRef = LUA_REFNIL;
static int spellRecTableRef = LUA_REFNIL;
// Map to store separate references for each array field name (for Field function)
@@ -19,6 +33,154 @@ namespace Nampower {
// Map to store separate references for nested array fields (for main table function)
static std::unordered_map<std::string, int> spellRecNestedArrayRefs;
uint32_t Script_GetCurrentCastingInfo(uintptr_t *luaState) {
luaState = GetLuaStatePtr(); // pcall leads to corrupted lua state pointer on added scripts, not sure why
auto const castingSpellId = reinterpret_cast<uint32_t *>(Offsets::CastingSpellId);
lua_pushnumber(luaState, *castingSpellId);
auto const isCasting = gCastData.castEndMs > GetTime();
auto const isChanneling = gCastData.channeling;
auto const visualSpellId = reinterpret_cast<uint32_t *>(Offsets::VisualSpellId);
lua_pushnumber(luaState, *visualSpellId);
auto const autoRepeatingSpellId = reinterpret_cast<uint32_t *>(Offsets::AutoRepeatingSpellId);
lua_pushnumber(luaState, *autoRepeatingSpellId);
auto playerUnit = game::GetObjectPtr(game::ClntObjMgrGetActivePlayerGuid());
if (isCasting) {
lua_pushnumber(luaState, 1);
} else {
lua_pushnumber(luaState, 0);
}
if (isChanneling) {
lua_pushnumber(luaState, 1);
} else {
lua_pushnumber(luaState, 0);
}
if (gCastData.pendingOnSwingCast) {
lua_pushnumber(luaState, 1);
} else {
lua_pushnumber(luaState, 0);
}
auto const attackPtr = playerUnit + 0x312; // auto attacking
if (attackPtr && *reinterpret_cast<uint32_t *>(attackPtr) > 0) {
lua_pushnumber(luaState, 1);
} else {
lua_pushnumber(luaState, 0);
}
return 7;
}
uint32_t Script_GetCastInfo(uintptr_t *luaState) {
luaState = GetLuaStatePtr(); // pcall leads to corrupted lua state pointer on added scripts, not sure why
uint32_t activeSpellId = 0;
uint32_t castEndTime = 0;
CastSpellParams *castParams = nullptr;
if (gCastData.channeling && gCastData.channelSpellId != 0) {
activeSpellId = gCastData.channelSpellId;
castEndTime = gCastData.channelEndMs;
castParams = gCastHistory.findNewestSuccessfulSpellId(activeSpellId);
} else if (gCastData.castSpellId != 0) {
activeSpellId = gCastData.castSpellId;
castEndTime = (gCastData.castEndMs > gCastData.gcdEndMs) ? gCastData.castEndMs : gCastData.gcdEndMs;
castParams = gCastHistory.findNewestWaitingForServerSpellId(activeSpellId);
}
if (activeSpellId == 0) {
lua_pushnil(luaState);
return 1;
}
if (castParams == nullptr || castParams->castId == 0) {
lua_pushnil(luaState);
return 1;
}
GetTableRef(luaState, castInfoTableRef);
uint32_t currentTime = GetTime();
uint64_t currentWowTime = GetWowTimeMs();
int64_t timeOffset = static_cast<int64_t>(currentWowTime) - static_cast<int64_t>(currentTime);
double castStartTimeWow = (castParams->castStartTimeMs + timeOffset) / 1000.0;
double castEndTimeWow = (castEndTime + timeOffset) / 1000.0;
double gcdEndTimeWow = (gCastData.gcdEndMs + timeOffset) / 1000.0;
PushTableValue(luaState, LuaFields::castId, castParams->castId);
PushTableValue(luaState, LuaFields::spellId, castParams->spellId);
PushTableValue(luaState, LuaFields::guid, ConvertGuidToString(castParams->guid));
PushTableValue(luaState, LuaFields::castType, static_cast<uint32_t>(castParams->castType));
PushTableValue(luaState, LuaFields::castStartS, castStartTimeWow);
PushTableValue(luaState, LuaFields::castEndS, castEndTimeWow);
uint32_t timeRemaining = (castEndTime > currentTime) ? (castEndTime - currentTime) : 0;
PushTableValue(luaState, LuaFields::castRemainingMs, timeRemaining);
uint32_t duration = (castEndTime > castParams->castStartTimeMs) ?
(castEndTime - castParams->castStartTimeMs) : 0;
PushTableValue(luaState, LuaFields::castDurationMs, duration);
PushTableValue(luaState, LuaFields::gcdEndS, gcdEndTimeWow);
uint32_t gcdRemaining = (gCastData.gcdEndMs > currentTime) ? (gCastData.gcdEndMs - currentTime) : 0;
PushTableValue(luaState, LuaFields::gcdRemainingMs, gcdRemaining);
return 1;
}
uint32_t Script_ChannelStopCastingNextTick(uintptr_t *luaState) {
luaState = GetLuaStatePtr(); // pcall leads to corrupted lua state pointer on added scripts, not sure why
if (gCastData.channeling) {
DEBUG_LOG("ChannelStopCastingNextTick activated, canceling next tick");
gCastData.cancelChannelNextTick = true;
}
return 0;
}
uint32_t Script_GetSpellIconTexture(uintptr_t *luaState) {
luaState = GetLuaStatePtr(); // pcall leads to corrupted lua state pointer on added scripts, not sure why
if (!lua_isnumber(luaState, 1)) {
lua_error(luaState, "Usage: GetSpellIconTexture(spellIconId)");
return 0;
}
int32_t iconId = static_cast<int32_t>(lua_tonumber(luaState, 1));
int32_t maxIconId = *reinterpret_cast<int32_t *>(Offsets::SpellIconDBMaxId);
uintptr_t *iconArray = *reinterpret_cast<uintptr_t **>(Offsets::SpellIconDBArray);
if (iconId < 0 || iconId > maxIconId) {
lua_pushnil(luaState);
return 1;
}
uintptr_t iconEntry = reinterpret_cast<uintptr_t *>(iconArray)[iconId];
if (iconEntry == 0) {
lua_pushnil(luaState);
return 1;
}
char *texturePath = *reinterpret_cast<char **>(iconEntry + 4);
if (texturePath && texturePath[0] != '\0' && !strstr(texturePath, "QuestionMark")) {
lua_pushstring(luaState, texturePath);
} else {
lua_pushnil(luaState);
}
return 1;
}
uint32_t Script_CastSpellByNameNoQueue(uintptr_t *luaState) {
luaState = GetLuaStatePtr(); // pcall leads to corrupted lua state pointer on added scripts, not sure why
+8
View File
@@ -8,6 +8,12 @@
#include "main.hpp"
namespace Nampower {
uint32_t Script_GetCurrentCastingInfo(uintptr_t *luaState);
uint32_t Script_GetCastInfo(uintptr_t *luaState);
uint32_t Script_ChannelStopCastingNextTick(uintptr_t *luaState);
uint32_t Script_CastSpellByNameNoQueue(uintptr_t *luaState);
uint32_t Script_CastSpellNoQueue(uintptr_t *luaState);
@@ -36,6 +42,8 @@ namespace Nampower {
uint32_t Script_GetSpellPower(uintptr_t *luaState);
uint32_t Script_GetSpellIconTexture(uintptr_t *luaState);
uint32_t Script_GetSpellDuration(uintptr_t *luaState);
uint32_t Script_GetSpellRangeData(uintptr_t *luaState);
+502
View File
@@ -0,0 +1,502 @@
//
// Created by pmacc on 4/7/2026.
//
#include "unit_scripts.hpp"
#include "helper.hpp"
#include "lua_refs.hpp"
#include "offsets.hpp"
#include "party_member_fields.hpp"
#include "unit_fields.hpp"
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <string>
#include <unordered_map>
namespace Nampower {
// Reusable table references to reduce memory allocations
static int unitDataTableRef = LUA_REFNIL;
// Maps to store separate references for each array field name (for Field functions)
static std::unordered_map<std::string, int> unitFieldsArrayFieldRefs;
// Maps to store separate references for nested array fields (for main table functions)
static std::unordered_map<std::string, int> unitFieldsNestedArrayRefs;
uint32_t Script_GetRaidTargets(uintptr_t *luaState) {
luaState = GetLuaStatePtr();
auto raidTargets =
reinterpret_cast<uint64_t *>(static_cast<uint32_t>(Offsets::CGRaidInfo_m_raidtargets));
lua_newtable(luaState);
for (uint32_t i = 0; i < 8; ++i) {
lua_pushnumber(luaState, i + 1);
PushGuidString(luaState, raidTargets[i]);
lua_settable(luaState, -3);
}
return 1;
}
uint32_t Script_GetUnitData(uintptr_t *luaState) {
luaState = GetLuaStatePtr(); // pcall leads to corrupted lua state pointer on added scripts, not sure why
if (!lua_isstring(luaState, 1)) {
lua_error(luaState, "Usage: GetUnitData(unitToken, [copy]) - unitToken can be 'player', 'target', 'pet', etc., or a GUID string");
return 0;
}
const char *unitToken = lua_tostring(luaState, 1);
bool useCopy = false;
if (lua_isnumber(luaState, 2)) {
useCopy = static_cast<int>(lua_tonumber(luaState, 2)) != 0;
}
uint64_t guid = GetUnitGuidFromString(unitToken);
if (guid == 0) {
lua_pushnil(luaState);
return 1;
}
auto unit = game::GetObjectPtr(guid);
if (!unit) {
lua_pushnil(luaState);
return 1;
}
auto *unitFields = *reinterpret_cast<game::UnitFields **>(unit + 68);
if (!unitFields) {
lua_pushnil(luaState);
return 1;
}
if (useCopy) {
lua_newtable(luaState);
} else {
GetTableRef(luaState, unitDataTableRef);
}
for (size_t i = 0; i < unitFieldsFieldsCount; ++i) {
const auto &field = unitFieldsFields[i];
lua_pushstring(luaState, const_cast<char *>(field.name));
const char *fieldPtr = reinterpret_cast<const char *>(unitFields) + field.offset;
switch (field.type) {
case FieldType::UINT32:
lua_pushnumber(luaState, *reinterpret_cast<const uint32_t *>(fieldPtr));
break;
case FieldType::UINT8:
lua_pushnumber(luaState, *reinterpret_cast<const uint8_t *>(fieldPtr));
break;
case FieldType::FLOAT:
lua_pushnumber(luaState, *reinterpret_cast<const float *>(fieldPtr));
break;
case FieldType::UINT64:
PushGuidString(luaState, *reinterpret_cast<const uint64_t *>(fieldPtr));
break;
default:
lua_pushnil(luaState);
break;
}
lua_settable(luaState, -3);
}
if (useCopy) {
PushArrayFieldsToLua(luaState, unitFields, unitFieldsArrayFields, unitFieldsArrayFieldsCount);
} else {
PushArrayFieldsToLuaWithRefs(luaState, unitFields, unitFieldsArrayFields, unitFieldsArrayFieldsCount, unitFieldsNestedArrayRefs);
}
return 1;
}
uint32_t Script_GetUnitField(uintptr_t *luaState) {
luaState = GetLuaStatePtr(); // pcall leads to corrupted lua state pointer on added scripts, not sure why
if (!lua_isstring(luaState, 1) || !lua_isstring(luaState, 2)) {
lua_error(luaState, "Usage: GetUnitField(unitToken, fieldName, [copy]) - unitToken can be 'player', 'target', 'pet', etc., or a GUID string");
return 0;
}
InitializeUnitFieldMaps();
const char *unitToken = lua_tostring(luaState, 1);
const char *fieldName = lua_tostring(luaState, 2);
bool useCopy = false;
if (lua_isnumber(luaState, 3)) {
useCopy = static_cast<int>(lua_tonumber(luaState, 3)) != 0;
}
uint64_t guid = GetUnitGuidFromString(unitToken);
if (guid == 0) {
lua_pushnil(luaState);
return 1;
}
auto unit = game::GetObjectPtr(guid);
if (!unit) {
if (auto *info = game::GetPartyOrRaidMemberInfo(guid)) {
if (!GetCGPartyMemberField(luaState, info, fieldName, useCopy)) {
lua_pushnil(luaState);
}
return 1;
}
if (auto *info = game::GetPartyOrRaidPetInfo(guid)) {
if (!GetCGPartyMemberPetField(luaState, info, fieldName, useCopy)) {
lua_pushnil(luaState);
}
return 1;
}
lua_pushnil(luaState);
return 1;
}
auto *unitFields = *reinterpret_cast<game::UnitFields **>(unit + 68);
if (!unitFields) {
lua_pushnil(luaState);
return 1;
}
auto simpleIt = unitFieldsFieldMap.find(fieldName);
if (simpleIt != unitFieldsFieldMap.end()) {
size_t i = simpleIt->second;
const char *fieldPtr = reinterpret_cast<const char *>(unitFields) + unitFieldsFields[i].offset;
switch (unitFieldsFields[i].type) {
case FieldType::UINT32:
lua_pushnumber(luaState, *reinterpret_cast<const uint32_t *>(fieldPtr));
return 1;
case FieldType::UINT8:
lua_pushnumber(luaState, *reinterpret_cast<const uint8_t *>(fieldPtr));
return 1;
case FieldType::UINT64:
PushGuidString(luaState, *reinterpret_cast<const uint64_t *>(fieldPtr));
return 1;
case FieldType::FLOAT:
lua_pushnumber(luaState, *reinterpret_cast<const float *>(fieldPtr));
return 1;
default:
break;
}
}
auto arrayIt = unitFieldsArrayFieldMap.find(fieldName);
if (arrayIt != unitFieldsArrayFieldMap.end()) {
size_t i = arrayIt->second;
const auto &field = unitFieldsArrayFields[i];
if (useCopy) {
lua_newtable(luaState);
} else {
GetTableRef(luaState, unitFieldsArrayFieldRefs[fieldName]);
}
const char *fieldPtr = reinterpret_cast<const char *>(unitFields) + field.offset;
for (size_t j = 0; j < field.count; ++j) {
lua_pushnumber(luaState, j + 1);
switch (field.type) {
case FieldType::UINT32:
lua_pushnumber(luaState, reinterpret_cast<const uint32_t *>(fieldPtr)[j]);
break;
case FieldType::UINT8:
lua_pushnumber(luaState, reinterpret_cast<const uint8_t *>(fieldPtr)[j]);
break;
case FieldType::FLOAT:
lua_pushnumber(luaState, reinterpret_cast<const float *>(fieldPtr)[j]);
break;
default:
lua_pushnumber(luaState, 0);
break;
}
lua_settable(luaState, -3);
}
return 1;
}
lua_error(luaState, "Unknown field name");
return 0;
}
uint32_t Script_SetMouseoverUnit(uintptr_t *luaState) {
luaState = GetLuaStatePtr();
uint64_t guid = 0;
if (lua_isstring(luaState, 1)) {
const char *unitToken = lua_tostring(luaState, 1);
auto const getGUIDFromName = reinterpret_cast<GetGUIDFromNameT>(Offsets::GetGUIDFromName);
guid = getGUIDFromName(unitToken);
}
auto const handleObjectTrackChange = reinterpret_cast<CGGameUI_HandleObjectTrackChangeT>(
Offsets::CGGameUI_HandleObjectTrackChange);
handleObjectTrackChange(guid, 0, 0);
auto const mouseoverLow = *reinterpret_cast<uint32_t *>(Offsets::MouseoverGuidLow);
auto const mouseoverHigh = *reinterpret_cast<uint32_t *>(Offsets::MouseoverGuidHigh);
if (mouseoverLow != 0 || mouseoverHigh != 0) {
lua_pushnumber(luaState, 1);
} else {
lua_pushnil(luaState);
}
return 1;
}
uint32_t Script_SetLocalRaidTargetIndex(uintptr_t *luaState) {
luaState = GetLuaStatePtr();
if (!lua_isstring(luaState, 1) || !lua_isnumber(luaState, 2)) {
lua_error(luaState, "Usage: SetLocalRaidTargetIndex(unitStr, raidTargetIndex)");
return 0;
}
const char *unitStr = lua_tostring(luaState, 1);
uint32_t raidTargetIndex = static_cast<uint32_t>(lua_tonumber(luaState, 2));
uint64_t targetGuid = GetUnitGuidFromString(unitStr);
if (targetGuid == 0) {
return 0;
}
auto localPlayer = game::GetObjectPtr(game::ClntObjMgrGetActivePlayerGuid());
auto targetUnit = game::GetObjectPtr(targetGuid);
if (!localPlayer || !targetUnit) {
return 0;
}
using CGUnit_C_CanInteractT = uint32_t (__thiscall *)(uintptr_t *thisPtr, uintptr_t *target);
auto const canInteract =
reinterpret_cast<CGUnit_C_CanInteractT>(static_cast<uint32_t>(Offsets::CGUnit_C_CanInteract));
auto const *targetObject = reinterpret_cast<game::CGObject const *>(targetUnit);
if (targetObject->m_obj &&
targetObject->m_obj->m_type == game::TYPE_OBJECT &&
!canInteract(localPlayer, targetUnit)) {
auto const targetTypeMask = targetObject->m_obj->m_type;
if (((targetTypeMask >> 4) & 1) != 0) {
lua_error(luaState, "Cannot set raid target index: player cannot interact with object");
return 0;
}
}
if (raidTargetIndex > 8) {
lua_error(luaState, "SetLocalRaidTargetIndex: raidTargetIndex must be in range 0-8");
return 0;
}
using CGUnit_C_NamePlateUpdateRaidTargetT = void (__thiscall *)(uintptr_t *thisPtr);
auto const namePlateUpdateRaidTarget =
reinterpret_cast<CGUnit_C_NamePlateUpdateRaidTargetT>(
static_cast<uint32_t>(Offsets::CGUnit_C_NamePlateUpdateRaidTarget));
auto raidTargets =
reinterpret_cast<uint64_t *>(static_cast<uint32_t>(Offsets::CGRaidInfo_m_raidtargets));
auto const previousTargetGuid = raidTargets[raidTargetIndex];
if (previousTargetGuid != 0) {
raidTargets[raidTargetIndex] = 0;
auto previousTargetUnit = game::GetObjectPtr(previousTargetGuid);
if (previousTargetUnit) {
namePlateUpdateRaidTarget(previousTargetUnit);
}
}
raidTargets[raidTargetIndex] = targetGuid;
namePlateUpdateRaidTarget(targetUnit);
return 1;
}
uint32_t Script_GetUnitGUID(uintptr_t *luaState) {
luaState = GetLuaStatePtr();
if (!lua_isstring(luaState, 1)) {
lua_error(luaState, "Usage: GetUnitGUID(unitToken)");
return 0;
}
const char *unitToken = lua_tostring(luaState, 1);
if (!unitToken || unitToken[0] == '\0') {
lua_pushnil(luaState);
return 1;
}
auto const getGUIDFromName = reinterpret_cast<GetGUIDFromNameT>(Offsets::GetGUIDFromName);
const uint64_t guid = getGUIDFromName(unitToken);
if (guid == 0) {
lua_pushnil(luaState);
} else {
PushGuidString(luaState, guid);
}
return 1;
}
uint64_t GetGUIDFromNameHook(hadesmem::PatchDetourBase *detour, const char *nameStr) {
auto const original = detour->GetTrampolineT<GetGUIDFromNameT>();
if (!nameStr || nameStr[0] == '\0') {
return 0;
}
const size_t tokenLen = std::strlen(nameStr);
const bool isHexToken = (nameStr[0] == '0' && (nameStr[1] == 'x' || nameStr[1] == 'X'));
const bool hasHexAndUnitSuffix = isHexToken && tokenLen > 18 &&
(_stricmp(nameStr + 18, "owner") == 0 ||
_stricmp(nameStr + 18, "target") == 0 ||
_stricmp(nameStr + 18, "pet") == 0);
if (_strnicmp(nameStr, "mark", 4) == 0) {
const char digit = nameStr[4];
if (digit >= '1' && digit <= '8' && nameStr[5] == '\0') {
const uint32_t markIndex = static_cast<uint32_t>(digit - '1');
auto const markGuids = reinterpret_cast<uint64_t *>(static_cast<uintptr_t>(Offsets::CGRaidInfo_m_raidtargets));
return markGuids[markIndex];
}
}
if (!hasHexAndUnitSuffix) {
uint64_t result = original(nameStr);
if (result != 0) {
return result;
}
}
if (isHexToken) {
if (tokenLen < 18) {
return 0;
}
char guidHex[17] = {};
std::memcpy(guidHex, nameStr + 2, 16);
if (!std::all_of(guidHex, guidHex + 16,
[](unsigned char c) { return std::isxdigit(c) != 0; })) {
return 0;
}
const uint64_t parsedGuid = std::strtoull(guidHex, nullptr, 16);
const char *const hexSuffix = nameStr + 18;
if (_stricmp(hexSuffix, "owner") == 0) {
return game::UnitGetOwnerGuidForGuid(parsedGuid);
}
if (_stricmp(hexSuffix, "target") == 0) {
return game::UnitGetTargetGuidForGuid(parsedGuid);
}
if (_stricmp(hexSuffix, "pet") == 0) {
return game::UnitGetPetGuidForGuid(parsedGuid);
}
return parsedGuid;
}
if (tokenLen <= 0x14) {
auto resolveBaseGuid = [&](size_t suffixLen) -> uint64_t {
char buf[0x15] = {};
std::memcpy(buf, nameStr, tokenLen - suffixLen);
auto const hooked = reinterpret_cast<GetGUIDFromNameT>(
static_cast<uint32_t>(Offsets::GetGUIDFromName));
return hooked(buf);
};
if (tokenLen > 5 && _stricmp(nameStr + tokenLen - 5, "owner") == 0) {
const uint64_t baseGuid = resolveBaseGuid(5);
return game::UnitGetOwnerGuidForGuid(baseGuid);
}
if (tokenLen > 6 && _stricmp(nameStr + tokenLen - 6, "target") == 0) {
const uint64_t baseGuid = resolveBaseGuid(6);
return game::UnitGetTargetGuidForGuid(baseGuid);
}
if (tokenLen > 3 && _stricmp(nameStr + tokenLen - 3, "pet") == 0) {
const uint64_t baseGuid = resolveBaseGuid(3);
return game::UnitGetPetGuidForGuid(baseGuid);
}
}
return 0;
}
uint32_t CSimpleFrame_GetNameHook(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
luaState = GetLuaStatePtr();
static auto typeIdCounter = reinterpret_cast<uint32_t *>(0x00CEEF6C);
static auto nameplateTypeId = reinterpret_cast<uint32_t *>(0x00CF0C3C);
if (*nameplateTypeId == 0) {
*typeIdCounter = *typeIdCounter + 1;
*nameplateTypeId = *typeIdCounter;
}
uint32_t typeId = *nameplateTypeId;
uintptr_t *frameObj = nullptr;
uintptr_t *vtable = nullptr;
int luaArgType = lua_type(luaState, 1);
if (luaArgType == LUA_TTABLE) {
lua_rawgeti(luaState, 1, 0);
frameObj = reinterpret_cast<uintptr_t *>(lua_touserdata(luaState, -1));
lua_settop(luaState, -2);
if (frameObj == nullptr) {
lua_error(luaState, "Attempt to find 'this' in non-frame table");
} else if (!IsReadableMemory(frameObj, sizeof(uintptr_t))) {
lua_pushnil(luaState);
return 1;
} else {
vtable = reinterpret_cast<uintptr_t *>(*frameObj);
if (!IsReadableMemory(vtable, 5 * sizeof(uintptr_t))) {
lua_pushnil(luaState);
return 1;
}
using IsTypeT = char (__thiscall *)(uintptr_t *thisPtr, uint32_t typeId);
auto isType = reinterpret_cast<IsTypeT>(vtable[4]);
if (isType(frameObj, typeId) == 0) {
lua_error(luaState, "Wrong object type for member function");
}
}
} else {
lua_error(luaState, "Attempt to find 'this' in non-table value");
}
if (lua_isnumber(luaState, 2) && static_cast<int>(lua_tonumber(luaState, 2)) != 0) {
if (!IsReadableMemory(frameObj + 0x13a, 2 * sizeof(uint32_t))) {
lua_pushnil(luaState);
} else {
uint32_t guidLow = frameObj[0x13a];
uint32_t guidHigh = frameObj[0x13b];
uint64_t guid = (static_cast<uint64_t>(guidHigh) << 32) | guidLow;
PushGuidString(luaState, guid);
}
} else {
using GetNameT = char *(__thiscall *)(uintptr_t *thisPtr);
auto getName = reinterpret_cast<GetNameT>(vtable[1]);
char *name = getName(frameObj);
if (name != nullptr && name[0] != '\0') {
lua_pushstring(luaState, name);
} else {
lua_pushnil(luaState);
}
}
return 1;
}
}
+20
View File
@@ -0,0 +1,20 @@
//
// Created by pmacc on 4/7/2026.
//
#pragma once
#include <Windows.h>
#include "main.hpp"
namespace Nampower {
uint32_t Script_GetRaidTargets(uintptr_t *luaState);
uint32_t Script_GetUnitData(uintptr_t *luaState);
uint32_t Script_GetUnitField(uintptr_t *luaState);
uint32_t Script_SetMouseoverUnit(uintptr_t *luaState);
uint32_t Script_SetLocalRaidTargetIndex(uintptr_t *luaState);
uint32_t Script_GetUnitGUID(uintptr_t *luaState);
uint32_t CSimpleFrame_GetNameHook(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
uint64_t GetGUIDFromNameHook(hadesmem::PatchDetourBase *detour, const char *nameStr);
}