diff --git a/tools/editor/editor_app.cpp b/tools/editor/editor_app.cpp index dacc143b..af1208b1 100644 --- a/tools/editor/editor_app.cpp +++ b/tools/editor/editor_app.cpp @@ -1792,6 +1792,44 @@ void EditorApp::randomPopulateZone(int creatureCount, int objectCount, " creatures + " + std::to_string(placedObjects) + " objects"); } +void EditorApp::snapAllSpawnsToGround() { + if (!terrain_.isLoaded()) { + showToast("Load a tile first"); + return; + } + auto castDown = [&](const glm::vec3& pos, glm::vec3& hit) { + rendering::Ray ray; + ray.origin = pos + glm::vec3(0, 0, 500); + ray.direction = glm::vec3(0, 0, -1); + return terrainEditor_.raycastTerrain(ray, hit); + }; + int snappedC = 0, snappedO = 0; + for (auto& s : npcSpawner_.getSpawns()) { + glm::vec3 hit; + if (castDown(s.position, hit)) { + s.position.z = hit.z; + snappedC++; + } + for (auto& wp : s.patrolPath) { + if (castDown(wp.position, hit)) wp.position.z = hit.z; + } + } + for (auto& o : objectPlacer_.getObjects()) { + glm::vec3 hit; + if (castDown(o.position, hit)) { + o.position.z = hit.z; + snappedO++; + } + } + if (snappedC > 0 || snappedO > 0) { + objectsDirty_ = true; + autoSavePendingChanges_ = true; + viewport_.updateNpcMarkers(npcSpawner_.getSpawns()); + } + showToast("Snapped " + std::to_string(snappedC) + " creature(s) + " + + std::to_string(snappedO) + " object(s) to ground"); +} + void EditorApp::clearAllObjects() { vkDeviceWaitIdle(window_->getVkContext()->getDevice()); objectPlacer_.clearAll(); diff --git a/tools/editor/editor_app.hpp b/tools/editor/editor_app.hpp index 5d0c573c..fc12a82b 100644 --- a/tools/editor/editor_app.hpp +++ b/tools/editor/editor_app.hpp @@ -102,6 +102,10 @@ public: // tile's world bbox. Mirrors the --random-populate-zone CLI // command so users can do bulk population from inside the editor. void randomPopulateZone(int creatureCount, int objectCount, uint32_t seed); + // Re-snap every creature + object Z to actual terrain. In-editor + // mirror of the --snap-zone-to-ground CLI; useful after terrain + // edits or random population to fix floating/buried spawns. + void snapAllSpawnsToGround(); void centerOnTerrain(); // Multi-tile support diff --git a/tools/editor/editor_ui.cpp b/tools/editor/editor_ui.cpp index c8abfa21..311be3df 100644 --- a/tools/editor/editor_ui.cpp +++ b/tools/editor/editor_ui.cpp @@ -395,6 +395,14 @@ void EditorUI::renderMenuBar(EditorApp& app) { "Same seed → same population (reproducible)"); ImGui::EndMenu(); } + if (ImGui::MenuItem("Snap All Spawns to Ground", nullptr, false, + app.hasTerrainLoaded())) { + app.snapAllSpawnsToGround(); + } + if (ImGui::IsItemHovered()) + ImGui::SetTooltip( + "Re-snap every creature + object's Z to actual terrain height.\n" + "Run after terrain edits to fix floating/buried spawns."); if (ImGui::MenuItem("Clear All Objects/NPCs", nullptr, false, app.hasTerrainLoaded())) { if (app.getObjectPlacer().objectCount() > 0 || app.getNpcSpawner().spawnCount() > 0) app.clearAllObjects();