fix: warmup checks WMO floor + terrain + tile count before spawning

Stormwind players stand on WMO floors, not terrain. The terrain-only
check passed immediately (terrain exists below the city) but the WMO
floor hadn't loaded yet, so the player fell through.

Now checks three ground sources in order:
1. Terrain height at spawn point
2. WMO floor height at spawn point (for cities/buildings)
3. After 8s, accepts if 4+ terrain tiles are loaded (fallback)

Won't exit warmup until at least one ground source returns valid height,
or the 25s hard cap is reached.
This commit is contained in:
Kelsi 2026-03-28 11:23:18 -07:00
parent 7b26938e45
commit 8aaa2e7ff3

View file

@ -5318,27 +5318,44 @@ void Application::loadOnlineWorldTerrain(uint32_t mapId, float x, float y, float
idleIterations = 0; idleIterations = 0;
} }
// Don't exit warmup until the terrain tile under the player's feet is loaded. // Don't exit warmup until the ground under the player exists.
// Use the world entry position (from the server), not getCharacterPosition() // In cities like Stormwind, players stand on WMO floors, not terrain.
// which may be (0,0,0) during warmup. // Check BOTH terrain AND WMO floor — require at least one to be valid.
bool terrainReady = true; bool groundReady = false;
if (renderer && renderer->getTerrainManager()) { if (renderer) {
auto* tm = renderer->getTerrainManager();
// Convert canonical server coords to render coords for terrain query
glm::vec3 renderSpawn = core::coords::canonicalToRender( glm::vec3 renderSpawn = core::coords::canonicalToRender(
glm::vec3(x, y, z)); glm::vec3(x, y, z));
terrainReady = tm->getHeightAt(renderSpawn.x, renderSpawn.y).has_value(); float rx = renderSpawn.x, ry = renderSpawn.y, rz = renderSpawn.z;
if (!terrainReady && elapsed > 5.0f && static_cast<int>(elapsed) % 3 == 0) {
LOG_WARNING("Warmup: terrain not ready at spawn (", renderSpawn.x, // Check terrain
",", renderSpawn.y, ") after ", elapsed, "s"); if (auto* tm = renderer->getTerrainManager()) {
if (tm->getHeightAt(rx, ry).has_value()) groundReady = true;
}
// Check WMO floor (cities, buildings)
if (!groundReady) {
if (auto* wmo = renderer->getWMORenderer()) {
if (wmo->getFloorHeight(rx, ry, rz + 5.0f).has_value()) groundReady = true;
}
}
// After minimum warmup, also accept if enough terrain tiles are loaded
// (player may be on M2 collision or other surface)
if (!groundReady && elapsed >= 8.0f) {
if (auto* tm = renderer->getTerrainManager()) {
groundReady = (tm->getLoadedTileCount() >= 4);
}
}
if (!groundReady && elapsed > 5.0f && static_cast<int>(elapsed * 2) % 3 == 0) {
LOG_WARNING("Warmup: ground not ready at spawn (", rx, ",", ry, ",", rz,
") after ", elapsed, "s");
} }
} }
// Exit when: (min time passed AND queues drained AND terrain ready) OR hard cap // Exit when: (min time passed AND queues drained AND ground ready) OR hard cap
bool readyToExit = (elapsed >= kMinWarmupSeconds && idleIterations >= kIdleThreshold && terrainReady); bool readyToExit = (elapsed >= kMinWarmupSeconds && idleIterations >= kIdleThreshold && groundReady);
if (readyToExit || elapsed >= kMaxWarmupSeconds) { if (readyToExit || elapsed >= kMaxWarmupSeconds) {
if (elapsed >= kMaxWarmupSeconds && !terrainReady) { if (elapsed >= kMaxWarmupSeconds && !groundReady) {
LOG_WARNING("Warmup hit hard cap (", kMaxWarmupSeconds, "s), terrain NOT ready — may fall through world"); LOG_WARNING("Warmup hit hard cap (", kMaxWarmupSeconds, "s), ground NOT ready — may fall through world");
} else if (elapsed >= kMaxWarmupSeconds) { } else if (elapsed >= kMaxWarmupSeconds) {
LOG_WARNING("Warmup hit hard cap (", kMaxWarmupSeconds, "s), entering world with pending work"); LOG_WARNING("Warmup hit hard cap (", kMaxWarmupSeconds, "s), entering world with pending work");
} }