fix NPC selection and cast errors

This commit is contained in:
Kelsi
2026-07-19 17:48:36 -07:00
parent 0123173e90
commit e4acdee419
4 changed files with 42 additions and 5 deletions

View File

@@ -127,7 +127,10 @@ inline const char* getSpellCastResultString(uint8_t result, int powerType = -1)
case 24: return "Charmed";
case 25: return "Chest in use";
case 26: return "Confused";
case 27: return nullptr; // DONT_REPORT — suppress message
// DONT_REPORT carries no localized reason of its own. Returning null
// sent it through the unknown-code fallback and exposed "error 27" to
// players, which is less useful than a safe generic explanation.
case 27: return "You can't do that right now";
case 28: return "Equipped item required";
case 29: return "Equipped item class (mainhand)";
case 30: return "Equipped item class (mainhand)";

View File

@@ -190,6 +190,13 @@ private:
pipeline::M2Model data; // Original model data
std::vector<glm::mat4> bindPose; // Inverse bind pose matrices
// Tight bind-pose bounds from rendered vertices. M2 header
// boundingBox/boundingRadius describe collision geometry on many
// creatures and can cover little more than their feet.
glm::vec3 visualBoundMin{0.0f};
glm::vec3 visualBoundMax{0.0f};
float visualBoundRadius = 0.0f;
// Textures loaded from BLP (indexed by texture array position)
std::vector<VkTexture*> textureIds;

View File

@@ -1636,6 +1636,16 @@ bool CharacterRenderer::loadModel(const pipeline::M2Model& model, uint32_t id) {
M2ModelGPU gpuModel;
gpuModel.data = model;
if (!model.vertices.empty()) {
gpuModel.visualBoundMin = glm::vec3(std::numeric_limits<float>::max());
gpuModel.visualBoundMax = glm::vec3(-std::numeric_limits<float>::max());
for (const auto& vertex : model.vertices) {
gpuModel.visualBoundMin = glm::min(gpuModel.visualBoundMin, vertex.position);
gpuModel.visualBoundMax = glm::max(gpuModel.visualBoundMax, vertex.position);
}
gpuModel.visualBoundRadius =
glm::length(gpuModel.visualBoundMax - gpuModel.visualBoundMin) * 0.5f;
}
const auto classification = classifyM2Model(
model.name, model.boundMin, model.boundMax,
model.vertices.size(), model.particleEmitters.size());
@@ -3744,14 +3754,25 @@ bool CharacterRenderer::getInstanceBounds(uint32_t instanceId, glm::vec3& outCen
const auto& inst = it->second;
const auto& model = mIt->second.data;
glm::vec3 localCenter = (model.boundMin + model.boundMax) * 0.5f;
float radius = model.boundRadius;
glm::vec3 boundsMin = mIt->second.visualBoundMin;
glm::vec3 boundsMax = mIt->second.visualBoundMax;
float radius = mIt->second.visualBoundRadius;
if (radius <= 0.001f) {
radius = glm::length(model.boundMax - model.boundMin) * 0.5f;
boundsMin = model.boundMin;
boundsMax = model.boundMax;
radius = model.boundRadius;
if (radius <= 0.001f) {
radius = glm::length(boundsMax - boundsMin) * 0.5f;
}
}
float scale = std::max(0.001f, inst.scale);
outCenter = inst.position + localCenter * scale;
const glm::vec3 localCenter = (boundsMin + boundsMax) * 0.5f;
glm::mat4 rotation(1.0f);
rotation = glm::rotate(rotation, inst.rotation.z, glm::vec3(0.0f, 0.0f, 1.0f));
rotation = glm::rotate(rotation, inst.rotation.x, glm::vec3(1.0f, 0.0f, 0.0f));
rotation = glm::rotate(rotation, inst.rotation.y, glm::vec3(0.0f, 1.0f, 0.0f));
outCenter = inst.position + glm::vec3(rotation * glm::vec4(localCenter * scale, 0.0f));
outRadius = std::max(0.5f, radius * scale);
return true;
}

View File

@@ -1,6 +1,7 @@
#include <catch_amalgamated.hpp>
#include "game/spell_classification.hpp"
#include "game/spell_defines.hpp"
#include <string>
#include <unordered_map>
@@ -35,6 +36,11 @@ private:
} // namespace
TEST_CASE("DONT_REPORT cast failures remain player-facing", "[spell][failure]") {
REQUIRE(std::string(wowee::game::getSpellCastResultString(27)) ==
"You can't do that right now");
}
// ---------------------------------------------------------------------------
// Range classification
// ---------------------------------------------------------------------------