From 140a2e2c22e52366c5e1e7435102fb0ad98d50e1 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Thu, 5 Feb 2026 15:07:13 -0800 Subject: [PATCH] Fix single-player spawn coords and show action bar spell names --- include/ui/game_screen.hpp | 5 ++++ src/core/application.cpp | 4 +-- src/game/game_handler.cpp | 32 +++++++++++++---------- src/game/npc_manager.cpp | 3 ++- src/ui/game_screen.cpp | 53 +++++++++++++++++++++++++++++++++++++- 5 files changed, 79 insertions(+), 18 deletions(-) diff --git a/include/ui/game_screen.hpp b/include/ui/game_screen.hpp index e9db591f..35ca1b8e 100644 --- a/include/ui/game_screen.hpp +++ b/include/ui/game_screen.hpp @@ -7,6 +7,7 @@ #include "ui/spellbook_screen.hpp" #include #include +#include namespace wowee { namespace ui { @@ -129,6 +130,10 @@ private: InventoryScreen inventoryScreen; SpellbookScreen spellbookScreen; rendering::WorldMap worldMap; + + bool actionSpellDbAttempted = false; + bool actionSpellDbLoaded = false; + std::unordered_map actionSpellNames; }; }} // namespace wowee::ui diff --git a/src/core/application.cpp b/src/core/application.cpp index 2205d1d1..ed19803f 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -1058,7 +1058,7 @@ void Application::startSinglePlayer() { spClass_ = activeChar->characterClass; spMapId_ = activeChar->mapId; spZoneId_ = activeChar->zoneId; - spSpawnCanonical_ = core::coords::serverToCanonical(glm::vec3(activeChar->x, activeChar->y, activeChar->z)); + spSpawnCanonical_ = glm::vec3(activeChar->x, activeChar->y, activeChar->z); spYawDeg_ = 0.0f; spPitchDeg_ = -5.0f; @@ -1077,7 +1077,7 @@ void Application::startSinglePlayer() { if (hasCreate) { spMapId_ = createInfo.mapId; spZoneId_ = createInfo.zoneId; - spSpawnCanonical_ = core::coords::serverToCanonical(glm::vec3(createInfo.x, createInfo.y, createInfo.z)); + spSpawnCanonical_ = glm::vec3(createInfo.x, createInfo.y, createInfo.z); spYawDeg_ = glm::degrees(createInfo.orientation); spPitchDeg_ = -5.0f; spawnSnapToGround = true; diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index f3867aea..49d6649b 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -1597,11 +1597,10 @@ bool GameHandler::loadSinglePlayerCharacterState(uint64_t guid) { localPlayerMaxHealth_ = std::max(localPlayerHealth_, maxHealth); playerNextLevelXp_ = xpForLevel(localPlayerLevel_); - // Seed movement info for spawn - glm::vec3 canonical = core::coords::serverToCanonical(glm::vec3(posX, posY, posZ)); - movementInfo.x = canonical.x; - movementInfo.y = canonical.y; - movementInfo.z = canonical.z; + // Seed movement info for spawn (canonical coords in DB) + movementInfo.x = posX; + movementInfo.y = posY; + movementInfo.z = posZ; movementInfo.orientation = orientation; spLastDirtyX_ = movementInfo.x; @@ -1631,6 +1630,7 @@ void GameHandler::applySinglePlayerStartData(Race race, Class cls) { uint8_t raceVal = static_cast(race); uint8_t classVal = static_cast(cls); + bool addedItem = false; for (const auto& row : startDb.items) { if (row.itemId == 0 || row.amount == 0) continue; @@ -1655,7 +1655,9 @@ void GameHandler::applySinglePlayerStartData(Race race, Class cls) { def.name = "Item " + std::to_string(row.itemId); } - inventory.addItem(def); + if (inventory.addItem(def)) { + addedItem = true; + } } for (const auto& row : startDb.items) { @@ -1665,6 +1667,10 @@ void GameHandler::applySinglePlayerStartData(Race race, Class cls) { removeItemsFromInventory(inventory, row.itemId, static_cast(-row.amount)); } + if (!addedItem && startDb.items.empty()) { + addSystemChatMessage("No starting items found in playercreateinfo_item.sql."); + } + uint32_t raceMask = 1u << (raceVal > 0 ? (raceVal - 1) : 0); uint32_t classMask = 1u << (classVal > 0 ? (classVal - 1) : 0); for (const auto& row : startDb.spells) { @@ -1738,10 +1744,9 @@ void GameHandler::saveSinglePlayerCharacterState(bool force) { sqlite3_bind_int(stmt, 1, static_cast(localPlayerLevel_)); sqlite3_bind_int(stmt, 2, static_cast(active->zoneId)); sqlite3_bind_int(stmt, 3, static_cast(active->mapId)); - glm::vec3 serverPos = core::coords::canonicalToServer(glm::vec3(movementInfo.x, movementInfo.y, movementInfo.z)); - sqlite3_bind_double(stmt, 4, serverPos.x); - sqlite3_bind_double(stmt, 5, serverPos.y); - sqlite3_bind_double(stmt, 6, serverPos.z); + sqlite3_bind_double(stmt, 4, movementInfo.x); + sqlite3_bind_double(stmt, 5, movementInfo.y); + sqlite3_bind_double(stmt, 6, movementInfo.z); sqlite3_bind_double(stmt, 7, movementInfo.orientation); sqlite3_bind_int64(stmt, 8, static_cast(playerMoneyCopper_)); sqlite3_bind_int(stmt, 9, static_cast(playerXp_)); @@ -1901,13 +1906,12 @@ void GameHandler::saveSinglePlayerCharacterState(bool force) { spPeriodicTimer_ = 0.0f; // Update cached character list position/level for UI. - glm::vec3 serverPos = core::coords::canonicalToServer(glm::vec3(movementInfo.x, movementInfo.y, movementInfo.z)); for (auto& ch : characters) { if (ch.guid == activeCharacterGuid_) { ch.level = static_cast(localPlayerLevel_); - ch.x = serverPos.x; - ch.y = serverPos.y; - ch.z = serverPos.z; + ch.x = movementInfo.x; + ch.y = movementInfo.y; + ch.z = movementInfo.z; break; } } diff --git a/src/game/npc_manager.cpp b/src/game/npc_manager.cpp index 159a3f75..cdf40d8c 100644 --- a/src/game/npc_manager.cpp +++ b/src/game/npc_manager.cpp @@ -589,7 +589,8 @@ std::vector NpcManager::loadSpawnDefsFromAzerothCoreDb( float o = std::stof(cols[10]); uint32_t curhealth = static_cast(std::stoul(cols[14])); - glm::vec3 canonical = core::coords::serverToCanonical(glm::vec3(sx, sy, sz)); + // AzerothCore DB uses client/canonical coordinates. + glm::vec3 canonical = glm::vec3(sx, sy, sz); float dx = canonical.x - playerCanonical.x; float dy = canonical.y - playerCanonical.y; if (dx * dx + dy * dy > kRadius * kRadius) return true; diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 0344bb4d..83146ddc 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -1020,6 +1020,7 @@ void GameScreen::renderActionBar(game::GameHandler& gameHandler) { auto* window = core::Application::getInstance().getWindow(); float screenW = window ? static_cast(window->getWidth()) : 1280.0f; float screenH = window ? static_cast(window->getHeight()) : 720.0f; + auto* assetMgr = core::Application::getInstance().getAssetManager(); float slotSize = 48.0f; float spacing = 4.0f; @@ -1060,9 +1061,51 @@ void GameScreen::renderActionBar(game::GameHandler& gameHandler) { ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.3f, 0.3f, 0.5f, 0.9f)); } + auto getSpellName = [&](uint32_t spellId) -> std::string { + if (!actionSpellDbAttempted) { + actionSpellDbAttempted = true; + if (assetMgr && assetMgr->isInitialized()) { + auto dbc = assetMgr->loadDBC("Spell.dbc"); + if (dbc && dbc->isLoaded()) { + uint32_t fieldCount = dbc->getFieldCount(); + uint32_t nameField = 136; + if (fieldCount < 137) { + if (fieldCount > 10) { + nameField = fieldCount > 140 ? 136 : 1; + } else { + nameField = 1; + } + } + uint32_t count = dbc->getRecordCount(); + actionSpellNames.reserve(count); + for (uint32_t i = 0; i < count; ++i) { + uint32_t id = dbc->getUInt32(i, 0); + std::string name = dbc->getString(i, nameField); + if (!name.empty() && id > 0) { + actionSpellNames[id] = name; + } + } + actionSpellDbLoaded = true; + } + } + } + auto it = actionSpellNames.find(spellId); + if (it != actionSpellNames.end()) return it->second; + return "Spell #" + std::to_string(spellId); + }; + char label[32]; + std::string spellName; if (slot.type == game::ActionBarSlot::SPELL) { - snprintf(label, sizeof(label), "S%u", slot.id); + spellName = getSpellName(slot.id); + if (spellName.size() > 6) { + spellName = spellName.substr(0, 6); + } + snprintf(label, sizeof(label), "%s", spellName.c_str()); + } else if (slot.type == game::ActionBarSlot::ITEM) { + snprintf(label, sizeof(label), "Item"); + } else if (slot.type == game::ActionBarSlot::MACRO) { + snprintf(label, sizeof(label), "Macro"); } else { snprintf(label, sizeof(label), "--"); } @@ -1075,6 +1118,14 @@ void GameScreen::renderActionBar(game::GameHandler& gameHandler) { } ImGui::PopStyleColor(); + if (ImGui::IsItemHovered() && slot.type == game::ActionBarSlot::SPELL && slot.id != 0) { + std::string fullName = getSpellName(slot.id); + ImGui::BeginTooltip(); + ImGui::Text("%s", fullName.c_str()); + ImGui::TextDisabled("Spell ID: %u", slot.id); + ImGui::EndTooltip(); + } + // Cooldown overlay text if (onCooldown) { char cdText[16];