mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Fix creature render desync and corpse/loot state edge cases
- add per-frame nearby creature render sync from entity positions/orientation to prevent model-vs-target-circle drift - treat lootable dynflag as dead state hint for unit spawn/deferred-display paths - fire NPC death callback when a late display spawn is already dead/lootable - remove loot-response money fallback announce/SFX to stop duplicate copper messages on re-opened corpses
This commit is contained in:
parent
c00fc34bc2
commit
d3b04640f3
2 changed files with 38 additions and 22 deletions
|
|
@ -823,6 +823,35 @@ void Application::update(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
// Keep creature render instances aligned with authoritative entity positions.
|
||||
// This prevents desync where target circles move with server entities but
|
||||
// creature models remain at stale spawn positions.
|
||||
if (renderer && gameHandler && renderer->getCharacterRenderer()) {
|
||||
auto* charRenderer = renderer->getCharacterRenderer();
|
||||
glm::vec3 playerPos(0.0f);
|
||||
bool havePlayerPos = false;
|
||||
if (auto playerEntity = gameHandler->getEntityManager().getEntity(gameHandler->getPlayerGuid())) {
|
||||
playerPos = glm::vec3(playerEntity->getX(), playerEntity->getY(), playerEntity->getZ());
|
||||
havePlayerPos = true;
|
||||
}
|
||||
const float syncRadiusSq = 320.0f * 320.0f;
|
||||
for (const auto& [guid, instanceId] : creatureInstances_) {
|
||||
auto entity = gameHandler->getEntityManager().getEntity(guid);
|
||||
if (!entity || entity->getType() != game::ObjectType::UNIT) continue;
|
||||
|
||||
glm::vec3 canonical(entity->getX(), entity->getY(), entity->getZ());
|
||||
if (havePlayerPos) {
|
||||
glm::vec3 d = canonical - playerPos;
|
||||
if (glm::dot(d, d) > syncRadiusSq) continue;
|
||||
}
|
||||
|
||||
glm::vec3 renderPos = core::coords::canonicalToRender(canonical);
|
||||
charRenderer->setInstancePosition(instanceId, renderPos);
|
||||
float renderYaw = entity->getOrientation() + glm::radians(90.0f);
|
||||
charRenderer->setInstanceRotation(instanceId, glm::vec3(0.0f, 0.0f, renderYaw));
|
||||
}
|
||||
}
|
||||
|
||||
// Movement heartbeat is sent from GameHandler::update() to avoid
|
||||
// duplicate packets from multiple update loops.
|
||||
|
||||
|
|
|
|||
|
|
@ -3471,6 +3471,7 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
if (block.objectType == ObjectType::UNIT || block.objectType == ObjectType::PLAYER) {
|
||||
auto unit = std::static_pointer_cast<Unit>(entity);
|
||||
constexpr uint32_t UNIT_DYNFLAG_DEAD = 0x0008;
|
||||
constexpr uint32_t UNIT_DYNFLAG_LOOTABLE = 0x0001;
|
||||
bool unitInitiallyDead = false;
|
||||
const uint16_t ufHealth = fieldIndex(UF::UNIT_FIELD_HEALTH);
|
||||
const uint16_t ufPower = fieldIndex(UF::UNIT_FIELD_POWER1);
|
||||
|
|
@ -3501,7 +3502,7 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
else if (key == ufDynFlags) {
|
||||
unit->setDynamicFlags(val);
|
||||
if (block.objectType == ObjectType::UNIT &&
|
||||
(val & UNIT_DYNFLAG_DEAD) != 0) {
|
||||
((val & UNIT_DYNFLAG_DEAD) != 0 || (val & UNIT_DYNFLAG_LOOTABLE) != 0)) {
|
||||
unitInitiallyDead = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -3849,6 +3850,7 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
if (entity->getType() == ObjectType::UNIT || entity->getType() == ObjectType::PLAYER) {
|
||||
auto unit = std::static_pointer_cast<Unit>(entity);
|
||||
constexpr uint32_t UNIT_DYNFLAG_DEAD = 0x0008;
|
||||
constexpr uint32_t UNIT_DYNFLAG_LOOTABLE = 0x0001;
|
||||
uint32_t oldDisplayId = unit->getDisplayId();
|
||||
bool displayIdChanged = false;
|
||||
bool npcDeathNotified = false;
|
||||
|
|
@ -3997,6 +3999,12 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
} else if (creatureSpawnCallback_) {
|
||||
creatureSpawnCallback_(block.guid, unit->getDisplayId(),
|
||||
unit->getX(), unit->getY(), unit->getZ(), unit->getOrientation());
|
||||
bool isDeadNow = (unit->getHealth() == 0) ||
|
||||
((unit->getDynamicFlags() & (UNIT_DYNFLAG_DEAD | UNIT_DYNFLAG_LOOTABLE)) != 0);
|
||||
if (isDeadNow && !npcDeathNotified && npcDeathCallback_) {
|
||||
npcDeathCallback_(block.guid);
|
||||
npcDeathNotified = true;
|
||||
}
|
||||
}
|
||||
if (entity->getType() == ObjectType::UNIT && (unit->getNpcFlags() & 0x02) && socket) {
|
||||
network::Packet qsPkt(wireOpcode(Opcode::CMSG_QUESTGIVER_STATUS_QUERY));
|
||||
|
|
@ -8490,27 +8498,6 @@ void GameHandler::handleLootResponse(network::Packet& packet) {
|
|||
}
|
||||
|
||||
if (currentLoot.gold > 0) {
|
||||
// Some servers don't send SMSG_LOOT_MONEY_NOTIFY consistently.
|
||||
// Announce money immediately on loot response as a fallback.
|
||||
auto it = localLootState_.find(currentLoot.lootGuid);
|
||||
bool alreadyAnnounced = (it != localLootState_.end() && it->second.moneyTaken);
|
||||
if (!alreadyAnnounced) {
|
||||
addSystemChatMessage("Looted: " + formatCopperAmount(currentLoot.gold));
|
||||
auto* renderer = core::Application::getInstance().getRenderer();
|
||||
if (renderer) {
|
||||
if (auto* sfx = renderer->getUiSoundManager()) {
|
||||
if (currentLoot.gold >= 10000) {
|
||||
sfx->playLootCoinLarge();
|
||||
} else {
|
||||
sfx->playLootCoinSmall();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (it != localLootState_.end()) {
|
||||
it->second.moneyTaken = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (state == WorldState::IN_WORLD && socket) {
|
||||
// Auto-loot gold by sending CMSG_LOOT_MONEY (server handles the rest)
|
||||
auto pkt = LootMoneyPacket::build();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue