diff --git a/include/game/entity.hpp b/include/game/entity.hpp index a608f6f5..b4e08cca 100644 --- a/include/game/entity.hpp +++ b/include/game/entity.hpp @@ -153,6 +153,9 @@ public: ObjectType getType() const { return type; } void setType(ObjectType t) { type = t; } + /// True if this entity is a Unit or Player (both derive from Unit). + bool isUnit() const { return type == ObjectType::UNIT || type == ObjectType::PLAYER; } + // Fields (for update values) void setField(uint16_t index, uint32_t value) { fields[index] = value; diff --git a/src/core/application.cpp b/src/core/application.cpp index 69c75430..8e2130a8 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -7155,17 +7155,20 @@ void Application::spawnOnlinePlayer(uint64_t guid, } // Determine texture slots once per model - if (!playerTextureSlotsByModelId_.count(modelId)) { - PlayerTextureSlots slots; - if (const auto* md = charRenderer->getModelData(modelId)) { - for (size_t ti = 0; ti < md->textures.size(); ti++) { - uint32_t t = md->textures[ti].type; - if (t == 1 && slots.skin < 0) slots.skin = static_cast(ti); - else if (t == 6 && slots.hair < 0) slots.hair = static_cast(ti); - else if (t == 8 && slots.underwear < 0) slots.underwear = static_cast(ti); + { + auto [slotIt, inserted] = playerTextureSlotsByModelId_.try_emplace(modelId); + if (inserted) { + PlayerTextureSlots slots; + if (const auto* md = charRenderer->getModelData(modelId)) { + for (size_t ti = 0; ti < md->textures.size(); ti++) { + uint32_t t = md->textures[ti].type; + if (t == 1 && slots.skin < 0) slots.skin = static_cast(ti); + else if (t == 6 && slots.hair < 0) slots.hair = static_cast(ti); + else if (t == 8 && slots.underwear < 0) slots.underwear = static_cast(ti); + } } + slotIt->second = slots; } - playerTextureSlotsByModelId_[modelId] = slots; } // Create instance at server position @@ -7330,14 +7333,13 @@ void Application::setOnlinePlayerEquipment(uint64_t guid, } // If the player isn't spawned yet, store equipment until spawn. - if (!playerInstances_.count(guid) || !onlinePlayerAppearance_.count(guid)) { + auto appIt = onlinePlayerAppearance_.find(guid); + if (!playerInstances_.count(guid) || appIt == onlinePlayerAppearance_.end()) { pendingOnlinePlayerEquipment_[guid] = {displayInfoIds, inventoryTypes}; return; } - auto it = onlinePlayerAppearance_.find(guid); - if (it == onlinePlayerAppearance_.end()) return; - const OnlinePlayerAppearanceState& st = it->second; + const OnlinePlayerAppearanceState& st = appIt->second; auto* charRenderer = renderer->getCharacterRenderer(); if (!charRenderer) return; @@ -7533,9 +7535,10 @@ void Application::spawnOnlineGameObject(uint64_t guid, uint32_t entry, uint32_t } if (!gameObjectLookupsBuilt_) return; - if (gameObjectInstances_.count(guid)) { + auto goIt = gameObjectInstances_.find(guid); + if (goIt != gameObjectInstances_.end()) { // Already have a render instance — update its position (e.g. transport re-creation) - auto& info = gameObjectInstances_[guid]; + auto& info = goIt->second; glm::vec3 renderPos = core::coords::canonicalToRender(glm::vec3(x, y, z)); LOG_DEBUG("GameObject position update: displayId=", displayId, " guid=0x", std::hex, guid, std::dec, " pos=(", x, ", ", y, ", ", z, ")"); diff --git a/src/rendering/character_renderer.cpp b/src/rendering/character_renderer.cpp index 6432c672..92674ffd 100644 --- a/src/rendering/character_renderer.cpp +++ b/src/rendering/character_renderer.cpp @@ -546,12 +546,13 @@ CharacterRenderer::NormalMapResult CharacterRenderer::generateNormalHeightMapCPU const uint8_t* pixels = srcPixels.data(); // Step 1: Compute height from luminance + constexpr float kInv255 = 1.0f / 255.0f; std::vector heightMap(totalPixels); double sumH = 0.0, sumH2 = 0.0; for (uint32_t i = 0; i < totalPixels; i++) { - float r = pixels[i * 4 + 0] / 255.0f; - float g = pixels[i * 4 + 1] / 255.0f; - float b = pixels[i * 4 + 2] / 255.0f; + float r = pixels[i * 4 + 0] * kInv255; + float g = pixels[i * 4 + 1] * kInv255; + float b = pixels[i * 4 + 2] * kInv255; float h = 0.299f * r + 0.587f * g + 0.114f * b; heightMap[i] = h; sumH += h; diff --git a/src/rendering/wmo_renderer.cpp b/src/rendering/wmo_renderer.cpp index 0688ae31..5313f086 100644 --- a/src/rendering/wmo_renderer.cpp +++ b/src/rendering/wmo_renderer.cpp @@ -2159,12 +2159,13 @@ std::unique_ptr WMORenderer::generateNormalHeightMap( const uint32_t totalPixels = width * height; // Step 1: Compute height from luminance + constexpr float kInv255 = 1.0f / 255.0f; std::vector heightMap(totalPixels); double sumH = 0.0, sumH2 = 0.0; for (uint32_t i = 0; i < totalPixels; i++) { - float r = pixels[i * 4 + 0] / 255.0f; - float g = pixels[i * 4 + 1] / 255.0f; - float b = pixels[i * 4 + 2] / 255.0f; + float r = pixels[i * 4 + 0] * kInv255; + float g = pixels[i * 4 + 1] * kInv255; + float b = pixels[i * 4 + 2] * kInv255; float h = 0.299f * r + 0.587f * g + 0.114f * b; heightMap[i] = h; sumH += h; diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 7f86b209..5b97644c 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -3681,7 +3681,7 @@ void GameScreen::renderPetFrame(game::GameHandler& gameHandler) { auto petEntity = gameHandler.getEntityManager().getEntity(petGuid); if (!petEntity) return; - auto* petUnit = dynamic_cast(petEntity.get()); + auto* petUnit = petEntity->isUnit() ? static_cast(petEntity.get()) : nullptr; if (!petUnit) return; // Position below player frame. If in a group, push below party frames @@ -11255,7 +11255,7 @@ void GameScreen::renderCombatText(game::GameHandler& gameHandler) { // Fallback to entity canonical position auto entity = gameHandler.getEntityManager().getEntity(entry.dstGuid); if (entity) { - auto* unit = dynamic_cast(entity.get()); + auto* unit = entity->isUnit() ? static_cast(entity.get()) : nullptr; if (unit) { renderPos = core::coords::canonicalToRender( glm::vec3(unit->getX(), unit->getY(), unit->getZ())); @@ -11540,8 +11540,9 @@ void GameScreen::renderNameplates(game::GameHandler& gameHandler) { for (const auto& [guid, entityPtr] : gameHandler.getEntityManager().getEntities()) { if (!entityPtr || guid == playerGuid) continue; - auto* unit = dynamic_cast(entityPtr.get()); - if (!unit || unit->getMaxHealth() == 0) continue; + if (!entityPtr->isUnit()) continue; + auto* unit = static_cast(entityPtr.get()); + if (unit->getMaxHealth() == 0) continue; bool isPlayer = (entityPtr->getType() == game::ObjectType::PLAYER); bool isTarget = (guid == targetGuid); @@ -14403,7 +14404,7 @@ void GameScreen::renderGuildRoster(game::GameHandler& gameHandler) { if (sig.playerGuid != 0) { auto entity = gameHandler.getEntityManager().getEntity(sig.playerGuid); if (entity) { - auto* unit = dynamic_cast(entity.get()); + auto* unit = entity->isUnit() ? static_cast(entity.get()) : nullptr; if (unit) sigName = unit->getName(); } } @@ -15801,7 +15802,7 @@ void GameScreen::renderLootWindow(game::GameHandler& gameHandler) { const auto& candidates = gameHandler.getMasterLootCandidates(); for (uint64_t candidateGuid : candidates) { auto entity = gameHandler.getEntityManager().getEntity(candidateGuid); - auto* unit = entity ? dynamic_cast(entity.get()) : nullptr; + auto* unit = (entity && entity->isUnit()) ? static_cast(entity.get()) : nullptr; const char* cName = unit ? unit->getName().c_str() : nullptr; char nameBuf[64]; if (!cName || cName[0] == '\0') {