From 2f07d5af6e7ccac375729216ef8a31517a7e9a55 Mon Sep 17 00:00:00 2001 From: avitasia Date: Mon, 1 Dec 2025 19:36:45 -0800 Subject: [PATCH] add more item/spell dbc functions, improve flexibility of spell lua functions --- README.md | 33 ++++ nampower/CMakeLists.txt | 4 + nampower/dbc_fields.cpp | 189 ++++++++++++++++++ nampower/dbc_fields.hpp | 137 +++++++++++++ nampower/game.hpp | 5 +- nampower/helper.cpp | 149 +++++++++++++- nampower/helper.hpp | 5 + nampower/items.cpp | 293 ++++++++++++++++++++++++++++ nampower/items.hpp | 17 ++ nampower/main.cpp | 88 ++++----- nampower/main.hpp | 11 +- nampower/offsets.hpp | 24 +-- nampower/scripts.cpp | 405 +++++++++++++++++++++++++++++++++++---- nampower/scripts.hpp | 34 ++-- nampower/spellevents.cpp | 9 - nampower/spellevents.hpp | 3 - 16 files changed, 1270 insertions(+), 136 deletions(-) create mode 100644 nampower/dbc_fields.cpp create mode 100644 nampower/dbc_fields.hpp create mode 100644 nampower/items.cpp create mode 100644 nampower/items.hpp diff --git a/README.md b/README.md index b9a17f2..d8257b6 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/nampower/CMakeLists.txt b/nampower/CMakeLists.txt index fb468e1..6b3b5ad 100644 --- a/nampower/CMakeLists.txt +++ b/nampower/CMakeLists.txt @@ -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}) diff --git a/nampower/dbc_fields.cpp b/nampower/dbc_fields.cpp new file mode 100644 index 0000000..c25561c --- /dev/null +++ b/nampower/dbc_fields.cpp @@ -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 itemStatsFieldMap; + std::unordered_map itemStatsArrayFieldMap; + std::unordered_map spellRecFieldMap; + std::unordered_map 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; + } +} diff --git a/nampower/dbc_fields.hpp b/nampower/dbc_fields.hpp new file mode 100644 index 0000000..675da27 --- /dev/null +++ b/nampower/dbc_fields.hpp @@ -0,0 +1,137 @@ +// +// Created by pmacc on 1/8/2025. +// + +#pragma once + +#include "main.hpp" +#include +#include +#include + +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 itemStatsFieldMap; + extern std::unordered_map itemStatsArrayFieldMap; + extern std::unordered_map spellRecFieldMap; + extern std::unordered_map 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 + 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(field.name)); + + const char* fieldPtr = reinterpret_cast(obj) + field.offset; + + switch (field.type) { + case FieldType::INT32: + lua_pushnumber(luaState, *reinterpret_cast(fieldPtr)); + break; + case FieldType::UINT32: + lua_pushnumber(luaState, *reinterpret_cast(fieldPtr)); + break; + case FieldType::FLOAT: + lua_pushnumber(luaState, *reinterpret_cast(fieldPtr)); + break; + case FieldType::UINT64: + lua_pushnumber(luaState, static_cast(*reinterpret_cast(fieldPtr))); + break; + case FieldType::STRING: { + const char* str = *reinterpret_cast(fieldPtr); + lua_pushstring(luaState, str ? const_cast(str) : const_cast("")); + break; + } + } + lua_settable(luaState, -3); + } + } + + template + 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(field.name)); + lua_newtable(luaState); + + const char* fieldPtr = reinterpret_cast(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(fieldPtr)[j]); + break; + case FieldType::UINT32: + lua_pushnumber(luaState, reinterpret_cast(fieldPtr)[j]); + break; + case FieldType::FLOAT: + lua_pushnumber(luaState, reinterpret_cast(fieldPtr)[j]); + break; + case FieldType::UINT64: + lua_pushnumber(luaState, static_cast(reinterpret_cast(fieldPtr)[j])); + break; + case FieldType::STRING: { + const char* str = reinterpret_cast(fieldPtr)[j]; + lua_pushstring(luaState, str ? const_cast(str) : const_cast("")); + break; + } + } + lua_settable(luaState, -3); + } + lua_settable(luaState, -3); + } + } +} diff --git a/nampower/game.hpp b/nampower/game.hpp index a46500c..42a5c08 100644 --- a/nampower/game.hpp +++ b/nampower/game.hpp @@ -30,6 +30,7 @@ #pragma once #include +#include 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; diff --git a/nampower/helper.cpp b/nampower/helper.cpp index 12dd307..973bab9 100644 --- a/nampower/helper.cpp +++ b/nampower/helper.cpp @@ -5,8 +5,155 @@ #include "helper.hpp" #include "offsets.hpp" #include "main.hpp" +#include "dbc_fields.hpp" +#include +#include +#include +#include +#include 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 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(Offsets::CGSpellBook_mKnownSpells) + slot * 4); + } else if (type == 1) { + spellId = *reinterpret_cast(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(Offsets::Language); + auto const actualName = reinterpret_cast(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(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(Offsets::CGSpellBook_mKnownSpells) + + spellSlot * 4); + } else { + spellId = *reinterpret_cast(uint32_t(Offsets::CGSpellBook_mKnownPetSpells) + + spellSlot * 4); + } + } + + return spellId; + } + + uint32_t ConvertSpellIdToSpellSlot(uint32_t spellId, uint32_t bookType) { + static std::unordered_map 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(Offsets::CGSpellBook_mKnownSpells) + slot * 4); + } else { + slotSpellId = *reinterpret_cast(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(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(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(Offsets::NameplateDistance) = distance * distance; } -} \ No newline at end of file +} diff --git a/nampower/helper.hpp b/nampower/helper.hpp index 91eee88..2dd6268 100644 --- a/nampower/helper.hpp +++ b/nampower/helper.hpp @@ -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); diff --git a/nampower/items.cpp b/nampower/items.cpp new file mode 100644 index 0000000..532ac02 --- /dev/null +++ b/nampower/items.cpp @@ -0,0 +1,293 @@ +// +// Created by pmacc on 9/21/2024. +// + +#include "items.hpp" +#include "offsets.hpp" +#include "logging.hpp" + +#include +#include +#include +#include + +namespace Nampower { + // Global dictionary to store itemId -> ItemStats_C mappings + static std::unordered_map itemStatsCache; + + auto const getRow = reinterpret_cast(Offsets::DBCache_ItemCacheDBGetRow); + auto const itemCache = reinterpret_cast(Offsets::ItemDBCache); + + // Callback function for DBCache + void __stdcall ItemStatsCallback(uintptr_t *tooltip, char param_2) { + if (tooltip != nullptr) { + auto itemId = *reinterpret_cast(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(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(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(*p) < 0x20) { + char buf[8]; + snprintf(buf, sizeof(buf), "\\u%04x", static_cast(*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(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()); + } + } +} diff --git a/nampower/items.hpp b/nampower/items.hpp new file mode 100644 index 0000000..bfb1432 --- /dev/null +++ b/nampower/items.hpp @@ -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); +} diff --git a/nampower/main.cpp b/nampower/main.cpp index 21acb81..732e976 100644 --- a/nampower/main.cpp +++ b/nampower/main.cpp @@ -36,6 +36,7 @@ #include "scripts.hpp" #include "spellchannel.hpp" #include "helper.hpp" +#include "items.hpp" #include #include @@ -99,23 +100,10 @@ namespace Nampower { std::unique_ptr> gSpellGoDetour; std::unique_ptr> gSpellTargetUnitDetour; std::unique_ptr> gSpellStopCastingDetour; - std::unique_ptr> gCastSpellByNameNoQueueDetour; - std::unique_ptr> gQueueSpellByNameDetour; - std::unique_ptr> qQueueScriptDetour; - std::unique_ptr> gIsSpellInRangeDetour; - std::unique_ptr> gIsSpellUsableDetour; - std::unique_ptr> gGetCurrentCastingInfoDetour; - std::unique_ptr> gGetSpellIdForNameDetour; - std::unique_ptr> gGetSpellNameAndRankForIdDetour; - std::unique_ptr> gGetSpellSlotAndTypeForNameDetour; - std::unique_ptr> gChannelStopCastingNextTickDetour; std::unique_ptr> gOnSpriteRightClickDetour; std::unique_ptr> gSpell_C_HandleSpriteClickDetour; std::unique_ptr> gSpell_C_TargetSpellDetour; - std::unique_ptr> gGetNampowerVersionDetour; - std::unique_ptr> gGetItemLevelDetour; - std::unique_ptr> gSpellCooldownDetour; std::unique_ptr> gSpellDelayedDetour; std::unique_ptr> gCastResultHandlerDetour; @@ -130,6 +118,10 @@ namespace Nampower { std::unique_ptr> gCGPlayer_C_OnAttackIconPressedDetour; + std::unique_ptr> gInvalidFunctionPtrCheckDetour; + + std::unique_ptr> gGetSpellSlotFromLuaDetour; + uint32_t GetTime() { return static_cast(std::chrono::duration_cast( 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(process, Offsets::Spell_C_TargetSpell, &Spell_C_TargetSpellHook); - gCastSpellByNameNoQueueDetour = createHook(process, Offsets::Script_CastSpellByNameNoQueue, - Script_CastSpellByNameNoQueue); - gQueueSpellByNameDetour = createHook(process, Offsets::Script_QueueSpellByName, - Script_QueueSpellByName); - qQueueScriptDetour = createHook(process, Offsets::Script_QueueScript, Script_QueueScript); - gIsSpellInRangeDetour = createHook(process, Offsets::Script_IsSpellInRange, Script_IsSpellInRange); - gIsSpellUsableDetour = createHook(process, Offsets::Script_IsSpellUsable, Script_IsSpellUsable); - gGetCurrentCastingInfoDetour = createHook(process, Offsets::Script_GetCurrentCastingInfo, - Script_GetCurrentCastingInfo); - gGetSpellIdForNameDetour = createHook(process, Offsets::Script_GetSpellIdForName, - Script_GetSpellIdForName); - gGetSpellNameAndRankForIdDetour = createHook(process, Offsets::Script_GetSpellNameAndRankForId, - Script_GetSpellNameAndRankForId); - gGetSpellSlotAndTypeForNameDetour = createHook(process, Offsets::Script_GetSpellSlotTypeIdForName, - Script_GetSpellSlotTypeIdForName); gOnSpriteRightClickDetour = createHook(process, Offsets::OnSpriteRightClick, OnSpriteRightClickHook); - gChannelStopCastingNextTickDetour = createHook(process, Offsets::Script_ChannelStopCastingNextTick, - Script_ChannelStopCastingNextTick); - gGetNampowerVersionDetour = createHook(process, Offsets::Script_GetNampowerVersion, - Script_GetNampowerVersion); - gGetItemLevelDetour = createHook(process, Offsets::Script_GetItemLevel, Script_GetItemLevel); gIEndSceneDetour = createHook(process, Offsets::ISceneEndPtr, &ISceneEndHook); + gInvalidFunctionPtrCheckDetour = createHook(process, Offsets::InvalidFunctionPtrCheck, + &InvalidFunctionPtrCheckHook); + gGetSpellSlotFromLuaDetour = createHook(process, Offsets::GetSpellSlotFromLua, + &GetSpellSlotFromLuaHook); // gCGPlayer_C_OnAttackIconPressedDetour = createHook(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(Offsets::Script_QueueSpellByName)); + RegisterLuaFunction(queueSpellByName, reinterpret_cast(Script_QueueSpellByName)); char castSpellByNameNoQueue[] = "CastSpellByNameNoQueue"; - RegisterLuaFunction(castSpellByNameNoQueue, - reinterpret_cast(Offsets::Script_CastSpellByNameNoQueue)); + RegisterLuaFunction(castSpellByNameNoQueue, reinterpret_cast(Script_CastSpellByNameNoQueue)); char queueScript[] = "QueueScript"; - RegisterLuaFunction(queueScript, reinterpret_cast(Offsets::Script_QueueScript)); + RegisterLuaFunction(queueScript, reinterpret_cast(Script_QueueScript)); char isSpellInRange[] = "IsSpellInRange"; - RegisterLuaFunction(isSpellInRange, reinterpret_cast(Offsets::Script_IsSpellInRange)); + RegisterLuaFunction(isSpellInRange, reinterpret_cast(Script_IsSpellInRange)); char isSpellUsable[] = "IsSpellUsable"; - RegisterLuaFunction(isSpellUsable, reinterpret_cast(Offsets::Script_IsSpellUsable)); + RegisterLuaFunction(isSpellUsable, reinterpret_cast(Script_IsSpellUsable)); char getCurrentCastingInfo[] = "GetCurrentCastingInfo"; - RegisterLuaFunction(getCurrentCastingInfo, - reinterpret_cast(Offsets::Script_GetCurrentCastingInfo)); + RegisterLuaFunction(getCurrentCastingInfo, reinterpret_cast(Script_GetCurrentCastingInfo)); char getSpellIdForName[] = "GetSpellIdForName"; - RegisterLuaFunction(getSpellIdForName, - reinterpret_cast(Offsets::Script_GetSpellIdForName)); + RegisterLuaFunction(getSpellIdForName, reinterpret_cast(Script_GetSpellIdForName)); char getSpellNameAndRankForId[] = "GetSpellNameAndRankForId"; - RegisterLuaFunction(getSpellNameAndRankForId, - reinterpret_cast(Offsets::Script_GetSpellNameAndRankForId)); + RegisterLuaFunction(getSpellNameAndRankForId, reinterpret_cast(Script_GetSpellNameAndRankForId)); char getSpellSlotTypeIdForName[] = "GetSpellSlotTypeIdForName"; - RegisterLuaFunction(getSpellSlotTypeIdForName, - reinterpret_cast(Offsets::Script_GetSpellSlotTypeIdForName)); + RegisterLuaFunction(getSpellSlotTypeIdForName, reinterpret_cast(Script_GetSpellSlotTypeIdForName)); char channelStopCastingNextTick[] = "ChannelStopCastingNextTick"; - RegisterLuaFunction(channelStopCastingNextTick, - reinterpret_cast(Offsets::Script_ChannelStopCastingNextTick)); + RegisterLuaFunction(channelStopCastingNextTick, reinterpret_cast(Script_ChannelStopCastingNextTick)); char getNampowerVersion[] = "GetNampowerVersion"; - RegisterLuaFunction(getNampowerVersion, reinterpret_cast(Offsets::Script_GetNampowerVersion)); + RegisterLuaFunction(getNampowerVersion, reinterpret_cast(Script_GetNampowerVersion)); char getItemILevel[] = "GetItemLevel"; - RegisterLuaFunction(getItemILevel, reinterpret_cast(Offsets::Script_GetItemLevel)); + RegisterLuaFunction(getItemILevel, reinterpret_cast(Script_GetItemLevel)); + + char getItemStats[] = "GetItemStats"; + RegisterLuaFunction(getItemStats, reinterpret_cast(Script_GetItemStats)); + + char getSpellRec[] = "GetSpellRec"; + RegisterLuaFunction(getSpellRec, reinterpret_cast(Script_GetSpellRec)); + + char getItemStatsField[] = "GetItemStatsField"; + RegisterLuaFunction(getItemStatsField, reinterpret_cast(Script_GetItemStatsField)); + + char getSpellRecField[] = "GetSpellRecField"; + RegisterLuaFunction(getSpellRecField, reinterpret_cast(Script_GetSpellRecField)); } std::once_flag loadFlag; diff --git a/nampower/main.hpp b/nampower/main.hpp index a1f2425..294f02c 100644 --- a/nampower/main.hpp +++ b/nampower/main.hpp @@ -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); diff --git a/nampower/offsets.hpp b/nampower/offsets.hpp index 063cf97..d565e09 100644 --- a/nampower/offsets.hpp +++ b/nampower/offsets.hpp @@ -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, }; diff --git a/nampower/scripts.cpp b/nampower/scripts.cpp index d1989ab..56b1be8 100644 --- a/nampower/scripts.cpp +++ b/nampower/scripts.cpp @@ -4,6 +4,10 @@ #include "scripts.hpp" #include "offsets.hpp" +#include "items.hpp" +#include "dbc_fields.hpp" +#include "helper.hpp" +#include namespace Nampower { auto const lua_error = reinterpret_cast(Offsets::lua_error); @@ -14,38 +18,17 @@ namespace Nampower { auto const lua_tostring = reinterpret_cast(Offsets::lua_tostring); auto const lua_tonumber = reinterpret_cast(Offsets::lua_tonumber); - auto const lua_pushnumber = reinterpret_cast(Offsets::lua_pushnumber); - auto const lua_pushstring = reinterpret_cast(Offsets::lua_pushstring); + // Export Lua functions for dbc_fields.hpp templates + lua_pushnumberT lua_pushnumber = reinterpret_cast(Offsets::lua_pushnumber); + lua_pushstringT lua_pushstring = reinterpret_cast(Offsets::lua_pushstring); + lua_newtableT lua_newtable = reinterpret_cast(Offsets::lua_newtable); + lua_settableT lua_settable = reinterpret_cast(Offsets::lua_settable); bool gScriptQueued; int gScriptPriority = 1; char *queuedScript; - uint32_t GetSpellSlotAndTypeForName(const char *spellName, uint32_t *spellType) { - auto const getSpellSlotAndType = reinterpret_cast(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(Offsets::CGSpellBook_mKnownSpells) + - spellSlot * 4); - } else { - spellId = *reinterpret_cast(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(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(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; } -} \ No newline at end of file + + 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(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(Offsets::Language); + lua_pushstring(luaState, const_cast("displayName")); + lua_pushstring(luaState, + item->m_displayName[language] ? item->m_displayName[language] : const_cast("")); + lua_settable(luaState, -3); + + lua_pushstring(luaState, const_cast("description")); + lua_pushstring(luaState, item->m_description ? item->m_description : const_cast("")); + 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(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(Offsets::Language); + lua_pushstring(luaState, const_cast("name")); + lua_pushstring(luaState, spell->SpellName[language] ? const_cast(spell->SpellName[language]) + : const_cast("")); + lua_settable(luaState, -3); + + lua_pushstring(luaState, const_cast("rank")); + lua_pushstring(luaState, reinterpret_cast(spell->Rank[language]) + ? const_cast(reinterpret_cast(spell->Rank[language])) + : const_cast("")); + 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(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(item) + itemStatsFields[i].offset; + + switch (itemStatsFields[i].type) { + case FieldType::INT32: + lua_pushnumber(luaState, *reinterpret_cast(fieldPtr)); + return 1; + case FieldType::UINT32: + lua_pushnumber(luaState, *reinterpret_cast(fieldPtr)); + return 1; + case FieldType::FLOAT: + lua_pushnumber(luaState, *reinterpret_cast(fieldPtr)); + return 1; + case FieldType::STRING: { + const char *str = *reinterpret_cast(fieldPtr); + lua_pushstring(luaState, str ? const_cast(str) : const_cast("")); + 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(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(fieldPtr)[j]); + break; + case FieldType::UINT32: + lua_pushnumber(luaState, reinterpret_cast(fieldPtr)[j]); + break; + case FieldType::FLOAT: + lua_pushnumber(luaState, reinterpret_cast(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(Offsets::Language); + lua_pushstring(luaState, + item->m_displayName[language] ? item->m_displayName[language] : const_cast("")); + return 1; + } + if (strcmp(fieldName, "description") == 0) { + lua_pushstring(luaState, item->m_description ? item->m_description : const_cast("")); + 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(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(spell) + spellRecFields[i].offset; + + switch (spellRecFields[i].type) { + case FieldType::INT32: + lua_pushnumber(luaState, *reinterpret_cast(fieldPtr)); + return 1; + case FieldType::UINT32: + lua_pushnumber(luaState, *reinterpret_cast(fieldPtr)); + return 1; + case FieldType::FLOAT: + lua_pushnumber(luaState, *reinterpret_cast(fieldPtr)); + return 1; + case FieldType::UINT64: + lua_pushnumber(luaState, static_cast(*reinterpret_cast(fieldPtr))); + return 1; + case FieldType::STRING: { + const char *str = *reinterpret_cast(fieldPtr); + lua_pushstring(luaState, str ? const_cast(str) : const_cast("")); + 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(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(fieldPtr)[j]); + break; + case FieldType::UINT32: + lua_pushnumber(luaState, reinterpret_cast(fieldPtr)[j]); + break; + case FieldType::FLOAT: + lua_pushnumber(luaState, reinterpret_cast(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(Offsets::Language); + lua_pushstring(luaState, spell->SpellName[language] ? const_cast(spell->SpellName[language]) + : const_cast("")); + return 1; + } + if (strcmp(fieldName, "rank") == 0) { + auto const language = *reinterpret_cast(Offsets::Language); + lua_pushstring(luaState, reinterpret_cast(spell->Rank[language]) + ? const_cast(reinterpret_cast(spell->Rank[language])) + : const_cast("")); + 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(); + // Fall back to original function + return getSpellSlotFromLua(param_1, slot, type); + } + + return 0; + } +} diff --git a/nampower/scripts.hpp b/nampower/scripts.hpp index ab18b8e..ccbd19f 100644 --- a/nampower/scripts.hpp +++ b/nampower/scripts.hpp @@ -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); } \ No newline at end of file diff --git a/nampower/spellevents.cpp b/nampower/spellevents.cpp index 300dba0..e105a39 100644 --- a/nampower/spellevents.cpp +++ b/nampower/spellevents.cpp @@ -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(); - - DEBUG_LOG("Attack icon pressed for guid " << guid); - - return onAttackIconPressed(this_ptr, dummy_edx, 0); - } } \ No newline at end of file diff --git a/nampower/spellevents.hpp b/nampower/spellevents.hpp index 00898b0..44fed0c 100644 --- a/nampower/spellevents.hpp +++ b/nampower/spellevents.hpp @@ -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); } \ No newline at end of file