Kelsidavis-WoWee/include/ui/spellbook_screen.hpp
Kelsi c2b271df6f Fix spellbook tabs race condition with DBC lazy loading
lookupSpellName() (called by action bar tooltips) was triggering
loadSpellDBC() early, setting dbcLoadAttempted=true before the
spellbook opened. This caused loadSkillLineDBCs() to be skipped,
so all spells were categorized into "General". Fixed by checking
each DBC's own loaded flag independently and re-categorizing when
skill line data becomes available.
2026-02-26 01:21:51 -08:00

96 lines
3.3 KiB
C++

#pragma once
#include "game/game_handler.hpp"
#include <vulkan/vulkan.h>
#include <imgui.h>
#include <string>
#include <vector>
#include <map>
#include <unordered_map>
namespace wowee {
namespace pipeline { class AssetManager; }
namespace ui {
struct SpellInfo {
uint32_t spellId = 0;
std::string name;
std::string rank;
std::string description; // Tooltip/description text from Spell.dbc
uint32_t iconId = 0; // SpellIconID
uint32_t attributes = 0; // Spell attributes (field 4)
uint32_t castTimeMs = 0; // Cast time in ms (0 = instant)
uint32_t manaCost = 0; // Mana cost
uint32_t powerType = 0; // 0=mana, 1=rage, 2=focus, 3=energy
uint32_t rangeIndex = 0; // Range index from SpellRange.dbc
bool isPassive() const { return (attributes & 0x40) != 0; }
};
struct SpellTabInfo {
std::string name;
std::vector<const SpellInfo*> spells;
};
class SpellbookScreen {
public:
void render(game::GameHandler& gameHandler, pipeline::AssetManager* assetManager);
bool isOpen() const { return open; }
void toggle() { open = !open; }
void setOpen(bool o) { open = o; }
// Spell name lookup — triggers DBC load if needed, used by action bar tooltips
std::string lookupSpellName(uint32_t spellId, pipeline::AssetManager* assetManager);
// Drag-and-drop state for action bar assignment
bool isDraggingSpell() const { return draggingSpell_; }
uint32_t getDragSpellId() const { return dragSpellId_; }
void consumeDragSpell() { draggingSpell_ = false; dragSpellId_ = 0; dragSpellIconTex_ = VK_NULL_HANDLE; }
private:
bool open = false;
bool pKeyWasDown = false;
// Spell data (loaded from Spell.dbc)
bool dbcLoaded = false;
bool dbcLoadAttempted = false;
std::unordered_map<uint32_t, SpellInfo> spellData;
// Icon data (loaded from SpellIcon.dbc)
bool iconDbLoaded = false;
std::unordered_map<uint32_t, std::string> spellIconPaths; // SpellIconID -> path
std::unordered_map<uint32_t, VkDescriptorSet> spellIconCache; // SpellIconID -> texture
// Skill line data (loaded from SkillLine.dbc + SkillLineAbility.dbc)
bool skillLineDbLoaded = false;
std::unordered_map<uint32_t, std::string> skillLineNames;
std::unordered_map<uint32_t, uint32_t> skillLineCategories;
std::unordered_multimap<uint32_t, uint32_t> spellToSkillLine;
// Categorized spell tabs
std::vector<SpellTabInfo> spellTabs;
size_t lastKnownSpellCount = 0;
bool categorizedWithSkillLines = false;
// Search filter
char searchFilter_[128] = "";
// Drag-and-drop from spellbook to action bar
bool draggingSpell_ = false;
uint32_t dragSpellId_ = 0;
VkDescriptorSet dragSpellIconTex_ = VK_NULL_HANDLE;
void loadSpellDBC(pipeline::AssetManager* assetManager);
void loadSpellIconDBC(pipeline::AssetManager* assetManager);
void loadSkillLineDBCs(pipeline::AssetManager* assetManager);
void categorizeSpells(const std::unordered_set<uint32_t>& knownSpells);
VkDescriptorSet getSpellIcon(uint32_t iconId, pipeline::AssetManager* assetManager);
const SpellInfo* getSpellInfo(uint32_t spellId) const;
// Tooltip rendering helper
void renderSpellTooltip(const SpellInfo* info, game::GameHandler& gameHandler);
};
} // namespace ui
} // namespace wowee