mirror of
https://github.com/brues-code/nampower.git
synced 2026-10-02 19:50:01 +00:00
275 lines
10 KiB
C++
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;
|
|
}
|
|
}
|