diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index 1d54c914..cdc210cd 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -814,26 +814,35 @@ public: float getTargetCastTimeRemaining() const { return spellHandler_ ? spellHandler_->getTargetCastTimeRemaining() : 0.0f; } bool isTargetCastInterruptible() const { return spellHandler_ ? spellHandler_->isTargetCastInterruptible() : true; } - // Talents - uint8_t getActiveTalentSpec() const { return activeTalentSpec_; } - uint8_t getUnspentTalentPoints() const { return unspentTalentPoints_[activeTalentSpec_]; } - uint8_t getUnspentTalentPoints(uint8_t spec) const { return spec < 2 ? unspentTalentPoints_[spec] : 0; } - const std::unordered_map& getLearnedTalents() const { return learnedTalents_[activeTalentSpec_]; } + // Talents — delegate to SpellHandler as canonical authority + uint8_t getActiveTalentSpec() const { return spellHandler_ ? spellHandler_->getActiveTalentSpec() : 0; } + uint8_t getUnspentTalentPoints() const { return spellHandler_ ? spellHandler_->getUnspentTalentPoints() : 0; } + uint8_t getUnspentTalentPoints(uint8_t spec) const { return spellHandler_ ? spellHandler_->getUnspentTalentPoints(spec) : 0; } + const std::unordered_map& getLearnedTalents() const { + if (spellHandler_) return spellHandler_->getLearnedTalents(); + static const std::unordered_map empty; + return empty; + } const std::unordered_map& getLearnedTalents(uint8_t spec) const { - static std::unordered_map empty; - return spec < 2 ? learnedTalents_[spec] : empty; + if (spellHandler_) return spellHandler_->getLearnedTalents(spec); + static const std::unordered_map empty; + return empty; } // Glyphs (WotLK): up to 6 glyph slots per spec (3 major + 3 minor) static constexpr uint8_t MAX_GLYPH_SLOTS = 6; - const std::array& getGlyphs() const { return learnedGlyphs_[activeTalentSpec_]; } + const std::array& getGlyphs() const { + if (spellHandler_) return spellHandler_->getGlyphs(); + static const std::array empty{}; + return empty; + } const std::array& getGlyphs(uint8_t spec) const { - static std::array empty{}; - return spec < 2 ? learnedGlyphs_[spec] : empty; + if (spellHandler_) return spellHandler_->getGlyphs(spec); + static const std::array empty{}; + return empty; } uint8_t getTalentRank(uint32_t talentId) const { - auto it = learnedTalents_[activeTalentSpec_].find(talentId); - return (it != learnedTalents_[activeTalentSpec_].end()) ? it->second : 0; + return spellHandler_ ? spellHandler_->getTalentRank(talentId) : 0; } void learnTalent(uint32_t talentId, uint32_t requestedRank); void switchTalentSpec(uint8_t newSpec); @@ -1431,7 +1440,7 @@ public: // Equipment Sets (aliased from handler_types.hpp) using EquipmentSetInfo = game::EquipmentSetInfo; - const std::vector& getEquipmentSets() const { return equipmentSetInfo_; } + const std::vector& getEquipmentSets() const; bool supportsEquipmentSets() const; void useEquipmentSet(uint32_t setId); void saveEquipmentSet(const std::string& name, const std::string& iconName = "INV_Misc_QuestionMark", diff --git a/include/game/social_handler.hpp b/include/game/social_handler.hpp index a3501d61..6f2cdbc2 100644 --- a/include/game/social_handler.hpp +++ b/include/game/social_handler.hpp @@ -347,6 +347,7 @@ private: void handleSetFactionAtWar(network::Packet& packet); void handleSetFactionVisible(network::Packet& packet); void handleGroupSetLeader(network::Packet& packet); + void handleTalentsInfo(network::Packet& packet); GameHandler& owner_; diff --git a/include/game/spell_handler.hpp b/include/game/spell_handler.hpp index 5d8d617c..4a946c62 100644 --- a/include/game/spell_handler.hpp +++ b/include/game/spell_handler.hpp @@ -162,8 +162,8 @@ public: void useItemInBag(int bagIndex, int slotIndex); void useItemById(uint32_t itemId); - // Equipment sets - const std::vector& getEquipmentSets() const { return equipmentSetInfo_; } + // Equipment sets — canonical data owned by InventoryHandler; + // GameHandler::getEquipmentSets() delegates to inventoryHandler_. // Pet spells void sendPetAction(uint32_t action, uint64_t targetGuid = 0); @@ -186,6 +186,7 @@ public: // Cast state void stopCasting(); void resetCastState(); + void resetTalentState(); void clearUnitCaches(); // Aura duration @@ -252,7 +253,6 @@ private: void handleUnlearnSpells(network::Packet& packet); void handleTalentsInfo(network::Packet& packet); void handleAchievementEarned(network::Packet& packet); - void handleEquipmentSetList(network::Packet& packet); friend class GameHandler; friend class InventoryHandler; @@ -313,18 +313,6 @@ private: bool petUnlearnPending_ = false; uint64_t petUnlearnGuid_ = 0; uint32_t petUnlearnCost_ = 0; - - // Equipment sets - struct EquipmentSet { - uint64_t setGuid = 0; - uint32_t setId = 0; - std::string name; - std::string iconName; - uint32_t ignoreSlotMask = 0; - std::array itemGuids{}; - }; - std::vector equipmentSets_; - std::vector equipmentSetInfo_; }; } // namespace game diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 80484f64..edbe8229 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -2660,8 +2660,7 @@ void GameHandler::registerOpcodeHandlers() { // Clear cached talent data so the talent screen reflects the reset. dispatchTable_[Opcode::SMSG_TALENTS_INVOLUNTARILY_RESET] = [this](network::Packet& packet) { // Clear cached talent data so the talent screen reflects the reset. - learnedTalents_[0].clear(); - learnedTalents_[1].clear(); + if (spellHandler_) spellHandler_->resetTalentState(); addUIError("Your talents have been reset by the server."); addSystemChatMessage("Your talents have been reset by the server."); packet.skipAll(); @@ -4917,14 +4916,7 @@ void GameHandler::handleLoginVerifyWorld(network::Packet& packet) { // Reset talent initialization so the first SMSG_TALENTS_INFO after login // correctly sets the active spec (static locals don't reset across logins). - talentsInitialized_ = false; - learnedTalents_[0].clear(); - learnedTalents_[1].clear(); - learnedGlyphs_[0].fill(0); - learnedGlyphs_[1].fill(0); - unspentTalentPoints_[0] = 0; - unspentTalentPoints_[1] = 0; - activeTalentSpec_ = 0; + if (spellHandler_) spellHandler_->resetTalentState(); // Auto-join default chat channels only on first world entry. autoJoinDefaultChannels(); @@ -5069,6 +5061,12 @@ void GameHandler::sendRequestVehicleExit() { vehicleId_ = 0; // Optimistically clear; server will confirm via SMSG_PLAYER_VEHICLE_DATA(0) } +const std::vector& GameHandler::getEquipmentSets() const { + if (inventoryHandler_) return inventoryHandler_->getEquipmentSets(); + static const std::vector empty; + return empty; +} + bool GameHandler::supportsEquipmentSets() const { return inventoryHandler_ && inventoryHandler_->supportsEquipmentSets(); } diff --git a/src/game/inventory_handler.cpp b/src/game/inventory_handler.cpp index 1c7e7118..1838e232 100644 --- a/src/game/inventory_handler.cpp +++ b/src/game/inventory_handler.cpp @@ -698,7 +698,7 @@ void InventoryHandler::handleLootResponse(network::Packet& packet) { const bool wotlkLoot = isActiveExpansion("wotlk"); if (!LootResponseParser::parse(packet, currentLoot_, wotlkLoot)) return; const bool hasLoot = !currentLoot_.items.empty() || currentLoot_.gold > 0; - if (!hasLoot && owner_.casting && owner_.currentCastSpellId != 0 && lastInteractedGoGuid_ != 0) { + if (!hasLoot && owner_.isCasting() && owner_.getCurrentCastSpellId() != 0 && lastInteractedGoGuid_ != 0) { LOG_DEBUG("Ignoring empty SMSG_LOOT_RESPONSE during gather cast"); return; } @@ -1500,14 +1500,30 @@ void InventoryHandler::refreshMailList() { void InventoryHandler::sendMail(const std::string& recipient, const std::string& subject, const std::string& body, uint64_t money, uint64_t cod) { - if (owner_.state != WorldState::IN_WORLD || !owner_.socket || mailboxGuid_ == 0) return; - std::vector itemGuids; - for (const auto& a : mailAttachments_) { - if (a.occupied()) itemGuids.push_back(a.itemGuid); + if (owner_.state != WorldState::IN_WORLD) { + LOG_WARNING("sendMail: not in world"); + return; } - auto packet = SendMailPacket::build(mailboxGuid_, recipient, subject, body, money, cod, - itemGuids); + if (!owner_.socket) { + LOG_WARNING("sendMail: no socket"); + return; + } + if (mailboxGuid_ == 0) { + LOG_WARNING("sendMail: mailboxGuid_ is 0 (mailbox closed?)"); + return; + } + // Collect attached item GUIDs + std::vector itemGuids; + for (const auto& att : mailAttachments_) { + if (att.occupied()) { + itemGuids.push_back(att.itemGuid); + } + } + auto packet = owner_.packetParsers_->buildSendMail(mailboxGuid_, recipient, subject, body, money, cod, itemGuids); + LOG_INFO("sendMail: to='", recipient, "' subject='", subject, "' money=", money, + " attachments=", itemGuids.size(), " mailboxGuid=", mailboxGuid_); owner_.socket->send(packet); + clearMailAttachments(); } bool InventoryHandler::attachItemFromBackpack(int backpackIndex) { diff --git a/src/game/movement_handler.cpp b/src/game/movement_handler.cpp index e9d2dc5f..3d4bf900 100644 --- a/src/game/movement_handler.cpp +++ b/src/game/movement_handler.cpp @@ -431,7 +431,7 @@ void MovementHandler::sendMovement(Opcode opcode) { const bool wasMoving = (movementInfo.flags & kMoveMask) != 0; // Cancel any timed (non-channeled) cast the moment the player starts moving. - if (owner_.casting && !owner_.castIsChannel) { + if (owner_.isCasting() && !owner_.isChanneling()) { const bool isPositionalMove = opcode == Opcode::MSG_MOVE_START_FORWARD || opcode == Opcode::MSG_MOVE_START_BACKWARD || @@ -798,7 +798,7 @@ void MovementHandler::dismount() { owner_.socket->send(pkt); LOG_INFO("Sent CMSG_CANCEL_AURA (mount spell ", savedMountAura, ") — Classic fallback"); } else { - for (const auto& a : owner_.playerAuras) { + for (const auto& a : owner_.getPlayerAuras()) { if (!a.isEmpty() && a.maxDurationMs < 0 && a.casterGuid == owner_.playerGuid) { auto pkt = CancelAuraPacket::build(a.spellId); owner_.socket->send(pkt); @@ -1808,6 +1808,9 @@ void MovementHandler::handleTeleportAck(network::Packet& packet) { movementInfo.orientation = core::coords::serverToCanonicalYaw(orientation); movementInfo.flags = 0; + // Clear cast bar on teleport — SpellHandler owns the casting_ flag + if (owner_.spellHandler_) owner_.spellHandler_->resetCastState(); + if (owner_.socket) { network::Packet ack(wireOpcode(Opcode::MSG_MOVE_TELEPORT_ACK)); const bool legacyGuidAck = @@ -1869,10 +1872,7 @@ void MovementHandler::handleNewWorld(network::Packet& packet) { owner_.clearHostileAttackers(); owner_.stopAutoAttack(); owner_.tabCycleStale = true; - owner_.casting = false; - owner_.castIsChannel = false; - owner_.currentCastSpellId = 0; - owner_.castTimeRemaining = 0.0f; + owner_.resetCastState(); owner_.craftQueueSpellId_ = 0; owner_.craftQueueRemaining_ = 0; owner_.queuedSpellId_ = 0; @@ -1941,12 +1941,7 @@ void MovementHandler::handleNewWorld(network::Packet& packet) { owner_.areaTriggerCheckTimer_ = -5.0f; owner_.areaTriggerSuppressFirst_ = true; owner_.stopAutoAttack(); - owner_.casting = false; - owner_.castIsChannel = false; - owner_.currentCastSpellId = 0; - owner_.pendingGameObjectInteractGuid_ = 0; - owner_.lastInteractedGoGuid_ = 0; - owner_.castTimeRemaining = 0.0f; + owner_.resetCastState(); owner_.craftQueueSpellId_ = 0; owner_.craftQueueRemaining_ = 0; owner_.queuedSpellId_ = 0; diff --git a/src/game/social_handler.cpp b/src/game/social_handler.cpp index a3b2cac7..debaa39d 100644 --- a/src/game/social_handler.cpp +++ b/src/game/social_handler.cpp @@ -1221,7 +1221,6 @@ void SocialHandler::handleGroupDecline(network::Packet& packet) { void SocialHandler::handleGroupList(network::Packet& packet) { const bool hasRoles = isActiveExpansion("wotlk"); - const uint32_t prevCount = partyData.memberCount; const uint8_t prevLootMethod = partyData.lootMethod; const bool wasInGroup = !partyData.isEmpty(); partyData = GroupListData{}; diff --git a/src/game/spell_handler.cpp b/src/game/spell_handler.cpp index 0385c91a..204c79ff 100644 --- a/src/game/spell_handler.cpp +++ b/src/game/spell_handler.cpp @@ -136,7 +136,7 @@ void SpellHandler::registerOpcodes(DispatchTable& table) { table[Opcode::SMSG_ACHIEVEMENT_EARNED] = [this](network::Packet& packet) { handleAchievementEarned(packet); }; - table[Opcode::SMSG_EQUIPMENT_SET_LIST] = [this](network::Packet& packet) { handleEquipmentSetList(packet); }; + // SMSG_EQUIPMENT_SET_LIST — owned by InventoryHandler::registerOpcodes // ---- Cast result / spell visuals / cooldowns / modifiers ---- table[Opcode::SMSG_CAST_RESULT] = [this](network::Packet& p) { handleCastResult(p); }; @@ -1423,43 +1423,7 @@ void SpellHandler::handleAchievementEarned(network::Packet& packet) { owner_.addonEventCallback_("ACHIEVEMENT_EARNED", {std::to_string(achievementId)}); } -void SpellHandler::handleEquipmentSetList(network::Packet& packet) { - if (packet.getSize() - packet.getReadPos() < 4) return; - uint32_t count = packet.readUInt32(); - if (count > 10) { - LOG_WARNING("SMSG_EQUIPMENT_SET_LIST: unexpected count ", count, ", ignoring"); - packet.setReadPos(packet.getSize()); - return; - } - equipmentSets_.clear(); - equipmentSets_.reserve(count); - for (uint32_t i = 0; i < count; ++i) { - if (packet.getSize() - packet.getReadPos() < 16) break; - EquipmentSet es; - es.setGuid = packet.readUInt64(); - es.setId = packet.readUInt32(); - es.name = packet.readString(); - es.iconName = packet.readString(); - es.ignoreSlotMask = packet.readUInt32(); - for (int slot = 0; slot < 19; ++slot) { - if (packet.getSize() - packet.getReadPos() < 8) break; - es.itemGuids[slot] = packet.readUInt64(); - } - equipmentSets_.push_back(std::move(es)); - } - // Populate public-facing info - equipmentSetInfo_.clear(); - equipmentSetInfo_.reserve(equipmentSets_.size()); - for (const auto& es : equipmentSets_) { - EquipmentSetInfo info; - info.setGuid = es.setGuid; - info.setId = es.setId; - info.name = es.name; - info.iconName = es.iconName; - equipmentSetInfo_.push_back(std::move(info)); - } - LOG_INFO("SMSG_EQUIPMENT_SET_LIST: ", equipmentSets_.size(), " equipment sets received"); -} +// SMSG_EQUIPMENT_SET_LIST — moved to InventoryHandler // ============================================================ // Pet spell methods (moved from GameHandler) @@ -1645,6 +1609,17 @@ void SpellHandler::resetCastState() { owner_.lastInteractedGoGuid_ = 0; } +void SpellHandler::resetTalentState() { + talentsInitialized_ = false; + learnedTalents_[0].clear(); + learnedTalents_[1].clear(); + learnedGlyphs_[0].fill(0); + learnedGlyphs_[1].fill(0); + unspentTalentPoints_[0] = 0; + unspentTalentPoints_[1] = 0; + activeTalentSpec_ = 0; +} + void SpellHandler::clearUnitCaches() { unitCastStates_.clear(); unitAurasCache_.clear(); diff --git a/src/rendering/character_renderer.cpp b/src/rendering/character_renderer.cpp index 92674ffd..78c177df 100644 --- a/src/rendering/character_renderer.cpp +++ b/src/rendering/character_renderer.cpp @@ -226,10 +226,8 @@ bool CharacterRenderer::initialize(VkContext* ctx, VkDescriptorSetLayout perFram // --- Load shaders --- rendering::VkShaderModule charVert, charFrag; - charVert.loadFromFile(device, "assets/shaders/character.vert.spv"); - charFrag.loadFromFile(device, "assets/shaders/character.frag.spv"); - - if (!charVert.isValid() || !charFrag.isValid()) { + if (!charVert.loadFromFile(device, "assets/shaders/character.vert.spv") || + !charFrag.loadFromFile(device, "assets/shaders/character.frag.spv")) { LOG_ERROR("Character: Missing required shaders, cannot initialize"); return false; } @@ -3287,10 +3285,8 @@ void CharacterRenderer::recreatePipelines() { // --- Load shaders --- rendering::VkShaderModule charVert, charFrag; - charVert.loadFromFile(device, "assets/shaders/character.vert.spv"); - charFrag.loadFromFile(device, "assets/shaders/character.frag.spv"); - - if (!charVert.isValid() || !charFrag.isValid()) { + if (!charVert.loadFromFile(device, "assets/shaders/character.vert.spv") || + !charFrag.loadFromFile(device, "assets/shaders/character.frag.spv")) { LOG_ERROR("CharacterRenderer::recreatePipelines: missing required shaders"); return; } diff --git a/src/rendering/charge_effect.cpp b/src/rendering/charge_effect.cpp index f4282b43..f6da288f 100644 --- a/src/rendering/charge_effect.cpp +++ b/src/rendering/charge_effect.cpp @@ -273,9 +273,12 @@ void ChargeEffect::recreatePipelines() { // ---- Rebuild ribbon trail pipeline (TRIANGLE_STRIP) ---- { VkShaderModule vertModule; - vertModule.loadFromFile(device, "assets/shaders/charge_ribbon.vert.spv"); VkShaderModule fragModule; - fragModule.loadFromFile(device, "assets/shaders/charge_ribbon.frag.spv"); + if (!vertModule.loadFromFile(device, "assets/shaders/charge_ribbon.vert.spv") || + !fragModule.loadFromFile(device, "assets/shaders/charge_ribbon.frag.spv")) { + LOG_ERROR("ChargeEffect::recreatePipelines: failed to load ribbon shader modules"); + return; + } VkPipelineShaderStageCreateInfo vertStage = vertModule.stageInfo(VK_SHADER_STAGE_VERTEX_BIT); VkPipelineShaderStageCreateInfo fragStage = fragModule.stageInfo(VK_SHADER_STAGE_FRAGMENT_BIT); @@ -323,9 +326,12 @@ void ChargeEffect::recreatePipelines() { // ---- Rebuild dust puff pipeline (POINT_LIST) ---- { VkShaderModule vertModule; - vertModule.loadFromFile(device, "assets/shaders/charge_dust.vert.spv"); VkShaderModule fragModule; - fragModule.loadFromFile(device, "assets/shaders/charge_dust.frag.spv"); + if (!vertModule.loadFromFile(device, "assets/shaders/charge_dust.vert.spv") || + !fragModule.loadFromFile(device, "assets/shaders/charge_dust.frag.spv")) { + LOG_ERROR("ChargeEffect::recreatePipelines: failed to load dust shader modules"); + return; + } VkPipelineShaderStageCreateInfo vertStage = vertModule.stageInfo(VK_SHADER_STAGE_VERTEX_BIT); VkPipelineShaderStageCreateInfo fragStage = fragModule.stageInfo(VK_SHADER_STAGE_FRAGMENT_BIT); diff --git a/src/rendering/lens_flare.cpp b/src/rendering/lens_flare.cpp index debddff5..9e462367 100644 --- a/src/rendering/lens_flare.cpp +++ b/src/rendering/lens_flare.cpp @@ -158,9 +158,12 @@ void LensFlare::recreatePipelines() { } VkShaderModule vertModule; - vertModule.loadFromFile(device, "assets/shaders/lens_flare.vert.spv"); VkShaderModule fragModule; - fragModule.loadFromFile(device, "assets/shaders/lens_flare.frag.spv"); + if (!vertModule.loadFromFile(device, "assets/shaders/lens_flare.vert.spv") || + !fragModule.loadFromFile(device, "assets/shaders/lens_flare.frag.spv")) { + LOG_ERROR("LensFlare::recreatePipelines: failed to load shader modules"); + return; + } VkPipelineShaderStageCreateInfo vertStage = vertModule.stageInfo(VK_SHADER_STAGE_VERTEX_BIT); VkPipelineShaderStageCreateInfo fragStage = fragModule.stageInfo(VK_SHADER_STAGE_FRAGMENT_BIT); diff --git a/src/rendering/lightning.cpp b/src/rendering/lightning.cpp index b7d28c1d..2c403a66 100644 --- a/src/rendering/lightning.cpp +++ b/src/rendering/lightning.cpp @@ -277,9 +277,12 @@ void Lightning::recreatePipelines() { // ---- Rebuild bolt pipeline (LINE_STRIP) ---- { VkShaderModule vertModule; - vertModule.loadFromFile(device, "assets/shaders/lightning_bolt.vert.spv"); VkShaderModule fragModule; - fragModule.loadFromFile(device, "assets/shaders/lightning_bolt.frag.spv"); + if (!vertModule.loadFromFile(device, "assets/shaders/lightning_bolt.vert.spv") || + !fragModule.loadFromFile(device, "assets/shaders/lightning_bolt.frag.spv")) { + LOG_ERROR("Lightning::recreatePipelines: failed to load bolt shader modules"); + return; + } VkPipelineShaderStageCreateInfo vertStage = vertModule.stageInfo(VK_SHADER_STAGE_VERTEX_BIT); VkPipelineShaderStageCreateInfo fragStage = fragModule.stageInfo(VK_SHADER_STAGE_FRAGMENT_BIT); @@ -315,9 +318,12 @@ void Lightning::recreatePipelines() { // ---- Rebuild flash pipeline (TRIANGLE_STRIP) ---- { VkShaderModule vertModule; - vertModule.loadFromFile(device, "assets/shaders/lightning_flash.vert.spv"); VkShaderModule fragModule; - fragModule.loadFromFile(device, "assets/shaders/lightning_flash.frag.spv"); + if (!vertModule.loadFromFile(device, "assets/shaders/lightning_flash.vert.spv") || + !fragModule.loadFromFile(device, "assets/shaders/lightning_flash.frag.spv")) { + LOG_ERROR("Lightning::recreatePipelines: failed to load flash shader modules"); + return; + } VkPipelineShaderStageCreateInfo vertStage = vertModule.stageInfo(VK_SHADER_STAGE_VERTEX_BIT); VkPipelineShaderStageCreateInfo fragStage = fragModule.stageInfo(VK_SHADER_STAGE_FRAGMENT_BIT); diff --git a/src/rendering/mount_dust.cpp b/src/rendering/mount_dust.cpp index 560e8a42..7292bcb5 100644 --- a/src/rendering/mount_dust.cpp +++ b/src/rendering/mount_dust.cpp @@ -157,9 +157,12 @@ void MountDust::recreatePipelines() { } VkShaderModule vertModule; - vertModule.loadFromFile(device, "assets/shaders/mount_dust.vert.spv"); VkShaderModule fragModule; - fragModule.loadFromFile(device, "assets/shaders/mount_dust.frag.spv"); + if (!vertModule.loadFromFile(device, "assets/shaders/mount_dust.vert.spv") || + !fragModule.loadFromFile(device, "assets/shaders/mount_dust.frag.spv")) { + LOG_ERROR("MountDust::recreatePipelines: failed to load shader modules"); + return; + } VkPipelineShaderStageCreateInfo vertStage = vertModule.stageInfo(VK_SHADER_STAGE_VERTEX_BIT); VkPipelineShaderStageCreateInfo fragStage = fragModule.stageInfo(VK_SHADER_STAGE_FRAGMENT_BIT); diff --git a/src/rendering/quest_marker_renderer.cpp b/src/rendering/quest_marker_renderer.cpp index 07498285..b9cb209a 100644 --- a/src/rendering/quest_marker_renderer.cpp +++ b/src/rendering/quest_marker_renderer.cpp @@ -193,9 +193,12 @@ void QuestMarkerRenderer::recreatePipelines() { } VkShaderModule vertModule; - vertModule.loadFromFile(device, "assets/shaders/quest_marker.vert.spv"); VkShaderModule fragModule; - fragModule.loadFromFile(device, "assets/shaders/quest_marker.frag.spv"); + if (!vertModule.loadFromFile(device, "assets/shaders/quest_marker.vert.spv") || + !fragModule.loadFromFile(device, "assets/shaders/quest_marker.frag.spv")) { + LOG_ERROR("QuestMarkerRenderer::recreatePipelines: failed to load shader modules"); + return; + } VkPipelineShaderStageCreateInfo vertStage = vertModule.stageInfo(VK_SHADER_STAGE_VERTEX_BIT); VkPipelineShaderStageCreateInfo fragStage = fragModule.stageInfo(VK_SHADER_STAGE_FRAGMENT_BIT); diff --git a/src/rendering/wmo_renderer.cpp b/src/rendering/wmo_renderer.cpp index 5313f086..4f827778 100644 --- a/src/rendering/wmo_renderer.cpp +++ b/src/rendering/wmo_renderer.cpp @@ -3129,7 +3129,6 @@ bool WMORenderer::checkWallCollision(const glm::vec3& from, const glm::vec3& to, glm::vec3 moveDir = to - from; float moveDistSq = glm::dot(moveDir, moveDir); if (moveDistSq < 1e-6f) return false; - float moveDist = std::sqrt(moveDistSq); // Player collision parameters — WoW-style horizontal cylinder // Tighter radius when inside for more responsive indoor collision