mirror of
https://github.com/brues-code/nampower.git
synced 2026-10-03 12:10:00 +00:00
refactor and add SetLocalRaidTargetIndex + GetRaidTargets
This commit is contained in:
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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,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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user