add more item/spell dbc functions, improve flexibility of spell lua functions

This commit is contained in:
avitasia
2025-12-01 19:36:45 -08:00
parent c812f19a05
commit 2f07d5af6e
16 changed files with 1270 additions and 136 deletions
+33
View File
@@ -98,6 +98,25 @@ SET NP_TargetingQueueWindowMs "1000"
- `NP_NameplateDistance` - The distance in yards to display nameplates. Defaults to whatever was set by the game or vanilla tweaks.
### Existing Lua Changes
#### Improved flexibility on spellbook Lua functions
These built-in Lua spell APIs now accept any of the following as their first argument: 1) spell slot (original behavior), 2) spell name, or 3) `spellId:number`.
Name and spellId lookups are cached internally and validated against current spellbook contents before reuse so you don't have to worry about performance implications or issues after respec'ing.
See examples below for differences in how BOOKTYPE works.
Functions: `GetSpellTexture`, `GetSpellName`, `GetSpellCooldown`, `GetSpellAutocast`, `ToggleSpellAutocast`, `PickupSpell`, `CastSpell`, `IsCurrentCast`, `IsSpellPassive`.
Examples:
```
/run print(GetSpellTexture(1, "spell")) -- booktype required
/run print(GetSpellTexture("spellId:25978")) -- defaults to BOOKTYPE_SPELL
/run print(GetSpellTexture("spellId:6268", "pet")) -- "pet" needed for pet spells
/run print(GetSpellTexture("Fireball")) -- name search
```
### Custom Lua Functions
#### QueueSpellByName(spellName)
@@ -209,6 +228,20 @@ Examples:
/run local slot, bookType, spellId=GetSpellSlotTypeIdForName("Frostbolt");print(slot);print(bookType);print(spellId)
```
#### GetItemStats(itemId)
Returns a Lua table containing all fields for the item’s `ItemStats_C` record (including localized `displayName` and `description`). Returns 0 if the item cannot be found or loaded.
#### GetItemStatsField(itemId, fieldName)
Fast lookup for a single field on an item. Returns the requested field value or raises a Lua error if the item or field is not found.
#### GetSpellRec(spellId)
Returns a Lua table containing all fields for the spell’s `SpellRec` record (including localized `name` and `rank`). Returns 0 if the spell cannot be found.
#### GetSpellRecField(spellId, fieldName)
Fast lookup for a single field on a spell. Returns the requested field value or raises a Lua error if the spell or field is not found.
Struct references (see `nampower/game.hpp` for full details):
#### GetNampowerVersion()
Returns the current version of Nampower split into major, minor and patch numbers.
+4
View File
@@ -24,6 +24,10 @@ set(SOURCE_FILES
spellevents.cpp
scripts.hpp
scripts.cpp
items.hpp
items.cpp
dbc_fields.hpp
dbc_fields.cpp
)
add_library(${DLL_NAME} SHARED ${SOURCE_FILES})
+189
View File
@@ -0,0 +1,189 @@
//
// Created by pmacc on 1/8/2025.
//
#include "dbc_fields.hpp"
#include "offsets.hpp"
namespace Nampower {
// Hash tables for O(1) field lookups
std::unordered_map<std::string, size_t> itemStatsFieldMap;
std::unordered_map<std::string, size_t> itemStatsArrayFieldMap;
std::unordered_map<std::string, size_t> spellRecFieldMap;
std::unordered_map<std::string, size_t> spellRecArrayFieldMap;
static bool fieldMapsInitialized = false;
// ItemStats_C field descriptors
const FieldDescriptor itemStatsFields[] = {
{"class", offsetof(game::ItemStats_C, m_class), FieldType::INT32},
{"subclass", offsetof(game::ItemStats_C, m_subclass), FieldType::INT32},
{"displayInfoID", offsetof(game::ItemStats_C, m_displayInfoID), FieldType::INT32},
{"quality", offsetof(game::ItemStats_C, m_quality), FieldType::INT32},
{"flags", offsetof(game::ItemStats_C, m_flags), FieldType::INT32},
{"buyPrice", offsetof(game::ItemStats_C, m_buyPrice), FieldType::INT32},
{"sellPrice", offsetof(game::ItemStats_C, m_sellPrice), FieldType::INT32},
{"inventoryType", offsetof(game::ItemStats_C, m_inventoryType), FieldType::INT32},
{"allowableClass", offsetof(game::ItemStats_C, m_allowableClass), FieldType::INT32},
{"allowableRace", offsetof(game::ItemStats_C, m_allowableRace), FieldType::INT32},
{"itemLevel", offsetof(game::ItemStats_C, m_itemLevel), FieldType::INT32},
{"requiredLevel", offsetof(game::ItemStats_C, m_requiredLevel), FieldType::INT32},
{"requiredSkill", offsetof(game::ItemStats_C, m_requiredSkill), FieldType::INT32},
{"requiredSkillRank", offsetof(game::ItemStats_C, m_requiredSkillRank), FieldType::INT32},
{"requiredSpell", offsetof(game::ItemStats_C, m_requiredSpell), FieldType::INT32},
{"requiredHonorRank", offsetof(game::ItemStats_C, m_requiredHonorRank), FieldType::INT32},
{"requiredCityRank", offsetof(game::ItemStats_C, m_requiredCityRank), FieldType::INT32},
{"requiredRep", offsetof(game::ItemStats_C, m_requiredRep), FieldType::INT32},
{"requiredRepRank", offsetof(game::ItemStats_C, m_requiredRepRank), FieldType::INT32},
{"maxCount", offsetof(game::ItemStats_C, m_maxCount), FieldType::INT32},
{"stackable", offsetof(game::ItemStats_C, m_stackable), FieldType::INT32},
{"containerSlots", offsetof(game::ItemStats_C, m_containerSlots), FieldType::INT32},
{"delay", offsetof(game::ItemStats_C, m_delay), FieldType::INT32},
{"ammoType", offsetof(game::ItemStats_C, m_ammoType), FieldType::INT32},
{"rangedModRange", offsetof(game::ItemStats_C, m_rangedModRange), FieldType::INT32},
{"bonding", offsetof(game::ItemStats_C, m_bonding), FieldType::INT32},
{"pageText", offsetof(game::ItemStats_C, m_pageText), FieldType::INT32},
{"languageID", offsetof(game::ItemStats_C, m_languageID), FieldType::INT32},
{"pageMaterial", offsetof(game::ItemStats_C, m_pageMaterial), FieldType::INT32},
{"startQuestID", offsetof(game::ItemStats_C, m_startQuestID), FieldType::INT32},
{"lockID", offsetof(game::ItemStats_C, m_lockID), FieldType::INT32},
{"material", offsetof(game::ItemStats_C, m_material), FieldType::INT32},
{"sheatheType", offsetof(game::ItemStats_C, m_sheatheType), FieldType::INT32},
{"randomProperty", offsetof(game::ItemStats_C, m_randomProperty), FieldType::INT32},
{"block", offsetof(game::ItemStats_C, m_block), FieldType::INT32},
{"itemSet", offsetof(game::ItemStats_C, m_itemSet), FieldType::INT32},
{"maxDurability", offsetof(game::ItemStats_C, m_maxDurability), FieldType::INT32},
{"area", offsetof(game::ItemStats_C, m_area), FieldType::INT32},
{"map", offsetof(game::ItemStats_C, m_map), FieldType::INT32},
{"duration", offsetof(game::ItemStats_C, m_duration), FieldType::INT32},
{"bagFamily", offsetof(game::ItemStats_C, m_bagFamily), FieldType::INT32},
};
const size_t itemStatsFieldsCount = sizeof(itemStatsFields) / sizeof(itemStatsFields[0]);
const ArrayFieldDescriptor itemStatsArrayFields[] = {
{"bonusStat", offsetof(game::ItemStats_C, m_bonusStat), FieldType::INT32, 10},
{"bonusAmount", offsetof(game::ItemStats_C, m_bonusAmount), FieldType::INT32, 10},
{"minDamage", offsetof(game::ItemStats_C, m_minDamage), FieldType::FLOAT, 5},
{"maxDamage", offsetof(game::ItemStats_C, m_maxDamage), FieldType::FLOAT, 5},
{"damageType", offsetof(game::ItemStats_C, m_damageType), FieldType::INT32, 5},
{"resistances", offsetof(game::ItemStats_C, m_resistances), FieldType::INT32, 7},
{"spellID", offsetof(game::ItemStats_C, m_spellID), FieldType::INT32, 5},
{"spellTrigger", offsetof(game::ItemStats_C, m_spellTrigger), FieldType::INT32, 5},
{"spellCharges", offsetof(game::ItemStats_C, m_spellCharges), FieldType::INT32, 5},
{"spellCooldown", offsetof(game::ItemStats_C, m_spellCooldown), FieldType::INT32, 5},
{"spellCategory", offsetof(game::ItemStats_C, m_spellCategory), FieldType::INT32, 5},
{"spellCategoryCooldown", offsetof(game::ItemStats_C, m_spellCategoryCooldown), FieldType::INT32, 5},
};
const size_t itemStatsArrayFieldsCount = sizeof(itemStatsArrayFields) / sizeof(itemStatsArrayFields[0]);
// SpellRec field descriptors
const FieldDescriptor spellRecFields[] = {
{"id", offsetof(game::SpellRec, Id), FieldType::UINT32},
{"school", offsetof(game::SpellRec, School), FieldType::UINT32},
{"category", offsetof(game::SpellRec, Category), FieldType::UINT32},
{"castUI", offsetof(game::SpellRec, castUI), FieldType::UINT32},
{"dispel", offsetof(game::SpellRec, Dispel), FieldType::UINT32},
{"mechanic", offsetof(game::SpellRec, Mechanic), FieldType::UINT32},
{"attributes", offsetof(game::SpellRec, Attributes), FieldType::UINT32},
{"attributesEx", offsetof(game::SpellRec, AttributesEx), FieldType::UINT32},
{"attributesEx2", offsetof(game::SpellRec, AttributesEx2), FieldType::UINT32},
{"attributesEx3", offsetof(game::SpellRec, AttributesEx3), FieldType::UINT32},
{"attributesEx4", offsetof(game::SpellRec, AttributesEx4), FieldType::UINT32},
{"stances", offsetof(game::SpellRec, Stances), FieldType::UINT32},
{"stancesNot", offsetof(game::SpellRec, StancesNot), FieldType::UINT32},
{"targets", offsetof(game::SpellRec, Targets), FieldType::UINT32},
{"targetCreatureType", offsetof(game::SpellRec, TargetCreatureType), FieldType::UINT32},
{"requiresSpellFocus", offsetof(game::SpellRec, RequiresSpellFocus), FieldType::UINT32},
{"casterAuraState", offsetof(game::SpellRec, CasterAuraState), FieldType::UINT32},
{"targetAuraState", offsetof(game::SpellRec, TargetAuraState), FieldType::UINT32},
{"castingTimeIndex", offsetof(game::SpellRec, CastingTimeIndex), FieldType::UINT32},
{"recoveryTime", offsetof(game::SpellRec, RecoveryTime), FieldType::UINT32},
{"categoryRecoveryTime", offsetof(game::SpellRec, CategoryRecoveryTime), FieldType::UINT32},
{"interruptFlags", offsetof(game::SpellRec, InterruptFlags), FieldType::UINT32},
{"auraInterruptFlags", offsetof(game::SpellRec, AuraInterruptFlags), FieldType::UINT32},
{"channelInterruptFlags", offsetof(game::SpellRec, ChannelInterruptFlags), FieldType::UINT32},
{"procFlags", offsetof(game::SpellRec, procFlags), FieldType::UINT32},
{"procChance", offsetof(game::SpellRec, procChance), FieldType::UINT32},
{"procCharges", offsetof(game::SpellRec, procCharges), FieldType::UINT32},
{"maxLevel", offsetof(game::SpellRec, maxLevel), FieldType::UINT32},
{"baseLevel", offsetof(game::SpellRec, baseLevel), FieldType::UINT32},
{"spellLevel", offsetof(game::SpellRec, spellLevel), FieldType::UINT32},
{"durationIndex", offsetof(game::SpellRec, DurationIndex), FieldType::UINT32},
{"powerType", offsetof(game::SpellRec, powerType), FieldType::UINT32},
{"manaCost", offsetof(game::SpellRec, manaCost), FieldType::UINT32},
{"manaCostPerlevel", offsetof(game::SpellRec, manaCostPerlevel), FieldType::UINT32},
{"manaPerSecond", offsetof(game::SpellRec, manaPerSecond), FieldType::UINT32},
{"manaPerSecondPerLevel", offsetof(game::SpellRec, manaPerSecondPerLevel), FieldType::UINT32},
{"rangeIndex", offsetof(game::SpellRec, rangeIndex), FieldType::UINT32},
{"speed", offsetof(game::SpellRec, speed), FieldType::FLOAT},
{"modalNextSpell", offsetof(game::SpellRec, modalNextSpell), FieldType::UINT32},
{"stackAmount", offsetof(game::SpellRec, StackAmount), FieldType::UINT32},
{"equippedItemClass", offsetof(game::SpellRec, EquippedItemClass), FieldType::INT32},
{"equippedItemSubClassMask", offsetof(game::SpellRec, EquippedItemSubClassMask), FieldType::INT32},
{"equippedItemInventoryTypeMask", offsetof(game::SpellRec, EquippedItemInventoryTypeMask), FieldType::INT32},
{"spellVisual", offsetof(game::SpellRec, SpellVisual), FieldType::UINT32},
{"spellVisual2", offsetof(game::SpellRec, SpellVisual2), FieldType::UINT32},
{"spellIconID", offsetof(game::SpellRec, SpellIconID), FieldType::UINT32},
{"activeIconID", offsetof(game::SpellRec, activeIconID), FieldType::UINT32},
{"spellPriority", offsetof(game::SpellRec, spellPriority), FieldType::UINT32},
{"manaCostPercentage", offsetof(game::SpellRec, ManaCostPercentage), FieldType::UINT32},
{"startRecoveryCategory", offsetof(game::SpellRec, StartRecoveryCategory), FieldType::UINT32},
{"startRecoveryTime", offsetof(game::SpellRec, StartRecoveryTime), FieldType::UINT32},
{"maxTargetLevel", offsetof(game::SpellRec, MaxTargetLevel), FieldType::UINT32},
{"spellFamilyName", offsetof(game::SpellRec, SpellFamilyName), FieldType::UINT32},
{"spellFamilyFlags", offsetof(game::SpellRec, SpellFamilyFlags), FieldType::UINT64},
{"maxAffectedTargets", offsetof(game::SpellRec, MaxAffectedTargets), FieldType::UINT32},
{"dmgClass", offsetof(game::SpellRec, DmgClass), FieldType::UINT32},
{"preventionType", offsetof(game::SpellRec, PreventionType), FieldType::UINT32},
{"stanceBarOrder", offsetof(game::SpellRec, StanceBarOrder), FieldType::UINT32},
{"minFactionId", offsetof(game::SpellRec, MinFactionId), FieldType::UINT32},
{"minReputation", offsetof(game::SpellRec, MinReputation), FieldType::UINT32},
{"requiredAuraVision", offsetof(game::SpellRec, RequiredAuraVision), FieldType::UINT32},
};
const size_t spellRecFieldsCount = sizeof(spellRecFields) / sizeof(spellRecFields[0]);
const ArrayFieldDescriptor spellRecArrayFields[] = {
{"totem", offsetof(game::SpellRec, Totem), FieldType::UINT32, 2},
{"reagent", offsetof(game::SpellRec, Reagent), FieldType::INT32, 8},
{"reagentCount", offsetof(game::SpellRec, ReagentCount), FieldType::UINT32, 8},
{"effect", offsetof(game::SpellRec, Effect), FieldType::UINT32, 3},
{"effectDieSides", offsetof(game::SpellRec, EffectDieSides), FieldType::INT32, 3},
{"effectBaseDice", offsetof(game::SpellRec, EffectBaseDice), FieldType::UINT32, 3},
{"effectDicePerLevel", offsetof(game::SpellRec, EffectDicePerLevel), FieldType::FLOAT, 3},
{"effectRealPointsPerLevel", offsetof(game::SpellRec, EffectRealPointsPerLevel), FieldType::FLOAT, 3},
{"effectBasePoints", offsetof(game::SpellRec, EffectBasePoints), FieldType::INT32, 3},
{"effectMechanic", offsetof(game::SpellRec, EffectMechanic), FieldType::UINT32, 3},
{"effectImplicitTargetA", offsetof(game::SpellRec, EffectImplicitTargetA), FieldType::UINT32, 3},
{"effectImplicitTargetB", offsetof(game::SpellRec, EffectImplicitTargetB), FieldType::UINT32, 3},
{"effectRadiusIndex", offsetof(game::SpellRec, EffectRadiusIndex), FieldType::UINT32, 3},
{"effectApplyAuraName", offsetof(game::SpellRec, EffectApplyAuraName), FieldType::UINT32, 3},
{"effectAmplitude", offsetof(game::SpellRec, EffectAmplitude), FieldType::UINT32, 3},
{"effectMultipleValue", offsetof(game::SpellRec, EffectMultipleValue), FieldType::FLOAT, 3},
{"effectChainTarget", offsetof(game::SpellRec, EffectChainTarget), FieldType::UINT32, 3},
{"effectItemType", offsetof(game::SpellRec, EffectItemType), FieldType::UINT32, 3},
{"effectMiscValue", offsetof(game::SpellRec, EffectMiscValue), FieldType::INT32, 3},
{"effectTriggerSpell", offsetof(game::SpellRec, EffectTriggerSpell), FieldType::UINT32, 3},
{"effectPointsPerComboPoint", offsetof(game::SpellRec, EffectPointsPerComboPoint), FieldType::FLOAT, 3},
{"dmgMultiplier", offsetof(game::SpellRec, DmgMultiplier), FieldType::FLOAT, 3},
};
const size_t spellRecArrayFieldsCount = sizeof(spellRecArrayFields) / sizeof(spellRecArrayFields[0]);
void InitializeFieldMaps() {
if (fieldMapsInitialized) return;
// Build hash maps for O(1) lookups
for (size_t i = 0; i < itemStatsFieldsCount; ++i) {
itemStatsFieldMap[itemStatsFields[i].name] = i;
}
for (size_t i = 0; i < itemStatsArrayFieldsCount; ++i) {
itemStatsArrayFieldMap[itemStatsArrayFields[i].name] = i;
}
for (size_t i = 0; i < spellRecFieldsCount; ++i) {
spellRecFieldMap[spellRecFields[i].name] = i;
}
for (size_t i = 0; i < spellRecArrayFieldsCount; ++i) {
spellRecArrayFieldMap[spellRecArrayFields[i].name] = i;
}
fieldMapsInitialized = true;
}
}
+137
View File
@@ -0,0 +1,137 @@
//
// Created by pmacc on 1/8/2025.
//
#pragma once
#include "main.hpp"
#include <unordered_map>
#include <string>
#include <cstddef>
namespace Nampower {
// Field type enumeration
enum class FieldType {
INT32,
UINT32,
FLOAT,
STRING,
UINT64
};
// Field descriptor for simple fields
struct FieldDescriptor {
const char* name;
size_t offset;
FieldType type;
};
// Field descriptor for array fields
struct ArrayFieldDescriptor {
const char* name;
size_t offset;
FieldType type;
size_t count;
};
// ItemStats_C field descriptors
extern const FieldDescriptor itemStatsFields[];
extern const size_t itemStatsFieldsCount;
extern const ArrayFieldDescriptor itemStatsArrayFields[];
extern const size_t itemStatsArrayFieldsCount;
// SpellRec field descriptors
extern const FieldDescriptor spellRecFields[];
extern const size_t spellRecFieldsCount;
extern const ArrayFieldDescriptor spellRecArrayFields[];
extern const size_t spellRecArrayFieldsCount;
// Hash tables for O(1) field lookups
extern std::unordered_map<std::string, size_t> itemStatsFieldMap;
extern std::unordered_map<std::string, size_t> itemStatsArrayFieldMap;
extern std::unordered_map<std::string, size_t> spellRecFieldMap;
extern std::unordered_map<std::string, size_t> spellRecArrayFieldMap;
// Initialize field maps (call once before using field lookups)
void InitializeFieldMaps();
// Forward declarations for Lua functions (defined in offsets.hpp/scripts.cpp)
using lua_pushnumberT = void (__fastcall *)(uintptr_t *, double);
using lua_pushstringT = void (__fastcall *)(uintptr_t *, char *);
using lua_newtableT = void (__fastcall *)(uintptr_t *);
using lua_settableT = void (__fastcall *)(uintptr_t *, int);
extern lua_pushnumberT lua_pushnumber;
extern lua_pushstringT lua_pushstring;
extern lua_newtableT lua_newtable;
extern lua_settableT lua_settable;
// Helper template functions to push fields to Lua (inline for performance)
template<typename T>
inline void PushFieldsToLua(uintptr_t* luaState, const T* obj, const FieldDescriptor* fields, size_t fieldCount) {
for (size_t i = 0; i < fieldCount; ++i) {
const auto& field = fields[i];
lua_pushstring(luaState, const_cast<char*>(field.name));
const char* fieldPtr = reinterpret_cast<const char*>(obj) + field.offset;
switch (field.type) {
case FieldType::INT32:
lua_pushnumber(luaState, *reinterpret_cast<const int32_t*>(fieldPtr));
break;
case FieldType::UINT32:
lua_pushnumber(luaState, *reinterpret_cast<const uint32_t*>(fieldPtr));
break;
case FieldType::FLOAT:
lua_pushnumber(luaState, *reinterpret_cast<const float*>(fieldPtr));
break;
case FieldType::UINT64:
lua_pushnumber(luaState, static_cast<double>(*reinterpret_cast<const uint64_t*>(fieldPtr)));
break;
case FieldType::STRING: {
const char* str = *reinterpret_cast<const char* const*>(fieldPtr);
lua_pushstring(luaState, str ? const_cast<char*>(str) : const_cast<char*>(""));
break;
}
}
lua_settable(luaState, -3);
}
}
template<typename T>
inline void PushArrayFieldsToLua(uintptr_t* luaState, const T* obj, const ArrayFieldDescriptor* fields, size_t fieldCount) {
for (size_t i = 0; i < fieldCount; ++i) {
const auto& field = fields[i];
lua_pushstring(luaState, const_cast<char*>(field.name));
lua_newtable(luaState);
const char* fieldPtr = reinterpret_cast<const char*>(obj) + field.offset;
for (size_t j = 0; j < field.count; ++j) {
lua_pushnumber(luaState, j + 1); // Lua is 1-indexed
switch (field.type) {
case FieldType::INT32:
lua_pushnumber(luaState, reinterpret_cast<const int32_t*>(fieldPtr)[j]);
break;
case FieldType::UINT32:
lua_pushnumber(luaState, reinterpret_cast<const uint32_t*>(fieldPtr)[j]);
break;
case FieldType::FLOAT:
lua_pushnumber(luaState, reinterpret_cast<const float*>(fieldPtr)[j]);
break;
case FieldType::UINT64:
lua_pushnumber(luaState, static_cast<double>(reinterpret_cast<const uint64_t*>(fieldPtr)[j]));
break;
case FieldType::STRING: {
const char* str = reinterpret_cast<const char* const*>(fieldPtr)[j];
lua_pushstring(luaState, str ? const_cast<char*>(str) : const_cast<char*>(""));
break;
}
}
lua_settable(luaState, -3);
}
lua_settable(luaState, -3);
}
}
}
+3 -2
View File
@@ -30,6 +30,7 @@
#pragma once
#include <cstdint>
#include <string>
namespace game {
#pragma pack(push, 1)
@@ -209,7 +210,7 @@ namespace game {
int m_containerSlots;
int m_bonusStat[10];
int m_bonusAmount[10];
int m_minDamage[5];
float m_minDamage[5];
float m_maxDamage[5];
int m_damageType[5];
int m_resistances[7];
@@ -229,7 +230,7 @@ namespace game {
int m_pageMaterial;
int m_startQuestID;
int m_lockID;
char *m_material;
int m_material;
int m_sheatheType;
int m_randomProperty;
int m_block;
+148 -1
View File
@@ -5,8 +5,155 @@
#include "helper.hpp"
#include "offsets.hpp"
#include "main.hpp"
#include "dbc_fields.hpp"
#include <algorithm>
#include <cctype>
#include <cstring>
#include <unordered_map>
#include <string>
namespace Nampower {
uint32_t GetSpellSlotAndTypeForName(const char *spellName, uint32_t *spellType) {
struct CachedEntry {
uint32_t slot;
uint32_t type;
std::string cachedNameLower;
};
static std::unordered_map<std::string, CachedEntry> spellSlotCache;
auto const normalize = [](const char *name) -> std::string {
std::string out = name ? name : "";
std::transform(out.begin(), out.end(), out.begin(), [](unsigned char c) { return char(std::tolower(c)); });
return out;
};
const std::string key = normalize(spellName);
auto const spellNameMatchesAtSlot = [&normalize](const char *requestedLower, uint32_t slot, uint32_t type) -> bool {
if (!requestedLower) {
return false;
}
uint32_t spellId = 0;
if (type == 0) {
spellId = *reinterpret_cast<uint32_t *>(uint32_t(Offsets::CGSpellBook_mKnownSpells) + slot * 4);
} else if (type == 1) {
spellId = *reinterpret_cast<uint32_t *>(uint32_t(Offsets::CGSpellBook_mKnownPetSpells) + slot * 4);
} else {
return false;
}
if (spellId == 0) {
return false;
}
auto const spell = game::GetSpellInfo(spellId);
if (!spell) {
return false;
}
auto const language = *reinterpret_cast<uint32_t *>(Offsets::Language);
auto const actualName = reinterpret_cast<const char *>(spell->SpellName[language]);
if (!actualName) {
return false;
}
auto const actualLower = normalize(actualName);
return _stricmp(actualLower.c_str(), requestedLower) == 0;
};
if (!key.empty()) {
auto cacheIt = spellSlotCache.find(key);
if (cacheIt != spellSlotCache.end()) {
auto const &entry = cacheIt->second;
if (entry.slot < 1024 && spellNameMatchesAtSlot(key.c_str(), entry.slot, entry.type)) {
if (spellType) {
*spellType = entry.type;
}
return entry.slot;
}
}
}
auto const getSpellSlotAndType = reinterpret_cast<GetSpellSlotAndTypeT>(Offsets::GetSpellSlotAndType);
auto const slot = getSpellSlotAndType(spellName, spellType);
if (slot < 1024 && spellType && !key.empty()) {
spellSlotCache[key] = {slot, *spellType, key};
}
return slot;
}
uint32_t GetSpellIdFromSpellName(const char *spellName) {
uint32_t bookType;
uint32_t spellSlot = GetSpellSlotAndTypeForName(spellName, &bookType);
uint32_t spellId = 0;
if (spellSlot < 1024) {
if (bookType == 0) {
spellId = *reinterpret_cast<uint32_t *>(uint32_t(Offsets::CGSpellBook_mKnownSpells) +
spellSlot * 4);
} else {
spellId = *reinterpret_cast<uint32_t *>(uint32_t(Offsets::CGSpellBook_mKnownPetSpells) +
spellSlot * 4);
}
}
return spellId;
}
uint32_t ConvertSpellIdToSpellSlot(uint32_t spellId, uint32_t bookType) {
static std::unordered_map<uint64_t, uint32_t> spellIdToSlotCache;
auto const cacheKey = (uint64_t(spellId) << 1) | (bookType & 0x1);
auto const slotHasSpellId = [bookType](uint32_t slot, uint32_t expectedId) -> bool {
if (slot >= 1024) {
return false;
}
uint32_t slotSpellId = 0;
if (bookType == 0) {
slotSpellId = *reinterpret_cast<uint32_t *>(uint32_t(Offsets::CGSpellBook_mKnownSpells) + slot * 4);
} else {
slotSpellId = *reinterpret_cast<uint32_t *>(uint32_t(Offsets::CGSpellBook_mKnownPetSpells) + slot * 4);
}
return slotSpellId == expectedId;
};
auto cacheIt = spellIdToSlotCache.find(cacheKey);
if (cacheIt != spellIdToSlotCache.end()) {
if (slotHasSpellId(cacheIt->second, spellId)) {
return cacheIt->second;
}
}
// Search through player spellbook
if (bookType == 0) {
for (uint32_t spellSlot = 0; spellSlot < 1024; spellSlot++) {
uint32_t slotSpellId = *reinterpret_cast<uint32_t *>(uint32_t(Offsets::CGSpellBook_mKnownSpells) +
spellSlot * 4);
if (slotSpellId == spellId) {
spellIdToSlotCache[cacheKey] = spellSlot;
return spellSlot;
}
}
} else {
// Search through pet spellbook
for (uint32_t spellSlot = 0; spellSlot < 1024; spellSlot++) {
uint32_t slotSpellId = *reinterpret_cast<uint32_t *>(uint32_t(Offsets::CGSpellBook_mKnownPetSpells) +
spellSlot * 4);
if (slotSpellId == spellId) {
spellIdToSlotCache[cacheKey] = spellSlot;
return spellSlot;
}
}
}
return 0;
}
bool SpellIsOnGcd(const game::SpellRec *spell) {
if (spell->Id == 51714) {
// power overwhelming gcd removed but client not updated
@@ -122,4 +269,4 @@ namespace Nampower {
void SetNameplateDistance(float distance) {
*reinterpret_cast<float *>(Offsets::NameplateDistance) = distance * distance;
}
}
}
+5
View File
@@ -7,6 +7,11 @@
#include "game.hpp"
namespace Nampower {
uint32_t GetSpellSlotAndTypeForName(const char *spellName, uint32_t *spellType);
uint32_t GetSpellIdFromSpellName(const char *spellName);
uint32_t ConvertSpellIdToSpellSlot(uint32_t spellId, uint32_t bookType);
bool SpellIsOnGcd(const game::SpellRec *spell);
+293
View File
@@ -0,0 +1,293 @@
//
// Created by pmacc on 9/21/2024.
//
#include "items.hpp"
#include "offsets.hpp"
#include "logging.hpp"
#include <fstream>
#include <sstream>
#include <string>
#include <unordered_map>
namespace Nampower {
// Global dictionary to store itemId -> ItemStats_C mappings
static std::unordered_map<uint32_t, game::ItemStats_C *> itemStatsCache;
auto const getRow = reinterpret_cast<DBCache_ItemCacheDBGetRowT>(Offsets::DBCache_ItemCacheDBGetRow);
auto const itemCache = reinterpret_cast<void *>(Offsets::ItemDBCache);
// Callback function for DBCache
void __stdcall ItemStatsCallback(uintptr_t *tooltip, char param_2) {
if (tooltip != nullptr) {
auto itemId = *reinterpret_cast<uint32_t *>(tooltip + 0xE6);
int ptr;
auto itemStats = getRow(itemCache, nullptr, itemId, &ptr, &ItemStatsCallback, nullptr, 0);
if (itemStats) {
DEBUG_LOG("Queried itemId " << itemId << " -> " << itemStats);
// Store in cache
itemStatsCache[itemId] = reinterpret_cast<game::ItemStats_C *>(itemStats);
}
}
}
void LoadItem(uint32_t itemId) {
// Check if already in cache
auto it = itemStatsCache.find(itemId);
if (it != itemStatsCache.end()) {
return;
}
if (!itemCache) {
return;
}
int ptr;
auto itemStats = getRow(itemCache, nullptr, itemId, &ptr, &ItemStatsCallback, nullptr, 0);
if (itemStats) {
// Store in cache
itemStatsCache[itemId] = reinterpret_cast<game::ItemStats_C *>(itemStats);
} else {
// wait for .005 sec
Sleep(5);
}
}
game::ItemStats_C* GetItemStats(uint32_t itemId) {
auto it = itemStatsCache.find(itemId);
if (it != itemStatsCache.end()) {
return it->second;
}
return nullptr;
}
std::string escapeJsonString(const char *str) {
if (!str) return "null";
std::string result = "\"";
for (const char *p = str; *p; ++p) {
switch (*p) {
case '"':
result += "\\\"";
break;
case '\\':
result += "\\\\";
break;
case '\b':
result += "\\b";
break;
case '\f':
result += "\\f";
break;
case '\n':
result += "\\n";
break;
case '\r':
result += "\\r";
break;
case '\t':
result += "\\t";
break;
default:
if (static_cast<unsigned char>(*p) < 0x20) {
char buf[8];
snprintf(buf, sizeof(buf), "\\u%04x", static_cast<unsigned char>(*p));
result += buf;
} else {
result += *p;
}
}
}
result += "\"";
return result;
}
// Register a function if you want to use this
// Don't want everyone scraping all items.
void ExportAllItems() {
DEBUG_LOG("Starting getAllItems - fetching items 1 to 100000");
// Loop through all item IDs
for (uint32_t itemId = 1; itemId <= 100000; ++itemId) {
LoadItem(itemId);
// Log progress every 1000 items
if (itemId % 1000 == 0) {
DEBUG_LOG("Progress: " << itemId << " / 100000 items processed");
}
}
DEBUG_LOG("Finished fetching items, found " << itemStatsCache.size() << " valid items");
DEBUG_LOG("Writing items to items.json");
// Write all cached items to JSON file (overwrite mode)
try {
std::ofstream jsonFile("items.json", std::ios::trunc);
if (!jsonFile.is_open()) {
DEBUG_LOG("Failed to open items.json for writing");
return;
}
jsonFile << "[\n";
bool first = true;
for (const auto &pair: itemStatsCache) {
if (!first) {
jsonFile << ",\n";
}
first = false;
uint32_t itemId = pair.first;
game::ItemStats_C *item = pair.second;
if (!item) continue;
std::ostringstream json;
json << " {\n";
json << " \"id\": " << itemId << ",\n";
json << " \"class\": " << item->m_class << ",\n";
json << " \"subclass\": " << item->m_subclass << ",\n";
json << " \"displayName\": " << escapeJsonString(item->m_displayName[0]) << ",\n";
json << " \"displayInfoID\": " << item->m_displayInfoID << ",\n";
json << " \"quality\": " << item->m_quality << ",\n";
json << " \"flags\": " << static_cast<int>(item->m_flags) << ",\n";
json << " \"buyPrice\": " << item->m_buyPrice << ",\n";
json << " \"sellPrice\": " << item->m_sellPrice << ",\n";
json << " \"inventoryType\": " << item->m_inventoryType << ",\n";
json << " \"allowableClass\": " << item->m_allowableClass << ",\n";
json << " \"allowableRace\": " << item->m_allowableRace << ",\n";
json << " \"itemLevel\": " << item->m_itemLevel << ",\n";
json << " \"requiredLevel\": " << item->m_requiredLevel << ",\n";
json << " \"requiredSkill\": " << item->m_requiredSkill << ",\n";
json << " \"requiredSkillRank\": " << item->m_requiredSkillRank << ",\n";
json << " \"requiredSpell\": " << item->m_requiredSpell << ",\n";
json << " \"requiredHonorRank\": " << item->m_requiredHonorRank << ",\n";
json << " \"requiredCityRank\": " << item->m_requiredCityRank << ",\n";
json << " \"requiredRep\": " << item->m_requiredRep << ",\n";
json << " \"requiredRepRank\": " << item->m_requiredRepRank << ",\n";
json << " \"maxCount\": " << item->m_maxCount << ",\n";
json << " \"stackable\": " << item->m_stackable << ",\n";
json << " \"containerSlots\": " << item->m_containerSlots << ",\n";
// Arrays
json << " \"bonusStat\": [";
for (int i = 0; i < 10; ++i) {
json << item->m_bonusStat[i];
if (i < 9) json << ", ";
}
json << "],\n";
json << " \"bonusAmount\": [";
for (int i = 0; i < 10; ++i) {
json << item->m_bonusAmount[i];
if (i < 9) json << ", ";
}
json << "],\n";
json << " \"minDamage\": [";
for (int i = 0; i < 5; ++i) {
json << item->m_minDamage[i];
if (i < 4) json << ", ";
}
json << "],\n";
json << " \"maxDamage\": [";
for (int i = 0; i < 5; ++i) {
json << item->m_maxDamage[i];
if (i < 4) json << ", ";
}
json << "],\n";
json << " \"damageType\": [";
for (int i = 0; i < 5; ++i) {
json << item->m_damageType[i];
if (i < 4) json << ", ";
}
json << "],\n";
json << " \"resistances\": [";
for (int i = 0; i < 7; ++i) {
json << item->m_resistances[i];
if (i < 6) json << ", ";
}
json << "],\n";
json << " \"delay\": " << item->m_delay << ",\n";
json << " \"ammoType\": " << item->m_ammoType << ",\n";
json << " \"rangedModRange\": " << item->m_rangedModRange << ",\n";
json << " \"spellID\": [";
for (int i = 0; i < 5; ++i) {
json << item->m_spellID[i];
if (i < 4) json << ", ";
}
json << "],\n";
json << " \"spellTrigger\": [";
for (int i = 0; i < 5; ++i) {
json << item->m_spellTrigger[i];
if (i < 4) json << ", ";
}
json << "],\n";
json << " \"spellCharges\": [";
for (int i = 0; i < 5; ++i) {
json << item->m_spellCharges[i];
if (i < 4) json << ", ";
}
json << "],\n";
json << " \"spellCooldown\": [";
for (int i = 0; i < 5; ++i) {
json << item->m_spellCooldown[i];
if (i < 4) json << ", ";
}
json << "],\n";
json << " \"spellCategory\": [";
for (int i = 0; i < 5; ++i) {
json << item->m_spellCategory[i];
if (i < 4) json << ", ";
}
json << "],\n";
json << " \"spellCategoryCooldown\": [";
for (int i = 0; i < 5; ++i) {
json << item->m_spellCategoryCooldown[i];
if (i < 4) json << ", ";
}
json << "],\n";
json << " \"bonding\": " << item->m_bonding << ",\n";
json << " \"description\": " << escapeJsonString(item->m_description) << ",\n";
json << " \"pageText\": " << item->m_pageText << ",\n";
json << " \"languageID\": " << item->m_languageID << ",\n";
json << " \"pageMaterial\": " << item->m_pageMaterial << ",\n";
json << " \"startQuestID\": " << item->m_startQuestID << ",\n";
json << " \"lockID\": " << item->m_lockID << ",\n";
json << " \"material\": " << item->m_material << ",\n";
json << " \"sheatheType\": " << item->m_sheatheType << ",\n";
json << " \"randomProperty\": " << item->m_randomProperty << ",\n";
json << " \"block\": " << item->m_block << ",\n";
json << " \"itemSet\": " << item->m_itemSet << ",\n";
json << " \"maxDurability\": " << item->m_maxDurability << ",\n";
json << " \"area\": " << item->m_area << ",\n";
json << " \"map\": " << item->m_map << ",\n";
json << " \"duration\": " << item->m_duration << ",\n";
json << " \"bagFamily\": " << item->m_bagFamily << "\n";
json << " }";
jsonFile << json.str();
}
jsonFile << "\n]\n";
jsonFile.close();
DEBUG_LOG("Successfully wrote " << itemStatsCache.size() << " items to items.json");
} catch (const std::exception &e) {
DEBUG_LOG("Exception writing to items.json: " << e.what());
}
}
}
+17
View File
@@ -0,0 +1,17 @@
//
// Created by pmacc on 9/21/2024.
//
#pragma once
#include "main.hpp"
namespace Nampower {
using TooltipItemStatsCallbackT = void (__stdcall *)(uintptr_t *tooltip, char param_2);
using DBCache_ItemCacheDBGetRowT = uint32_t * (__fastcall *)(void *this_ptr, void *dummy_edx, uint32_t itemId, int *param_2, TooltipItemStatsCallbackT callback, int *param_4, char param_5);
void ExportAllItems();
void LoadItem(uint32_t itemId);
game::ItemStats_C* GetItemStats(uint32_t itemId);
}
+37 -51
View File
@@ -36,6 +36,7 @@
#include "scripts.hpp"
#include "spellchannel.hpp"
#include "helper.hpp"
#include "items.hpp"
#include <cstdint>
#include <memory>
@@ -99,23 +100,10 @@ namespace Nampower {
std::unique_ptr<hadesmem::PatchDetour<SpellGoT>> gSpellGoDetour;
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> gSpellTargetUnitDetour;
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> gSpellStopCastingDetour;
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> gCastSpellByNameNoQueueDetour;
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> gQueueSpellByNameDetour;
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> qQueueScriptDetour;
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> gIsSpellInRangeDetour;
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> gIsSpellUsableDetour;
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> gGetCurrentCastingInfoDetour;
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> gGetSpellIdForNameDetour;
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> gGetSpellNameAndRankForIdDetour;
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> gGetSpellSlotAndTypeForNameDetour;
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> gChannelStopCastingNextTickDetour;
std::unique_ptr<hadesmem::PatchDetour<OnSpriteRightClickT>> gOnSpriteRightClickDetour;
std::unique_ptr<hadesmem::PatchDetour<Spell_C_HandleSpriteClickT>> gSpell_C_HandleSpriteClickDetour;
std::unique_ptr<hadesmem::PatchDetour<Spell_C_TargetSpellT>> gSpell_C_TargetSpellDetour;
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> gGetNampowerVersionDetour;
std::unique_ptr<hadesmem::PatchDetour<LuaScriptT>> gGetItemLevelDetour;
std::unique_ptr<hadesmem::PatchDetour<PacketHandlerT>> gSpellCooldownDetour;
std::unique_ptr<hadesmem::PatchDetour<PacketHandlerT>> gSpellDelayedDetour;
std::unique_ptr<hadesmem::PatchDetour<PacketHandlerT>> gCastResultHandlerDetour;
@@ -130,6 +118,10 @@ namespace Nampower {
std::unique_ptr<hadesmem::PatchDetour<CGPlayer_C_OnAttackIconPressedT>> gCGPlayer_C_OnAttackIconPressedDetour;
std::unique_ptr<hadesmem::PatchDetour<InvalidFunctionPtrCheckT>> gInvalidFunctionPtrCheckDetour;
std::unique_ptr<hadesmem::PatchDetour<GetSpellSlotFromLuaT>> gGetSpellSlotFromLuaDetour;
uint32_t GetTime() {
return static_cast<uint32_t>(std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch()).count()) - gStartTime;
@@ -543,6 +535,10 @@ namespace Nampower {
return iSceneEnd(ptr);
}
void InvalidFunctionPtrCheckHook(hadesmem::PatchDetourBase *detour, uint32_t param_1) {
// remove original call to allow registering lua functions at any address range
}
void updateFromCvar(const char *cvar, const char *value) {
if (strcmp(cvar, "NP_QueueCastTimeSpells") == 0) {
gUserSettings.queueCastTimeSpells = atoi(value) != 0;
@@ -1116,29 +1112,13 @@ namespace Nampower {
&Script_SpellStopCastingHook);
gSpell_C_TargetSpellDetour = createHook<Spell_C_TargetSpellT>(process, Offsets::Spell_C_TargetSpell,
&Spell_C_TargetSpellHook);
gCastSpellByNameNoQueueDetour = createHook<LuaScriptT>(process, Offsets::Script_CastSpellByNameNoQueue,
Script_CastSpellByNameNoQueue);
gQueueSpellByNameDetour = createHook<LuaScriptT>(process, Offsets::Script_QueueSpellByName,
Script_QueueSpellByName);
qQueueScriptDetour = createHook<LuaScriptT>(process, Offsets::Script_QueueScript, Script_QueueScript);
gIsSpellInRangeDetour = createHook<LuaScriptT>(process, Offsets::Script_IsSpellInRange, Script_IsSpellInRange);
gIsSpellUsableDetour = createHook<LuaScriptT>(process, Offsets::Script_IsSpellUsable, Script_IsSpellUsable);
gGetCurrentCastingInfoDetour = createHook<LuaScriptT>(process, Offsets::Script_GetCurrentCastingInfo,
Script_GetCurrentCastingInfo);
gGetSpellIdForNameDetour = createHook<LuaScriptT>(process, Offsets::Script_GetSpellIdForName,
Script_GetSpellIdForName);
gGetSpellNameAndRankForIdDetour = createHook<LuaScriptT>(process, Offsets::Script_GetSpellNameAndRankForId,
Script_GetSpellNameAndRankForId);
gGetSpellSlotAndTypeForNameDetour = createHook<LuaScriptT>(process, Offsets::Script_GetSpellSlotTypeIdForName,
Script_GetSpellSlotTypeIdForName);
gOnSpriteRightClickDetour = createHook<OnSpriteRightClickT>(process, Offsets::OnSpriteRightClick,
OnSpriteRightClickHook);
gChannelStopCastingNextTickDetour = createHook<LuaScriptT>(process, Offsets::Script_ChannelStopCastingNextTick,
Script_ChannelStopCastingNextTick);
gGetNampowerVersionDetour = createHook<LuaScriptT>(process, Offsets::Script_GetNampowerVersion,
Script_GetNampowerVersion);
gGetItemLevelDetour = createHook<LuaScriptT>(process, Offsets::Script_GetItemLevel, Script_GetItemLevel);
gIEndSceneDetour = createHook<ISceneEndT>(process, Offsets::ISceneEndPtr, &ISceneEndHook);
gInvalidFunctionPtrCheckDetour = createHook<InvalidFunctionPtrCheckT>(process, Offsets::InvalidFunctionPtrCheck,
&InvalidFunctionPtrCheckHook);
gGetSpellSlotFromLuaDetour = createHook<GetSpellSlotFromLuaT>(process, Offsets::GetSpellSlotFromLua,
&GetSpellSlotFromLuaHook);
// gCGPlayer_C_OnAttackIconPressedDetour = createHook<CGPlayer_C_OnAttackIconPressedT>(process,
// Offsets::CGPlayer_C_OnAttackIconPressed,
@@ -1169,46 +1149,52 @@ namespace Nampower {
// register our own lua functions
DEBUG_LOG("Registering Custom Lua functions");
char queueSpellByName[] = "QueueSpellByName";
RegisterLuaFunction(queueSpellByName, reinterpret_cast<uintptr_t *>(Offsets::Script_QueueSpellByName));
RegisterLuaFunction(queueSpellByName, reinterpret_cast<uintptr_t *>(Script_QueueSpellByName));
char castSpellByNameNoQueue[] = "CastSpellByNameNoQueue";
RegisterLuaFunction(castSpellByNameNoQueue,
reinterpret_cast<uintptr_t *>(Offsets::Script_CastSpellByNameNoQueue));
RegisterLuaFunction(castSpellByNameNoQueue, reinterpret_cast<uintptr_t *>(Script_CastSpellByNameNoQueue));
char queueScript[] = "QueueScript";
RegisterLuaFunction(queueScript, reinterpret_cast<uintptr_t *>(Offsets::Script_QueueScript));
RegisterLuaFunction(queueScript, reinterpret_cast<uintptr_t *>(Script_QueueScript));
char isSpellInRange[] = "IsSpellInRange";
RegisterLuaFunction(isSpellInRange, reinterpret_cast<uintptr_t *>(Offsets::Script_IsSpellInRange));
RegisterLuaFunction(isSpellInRange, reinterpret_cast<uintptr_t *>(Script_IsSpellInRange));
char isSpellUsable[] = "IsSpellUsable";
RegisterLuaFunction(isSpellUsable, reinterpret_cast<uintptr_t *>(Offsets::Script_IsSpellUsable));
RegisterLuaFunction(isSpellUsable, reinterpret_cast<uintptr_t *>(Script_IsSpellUsable));
char getCurrentCastingInfo[] = "GetCurrentCastingInfo";
RegisterLuaFunction(getCurrentCastingInfo,
reinterpret_cast<uintptr_t *>(Offsets::Script_GetCurrentCastingInfo));
RegisterLuaFunction(getCurrentCastingInfo, reinterpret_cast<uintptr_t *>(Script_GetCurrentCastingInfo));
char getSpellIdForName[] = "GetSpellIdForName";
RegisterLuaFunction(getSpellIdForName,
reinterpret_cast<uintptr_t *>(Offsets::Script_GetSpellIdForName));
RegisterLuaFunction(getSpellIdForName, reinterpret_cast<uintptr_t *>(Script_GetSpellIdForName));
char getSpellNameAndRankForId[] = "GetSpellNameAndRankForId";
RegisterLuaFunction(getSpellNameAndRankForId,
reinterpret_cast<uintptr_t *>(Offsets::Script_GetSpellNameAndRankForId));
RegisterLuaFunction(getSpellNameAndRankForId, reinterpret_cast<uintptr_t *>(Script_GetSpellNameAndRankForId));
char getSpellSlotTypeIdForName[] = "GetSpellSlotTypeIdForName";
RegisterLuaFunction(getSpellSlotTypeIdForName,
reinterpret_cast<uintptr_t *>(Offsets::Script_GetSpellSlotTypeIdForName));
RegisterLuaFunction(getSpellSlotTypeIdForName, reinterpret_cast<uintptr_t *>(Script_GetSpellSlotTypeIdForName));
char channelStopCastingNextTick[] = "ChannelStopCastingNextTick";
RegisterLuaFunction(channelStopCastingNextTick,
reinterpret_cast<uintptr_t *>(Offsets::Script_ChannelStopCastingNextTick));
RegisterLuaFunction(channelStopCastingNextTick, reinterpret_cast<uintptr_t *>(Script_ChannelStopCastingNextTick));
char getNampowerVersion[] = "GetNampowerVersion";
RegisterLuaFunction(getNampowerVersion, reinterpret_cast<uintptr_t *>(Offsets::Script_GetNampowerVersion));
RegisterLuaFunction(getNampowerVersion, reinterpret_cast<uintptr_t *>(Script_GetNampowerVersion));
char getItemILevel[] = "GetItemLevel";
RegisterLuaFunction(getItemILevel, reinterpret_cast<uintptr_t *>(Offsets::Script_GetItemLevel));
RegisterLuaFunction(getItemILevel, reinterpret_cast<uintptr_t *>(Script_GetItemLevel));
char getItemStats[] = "GetItemStats";
RegisterLuaFunction(getItemStats, reinterpret_cast<uintptr_t *>(Script_GetItemStats));
char getSpellRec[] = "GetSpellRec";
RegisterLuaFunction(getSpellRec, reinterpret_cast<uintptr_t *>(Script_GetSpellRec));
char getItemStatsField[] = "GetItemStatsField";
RegisterLuaFunction(getItemStatsField, reinterpret_cast<uintptr_t *>(Script_GetItemStatsField));
char getSpellRecField[] = "GetSpellRecField";
RegisterLuaFunction(getSpellRecField, reinterpret_cast<uintptr_t *>(Script_GetSpellRecField));
}
std::once_flag loadFlag;
+8 -3
View File
@@ -30,8 +30,8 @@ namespace Nampower {
constexpr uint32_t BUFFER_DECREASE_FREQUENCY = 10000; // time in ms between changes to lower buffer
constexpr uint32_t MAJOR_VERSION = 2;
constexpr uint32_t MINOR_VERSION = 11;
constexpr uint32_t PATCH_VERSION = 2;
constexpr uint32_t MINOR_VERSION = 12;
constexpr uint32_t PATCH_VERSION = 0;
constexpr int32_t LUA_REGISTRYINDEX = -10000;
constexpr int32_t LUA_GLOBALSINDEX = -10001;
@@ -96,7 +96,8 @@ namespace Nampower {
using CGPlayer_C_OnAttackIconPressedT = int (__fastcall *)(uintptr_t *this_ptr, void *dummy_edx, uint64_t guid);
using GetSpellSlotAndTypeT = int (__fastcall *)(const char *, uint32_t *);
using GetSpellSlotAndTypeT = uint32_t (__fastcall *)(const char *, uint32_t *);
using GetSpellSlotFromLuaT = uint32_t (__fastcall *)(int param_1, uint32_t *slot, uint32_t *type);
using GetTimeMsT = uint64_t (__stdcall *)();
using GetClientConnectionT = uintptr_t *(__stdcall *)();
@@ -124,6 +125,8 @@ namespace Nampower {
using lua_pushnilT = void (__fastcall *)(uintptr_t *);
using lua_errorT = void (__cdecl *)(uintptr_t *, const char *);
using lua_settopT = void (__fastcall *)(uintptr_t *, int);
using lua_newtableT = void (__fastcall *)(uintptr_t *);
using lua_settableT = void (__fastcall *)(uintptr_t *, int);
using Spell_C_CooldownEventTriggeredT = void (__fastcall *)(uint32_t spellId,
uint64_t *targetGUID,
@@ -147,6 +150,8 @@ namespace Nampower {
void *callbackPtr,
int category, char unk2, int unk3);
using InvalidFunctionPtrCheckT = void (__fastcall *)(uint32_t param_1);
void RegisterLuaFunction(char *, uintptr_t *func);
void LuaCall(const char *code);
+10 -14
View File
@@ -49,6 +49,7 @@ enum class Offsets : std::uint32_t {
AutoRepeatingSpellId = 0xCEAC30,
VisualSpellId = 0X00CEAC58,
GetSpellSlotAndType = 0X004B3950,
GetSpellSlotFromLua = 0X004B3EC0,
OsGetAsyncTimeMs = 0X0042B790,
ChannelTargetGuid = 0xC4D980,
NameplateDistance = 0xC4D988, // float containing the distance squared ready to be pythagorean'd
@@ -124,20 +125,6 @@ enum class Offsets : std::uint32_t {
Script_RunScript = 0x0048B980,
Script_SpellStopCasting = 0x006E6E80,
// Added script functions
Script_QueueSpellByName = 0x004B4B38, // unused address at the end of Script_CastSpellByName. Need to be < 0x7FEDAC to avoid Invalid Function Pointer
Script_CastSpellByNameNoQueue = 0X004B4B64,
Script_QueueScript = 0X0048B968, // unused address at the end of Script_StopCinematic
Script_IsSpellInRange = 0x004E76D8,
Script_IsSpellUsable = 0X004E77A4,
Script_GetCurrentCastingInfo = 0X004E77F8,
Script_GetSpellIdForName = 0X004E7828,
Script_GetSpellNameAndRankForId = 0X004E7844,
Script_GetSpellSlotTypeIdForName = 0X004E784A,
Script_ChannelStopCastingNextTick = 0X004E7858,
Script_GetNampowerVersion = 0X004E7874,
Script_GetItemLevel = 0X004E787A,
lua_state_ptr = 0x7040D0,
lua_isstring = 0x006F3510,
@@ -152,6 +139,11 @@ enum class Offsets : std::uint32_t {
lua_pcall = 0x006F41A0,
lua_error = 0X006F4940,
lua_settop = 0X006F3080,
lua_newtable = 0x006F3C90,
lua_settable = 0x006F3E20,
lua_pushboolean = 0x006F39F0,
lua_rawseti = 0x006F3EA0,
lua_gettop = 0x006F3070,
CGInputControlGetActive = 0XBE1148,
@@ -177,4 +169,8 @@ enum class Offsets : std::uint32_t {
CGUnit_C_ClearSpellEffect = 0x00614150,
CGPlayer_C_OnAttackIconPressed = 0X006131A0,
DBCache_ItemCacheDBGetRow = 0x0055BA30,
InvalidFunctionPtrCheck = 0x0042A320,
};
+364 -41
View File
@@ -4,6 +4,10 @@
#include "scripts.hpp"
#include "offsets.hpp"
#include "items.hpp"
#include "dbc_fields.hpp"
#include "helper.hpp"
#include <cstring>
namespace Nampower {
auto const lua_error = reinterpret_cast<lua_errorT>(Offsets::lua_error);
@@ -14,38 +18,17 @@ namespace Nampower {
auto const lua_tostring = reinterpret_cast<lua_tostringT>(Offsets::lua_tostring);
auto const lua_tonumber = reinterpret_cast<lua_tonumberT>(Offsets::lua_tonumber);
auto const lua_pushnumber = reinterpret_cast<lua_pushnumberT>(Offsets::lua_pushnumber);
auto const lua_pushstring = reinterpret_cast<lua_pushstringT>(Offsets::lua_pushstring);
// Export Lua functions for dbc_fields.hpp templates
lua_pushnumberT lua_pushnumber = reinterpret_cast<lua_pushnumberT>(Offsets::lua_pushnumber);
lua_pushstringT lua_pushstring = reinterpret_cast<lua_pushstringT>(Offsets::lua_pushstring);
lua_newtableT lua_newtable = reinterpret_cast<lua_newtableT>(Offsets::lua_newtable);
lua_settableT lua_settable = reinterpret_cast<lua_settableT>(Offsets::lua_settable);
bool gScriptQueued;
int gScriptPriority = 1;
char *queuedScript;
uint32_t GetSpellSlotAndTypeForName(const char *spellName, uint32_t *spellType) {
auto const getSpellSlotAndType = reinterpret_cast<GetSpellSlotAndTypeT>(Offsets::GetSpellSlotAndType);
return getSpellSlotAndType(spellName, spellType);
}
uint32_t GetSpellIdFromSpellName(const char *spellName) {
uint32_t bookType;
uint32_t spellSlot = GetSpellSlotAndTypeForName(spellName, &bookType);
uint32_t spellId = 0;
if (spellSlot < 1024) {
if (bookType == 0) {
spellId = *reinterpret_cast<uint32_t *>(uint32_t(Offsets::CGSpellBook_mKnownSpells) +
spellSlot * 4);
} else {
spellId = *reinterpret_cast<uint32_t *>(uint32_t(Offsets::CGSpellBook_mKnownPetSpells) +
spellSlot * 4);
}
}
return spellId;
}
uint32_t Script_CastSpellByNameNoQueue(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
uint32_t Script_CastSpellByNameNoQueue(uintptr_t *luaState) {
DEBUG_LOG("Casting next spell without queuing");
// turn on forceQueue and then call regular CastSpellByName
gNoQueueCast = true;
@@ -56,7 +39,7 @@ namespace Nampower {
return result;
}
uint32_t Script_QueueSpellByName(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
uint32_t Script_QueueSpellByName(uintptr_t *luaState) {
DEBUG_LOG("Force queuing next cast spell");
// turn on forceQueue and then call regular CastSpellByName
gForceQueueCast = true;
@@ -78,7 +61,7 @@ namespace Nampower {
return spellStopCasting(luaState);
}
uint32_t Script_IsSpellInRange(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
uint32_t Script_IsSpellInRange(uintptr_t *luaState) {
auto param1IsString = lua_isstring(luaState, 1);
auto param1IsNumber = lua_isnumber(luaState, 1);
if (param1IsString || param1IsNumber) {
@@ -150,7 +133,7 @@ namespace Nampower {
return 0;
}
uint32_t Script_IsSpellUsable(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
uint32_t Script_IsSpellUsable(uintptr_t *luaState) {
auto param1IsString = lua_isstring(luaState, 1);
auto param1IsNumber = lua_isnumber(luaState, 1);
if (param1IsString || param1IsNumber) {
@@ -204,7 +187,7 @@ namespace Nampower {
return 0;
}
uint32_t Script_GetCurrentCastingInfo(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
uint32_t Script_GetCurrentCastingInfo(uintptr_t *luaState) {
auto const castingSpellId = reinterpret_cast<uint32_t *>(Offsets::CastingSpellId);
lua_pushnumber(luaState, *castingSpellId);
@@ -246,7 +229,7 @@ namespace Nampower {
return 7;
}
uint32_t Script_GetSpellIdForName(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
uint32_t Script_GetSpellIdForName(uintptr_t *luaState) {
if (lua_isstring(luaState, 1)) {
auto const spellName = lua_tostring(luaState, 1);
auto const spellId = GetSpellIdFromSpellName(spellName);
@@ -259,7 +242,7 @@ namespace Nampower {
return 0;
}
uint32_t Script_GetSpellNameAndRankForId(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
uint32_t Script_GetSpellNameAndRankForId(uintptr_t *luaState) {
if (lua_isnumber(luaState, 1)) {
auto const spellId = uint32_t(lua_tonumber(luaState, 1));
auto const spell = game::GetSpellInfo(spellId);
@@ -271,7 +254,7 @@ namespace Nampower {
return 2;
} else {
DEBUG_LOG("Spell not found for id: " << spellId);
lua_error(luaState, "Spell not found");
lua_error(luaState, "Spell not found");
}
} else {
lua_error(luaState, "Usage: GetSpellNameAndRankForId(spellId)");
@@ -280,7 +263,7 @@ namespace Nampower {
return 0;
}
uint32_t Script_GetSpellSlotTypeIdForName(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
uint32_t Script_GetSpellSlotTypeIdForName(uintptr_t *luaState) {
if (lua_isstring(luaState, 1)) {
auto const spellName = lua_tostring(luaState, 1);
uint32_t bookType;
@@ -330,7 +313,7 @@ namespace Nampower {
return 0;
}
uint32_t Script_ChannelStopCastingNextTick(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
uint32_t Script_ChannelStopCastingNextTick(uintptr_t *luaState) {
if (gCastData.channeling) {
DEBUG_LOG("ChannelStopCastingNextTick activated, canceling next tick");
gCastData.cancelChannelNextTick = true;
@@ -339,7 +322,7 @@ namespace Nampower {
return 0;
}
uint32_t Script_GetNampowerVersion(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
uint32_t Script_GetNampowerVersion(uintptr_t *luaState) {
lua_pushnumber(luaState, MAJOR_VERSION);
lua_pushnumber(luaState, MINOR_VERSION);
lua_pushnumber(luaState, PATCH_VERSION);
@@ -347,7 +330,7 @@ namespace Nampower {
return 3;
}
uint32_t Script_GetItemLevel(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
uint32_t Script_GetItemLevel(uintptr_t *luaState) {
if (lua_isnumber(luaState, 1)) {
auto const itemId = static_cast<uint32_t>(lua_tonumber(luaState, 1));
@@ -380,7 +363,7 @@ namespace Nampower {
return 0;
}
bool Script_QueueScript(hadesmem::PatchDetourBase *detour, uintptr_t *luaState) {
uint32_t Script_QueueScript(uintptr_t *luaState) {
DEBUG_LOG("Trying to queue script");
auto const currentTime = GetTime();
@@ -418,7 +401,7 @@ namespace Nampower {
return runScript(luaState);
}
return false;
return 0;
}
bool RunQueuedScript(int priority) {
@@ -440,4 +423,344 @@ namespace Nampower {
return false;
}
}
uint32_t Script_GetItemStats(uintptr_t *luaState) {
if (!lua_isnumber(luaState, 1)) {
lua_error(luaState, "Usage: GetItemStats(itemId)");
return 0;
}
uint32_t itemId = static_cast<uint32_t>(lua_tonumber(luaState, 1));
// Load item if not cached
LoadItem(itemId);
// Get from cache
game::ItemStats_C *item = GetItemStats(itemId);
if (!item) {
lua_error(luaState, "Item not found");
return 0;
}
// Create new table
lua_newtable(luaState);
// Push all simple fields using descriptors
PushFieldsToLua(luaState, item, itemStatsFields, itemStatsFieldsCount);
// Push string fields manually (require language)
auto const language = *reinterpret_cast<uint32_t *>(Offsets::Language);
lua_pushstring(luaState, const_cast<char *>("displayName"));
lua_pushstring(luaState,
item->m_displayName[language] ? item->m_displayName[language] : const_cast<char *>(""));
lua_settable(luaState, -3);
lua_pushstring(luaState, const_cast<char *>("description"));
lua_pushstring(luaState, item->m_description ? item->m_description : const_cast<char *>(""));
lua_settable(luaState, -3);
// Push all array fields using descriptors
PushArrayFieldsToLua(luaState, item, itemStatsArrayFields, itemStatsArrayFieldsCount);
return 1; // Return the table
}
uint32_t Script_GetSpellRec(uintptr_t *luaState) {
if (!lua_isnumber(luaState, 1)) {
lua_error(luaState, "Usage: GetSpellRec(spellId)");
return 0;
}
uint32_t spellId = static_cast<uint32_t>(lua_tonumber(luaState, 1));
// Get spell info
auto spell = game::GetSpellInfo(spellId);
if (!spell) {
lua_error(luaState, "Spell not found");
return 0;
}
// Create new table
lua_newtable(luaState);
// Push all simple fields using descriptors
PushFieldsToLua(luaState, spell, spellRecFields, spellRecFieldsCount);
// Push string fields manually (require language)
auto const language = *reinterpret_cast<uint32_t *>(Offsets::Language);
lua_pushstring(luaState, const_cast<char *>("name"));
lua_pushstring(luaState, spell->SpellName[language] ? const_cast<char *>(spell->SpellName[language])
: const_cast<char *>(""));
lua_settable(luaState, -3);
lua_pushstring(luaState, const_cast<char *>("rank"));
lua_pushstring(luaState, reinterpret_cast<const char *>(spell->Rank[language])
? const_cast<char *>(reinterpret_cast<const char *>(spell->Rank[language]))
: const_cast<char *>(""));
lua_settable(luaState, -3);
// Push all array fields using descriptors
PushArrayFieldsToLua(luaState, spell, spellRecArrayFields, spellRecArrayFieldsCount);
return 1; // Return the table
}
uint32_t Script_GetItemStatsField(uintptr_t *luaState) {
if (!lua_isnumber(luaState, 1) || !lua_isstring(luaState, 2)) {
lua_error(luaState, "Usage: GetItemStatsField(itemId, fieldName)");
return 0;
}
InitializeFieldMaps();
uint32_t itemId = static_cast<uint32_t>(lua_tonumber(luaState, 1));
const char *fieldName = lua_tostring(luaState, 2);
// Load item if not cached
LoadItem(itemId);
// Get from cache
game::ItemStats_C *item = GetItemStats(itemId);
if (!item) {
lua_pushnumber(luaState, 0); // Return nil/false to indicate not found
return 1;
}
// O(1) hash table lookup for simple fields
auto simpleIt = itemStatsFieldMap.find(fieldName);
if (simpleIt != itemStatsFieldMap.end()) {
size_t i = simpleIt->second;
const char *fieldPtr = reinterpret_cast<const char *>(item) + itemStatsFields[i].offset;
switch (itemStatsFields[i].type) {
case FieldType::INT32:
lua_pushnumber(luaState, *reinterpret_cast<const int32_t *>(fieldPtr));
return 1;
case FieldType::UINT32:
lua_pushnumber(luaState, *reinterpret_cast<const uint32_t *>(fieldPtr));
return 1;
case FieldType::FLOAT:
lua_pushnumber(luaState, *reinterpret_cast<const float *>(fieldPtr));
return 1;
case FieldType::STRING: {
const char *str = *reinterpret_cast<const char *const *>(fieldPtr);
lua_pushstring(luaState, str ? const_cast<char *>(str) : const_cast<char *>(""));
return 1;
}
default:
break;
}
}
// O(1) hash table lookup for array fields
auto arrayIt = itemStatsArrayFieldMap.find(fieldName);
if (arrayIt != itemStatsArrayFieldMap.end()) {
size_t i = arrayIt->second;
const auto &field = itemStatsArrayFields[i];
lua_newtable(luaState);
const char *fieldPtr = reinterpret_cast<const char *>(item) + field.offset;
for (size_t j = 0; j < field.count; ++j) {
lua_pushnumber(luaState, j + 1);
switch (field.type) {
case FieldType::INT32:
lua_pushnumber(luaState, reinterpret_cast<const int32_t *>(fieldPtr)[j]);
break;
case FieldType::UINT32:
lua_pushnumber(luaState, reinterpret_cast<const uint32_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;
}
// Check for special string fields
if (strcmp(fieldName, "displayName") == 0) {
auto const language = *reinterpret_cast<uint32_t *>(Offsets::Language);
lua_pushstring(luaState,
item->m_displayName[language] ? item->m_displayName[language] : const_cast<char *>(""));
return 1;
}
if (strcmp(fieldName, "description") == 0) {
lua_pushstring(luaState, item->m_description ? item->m_description : const_cast<char *>(""));
return 1;
}
// Field not found
lua_error(luaState, "Unknown field name");
return 0;
}
uint32_t Script_GetSpellRecField(uintptr_t *luaState) {
if (!lua_isnumber(luaState, 1) || !lua_isstring(luaState, 2)) {
lua_error(luaState, "Usage: GetSpellRecField(spellId, fieldName)");
return 0;
}
InitializeFieldMaps();
uint32_t spellId = static_cast<uint32_t>(lua_tonumber(luaState, 1));
const char *fieldName = lua_tostring(luaState, 2);
// Get spell info
auto spell = game::GetSpellInfo(spellId);
if (!spell) {
lua_pushnumber(luaState, 0); // Return nil/false to indicate not found
return 1;
}
// O(1) hash table lookup for simple fields
auto simpleIt = spellRecFieldMap.find(fieldName);
if (simpleIt != spellRecFieldMap.end()) {
size_t i = simpleIt->second;
const char *fieldPtr = reinterpret_cast<const char *>(spell) + spellRecFields[i].offset;
switch (spellRecFields[i].type) {
case FieldType::INT32:
lua_pushnumber(luaState, *reinterpret_cast<const int32_t *>(fieldPtr));
return 1;
case FieldType::UINT32:
lua_pushnumber(luaState, *reinterpret_cast<const uint32_t *>(fieldPtr));
return 1;
case FieldType::FLOAT:
lua_pushnumber(luaState, *reinterpret_cast<const float *>(fieldPtr));
return 1;
case FieldType::UINT64:
lua_pushnumber(luaState, static_cast<double>(*reinterpret_cast<const uint64_t *>(fieldPtr)));
return 1;
case FieldType::STRING: {
const char *str = *reinterpret_cast<const char *const *>(fieldPtr);
lua_pushstring(luaState, str ? const_cast<char *>(str) : const_cast<char *>(""));
return 1;
}
}
}
// O(1) hash table lookup for array fields
auto arrayIt = spellRecArrayFieldMap.find(fieldName);
if (arrayIt != spellRecArrayFieldMap.end()) {
size_t i = arrayIt->second;
const auto &field = spellRecArrayFields[i];
lua_newtable(luaState);
const char *fieldPtr = reinterpret_cast<const char *>(spell) + field.offset;
for (size_t j = 0; j < field.count; ++j) {
lua_pushnumber(luaState, j + 1);
switch (field.type) {
case FieldType::INT32:
lua_pushnumber(luaState, reinterpret_cast<const int32_t *>(fieldPtr)[j]);
break;
case FieldType::UINT32:
lua_pushnumber(luaState, reinterpret_cast<const uint32_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;
}
// Check for special string fields
if (strcmp(fieldName, "name") == 0) {
auto const language = *reinterpret_cast<uint32_t *>(Offsets::Language);
lua_pushstring(luaState, spell->SpellName[language] ? const_cast<char *>(spell->SpellName[language])
: const_cast<char *>(""));
return 1;
}
if (strcmp(fieldName, "rank") == 0) {
auto const language = *reinterpret_cast<uint32_t *>(Offsets::Language);
lua_pushstring(luaState, reinterpret_cast<const char *>(spell->Rank[language])
? const_cast<char *>(reinterpret_cast<const char *>(spell->Rank[language]))
: const_cast<char *>(""));
return 1;
}
// Field not found
lua_error(luaState, "Unknown field name");
return 0;
}
uint32_t GetSpellSlotFromLuaHook(hadesmem::PatchDetourBase *detour, int param_1, uint32_t *slot, uint32_t *type) {
// Get Lua state pointer
uintptr_t *luaState = GetLuaStatePtr();
// Check if first parameter is not a number
if (lua_isnumber(luaState, 1) == 0) {
const char *spellStr = lua_tostring(luaState, 1);
// Check if string starts with "spellId:" (case-insensitive)
if (_strnicmp(spellStr, "spellId:", 8) == 0) {
// Extract spell ID from string
uint32_t spellId = atoi(spellStr + 8);
if (spellId > 0) {
// Check if second parameter has "pet"
uint32_t bookType = 0;
if (lua_isstring(luaState, 2)) {
const char *bookTypeStr = lua_tostring(luaState, 2);
if (_strcmpi(bookTypeStr, "pet") == 0) {
bookType = 1;
}
}
uint32_t spellSlot = ConvertSpellIdToSpellSlot(spellId, bookType);
if(!spellSlot){
lua_error(luaState, "Unable convert spell id to spell slot.");
return 0;
}
if (slot) {
*slot = spellSlot;
}
if (type) {
*type = bookType; // Use the requested bookType
}
return 1;
} else {
lua_error(luaState, "Unable parse spell id. Format is spellId:123.");
return 0;
}
} else {
uint32_t bookType;
auto spellSlot = GetSpellSlotAndTypeForName(spellStr, &bookType);
// returns large number if spell not found
if (spellSlot == 0 || spellSlot > 100000) {
lua_error(luaState, "Unable to find spell slot for spell name.");
return 0;
} else {
if (slot) {
*slot = spellSlot;
}
if (type) {
*type = bookType;
}
}
return 1;
}
} else {
auto const getSpellSlotFromLua = detour->GetTrampolineT<GetSpellSlotFromLuaT>();
// Fall back to original function
return getSpellSlotFromLua(param_1, slot, type);
}
return 0;
}
}
+22 -12
View File
@@ -8,31 +8,41 @@
#include "main.hpp"
namespace Nampower {
uint32_t Script_CastSpellByNameNoQueue(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
uint32_t Script_CastSpellByNameNoQueue(uintptr_t *luaState);
uint32_t Script_QueueSpellByName(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
uint32_t Script_QueueSpellByName(uintptr_t *luaState);
uint32_t Script_IsSpellInRange(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
uint32_t Script_IsSpellInRange(uintptr_t *luaState);
uint32_t Script_IsSpellUsable(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
uint32_t Script_IsSpellUsable(uintptr_t *luaState);
uint32_t Script_SpellStopCastingHook(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
uint32_t Script_GetCurrentCastingInfo(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
uint32_t Script_GetCurrentCastingInfo(uintptr_t *luaState);
uint32_t Script_GetSpellIdForName(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
uint32_t Script_GetSpellIdForName(uintptr_t *luaState);
uint32_t Script_GetSpellNameAndRankForId(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
uint32_t Script_GetSpellNameAndRankForId(uintptr_t *luaState);
uint32_t Script_GetSpellSlotTypeIdForName(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
uint32_t Script_GetSpellSlotTypeIdForName(uintptr_t *luaState);
uint32_t Script_ChannelStopCastingNextTick(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
uint32_t Script_ChannelStopCastingNextTick(uintptr_t *luaState);
uint32_t Script_GetNampowerVersion(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
uint32_t Script_GetNampowerVersion(uintptr_t *luaState);
uint32_t Script_GetItemLevel(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
uint32_t Script_GetItemLevel(uintptr_t *luaState);
bool Script_QueueScript(hadesmem::PatchDetourBase *detour, uintptr_t *luaState);
uint32_t Script_QueueScript(uintptr_t *luaState);
uint32_t Script_GetItemStats(uintptr_t *luaState);
uint32_t Script_GetSpellRec(uintptr_t *luaState);
uint32_t Script_GetItemStatsField(uintptr_t *luaState);
uint32_t Script_GetSpellRecField(uintptr_t *luaState);
uint32_t GetSpellSlotFromLuaHook(hadesmem::PatchDetourBase *detour, int param_1, uint32_t *slot, uint32_t *type);
bool RunQueuedScript(int priority);
}
-9
View File
@@ -704,13 +704,4 @@ namespace Nampower {
return 1;
}
int CGPlayer_C_OnAttackIconPressedHook(hadesmem::PatchDetourBase *detour, uintptr_t *this_ptr, void *dummy_edx,
uint64_t guid) {
auto const onAttackIconPressed = detour->GetTrampolineT<CGPlayer_C_OnAttackIconPressedT>();
DEBUG_LOG("Attack icon pressed for guid " << guid);
return onAttackIconPressed(this_ptr, dummy_edx, 0);
}
}
-3
View File
@@ -35,7 +35,4 @@ namespace Nampower {
CDataStore *packet);
int PlaySpellVisualHandlerHook(hadesmem::PatchDetourBase *detour, uint32_t *opCode, CDataStore *packet);
int CGPlayer_C_OnAttackIconPressedHook(hadesmem::PatchDetourBase *detour, uintptr_t *this_ptr, void *dummy_edx,
uint64_t guid);
}