From 7d88c5f5386c5282047a4013fab02c7fdd5055f9 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 6 May 2026 01:17:25 -0700 Subject: [PATCH] feat(editor): Ctrl+D duplicates the selected object or NPC at (10,10) offset --- tools/editor/editor_app.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tools/editor/editor_app.cpp b/tools/editor/editor_app.cpp index 8b21ca79..926b1a4b 100644 --- a/tools/editor/editor_app.cpp +++ b/tools/editor/editor_app.cpp @@ -350,6 +350,29 @@ void EditorApp::processEvents() { objectPlacer_.selectAll(); showToast("Selected " + std::to_string(objectPlacer_.selectionCount()) + " objects"); } + // Ctrl+D = duplicate selected object or NPC, offset by (10,10) on the ground. + if (sc == SDL_SCANCODE_D && (event.key.keysym.mod & KMOD_CTRL)) { + if (auto* sel = objectPlacer_.getSelected()) { + std::string dupPath = sel->path; + glm::vec3 dupPos = sel->position + glm::vec3(10.0f, 10.0f, 0.0f); + glm::vec3 dupRot = sel->rotation; + float dupScale = sel->scale; + auto dupType = sel->type; + objectPlacer_.clearSelection(); + objectPlacer_.setActivePath(dupPath, dupType); + objectPlacer_.setPlacementScale(dupScale); + objectPlacer_.setPlacementRotationY(dupRot.y); + objectPlacer_.placeObject(dupPos); + objectsDirty_ = true; + showToast("Duplicated object"); + } else if (auto* npc = npcSpawner_.getSelected()) { + CreatureSpawn copy = *npc; + copy.position += glm::vec3(10, 10, 0); + npcSpawner_.placeCreature(copy); + objectsDirty_ = true; + showToast("Duplicated NPC"); + } + } // W: add patrol waypoint at cursor for the selected NPC (no modifiers, // editor must be in NPC mode and an NPC must be selected with Patrol behavior). if (sc == SDL_SCANCODE_W && mode_ == EditorMode::NPC &&