diff --git a/src/core/application.cpp b/src/core/application.cpp index 71832a88..d8ca5658 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -392,6 +392,9 @@ void Application::update(float deltaTime) { if (world) { world->update(deltaTime); } + // Spawn NPCs once when entering world + spawnNpcs(); + // Process deferred online creature spawns (throttled) processCreatureSpawnQueue(); processGameObjectSpawnQueue(); @@ -1404,16 +1407,29 @@ void Application::loadEquippedWeapons() { void Application::spawnNpcs() { if (npcsSpawned) return; - if (!assetManager || !assetManager->isInitialized()) return; - if (!renderer || !renderer->getCharacterRenderer() || !renderer->getCamera()) return; - if (!gameHandler) return; + LOG_INFO("spawnNpcs: checking preconditions..."); + if (!assetManager || !assetManager->isInitialized()) { + LOG_INFO("spawnNpcs: assetManager not ready"); + return; + } + if (!renderer || !renderer->getCharacterRenderer() || !renderer->getCamera()) { + LOG_INFO("spawnNpcs: renderer not ready"); + return; + } + if (!gameHandler) { + LOG_INFO("spawnNpcs: gameHandler not ready"); + return; + } + LOG_INFO("spawnNpcs: spawning NPCs..."); if (npcManager) { npcManager->clear(renderer->getCharacterRenderer(), &gameHandler->getEntityManager()); } npcManager = std::make_unique(); glm::vec3 playerSpawnGL = renderer->getCharacterPosition(); glm::vec3 playerCanonical = core::coords::renderToCanonical(playerSpawnGL); + LOG_INFO("spawnNpcs: player position GL=(", playerSpawnGL.x, ",", playerSpawnGL.y, ",", playerSpawnGL.z, + ") canonical=(", playerCanonical.x, ",", playerCanonical.y, ",", playerCanonical.z, ")"); std::string mapName = "Azeroth"; if (auto* minimap = renderer->getMinimap()) { mapName = minimap->getMapName(); diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 8d1d448b..e439c30e 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -4627,6 +4627,17 @@ void GameHandler::handleTrainerList(network::Packet& packet) { trainerWindowOpen_ = true; gossipWindowOpen = false; + // Debug: log known spells + LOG_INFO("Known spells count: ", knownSpells.size()); + if (knownSpells.size() <= 20) { + std::string spellList; + for (uint32_t id : knownSpells) { + if (!spellList.empty()) spellList += ", "; + spellList += std::to_string(id); + } + LOG_INFO("Known spells: ", spellList); + } + // Ensure caches are populated loadSpellNameCache(); loadSkillLineDbc(); @@ -4635,12 +4646,19 @@ void GameHandler::handleTrainerList(network::Packet& packet) { } void GameHandler::trainSpell(uint32_t spellId) { - if (state != WorldState::IN_WORLD || !socket) return; + LOG_INFO("trainSpell called: spellId=", spellId, " state=", (int)state, " socket=", (socket ? "yes" : "no")); + if (state != WorldState::IN_WORLD || !socket) { + LOG_WARNING("trainSpell: Not in world or no socket connection"); + return; + } + LOG_INFO("Sending CMSG_TRAINER_BUY_SPELL: guid=", currentTrainerList_.trainerGuid, + " trainerId=", currentTrainerList_.trainerType, " spellId=", spellId); auto packet = TrainerBuySpellPacket::build( currentTrainerList_.trainerGuid, currentTrainerList_.trainerType, spellId); socket->send(packet); + LOG_INFO("CMSG_TRAINER_BUY_SPELL sent"); } void GameHandler::closeTrainer() { diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index d2c381c8..97245b36 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -3689,6 +3689,19 @@ void GameScreen::renderTrainerWindow(game::GameHandler& gameHandler) { bool canTrain = !alreadyKnown && spell->state == 1 && prereqsMet && levelMet && (money >= spell->spellCost); + + // Debug logging for first spell to see why buttons are disabled + static bool logged = false; + if (!logged) { + LOG_INFO("Trainer button debug: spellId=", spell->spellId, + " alreadyKnown=", alreadyKnown, " state=", (int)spell->state, + " prereqsMet=", prereqsMet, " levelMet=", levelMet, + " canAfford=", (money >= spell->spellCost), + " money=", money, " cost=", spell->spellCost, + " canTrain=", canTrain); + logged = true; + } + if (!canTrain) ImGui::BeginDisabled(); if (ImGui::SmallButton("Train")) { gameHandler.trainSpell(spell->spellId);