mirror of
https://github.com/brues-code/nampower.git
synced 2026-09-30 02:36:02 +00:00
add logging and disenchant all ability
This commit is contained in:
+63
@@ -11,6 +11,7 @@ For installation, configuration, and general usage information, see the main [RE
|
||||
- [Spell Casting and Queuing](#spell-casting-and-queuing)
|
||||
- [Cast Information](#cast-information)
|
||||
- [Cooldown Information](#cooldown-information)
|
||||
- [Utility Functions](#utility-functions)
|
||||
---
|
||||
|
||||
## Performance Optimization - Table References
|
||||
@@ -892,3 +893,65 @@ should print 90 for atiesh
|
||||
|
||||
#### ChannelStopCastingNextTick()
|
||||
Will stop channeling early on the next tick if you have queue channeling spells enabled and try to cast a spell before the next tick (didn't know how to cancel channels without casting another spell). Uses your ChannelLatencyReductionPercentage to determine when to stop the channel.
|
||||
|
||||
---
|
||||
|
||||
### Utility Functions
|
||||
|
||||
#### DisenchantAll(itemIdOrName) or DisenchantAll(quality)
|
||||
Automatically disenchants items in your inventory. Can disenchant a specific item by ID/name, or all weapons and armor of a specified quality.
|
||||
|
||||
**⚠️ WARNING ⚠️**
|
||||
**THIS FUNCTION WILL AUTOMATICALLY DISENCHANT ITEMS WITHOUT CONFIRMATION!**
|
||||
- **Use at your own risk** - there is no undo for disenchanting
|
||||
- Only disenchants items from **player inventory bags (0-4)** - does NOT touch bank items
|
||||
- Make sure you have the Disenchant spell and the items are disenchantable before using
|
||||
- **Always double-check your bags** before running this command
|
||||
|
||||
**Parameters:**
|
||||
|
||||
**Mode 1: Disenchant by Item ID or Name**
|
||||
- `itemIdOrName` (number|string): Item ID (number) or item name (string)
|
||||
- Disenchants all copies of the specified item found in your bags
|
||||
- Works on any disenchantable item type (weapons, armor, etc.)
|
||||
|
||||
**Mode 2: Disenchant by Quality** *(weapons and armor only)*
|
||||
- `quality` (string): Must be either:
|
||||
- `"greens"` - Disenchants all uncommon (green) quality weapons and armor
|
||||
- `"blues"` - Disenchants all rare (blue) quality weapons and armor
|
||||
- Only affects **weapons** (class 2) and **armor** (class 4)
|
||||
|
||||
**Returns:**
|
||||
- `1` if the first disenchant succeeded
|
||||
- `0` if no matching items were found or the disenchant failed
|
||||
|
||||
**Behavior:**
|
||||
- Searches player inventory bags (0-4) only - **bank items are protected**
|
||||
- Finds the first matching item in your inventory
|
||||
- Casts Disenchant spell on that item
|
||||
- Automatically continues disenchanting matching items every 4 seconds
|
||||
- Stops when no more matching items are found or an error occurs
|
||||
|
||||
**Examples:**
|
||||
```lua
|
||||
-- Disenchant all green weapons and armor in your bags
|
||||
DisenchantAll("greens")
|
||||
|
||||
-- Disenchant all blue weapons and armor in your bags
|
||||
DisenchantAll("blues")
|
||||
|
||||
-- Disenchant a specific item by ID
|
||||
DisenchantAll(12345)
|
||||
|
||||
-- Disenchant a specific item by name
|
||||
DisenchantAll("Glowing Brightwood Staff")
|
||||
```
|
||||
|
||||
**Important Notes:**
|
||||
- The function runs continuously until all matching items are disenchanted
|
||||
- Items must be in your inventory bags (0-4), not in bank, keyring, or equipped
|
||||
- When using quality mode ("greens"/"blues"), only weapons and armor are affected
|
||||
- When using item ID/name mode, any disenchantable item can be targeted
|
||||
- Make sure you have enough bag space for the disenchanting materials
|
||||
- The function will stop if you run out of matching items or if the disenchant spell fails
|
||||
- **REVIEW YOUR BAGS CAREFULLY BEFORE USE** - disenchanting cannot be undone!
|
||||
|
||||
+9687
-9650
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -96,7 +96,7 @@ namespace game {
|
||||
const SpellRec *GetSpellInfo(uint32_t spellId) {
|
||||
auto const spellDb = reinterpret_cast<WowClientDB<SpellRec> *>(Offsets::SpellDb);
|
||||
|
||||
if (spellId > spellDb->m_maxId)
|
||||
if (spellId > spellDb->m_maxId || spellId <= 0)
|
||||
return nullptr;
|
||||
|
||||
return spellDb->m_recordsById[spellId];
|
||||
|
||||
@@ -336,6 +336,17 @@ namespace game {
|
||||
int charges;
|
||||
};
|
||||
|
||||
enum ItemQualities
|
||||
{
|
||||
ITEM_QUALITY_POOR = 0, // GREY
|
||||
ITEM_QUALITY_NORMAL = 1, // WHITE
|
||||
ITEM_QUALITY_UNCOMMON = 2, // GREEN
|
||||
ITEM_QUALITY_RARE = 3, // BLUE
|
||||
ITEM_QUALITY_EPIC = 4, // PURPLE
|
||||
ITEM_QUALITY_LEGENDARY = 5, // ORANGE
|
||||
ITEM_QUALITY_ARTIFACT = 6 // LIGHT YELLOW
|
||||
};
|
||||
|
||||
struct CGObject {
|
||||
void *vftable;
|
||||
uintptr_t *m_data;
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ namespace Nampower {
|
||||
lua_tonumberT lua_tonumber = reinterpret_cast<lua_tonumberT>(Offsets::lua_tonumber);
|
||||
lua_pushnumberT lua_pushnumber = reinterpret_cast<lua_pushnumberT>(Offsets::lua_pushnumber);
|
||||
lua_pushstringT lua_pushstring = reinterpret_cast<lua_pushstringT>(Offsets::lua_pushstring);
|
||||
lua_pushbooleanT lua_pushboolean = reinterpret_cast<lua_pushbooleanT>(Offsets::lua_pushboolean);
|
||||
// lua_pushbooleanT lua_pushboolean = reinterpret_cast<lua_pushbooleanT>(Offsets::lua_pushboolean); doesn't seem to work properly
|
||||
lua_pushnilT lua_pushnil = reinterpret_cast<lua_pushnilT>(Offsets::lua_pushnil);
|
||||
lua_newtableT lua_newtable = reinterpret_cast<lua_newtableT>(Offsets::lua_newtable);
|
||||
lua_settableT lua_settable = reinterpret_cast<lua_settableT>(Offsets::lua_settable);
|
||||
|
||||
@@ -217,6 +217,91 @@ namespace Nampower {
|
||||
return result;
|
||||
}
|
||||
|
||||
PlayerItemSearchResult FindPlayerDisenchantItem(int32_t quality) {
|
||||
PlayerItemSearchResult result{};
|
||||
|
||||
auto const getBagItem = reinterpret_cast<CGBag_C_GetItemAtSlotT>(Offsets::CGBag_C_GetItemAtSlot);
|
||||
auto const getContainerGuid = reinterpret_cast<GetContainerGuidT>(Offsets::GetContainerGuid);
|
||||
|
||||
auto playerGuid = game::ClntObjMgrGetActivePlayerGuid();
|
||||
auto playerUnit = game::GetObjectPtr(playerGuid);
|
||||
if (!playerUnit) {
|
||||
return result;
|
||||
}
|
||||
|
||||
auto inventory = game::GetPlayerInventoryPtr(playerUnit);
|
||||
|
||||
auto matchesItem = [&](game::CGItem_C *item) {
|
||||
if (!item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t itemId = game::GetItemId(item);
|
||||
auto *itemStats = GetItemStats(itemId);
|
||||
if (!itemStats) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if item is weapon or armor
|
||||
if (itemStats->m_class != game::ITEM_CLASS_WEAPON &&
|
||||
itemStats->m_class != game::ITEM_CLASS_ARMOR) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (itemStats->m_quality != quality) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
for (uint32_t slot = 0; slot <= 18; slot++) {
|
||||
auto item = getBagItem(inventory, slot);
|
||||
if (matchesItem(item)) {
|
||||
result.item = item;
|
||||
result.bagIndex = EQUIPPED_BAG_INDEX;
|
||||
result.slot = slot;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint32_t slot = 23; slot <= 38; slot++) {
|
||||
auto item = getBagItem(inventory, slot);
|
||||
if (matchesItem(item)) {
|
||||
result.item = item;
|
||||
result.bagIndex = 0;
|
||||
result.slot = slot;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t bagIndex = 1; bagIndex <= 4; bagIndex++) {
|
||||
uint64_t containerGuid = getContainerGuid(bagIndex - 1);
|
||||
if (containerGuid == 0) continue;
|
||||
|
||||
auto containerPtr = game::ClntObjMgrObjectPtr(game::TYPEMASK_CONTAINER, containerGuid);
|
||||
if (!containerPtr) continue;
|
||||
|
||||
auto bagPtr = GetBagPtrFromContainer(containerPtr);
|
||||
if (!bagPtr) continue;
|
||||
|
||||
auto bagSize = *bagPtr;
|
||||
for (uint32_t slot = 0; slot < bagSize; slot++) {
|
||||
auto item = getBagItem(bagPtr, slot);
|
||||
if (matchesItem(item)) {
|
||||
result.item = item;
|
||||
result.bagIndex = bagIndex;
|
||||
result.slot = slot;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// don't search bank
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string escapeJsonString(const char *str) {
|
||||
if (!str) return "null";
|
||||
|
||||
|
||||
@@ -100,6 +100,7 @@ namespace Nampower {
|
||||
void CacheItemNameToId(const char *itemName, uint32_t itemId);
|
||||
bool DoesItemMatch(uint32_t itemId, uint32_t searchItemId, const char *searchItemName);
|
||||
PlayerItemSearchResult FindPlayerItem(uint32_t searchItemId, const char *searchItemName);
|
||||
PlayerItemSearchResult FindPlayerDisenchantItem(int32_t quality);
|
||||
uintptr_t *GetBagPtrFromContainer(uintptr_t *containerPtr);
|
||||
|
||||
}
|
||||
|
||||
+21
-2
@@ -64,6 +64,10 @@ namespace Nampower {
|
||||
|
||||
uint32_t gBufferTimeMs; // adjusts dynamically depending on errors
|
||||
|
||||
uint32_t gDisenchantItemId = 0;
|
||||
int32_t gDisenchantQuality = -1; // -1 = unset
|
||||
uint32_t gNextDisenchantTimeMs = 0;
|
||||
|
||||
bool gForceQueueCast;
|
||||
bool gNoQueueCast;
|
||||
bool gQueuesProcessed;
|
||||
@@ -97,7 +101,7 @@ namespace Nampower {
|
||||
|
||||
std::unique_ptr<hadesmem::PatchDetour<SetCVarT> > gSetCVarDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<CGSpellBook_CastSpellT> > gCGSpellBook_CastSpellDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<CastSpellT> > gCastDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<Spell_C_CastSpellT> > gCastDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<SendCastT> > gSendCastDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<CancelSpellT> > gCancelSpellDetour;
|
||||
std::unique_ptr<hadesmem::PatchDetour<SignalEventT> > gSignalEventDetour;
|
||||
@@ -520,6 +524,18 @@ namespace Nampower {
|
||||
// script ran, stop processing
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check for disenchant all
|
||||
if (gDisenchantItemId != 0 || gDisenchantQuality != -1) {
|
||||
auto currentTime = GetTime();
|
||||
|
||||
// Try to disenchant when timer has elapsed
|
||||
if (gNextDisenchantTimeMs <= currentTime) {
|
||||
if (TryDisenchant()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gQueuesProcessed = true;
|
||||
@@ -1121,7 +1137,7 @@ namespace Nampower {
|
||||
process, Offsets::CGSpellBook_CastSpell, &CGSpellBook_CastSpellHook);
|
||||
gCGActionBar_UseActionDetour = createHook<CGActionBar_UseActionT>(
|
||||
process, Offsets::CGActionBar_UseAction, &CGActionBar_UseActionHook);
|
||||
gCastDetour = createHook<CastSpellT>(process, Offsets::Spell_C_CastSpell, &Spell_C_CastSpellHook);
|
||||
gCastDetour = createHook<Spell_C_CastSpellT>(process, Offsets::Spell_C_CastSpell, &Spell_C_CastSpellHook);
|
||||
gSendCastDetour = createHook<SendCastT>(process, Offsets::SendCast, &SendCastHook);
|
||||
gCancelSpellDetour = createHook<CancelSpellT>(process, Offsets::CancelSpell, &CancelSpellHook);
|
||||
gCastResultHandlerDetour = createHook<PacketHandlerT>(process, Offsets::CastResultHandler,
|
||||
@@ -1373,6 +1389,9 @@ namespace Nampower {
|
||||
|
||||
char getTrinkets[] = "GetTrinkets";
|
||||
RegisterLuaFunction(getTrinkets, reinterpret_cast<uintptr_t *>(Script_GetTrinkets));
|
||||
|
||||
char disenchantAll[] = "DisenchantAll";
|
||||
RegisterLuaFunction(disenchantAll, reinterpret_cast<uintptr_t *>(Script_DisenchantAll));
|
||||
}
|
||||
|
||||
void load() {
|
||||
|
||||
+7
-3
@@ -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 = 20;
|
||||
constexpr uint32_t PATCH_VERSION = 1;
|
||||
constexpr uint32_t MINOR_VERSION = 21;
|
||||
constexpr uint32_t PATCH_VERSION = 0;
|
||||
|
||||
constexpr int32_t LUA_REGISTRYINDEX = -10000;
|
||||
constexpr int32_t LUA_GLOBALSINDEX = -10001;
|
||||
@@ -42,6 +42,10 @@ namespace Nampower {
|
||||
|
||||
extern uint32_t gBufferTimeMs; // adjusts dynamically depending on errors
|
||||
|
||||
extern uint32_t gDisenchantItemId;
|
||||
extern int32_t gDisenchantQuality;
|
||||
extern uint32_t gNextDisenchantTimeMs;
|
||||
|
||||
extern bool gForceQueueCast;
|
||||
extern bool gNoQueueCast;
|
||||
|
||||
@@ -70,7 +74,7 @@ namespace Nampower {
|
||||
using RangeCheckSelectedT = bool (__fastcall *)(uintptr_t *playerUnit, const game::SpellRec *,
|
||||
std::uint64_t targetGuid, char ignoreErrors);
|
||||
using CGSpellBook_CastSpellT = void (__fastcall *)(uint32_t spellSlot, int bookType, uint64_t target);
|
||||
using CastSpellT = bool (__fastcall *)(uintptr_t *playerUnit, uint32_t spellId, uintptr_t *item,
|
||||
using Spell_C_CastSpellT = bool (__fastcall *)(uintptr_t *playerUnit, uint32_t spellId, game::CGItem_C *item,
|
||||
std::uint64_t targetGuid);
|
||||
using SendCastT = void (__fastcall *)(game::SpellCast *, char unk);
|
||||
using CancelSpellT = void (__fastcall *)(bool, bool, game::SpellCastResult);
|
||||
|
||||
@@ -611,4 +611,127 @@ namespace Nampower {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool TryDisenchant() {
|
||||
DEBUG_LOG("Trying disenchant with quality filter " << gDisenchantQuality << " itemId " << gDisenchantItemId);
|
||||
if (gDisenchantQuality < 0 && gDisenchantItemId == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the item in player inventory
|
||||
PlayerItemSearchResult itemSearchResult;
|
||||
if (gDisenchantItemId != 0) {
|
||||
// Item-specific disenchanting
|
||||
itemSearchResult = FindPlayerItem(gDisenchantItemId, nullptr);
|
||||
} else {
|
||||
// Quality-based disenchanting (weapons and armor only)
|
||||
itemSearchResult = FindPlayerDisenchantItem(gDisenchantQuality);
|
||||
}
|
||||
|
||||
if (!itemSearchResult.found()) {
|
||||
// Item not found, stop disenchanting
|
||||
DEBUG_LOG("No disenchantable item found, stopping disenchant");
|
||||
gDisenchantItemId = 0;
|
||||
gDisenchantQuality = -1;
|
||||
gNextDisenchantTimeMs = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the item GUID
|
||||
auto item = itemSearchResult.item;
|
||||
if (!item || !item->object.m_obj) {
|
||||
// Invalid item, stop disenchanting
|
||||
DEBUG_LOG("Invalid item, stopping disenchant");
|
||||
gDisenchantItemId = 0;
|
||||
gDisenchantQuality = -1;
|
||||
gNextDisenchantTimeMs = 0;
|
||||
return false;
|
||||
}
|
||||
uint64_t itemGuid = item->object.m_obj->m_guid;
|
||||
|
||||
// Get player unit for casting
|
||||
auto playerGuid = game::ClntObjMgrGetActivePlayerGuid();
|
||||
auto playerUnit = game::GetObjectPtr(playerGuid);
|
||||
|
||||
if (!playerUnit) {
|
||||
// No player unit, stop disenchanting
|
||||
DEBUG_LOG("No player unit, stopping disenchant");
|
||||
gDisenchantItemId = 0;
|
||||
gDisenchantQuality = -1;
|
||||
gNextDisenchantTimeMs = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set timer for next disenchant attempt (5 seconds to allow time for inventory to update)
|
||||
auto currentTime = GetTime();
|
||||
gNextDisenchantTimeMs = currentTime + 5000;
|
||||
|
||||
// Cast Disenchant spell (13262) on the item
|
||||
auto const castSpell = reinterpret_cast<Spell_C_CastSpellT>(Offsets::Spell_C_CastSpell);
|
||||
bool success = castSpell(playerUnit, 13262, nullptr, itemGuid);
|
||||
|
||||
if (!success) {
|
||||
// Cast failed, stop disenchanting
|
||||
DEBUG_LOG("Cast failed, stopping disenchant");
|
||||
gDisenchantItemId = 0;
|
||||
gDisenchantQuality = -1;
|
||||
gNextDisenchantTimeMs = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t Script_DisenchantAll(uintptr_t *luaState) {
|
||||
luaState = GetLuaStatePtr();
|
||||
|
||||
if (!lua_isnumber(luaState, 1) && !lua_isstring(luaState, 1)) {
|
||||
lua_error(luaState, "Usage: DisenchantAll(itemIdOrName) or DisenchantAll(quality)\n"
|
||||
"quality: \"greens\" for uncommon, \"blues\" for rare\n"
|
||||
"itemIdOrName: item ID (number) or item name (string)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Reset both modes
|
||||
gDisenchantItemId = 0;
|
||||
gDisenchantQuality = -1;
|
||||
|
||||
// Check number first (lua_isstring will also match numbers)
|
||||
if (lua_isnumber(luaState, 1)) {
|
||||
// It's an item ID
|
||||
gDisenchantItemId = static_cast<uint32_t>(lua_tonumber(luaState, 1));
|
||||
} else if (lua_isstring(luaState, 1)) {
|
||||
// It's a string - check if it's quality-based or item name
|
||||
const char *param = lua_tostring(luaState, 1);
|
||||
if (_stricmp(param, "greens") == 0) {
|
||||
gDisenchantQuality = game::ITEM_QUALITY_UNCOMMON; // 2
|
||||
} else if (_stricmp(param, "blues") == 0) {
|
||||
gDisenchantQuality = game::ITEM_QUALITY_RARE; // 3
|
||||
} else {
|
||||
// It's an item name
|
||||
uint32_t cachedItemId = GetItemIdFromCache(param);
|
||||
if (cachedItemId != 0) {
|
||||
gDisenchantItemId = cachedItemId;
|
||||
} else {
|
||||
// Try to find the item to get its ID
|
||||
auto itemSearchResult = FindPlayerItem(0, param);
|
||||
if (itemSearchResult.found()) {
|
||||
gDisenchantItemId = game::GetItemId(itemSearchResult.item);
|
||||
// Cache the item name for future lookups
|
||||
CacheItemNameToId(param, gDisenchantItemId);
|
||||
} else {
|
||||
// Item not found
|
||||
lua_pushnumber(luaState, 0);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Try the first disenchant
|
||||
bool success = TryDisenchant();
|
||||
|
||||
lua_pushnumber(luaState, success ? 1 : 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -30,5 +30,9 @@ namespace Nampower {
|
||||
|
||||
uint32_t Script_StartItemExport(uintptr_t *luaState);
|
||||
|
||||
bool TryDisenchant();
|
||||
|
||||
uint32_t Script_DisenchantAll(uintptr_t *luaState);
|
||||
|
||||
bool RunQueuedScript(int priority);
|
||||
}
|
||||
|
||||
+17
-5
@@ -299,7 +299,7 @@ namespace Nampower {
|
||||
void SaveCastParams(CastSpellParams *params,
|
||||
uint32_t *casterUnit,
|
||||
uint32_t spellId,
|
||||
uintptr_t *item,
|
||||
game::CGItem_C *item,
|
||||
std::uint64_t guid,
|
||||
uint32_t gcDCategory,
|
||||
uint32_t castTimeMs,
|
||||
@@ -444,7 +444,7 @@ namespace Nampower {
|
||||
|
||||
bool
|
||||
Spell_C_CastSpellHook(hadesmem::PatchDetourBase *detour, uint32_t *casterUnit, uint32_t spellId,
|
||||
uintptr_t *item,
|
||||
game::CGItem_C *item,
|
||||
std::uint64_t guid) {
|
||||
// save the detour to allow quickly calling this hook
|
||||
castSpellDetour = detour;
|
||||
@@ -459,7 +459,7 @@ namespace Nampower {
|
||||
DEBUG_LOG("Ignoring non active player cast of spell " << game::GetSpellName(spellId) << " " << spellId);
|
||||
// just call original function if caster is not the active player
|
||||
// happens with Doomguard rain of fire
|
||||
auto const castSpell = detour->GetTrampolineT<CastSpellT>();
|
||||
auto const castSpell = detour->GetTrampolineT<Spell_C_CastSpellT>();
|
||||
return castSpell(casterUnit, spellId, item, guid);
|
||||
}
|
||||
|
||||
@@ -478,6 +478,7 @@ namespace Nampower {
|
||||
if (casterUnit == playerUnit && gUserSettings.preventMountingWhenBuffCapped) {
|
||||
// Check for mount spell when player is buff capped
|
||||
if (SpellIsMounting(spell)) {
|
||||
// check if they are not already mounted
|
||||
if (playerUnit && game::UnitIsBuffCapped(playerUnit) && game::UnitGetMountDisplayId(playerUnit) == 0) {
|
||||
DEBUG_LOG("Blocking mount spell " << spellName << " due to buff cap");
|
||||
|
||||
@@ -562,7 +563,7 @@ namespace Nampower {
|
||||
gNextCastId++;
|
||||
|
||||
// try to cast the spell
|
||||
auto const castSpell = detour->GetTrampolineT<CastSpellT>();
|
||||
auto const castSpell = detour->GetTrampolineT<Spell_C_CastSpellT>();
|
||||
auto ret = castSpell(casterUnit, spellId, item, guid);
|
||||
|
||||
TriggerSpellCastEvent(ret, spellId, ON_SWING, guid, itemId);
|
||||
@@ -587,7 +588,7 @@ namespace Nampower {
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto const castSpell = detour->GetTrampolineT<CastSpellT>();
|
||||
auto const castSpell = detour->GetTrampolineT<Spell_C_CastSpellT>();
|
||||
|
||||
auto effectiveCastEndMs = EffectiveCastEndMs();
|
||||
auto remainingEffectiveCastTime = (effectiveCastEndMs > currentTime) ? effectiveCastEndMs - currentTime : 0;
|
||||
@@ -877,6 +878,10 @@ namespace Nampower {
|
||||
}
|
||||
|
||||
bool doesSpellApplyAura(const game::SpellRec *spell) {
|
||||
if (!spell) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned int i: spell->Effect) {
|
||||
switch (i) {
|
||||
case game::SPELL_EFFECT_APPLY_AURA:
|
||||
@@ -977,6 +982,11 @@ namespace Nampower {
|
||||
auto const castByActivePlayer = activePlayerGuid == *casterGUID;
|
||||
auto const spell = game::GetSpellInfo(spellId);
|
||||
|
||||
if (!spell || spell->Id <= 0) {
|
||||
DEBUG_LOG("Unable to determine spell information in SpellGo for " << spellId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (castByActivePlayer) {
|
||||
auto const currentTime = GetTime();
|
||||
// only care about our own casts
|
||||
@@ -1006,6 +1016,8 @@ namespace Nampower {
|
||||
if (gUserSettings.enableAuraCastEvents && doesSpellApplyAura(spell)) {
|
||||
auto targetGuidVal = targetGUID ? *targetGUID : *casterGUID;
|
||||
|
||||
DEBUG_LOG("Triggering aura cast event for " << game::GetSpellName(spell->Id)
|
||||
<< " (" << spell->Id << ") caster: " << *casterGUID << " target: " << targetGuidVal);
|
||||
TriggerAuraCastEvent(spell, *casterGUID, targetGuidVal, activePlayerGuid, castByActivePlayer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Nampower {
|
||||
|
||||
void CGActionBar_UseActionHook(hadesmem::PatchDetourBase *detour, uint32_t actionSlot, int param_2, int param_3);
|
||||
|
||||
bool Spell_C_CastSpellHook(hadesmem::PatchDetourBase *detour, uint32_t *casterUnit, uint32_t spellId, uintptr_t *item,
|
||||
bool Spell_C_CastSpellHook(hadesmem::PatchDetourBase *detour, uint32_t *casterUnit, uint32_t spellId, game::CGItem_C *item,
|
||||
std::uint64_t guid);
|
||||
|
||||
void
|
||||
|
||||
@@ -52,6 +52,14 @@ namespace Nampower {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for Disenchant (13262) failure and stop the disenchant loop
|
||||
if (spellId == 13262) {
|
||||
DEBUG_LOG("Disenchant spell failed with result " << int(spellResult) << ", stopping disenchant loop");
|
||||
gDisenchantItemId = 0;
|
||||
gDisenchantQuality = -1;
|
||||
gNextDisenchantTimeMs = 0;
|
||||
}
|
||||
|
||||
// ignore SPELL_FAILED_CANT_DO_THAT_YET for arcane surge gets sent all the time after success
|
||||
if (spellResult == game::SpellCastResult::SPELL_FAILED_CANT_DO_THAT_YET &&
|
||||
(spellId == 51933 ||
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@ struct CastSpellParams {
|
||||
|
||||
uint32_t *casterUnit;
|
||||
uint32_t spellId;
|
||||
uintptr_t *item;
|
||||
game::CGItem_C *item;
|
||||
uint64_t guid;
|
||||
/* *********************** */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user