Files
nampower/nampower/helper.cpp
T
2025-12-09 13:30:01 -08:00

275 lines
10 KiB
C++

//
// Created by pmacc on 9/21/2024.
//
#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 *>(static_cast<uint32_t>(Offsets::CGSpellBook_mKnownSpells) + slot * 4);
} else if (type == 1) {
spellId = *reinterpret_cast<uint32_t *>(static_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<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 *>(static_cast<uint32_t>(Offsets::CGSpellBook_mKnownSpells) +
spellSlot * 4);
} else {
spellId = *reinterpret_cast<uint32_t *>(static_cast<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 *>(static_cast<uint32_t>(Offsets::CGSpellBook_mKnownSpells) + slot * 4);
} else {
slotSpellId = *reinterpret_cast<uint32_t *>(static_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 *>(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
return false;
}
return spell->StartRecoveryCategory == 133;
}
bool SpellIsChanneling(const game::SpellRec *spell) {
return spell->AttributesEx & game::SPELL_ATTR_EX_IS_CHANNELED ||
spell->AttributesEx & game::SPELL_ATTR_EX_IS_SELF_CHANNELED;
}
bool SpellIsTargeting(const game::SpellRec *spell) {
return spell->Targets & game::SpellCastTargetFlags::TARGET_FLAG_SOURCE_LOCATION ||
spell->Targets & game::SpellCastTargetFlags::TARGET_FLAG_DEST_LOCATION;
}
bool SpellIsOnSwing(const game::SpellRec *spell) {
return spell->Attributes & game::SPELL_ATTR_ON_NEXT_SWING_1;
}
bool SpellIsAttackTradeskillOrEnchant(const game::SpellRec *spell) {
return (
spell->Effect[0] == game::SpellEffects::SPELL_EFFECT_ATTACK ||
spell->Attributes & game::SpellAttributes::SPELL_ATTR_TRADESPELL ||
spell->Effect[0] == game::SpellEffects::SPELL_EFFECT_TRADE_SKILL ||
spell->Effect[0] == game::SpellEffects::SPELL_EFFECT_TRANS_DOOR ||
spell->Effect[0] == game::SpellEffects::SPELL_EFFECT_ENCHANT_ITEM ||
spell->Effect[0] == game::SpellEffects::SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY ||
spell->Effect[0] == game::SpellEffects::SPELL_EFFECT_CREATE_ITEM ||
spell->Effect[0] == game::SpellEffects::SPELL_EFFECT_OPEN_LOCK ||
spell->Effect[0] == game::SpellEffects::SPELL_EFFECT_OPEN_LOCK_ITEM);
}
// if the spell is off cooldown, this will return the gcd, otherwise the cooldown
uint32_t GetGcdOrCooldownForSpell(uint32_t spellId) {
uint32_t duration;
uint64_t startTime;
uint32_t enable;
auto const getSpellCooldown = reinterpret_cast<Spell_C_GetSpellCooldownT>(Offsets::Spell_C_GetSpellCooldown);
getSpellCooldown(spellId, 0, &duration, &startTime, &enable);
if (spellId == 51714 && duration == 1500) {
// power overwhelming gcd removed but client not updated
return 0;
}
return duration;
}
uint32_t GetRemainingGcdOrCooldownForSpell(uint32_t spellId) {
uint32_t duration;
uint64_t startTime;
uint32_t enable;
auto const getSpellCooldown = reinterpret_cast<Spell_C_GetSpellCooldownT>(Offsets::Spell_C_GetSpellCooldown);
getSpellCooldown(spellId, 0, &duration, &startTime, &enable);
startTime = startTime & 0XFFFFFFFF; // only look at same bits that lua does
if (startTime != 0) {
auto currentLuaTime = GetWowTimeMs() & 0XFFFFFFFF;
auto remaining = (startTime + duration) - currentLuaTime;
return uint32_t(remaining);
}
return 0;
}
uint32_t GetRemainingCooldownForSpell(uint32_t spellId) {
uint32_t duration;
uint64_t startTime;
uint32_t enable;
auto const getSpellCooldown = reinterpret_cast<Spell_C_GetSpellCooldownT>(Offsets::Spell_C_GetSpellCooldown);
getSpellCooldown(spellId, 0, &duration, &startTime, &enable);
startTime = startTime & 0XFFFFFFFF; // only look at same bits that lua does
// ignore gcd cooldown by looking for duration > 1.5
if (startTime != 0 && duration > 1.5) {
auto currentLuaTime = GetWowTimeMs() & 0XFFFFFFFF;
auto remaining = (startTime + duration) - currentLuaTime;
return uint32_t(remaining);
}
return 0;
}
bool IsSpellOnCooldown(uint32_t spellId) {
uint32_t duration;
uint64_t startTime;
uint32_t enable;
auto const getSpellCooldown = reinterpret_cast<Spell_C_GetSpellCooldownT>(Offsets::Spell_C_GetSpellCooldown);
getSpellCooldown(spellId, 0, &duration, &startTime, &enable);
return startTime != 0 && duration > 1.5;
}
char *ConvertGuidToString(uint64_t guid) {
char *guidStr = new char[21]; // 2 for 0x prefix, 18 for the number, and 1 for '\0'
std::snprintf(guidStr, 21, "0x%016llX", static_cast<unsigned long long>(guid));
return guidStr;
}
float GetNameplateDistance() {
auto const distanceSquared = *reinterpret_cast<float *>(Offsets::NameplateDistance);
return sqrtf(distanceSquared);
}
void SetNameplateDistance(float distance) {
*reinterpret_cast<float *>(Offsets::NameplateDistance) = distance * distance;
}
}