diff --git a/tools/editor/editor_app.cpp b/tools/editor/editor_app.cpp index d5dbf034..1c029fa7 100644 --- a/tools/editor/editor_app.cpp +++ b/tools/editor/editor_app.cpp @@ -211,6 +211,9 @@ void EditorApp::processEvents() { objectsDirty_ = true; } } + if (sc == SDL_SCANCODE_S && (event.key.keysym.mod & KMOD_CTRL)) { + quickSave(); + } if (sc == SDL_SCANCODE_Z && (event.key.keysym.mod & KMOD_CTRL)) { if (mode_ == EditorMode::Sculpt) { if (event.key.keysym.mod & KMOD_SHIFT) @@ -544,6 +547,37 @@ void EditorApp::saveWDT(const std::string& path) { ADTWriter::writeWDT(loadedMap_, loadedTileX_, loadedTileY_, path); } +void EditorApp::exportZone(const std::string& outputDir) { + if (!terrain_.isLoaded() || loadedMap_.empty()) return; + + std::string base = outputDir + "/" + loadedMap_; + + // Save ADT + std::string adtPath = base + "/" + loadedMap_ + "_" + + std::to_string(loadedTileX_) + "_" + + std::to_string(loadedTileY_) + ".adt"; + saveADT(adtPath); + + // Save WDT + std::string wdtPath = base + "/" + loadedMap_ + ".wdt"; + saveWDT(wdtPath); + + // Save creature spawns + if (npcSpawner_.spawnCount() > 0) { + std::string npcPath = base + "/creatures.json"; + npcSpawner_.saveToFile(npcPath); + } + + lastSavePath_ = outputDir; + LOG_INFO("Zone exported to: ", base); +} + +void EditorApp::quickSave() { + if (!terrain_.isLoaded()) return; + std::string dir = lastSavePath_.empty() ? "output" : lastSavePath_; + exportZone(dir); +} + void EditorApp::requestQuit() { window_->setShouldClose(true); } diff --git a/tools/editor/editor_app.hpp b/tools/editor/editor_app.hpp index 88e51276..4fe0e23f 100644 --- a/tools/editor/editor_app.hpp +++ b/tools/editor/editor_app.hpp @@ -32,6 +32,8 @@ public: void createNewTerrain(const std::string& mapName, int tileX, int tileY, float baseHeight, Biome biome); void saveADT(const std::string& path); void saveWDT(const std::string& path); + void exportZone(const std::string& outputDir); + void quickSave(); void requestQuit(); void resetCamera(); @@ -96,6 +98,7 @@ private: bool painting_ = false; bool objectsDirty_ = false; bool openContextMenu_ = false; + std::string lastSavePath_; size_t lastObjectCount_ = 0; EditorMode mode_ = EditorMode::Sculpt; float waterHeight_ = 100.0f; diff --git a/tools/editor/editor_ui.cpp b/tools/editor/editor_ui.cpp index 8cfb5413..8beafd6b 100644 --- a/tools/editor/editor_ui.cpp +++ b/tools/editor/editor_ui.cpp @@ -59,14 +59,8 @@ void EditorUI::processActions(EditorApp& app) { loadRequested_ = false; app.loadADT(loadMapNameBuf_, loadTileX_, loadTileY_); } - if (saveAdtRequested_) { - saveAdtRequested_ = false; - app.saveADT(savePathBuf_); - } - if (saveWdtRequested_) { - saveWdtRequested_ = false; - app.saveWDT(std::string(savePathBuf_)); - } + (void)saveAdtRequested_; + (void)saveWdtRequested_; } void EditorUI::renderMenuBar(EditorApp& app) { @@ -75,7 +69,9 @@ void EditorUI::renderMenuBar(EditorApp& app) { if (ImGui::MenuItem("New Terrain...", "Ctrl+N")) showNewDialog_ = true; if (ImGui::MenuItem("Load ADT...", "Ctrl+O")) showLoadDialog_ = true; ImGui::Separator(); - if (ImGui::MenuItem("Save ADT...", "Ctrl+S", false, app.hasTerrainLoaded())) + if (ImGui::MenuItem("Quick Save", "Ctrl+S", false, app.hasTerrainLoaded())) + app.quickSave(); + if (ImGui::MenuItem("Export Zone...", nullptr, false, app.hasTerrainLoaded())) showSaveDialog_ = true; ImGui::Separator(); if (ImGui::MenuItem("Quit", "Alt+F4")) app.requestQuit(); @@ -168,18 +164,18 @@ void EditorUI::renderLoadDialog(EditorApp& /*app*/) { } void EditorUI::renderSaveDialog(EditorApp& app) { - ImGui::SetNextWindowSize(ImVec2(500, 200), ImGuiCond_FirstUseEver); - if (ImGui::Begin("Save", &showSaveDialog_)) { + ImGui::SetNextWindowSize(ImVec2(500, 220), ImGuiCond_FirstUseEver); + if (ImGui::Begin("Export Zone", &showSaveDialog_)) { if (savePathBuf_[0] == '\0' && app.hasTerrainLoaded()) - std::snprintf(savePathBuf_, sizeof(savePathBuf_), "output/%s/%s_%d_%d.adt", - app.getLoadedMap().c_str(), app.getLoadedMap().c_str(), - app.getLoadedTileX(), app.getLoadedTileY()); - ImGui::InputText("Path", savePathBuf_, sizeof(savePathBuf_)); + std::snprintf(savePathBuf_, sizeof(savePathBuf_), "output"); + ImGui::InputText("Output Directory", savePathBuf_, sizeof(savePathBuf_)); + ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1), + "Exports: ADT, WDT, and creature spawns to %s/%s/", + savePathBuf_, app.getLoadedMap().c_str()); ImGui::Spacing(); - if (ImGui::Button("Save ADT", ImVec2(140, 0))) { saveAdtRequested_ = true; showSaveDialog_ = false; } - ImGui::SameLine(); - if (ImGui::Button("Save ADT + WDT", ImVec2(140, 0))) { - saveAdtRequested_ = true; saveWdtRequested_ = true; showSaveDialog_ = false; + if (ImGui::Button("Export All", ImVec2(140, 0))) { + app.exportZone(savePathBuf_); + showSaveDialog_ = false; } ImGui::SameLine(); if (ImGui::Button("Cancel", ImVec2(80, 0))) showSaveDialog_ = false; @@ -639,14 +635,16 @@ void EditorUI::renderContextMenu(EditorApp& app) { void EditorUI::renderPropertiesPanel(EditorApp& app) { ImGuiViewport* vp = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(ImVec2(vp->Size.x - 280, 90), ImGuiCond_FirstUseEver); - ImGui::SetNextWindowSize(ImVec2(270, 180), ImGuiCond_FirstUseEver); - if (ImGui::Begin("Properties")) { + ImGui::SetNextWindowSize(ImVec2(270, 220), ImGuiCond_FirstUseEver); + if (ImGui::Begin("Info")) { auto* tr = app.getTerrainRenderer(); if (tr && tr->getChunkCount() > 0) { ImGui::Text("Map: %s [%d, %d]", app.getLoadedMap().c_str(), app.getLoadedTileX(), app.getLoadedTileY()); ImGui::Text("Chunks: %d Tris: %d", tr->getChunkCount(), tr->getTriangleCount()); - ImGui::Text("Objects: %zu", app.getObjectPlacer().objectCount()); + ImGui::Text("Objects: %zu NPCs: %zu", + app.getObjectPlacer().objectCount(), + app.getNpcSpawner().spawnCount()); } else { ImGui::TextColored(ImVec4(0.6f, 0.6f, 0.6f, 1.0f), "No terrain loaded"); } @@ -654,8 +652,19 @@ void EditorUI::renderPropertiesPanel(EditorApp& app) { auto pos = app.getEditorCamera().getCamera().getPosition(); ImGui::Text("Camera: %.0f, %.0f, %.0f", pos.x, pos.y, pos.z); ImGui::Text("Speed: %.0f (scroll)", app.getEditorCamera().getSpeed()); + + // Cursor world position + auto& brush = app.getTerrainEditor().brush(); + if (brush.isActive()) { + auto bp = brush.getPosition(); + ImGui::Text("Cursor: %.1f, %.1f, %.1f", bp.x, bp.y, bp.z); + } + + ImGui::Separator(); if (app.getTerrainEditor().hasUnsavedChanges()) - ImGui::TextColored(ImVec4(1, 0.8f, 0.3f, 1), "* Unsaved changes"); + ImGui::TextColored(ImVec4(1, 0.8f, 0.3f, 1), "* Unsaved (Ctrl+S to save)"); + else + ImGui::TextColored(ImVec4(0.5f, 0.8f, 0.5f, 1), "Saved"); } ImGui::End(); }