diff --git a/src/game/spell_handler.cpp b/src/game/spell_handler.cpp index 59e0d29f..47346c39 100644 --- a/src/game/spell_handler.cpp +++ b/src/game/spell_handler.cpp @@ -222,6 +222,14 @@ void SpellHandler::castSpell(uint32_t spellId, uint64_t targetGuid) { return; } + // Stop movement before casting — servers reject cast-time spells while moving + const uint32_t moveFlags = owner_.movementInfo.flags; + const bool isMoving = (moveFlags & 0x0Fu) != 0; // FORWARD|BACKWARD|STRAFE_LEFT|STRAFE_RIGHT + if (isMoving) { + owner_.movementInfo.flags &= ~0x0Fu; + owner_.sendMovement(Opcode::MSG_MOVE_STOP); + } + uint64_t target = targetGuid != 0 ? targetGuid : owner_.targetGuid; // Self-targeted spells like hearthstone should not send a target if (spellId == 8690) target = 0; diff --git a/src/rendering/character_renderer.cpp b/src/rendering/character_renderer.cpp index 79412156..2b347ac7 100644 --- a/src/rendering/character_renderer.cpp +++ b/src/rendering/character_renderer.cpp @@ -2024,6 +2024,13 @@ void CharacterRenderer::prepareRender(uint32_t frameIndex) { &instance.boneBuffer[frameIndex], &instance.boneAlloc[frameIndex], &allocInfo); instance.boneMapped[frameIndex] = allocInfo.pMappedData; + // Initialize all bone slots to identity so out-of-range indices + // produce correct (neutral) transforms instead of GPU garbage + if (instance.boneMapped[frameIndex]) { + auto* dst = static_cast(instance.boneMapped[frameIndex]); + for (int j = 0; j < MAX_BONES; j++) dst[j] = glm::mat4(1.0f); + } + VkDescriptorSetAllocateInfo ai{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO}; ai.descriptorPool = boneDescPool_; ai.descriptorSetCount = 1; @@ -2147,6 +2154,13 @@ void CharacterRenderer::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, &instance.boneBuffer[frameIndex], &instance.boneAlloc[frameIndex], &allocInfo); instance.boneMapped[frameIndex] = allocInfo.pMappedData; + // Initialize all bone slots to identity so out-of-range indices + // produce correct (neutral) transforms instead of GPU garbage + if (instance.boneMapped[frameIndex]) { + auto* dst = static_cast(instance.boneMapped[frameIndex]); + for (int j = 0; j < MAX_BONES; j++) dst[j] = glm::mat4(1.0f); + } + // Allocate descriptor set for bone SSBO VkDescriptorSetAllocateInfo ai{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO}; ai.descriptorPool = boneDescPool_; @@ -2787,6 +2801,13 @@ void CharacterRenderer::renderShadow(VkCommandBuffer cmd, const glm::mat4& light &inst.boneBuffer[frameIndex], &inst.boneAlloc[frameIndex], &ai); inst.boneMapped[frameIndex] = ai.pMappedData; + // Initialize all bone slots to identity so out-of-range indices + // produce correct (neutral) transforms instead of GPU garbage + if (inst.boneMapped[frameIndex]) { + auto* dst = static_cast(inst.boneMapped[frameIndex]); + for (int j = 0; j < MAX_BONES; j++) dst[j] = glm::mat4(1.0f); + } + VkDescriptorSetAllocateInfo dsAI{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO}; dsAI.descriptorPool = boneDescPool_; dsAI.descriptorSetCount = 1;