mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
feat: add spell book tab API for SpellBookFrame addon compatibility
Implement GetNumSpellTabs, GetSpellTabInfo, GetSpellBookItemInfo, and GetSpellBookItemName — the core functions SpellBookFrame.lua needs to organize known spells into class skill line tabs. Tabs are built lazily from knownSpells grouped by SkillLineAbility.dbc mappings (category 7 = class). A "General" tab collects spells not in any class skill line. Tabs auto-rebuild when the spell count changes. Also adds SpellBookTab struct and getSpellBookTabs() to GameHandler.
This commit is contained in:
parent
f29ebbdd71
commit
5086520354
3 changed files with 150 additions and 0 deletions
|
|
@ -830,6 +830,14 @@ public:
|
|||
void togglePetSpellAutocast(uint32_t spellId);
|
||||
const std::unordered_set<uint32_t>& getKnownSpells() const { return knownSpells; }
|
||||
|
||||
// Spell book tabs — groups known spells by class skill line for Lua API
|
||||
struct SpellBookTab {
|
||||
std::string name;
|
||||
std::string texture; // icon path
|
||||
std::vector<uint32_t> spellIds; // spells in this tab
|
||||
};
|
||||
const std::vector<SpellBookTab>& getSpellBookTabs();
|
||||
|
||||
// ---- Pet Stable ----
|
||||
struct StabledPet {
|
||||
uint32_t petNumber = 0; // server-side pet number (used for unstable/swap)
|
||||
|
|
@ -3443,6 +3451,8 @@ private:
|
|||
std::unordered_map<uint32_t, std::string> skillLineNames_;
|
||||
std::unordered_map<uint32_t, uint32_t> skillLineCategories_;
|
||||
std::unordered_map<uint32_t, uint32_t> spellToSkillLine_; // spellID -> skillLineID
|
||||
std::vector<SpellBookTab> spellBookTabs_;
|
||||
bool spellBookTabsDirty_ = true;
|
||||
bool skillLineDbcLoaded_ = false;
|
||||
bool skillLineAbilityLoaded_ = false;
|
||||
static constexpr size_t PLAYER_EXPLORED_ZONES_COUNT = 128;
|
||||
|
|
|
|||
|
|
@ -1396,6 +1396,86 @@ static int lua_IsSpellKnown(lua_State* L) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
// --- Spell Book Tab API ---
|
||||
|
||||
// GetNumSpellTabs() → count
|
||||
static int lua_GetNumSpellTabs(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
if (!gh) { lua_pushnumber(L, 0); return 1; }
|
||||
lua_pushnumber(L, gh->getSpellBookTabs().size());
|
||||
return 1;
|
||||
}
|
||||
|
||||
// GetSpellTabInfo(tabIndex) → name, texture, offset, numSpells
|
||||
// tabIndex is 1-based; offset is 1-based global spell book slot
|
||||
static int lua_GetSpellTabInfo(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
int tabIdx = static_cast<int>(luaL_checknumber(L, 1));
|
||||
if (!gh || tabIdx < 1) {
|
||||
lua_pushnil(L); return 1;
|
||||
}
|
||||
const auto& tabs = gh->getSpellBookTabs();
|
||||
if (tabIdx > static_cast<int>(tabs.size())) {
|
||||
lua_pushnil(L); return 1;
|
||||
}
|
||||
// Compute offset: sum of spells in all preceding tabs (1-based)
|
||||
int offset = 0;
|
||||
for (int i = 0; i < tabIdx - 1; ++i)
|
||||
offset += static_cast<int>(tabs[i].spellIds.size());
|
||||
const auto& tab = tabs[tabIdx - 1];
|
||||
lua_pushstring(L, tab.name.c_str()); // name
|
||||
lua_pushstring(L, tab.texture.c_str()); // texture
|
||||
lua_pushnumber(L, offset); // offset (0-based for WoW compat)
|
||||
lua_pushnumber(L, tab.spellIds.size()); // numSpells
|
||||
return 4;
|
||||
}
|
||||
|
||||
// GetSpellBookItemInfo(slot, bookType) → "SPELL", spellId
|
||||
// slot is 1-based global spell book index
|
||||
static int lua_GetSpellBookItemInfo(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
int slot = static_cast<int>(luaL_checknumber(L, 1));
|
||||
if (!gh || slot < 1) {
|
||||
lua_pushstring(L, "SPELL");
|
||||
lua_pushnumber(L, 0);
|
||||
return 2;
|
||||
}
|
||||
const auto& tabs = gh->getSpellBookTabs();
|
||||
int idx = slot; // 1-based
|
||||
for (const auto& tab : tabs) {
|
||||
if (idx <= static_cast<int>(tab.spellIds.size())) {
|
||||
lua_pushstring(L, "SPELL");
|
||||
lua_pushnumber(L, tab.spellIds[idx - 1]);
|
||||
return 2;
|
||||
}
|
||||
idx -= static_cast<int>(tab.spellIds.size());
|
||||
}
|
||||
lua_pushstring(L, "SPELL");
|
||||
lua_pushnumber(L, 0);
|
||||
return 2;
|
||||
}
|
||||
|
||||
// GetSpellBookItemName(slot, bookType) → name, subName
|
||||
static int lua_GetSpellBookItemName(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
int slot = static_cast<int>(luaL_checknumber(L, 1));
|
||||
if (!gh || slot < 1) { lua_pushnil(L); return 1; }
|
||||
const auto& tabs = gh->getSpellBookTabs();
|
||||
int idx = slot;
|
||||
for (const auto& tab : tabs) {
|
||||
if (idx <= static_cast<int>(tab.spellIds.size())) {
|
||||
uint32_t spellId = tab.spellIds[idx - 1];
|
||||
const std::string& name = gh->getSpellName(spellId);
|
||||
lua_pushstring(L, name.empty() ? "Unknown" : name.c_str());
|
||||
lua_pushstring(L, ""); // subName/rank
|
||||
return 2;
|
||||
}
|
||||
idx -= static_cast<int>(tab.spellIds.size());
|
||||
}
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lua_GetSpellCooldown(lua_State* L) {
|
||||
auto* gh = getGameHandler(L);
|
||||
if (!gh) { lua_pushnumber(L, 0); lua_pushnumber(L, 0); return 2; }
|
||||
|
|
@ -3915,6 +3995,10 @@ void LuaEngine::registerCoreAPI() {
|
|||
{"IsAddonMessagePrefixRegistered", lua_IsAddonMessagePrefixRegistered},
|
||||
{"CastSpellByName", lua_CastSpellByName},
|
||||
{"IsSpellKnown", lua_IsSpellKnown},
|
||||
{"GetNumSpellTabs", lua_GetNumSpellTabs},
|
||||
{"GetSpellTabInfo", lua_GetSpellTabInfo},
|
||||
{"GetSpellBookItemInfo", lua_GetSpellBookItemInfo},
|
||||
{"GetSpellBookItemName", lua_GetSpellBookItemName},
|
||||
{"GetSpellCooldown", lua_GetSpellCooldown},
|
||||
{"GetSpellPowerCost", lua_GetSpellPowerCost},
|
||||
{"IsSpellInRange", lua_IsSpellInRange},
|
||||
|
|
|
|||
|
|
@ -23033,6 +23033,62 @@ void GameHandler::loadSkillLineAbilityDbc() {
|
|||
}
|
||||
}
|
||||
|
||||
const std::vector<GameHandler::SpellBookTab>& GameHandler::getSpellBookTabs() {
|
||||
// Rebuild when spell count changes (learns/unlearns)
|
||||
static size_t lastSpellCount = 0;
|
||||
if (lastSpellCount == knownSpells.size() && !spellBookTabsDirty_)
|
||||
return spellBookTabs_;
|
||||
lastSpellCount = knownSpells.size();
|
||||
spellBookTabsDirty_ = false;
|
||||
spellBookTabs_.clear();
|
||||
|
||||
static constexpr uint32_t SKILLLINE_CATEGORY_CLASS = 7;
|
||||
|
||||
// Group known spells by class skill line
|
||||
std::map<uint32_t, std::vector<uint32_t>> bySkillLine;
|
||||
std::vector<uint32_t> general;
|
||||
|
||||
for (uint32_t spellId : knownSpells) {
|
||||
auto slIt = spellToSkillLine_.find(spellId);
|
||||
if (slIt != spellToSkillLine_.end()) {
|
||||
uint32_t skillLineId = slIt->second;
|
||||
auto catIt = skillLineCategories_.find(skillLineId);
|
||||
if (catIt != skillLineCategories_.end() && catIt->second == SKILLLINE_CATEGORY_CLASS) {
|
||||
bySkillLine[skillLineId].push_back(spellId);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
general.push_back(spellId);
|
||||
}
|
||||
|
||||
// Sort spells within each group by name
|
||||
auto byName = [this](uint32_t a, uint32_t b) {
|
||||
return getSpellName(a) < getSpellName(b);
|
||||
};
|
||||
|
||||
// "General" tab first (spells not in a class skill line)
|
||||
if (!general.empty()) {
|
||||
std::sort(general.begin(), general.end(), byName);
|
||||
spellBookTabs_.push_back({"General", "Interface\\Icons\\INV_Misc_Book_09", std::move(general)});
|
||||
}
|
||||
|
||||
// Class skill line tabs, sorted by name
|
||||
std::vector<std::pair<std::string, std::vector<uint32_t>>> named;
|
||||
for (auto& [skillLineId, spells] : bySkillLine) {
|
||||
auto nameIt = skillLineNames_.find(skillLineId);
|
||||
std::string tabName = (nameIt != skillLineNames_.end()) ? nameIt->second : "Unknown";
|
||||
std::sort(spells.begin(), spells.end(), byName);
|
||||
named.emplace_back(std::move(tabName), std::move(spells));
|
||||
}
|
||||
std::sort(named.begin(), named.end(), [](const auto& a, const auto& b) { return a.first < b.first; });
|
||||
|
||||
for (auto& [name, spells] : named) {
|
||||
spellBookTabs_.push_back({std::move(name), "Interface\\Icons\\INV_Misc_Book_09", std::move(spells)});
|
||||
}
|
||||
|
||||
return spellBookTabs_;
|
||||
}
|
||||
|
||||
void GameHandler::categorizeTrainerSpells() {
|
||||
trainerTabs_.clear();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue