From d00ddd1c73fc7e8e3ede9474b518ae0bf8911dd8 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 5 May 2026 14:38:01 -0700 Subject: [PATCH] feat(editor): WCP shortcut, help panel overhaul, active path restore - Ctrl+Shift+E keyboard shortcut for WCP content pack export - Help panel expanded: object tools (snap/align/flatten/scatter/select by type), all terrain generators, stamp save/load, export shortcuts - ObjectPlacer::loadFromFile restores activePath_ from loaded objects so users can continue placing the same type after loading - WCP export menu item now shows Ctrl+Shift+E hint --- tools/editor/editor_app.cpp | 4 ++++ tools/editor/editor_ui.cpp | 25 +++++++++++++++++++------ tools/editor/object_placer.cpp | 6 ++++++ 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/tools/editor/editor_app.cpp b/tools/editor/editor_app.cpp index f1df91cd..a6c27eb6 100644 --- a/tools/editor/editor_app.cpp +++ b/tools/editor/editor_app.cpp @@ -310,6 +310,10 @@ void EditorApp::processEvents() { } if (sc == SDL_SCANCODE_S && (event.key.keysym.mod & KMOD_CTRL)) quickSave(); + if (sc == SDL_SCANCODE_E && (event.key.keysym.mod & KMOD_CTRL) && + (event.key.keysym.mod & KMOD_SHIFT) && terrain_.isLoaded()) { + exportContentPack("output/" + loadedMap_ + ".wcp"); + } if (sc == SDL_SCANCODE_N && (event.key.keysym.mod & KMOD_CTRL)) ui_.openNewTerrainDialog(); if (sc == SDL_SCANCODE_O && (event.key.keysym.mod & KMOD_CTRL)) diff --git a/tools/editor/editor_ui.cpp b/tools/editor/editor_ui.cpp index 02a58f50..d011d45f 100644 --- a/tools/editor/editor_ui.cpp +++ b/tools/editor/editor_ui.cpp @@ -355,7 +355,7 @@ void EditorUI::renderMenuBar(EditorApp& app) { showSaveDialog_ = true; if (ImGui::MenuItem("Export Open Format (.wot/.whm)", nullptr, false, app.hasTerrainLoaded())) app.exportOpenFormat("output"); - if (ImGui::MenuItem("Export Content Pack (.wcp)", nullptr, false, app.hasTerrainLoaded())) { + if (ImGui::MenuItem("Export Content Pack (.wcp)", "Ctrl+Shift+E", false, app.hasTerrainLoaded())) { std::string wcpPath = "output/" + app.getLoadedMap() + ".wcp"; app.exportContentPack(wcpPath); } @@ -524,7 +524,7 @@ void EditorUI::renderMenuBar(EditorApp& app) { // Help overlay if (showHelp_) { - ImGui::SetNextWindowSize(ImVec2(400, 350), ImGuiCond_FirstUseEver); + ImGui::SetNextWindowSize(ImVec2(420, 500), ImGuiCond_FirstUseEver); if (ImGui::Begin("Keyboard Shortcuts", &showHelp_)) { ImGui::Text("Navigation:"); ImGui::BulletText("WASD — fly camera"); @@ -575,11 +575,24 @@ void EditorUI::renderMenuBar(EditorApp& app) { ImGui::BulletText("Scroll — zoom in/out"); ImGui::BulletText("Shift+Scroll — adjust speed"); ImGui::Separator(); + ImGui::Text("Object Tools:"); + ImGui::BulletText("Snap Ground — drop to terrain surface"); + ImGui::BulletText("Align Slope — rotate to terrain normal"); + ImGui::BulletText("Flatten Ground — level terrain around object"); + ImGui::BulletText("Scatter — mass-place with auto-align option"); + ImGui::BulletText("Select by Type — M2 models or WMO buildings"); + ImGui::Separator(); ImGui::Text("Terrain Tools:"); - ImGui::BulletText("Noise → Smooth → Scale → Clamp → Auto-paint"); - ImGui::BulletText("River/Road: Set Start → Set End"); - ImGui::BulletText("Stamp: Copy → Paste"); - ImGui::BulletText("Mirror X/Y for symmetric zones"); + ImGui::BulletText("Generators: Hill/Mesa/Crater/Canyon/Island/Ridge/Dunes"); + ImGui::BulletText("River/Road: click start → click end → Apply"); + ImGui::BulletText("Stamp: Copy → Save/Load → Paste (cross-zone)"); + ImGui::BulletText("Mirror X/Y, Rotate 90, Scale/Offset heights"); + ImGui::BulletText("Auto-paint by height/slope, scatter patches"); + ImGui::Separator(); + ImGui::Text("Export:"); + ImGui::BulletText("Ctrl+S — quick save (all formats)"); + ImGui::BulletText("Ctrl+Shift+E — export content pack (.wcp)"); + ImGui::BulletText("File → Batch Convert Assets (M2→WOM, WMO→WOB)"); } ImGui::End(); } diff --git a/tools/editor/object_placer.cpp b/tools/editor/object_placer.cpp index 6a8d82da..64b057ef 100644 --- a/tools/editor/object_placer.cpp +++ b/tools/editor/object_placer.cpp @@ -270,6 +270,12 @@ bool ObjectPlacer::loadFromFile(const std::string& path) { } } + // Restore active path from last loaded object for seamless placement + if (!objects_.empty()) { + activePath_ = objects_.back().path; + activeType_ = objects_.back().type; + } + LOG_INFO("Objects loaded: ", path, " (", objects_.size(), " objects)"); return true; } catch (const std::exception& e) {