diff --git a/include/core/application.hpp b/include/core/application.hpp index d6651361..a323df16 100644 --- a/include/core/application.hpp +++ b/include/core/application.hpp @@ -175,6 +175,7 @@ private: std::unordered_map facialHairGeosetMap_; std::unordered_map creatureInstances_; // guid → render instanceId std::unordered_map creatureModelIds_; // guid → loaded modelId + std::unordered_set deadCreatureGuids_; // GUIDs that should spawn in corpse/death pose std::unordered_map displayIdModelCache_; // displayId → modelId (model caching) uint32_t nextCreatureModelId_ = 5000; // Model IDs for online creatures uint32_t gryphonDisplayId_ = 0; diff --git a/src/core/application.cpp b/src/core/application.cpp index 9b0d2b13..bc6878f2 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -1694,6 +1694,7 @@ void Application::setupUICallbacks() { // NPC death callback (online mode) - play death animation gameHandler->setNpcDeathCallback([this](uint64_t guid) { + deadCreatureGuids_.insert(guid); auto it = creatureInstances_.find(guid); if (it != creatureInstances_.end() && renderer && renderer->getCharacterRenderer()) { renderer->getCharacterRenderer()->playAnimation(it->second, 1, false); // Death @@ -1702,6 +1703,7 @@ void Application::setupUICallbacks() { // NPC respawn callback (online mode) - reset to idle animation gameHandler->setNpcRespawnCallback([this](uint64_t guid) { + deadCreatureGuids_.erase(guid); auto it = creatureInstances_.find(guid); if (it != creatureInstances_.end() && renderer && renderer->getCharacterRenderer()) { renderer->getCharacterRenderer()->playAnimation(it->second, 0, true); // Idle @@ -2777,6 +2779,7 @@ void Application::loadOnlineWorldTerrain(uint32_t mapId, float x, float y, float auto* app = this; gameHandler->setNpcDeathCallback([cr, app](uint64_t guid) { + app->deadCreatureGuids_.insert(guid); auto it = app->creatureInstances_.find(guid); if (it != app->creatureInstances_.end() && cr) { cr->playAnimation(it->second, 1, false); // animation ID 1 = Death @@ -2784,6 +2787,7 @@ void Application::loadOnlineWorldTerrain(uint32_t mapId, float x, float y, float }); gameHandler->setNpcRespawnCallback([cr, app](uint64_t guid) { + app->deadCreatureGuids_.erase(guid); auto it = app->creatureInstances_.find(guid); if (it != app->creatureInstances_.end() && cr) { cr->playAnimation(it->second, 0, true); // animation ID 0 = Idle @@ -3802,8 +3806,13 @@ void Application::spawnOnlineCreature(uint64_t guid, uint32_t displayId, float x } } - // Play idle animation and fade in - charRenderer->playAnimation(instanceId, 0, true); + // Spawn in the correct pose. If the server marked this creature dead before + // the queued spawn was processed, start directly in death animation. + if (deadCreatureGuids_.count(guid)) { + charRenderer->playAnimation(instanceId, 1, false); // Death + } else { + charRenderer->playAnimation(instanceId, 0, true); // Idle + } charRenderer->startFadeIn(instanceId, 0.5f); // Track instance @@ -4970,6 +4979,7 @@ void Application::despawnOnlineCreature(uint64_t guid) { pendingCreatureSpawnGuids_.erase(guid); creatureSpawnRetryCounts_.erase(guid); creaturePermanentFailureGuids_.erase(guid); + deadCreatureGuids_.erase(guid); auto it = creatureInstances_.find(guid); if (it == creatureInstances_.end()) return; diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index bc22b4c3..3c0a3758 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -766,6 +766,19 @@ void GameHandler::update(float deltaTime) { } } + // Keep active melee attackers visually facing the player as positions change. + // Some servers don't stream frequent orientation updates during combat. + if (!hostileAttackers_.empty()) { + for (uint64_t attackerGuid : hostileAttackers_) { + auto attacker = entityManager.getEntity(attackerGuid); + if (!attacker) continue; + float dx = movementInfo.x - attacker->getX(); + float dy = movementInfo.y - attacker->getY(); + if (std::abs(dx) < 0.01f && std::abs(dy) < 0.01f) continue; + attacker->setOrientation(std::atan2(-dy, dx)); + } + } + // Close vendor/gossip/taxi window if player walks too far from NPC if (vendorWindowOpen && currentVendorItems.vendorGuid != 0) { auto npc = entityManager.getEntity(currentVendorItems.vendorGuid);