From 602926621aaa150e913883758f4da5eeee1d6136 Mon Sep 17 00:00:00 2001 From: Dean Blackborough Date: Sun, 12 Jul 2026 22:52:26 +0100 Subject: [PATCH 1/6] Update notes, not all commands should make the scene dirty, camera move for example --- NOTES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NOTES.md b/NOTES.md index dfc15b3..0135271 100644 --- a/NOTES.md +++ b/NOTES.md @@ -18,7 +18,7 @@ Track whether the current scene has unsaved editor-authored changes. Initial scope: -* [ ] Mark scene dirty when editor commands are executed. +* [ ] Mark the scene dirty when a command modifies persistent, editor-authored scene data. * [ ] Mark scene dirty when inspector edits are committed. * [ ] Mark scene dirty when authored objects are created, deleted, duplicated, moved, scaled, or reordered. * [ ] Clear dirty state after a successful save. From f5990cb399a859fd3f3261ecc17c33aacd9c6095 Mon Sep 17 00:00:00 2001 From: Dean Blackborough Date: Sun, 12 Jul 2026 23:07:54 +0100 Subject: [PATCH 2/6] Mark scene as dirty and default to all tools making state dirty except camera --- src/prune/editor/editor_command.cpp | 1 + src/prune/editor/editor_command.hpp | 1 + src/prune/scene/scene.hpp | 3 ++- src/prune/scene/scene_state.hpp | 1 + src/prune/scene/world_scene.cpp | 14 +++++++++++++- src/prune/scene/world_scene.hpp | 3 ++- src/prune/tooling/ui.cpp | 3 +++ 7 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/prune/editor/editor_command.cpp b/src/prune/editor/editor_command.cpp index 027f99e..0f1847c 100644 --- a/src/prune/editor/editor_command.cpp +++ b/src/prune/editor/editor_command.cpp @@ -209,6 +209,7 @@ namespace prune { ) { EditorCommand command{}; command.type = EditorCommandType::MoveViewport; + command.modifies_scene = false; command.label = std::string(label); command.detail = std::string(detail); command.before_camera = before; diff --git a/src/prune/editor/editor_command.hpp b/src/prune/editor/editor_command.hpp index e8addec..1761802 100644 --- a/src/prune/editor/editor_command.hpp +++ b/src/prune/editor/editor_command.hpp @@ -30,6 +30,7 @@ namespace prune { struct EditorCommand { EditorCommandType type = EditorCommandType::MoveObject; + bool modifies_scene = true; std::string label; std::string detail; GameObjectId object_id = k_invalid_game_object_id; diff --git a/src/prune/scene/scene.hpp b/src/prune/scene/scene.hpp index 71612cb..7738b8d 100644 --- a/src/prune/scene/scene.hpp +++ b/src/prune/scene/scene.hpp @@ -58,8 +58,9 @@ namespace prune { virtual void update_editor(float, const Input&) {} virtual void render(SDL_Renderer* renderer) = 0; - [[nodiscard]] virtual bool save_to_file(std::string_view path, std::string& error) const = 0; + [[nodiscard]] virtual bool save_to_file(std::string_view path, std::string& error) = 0; [[nodiscard]] virtual bool load_from_file(std::string_view path, std::string& error) = 0; + [[nodiscard]] virtual bool is_dirty() const noexcept = 0; [[nodiscard]] virtual std::string_view default_file_path() const noexcept = 0; [[nodiscard]] virtual std::string_view scene_type_id() const noexcept = 0; diff --git a/src/prune/scene/scene_state.hpp b/src/prune/scene/scene_state.hpp index 13517f7..62289c5 100644 --- a/src/prune/scene/scene_state.hpp +++ b/src/prune/scene/scene_state.hpp @@ -80,6 +80,7 @@ namespace prune { }; struct SceneState { + bool dirty = false; SceneViewport viewport{}; DragState drag_state{}; SceneOptions scene_options{}; diff --git a/src/prune/scene/world_scene.cpp b/src/prune/scene/world_scene.cpp index 1e85ae6..ae80be2 100644 --- a/src/prune/scene/world_scene.cpp +++ b/src/prune/scene/world_scene.cpp @@ -113,7 +113,7 @@ namespace prune { } } - bool WorldScene::save_to_file(std::string_view path, std::string& error) const + bool WorldScene::save_to_file(std::string_view path, std::string& error) { try { YAML::Node root; @@ -136,6 +136,7 @@ namespace prune { return false; } + m_state.dirty = false; return true; } catch (const YAML::Exception& ex) { @@ -180,6 +181,7 @@ namespace prune { m_state.events.clear(); m_state.drag_state = {}; m_state.editor_commands.clear(); + m_state.dirty = false; m_camera = loaded_camera; m_grid_options = loaded_grid_options; @@ -228,6 +230,10 @@ namespace prune { void WorldScene::record_editor_command(EditorCommand command) { + if (command.modifies_scene) { + m_state.dirty = true; + } + m_state.editor_commands.record(std::move(command)); } @@ -244,6 +250,9 @@ namespace prune { } apply_editor_command(*command, false); + if (command->modifies_scene) { + m_state.dirty = true; + } return true; } @@ -255,6 +264,9 @@ namespace prune { } apply_editor_command(*command, true); + if (command->modifies_scene) { + m_state.dirty = true; + } return true; } diff --git a/src/prune/scene/world_scene.hpp b/src/prune/scene/world_scene.hpp index 5c7db2e..e9ff7f9 100644 --- a/src/prune/scene/world_scene.hpp +++ b/src/prune/scene/world_scene.hpp @@ -25,8 +25,9 @@ namespace prune { void render(SDL_Renderer* renderer) final; void draw_viewport_overlays() final; - [[nodiscard]] bool save_to_file(std::string_view path, std::string& error) const final; + [[nodiscard]] bool save_to_file(std::string_view path, std::string& error) final; [[nodiscard]] bool load_from_file(std::string_view path, std::string& error) final; + [[nodiscard]] bool is_dirty() const noexcept final { return m_state.dirty; } void set_viewport(const SceneViewport& viewport) noexcept final; [[nodiscard]] const SceneViewport& get_viewport() const noexcept final { return m_state.viewport; } diff --git a/src/prune/tooling/ui.cpp b/src/prune/tooling/ui.cpp index 7504c75..91ed2ba 100644 --- a/src/prune/tooling/ui.cpp +++ b/src/prune/tooling/ui.cpp @@ -268,6 +268,9 @@ namespace prune { draw_scene_viewport(scene, renderer); if (ImGui::BeginMainMenuBar()) { + ImGui::Text("%.*s%s", static_cast(scene.scene_name().size()), scene.scene_name().data(), scene.is_dirty() ? " *" : ""); + ImGui::Separator(); + if (ImGui::BeginMenu("File")) { if (ImGui::BeginMenu("New Scene")) { for (const SceneDescriptor& descriptor : k_scene_descriptors) { From fe6c474309f7dbe384064a2c3c5ef77583e490a3 Mon Sep 17 00:00:00 2001 From: Dean Blackborough Date: Sun, 12 Jul 2026 23:10:12 +0100 Subject: [PATCH 3/6] Switched to makes_dirty --- src/prune/editor/editor_command.cpp | 2 +- src/prune/editor/editor_command.hpp | 2 +- src/prune/scene/world_scene.cpp | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/prune/editor/editor_command.cpp b/src/prune/editor/editor_command.cpp index 0f1847c..d536470 100644 --- a/src/prune/editor/editor_command.cpp +++ b/src/prune/editor/editor_command.cpp @@ -209,7 +209,7 @@ namespace prune { ) { EditorCommand command{}; command.type = EditorCommandType::MoveViewport; - command.modifies_scene = false; + command.makes_dirty = false; command.label = std::string(label); command.detail = std::string(detail); command.before_camera = before; diff --git a/src/prune/editor/editor_command.hpp b/src/prune/editor/editor_command.hpp index 1761802..d265b77 100644 --- a/src/prune/editor/editor_command.hpp +++ b/src/prune/editor/editor_command.hpp @@ -30,7 +30,7 @@ namespace prune { struct EditorCommand { EditorCommandType type = EditorCommandType::MoveObject; - bool modifies_scene = true; + bool makes_dirty = true; std::string label; std::string detail; GameObjectId object_id = k_invalid_game_object_id; diff --git a/src/prune/scene/world_scene.cpp b/src/prune/scene/world_scene.cpp index ae80be2..2ee7299 100644 --- a/src/prune/scene/world_scene.cpp +++ b/src/prune/scene/world_scene.cpp @@ -230,7 +230,7 @@ namespace prune { void WorldScene::record_editor_command(EditorCommand command) { - if (command.modifies_scene) { + if (command.makes_dirty) { m_state.dirty = true; } @@ -250,7 +250,7 @@ namespace prune { } apply_editor_command(*command, false); - if (command->modifies_scene) { + if (command->makes_dirty) { m_state.dirty = true; } return true; @@ -264,7 +264,7 @@ namespace prune { } apply_editor_command(*command, true); - if (command->modifies_scene) { + if (command->makes_dirty) { m_state.dirty = true; } return true; From 7ab26c39c027bdea71cf7dbd661119b07eb87912 Mon Sep 17 00:00:00 2001 From: Dean Blackborough Date: Sun, 12 Jul 2026 23:16:16 +0100 Subject: [PATCH 4/6] Removed warnings --- src/prune/app/app.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/prune/app/app.cpp b/src/prune/app/app.cpp index a55c767..9ecdb0c 100644 --- a/src/prune/app/app.cpp +++ b/src/prune/app/app.cpp @@ -237,9 +237,9 @@ namespace prune { const std::filesystem::path sound_path = std::filesystem::current_path() / "assets" / "sound-effects"; - m_audio->load_sound(sound_ids::shoot, sound_path / "shoot.wav", error); - m_audio->load_sound(sound_ids::jump, sound_path / "jump.wav", error); - m_audio->load_sound(sound_ids::explosion, sound_path / "explosion.wav", error); + static_cast(m_audio->load_sound(sound_ids::shoot, sound_path / "shoot.wav", error)); + static_cast(m_audio->load_sound(sound_ids::jump, sound_path / "jump.wav", error)); + static_cast(m_audio->load_sound(sound_ids::explosion, sound_path / "explosion.wav", error)); m_audio->map_event_to_sound(scene_events::player_fired, sound_ids::shoot); m_audio->map_event_to_sound(scene_events::player_jumped, sound_ids::jump); From b799d56f7f5383e636341708411bf23b924c2a9d Mon Sep 17 00:00:00 2001 From: Dean Blackborough Date: Sun, 12 Jul 2026 23:23:47 +0100 Subject: [PATCH 5/6] Updates notes and readme misisng features --- NOTES.md | 12 ++++++------ README.md | 11 ++++++++--- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/NOTES.md b/NOTES.md index 0135271..30c9b63 100644 --- a/NOTES.md +++ b/NOTES.md @@ -18,12 +18,12 @@ Track whether the current scene has unsaved editor-authored changes. Initial scope: -* [ ] Mark the scene dirty when a command modifies persistent, editor-authored scene data. -* [ ] Mark scene dirty when inspector edits are committed. -* [ ] Mark scene dirty when authored objects are created, deleted, duplicated, moved, scaled, or reordered. -* [ ] Clear dirty state after a successful save. -* [ ] Keep runtime-only behaviour out of dirty tracking. -* [ ] Show dirty state somewhere lightweight in the editor UI. +* [x] Mark the scene dirty when a command modifies persistent, editor-authored scene data. +* [x] Mark scene dirty when inspector edits are committed. +* [x] Mark scene dirty when authored objects are created, deleted, duplicated, moved, scaled, or reordered. +* [x] Clear dirty state after a successful save. +* [x] Keep runtime-only behaviour out of dirty tracking. +* [x] Show dirty state somewhere lightweight in the editor UI. Out of scope for the first pass: diff --git a/README.md b/README.md index d85f685..8b1210b 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,8 @@ Different scene types can share the same editor shell while defining their own b > Prune is still pre-release. Existing `.yml` scene files may break while the object model, scene descriptors, behaviour ids, concept metadata, and scene-specific save data are being shaped. > > Save compatibility will matter later. For now, the priority is getting the scene model and editor/runtime architecture right. +> +> Camera movements do not mark the scene as dirty but they are saved to the scene file. If you are testing save/load, be aware that camera movements will be persisted. ## Why this exists @@ -110,17 +112,20 @@ Prune currently has: - Shared editor camera and game camera foundations - Grid rendering and snapping - Live object selection -- Selected-object outline and first transform handle -- Handle-based object movement for authored movable objects +- Selected-object outlines and transform handles +- Handle-based movement for authored movable objects +- Handle-based scaling for single authored objects, with minimum-size constraints - Runtime object protection by default - Outliner and generic inspector panels - Scene-specific inspector sections - Scene-aware object concepts (scene roles) for selection, editability, movement, runtime-only objects, and collision meaning - YAML scene save/load +- Dirty state tracking for authored scene data - Scene factory creation and scene-type loading from save files - Rectangle and sprite rendering - Basic sprite resource map -- Shared scene renderer, interaction, camera, state, collision, and serialization pieces +- Basic audio playback and scene audio hooks, currently configured in code with no dedicated editor UI +- Shared scene renderer, interaction, camera, state, collision, serialization, and audio foundations - Shared `WorldScene` foundation for scene types - Platformer scene slice - Artillery game slice From 94149b76fabc2d60e70114bce28f061f1d4fc12a Mon Sep 17 00:00:00 2001 From: Dean Blackborough Date: Sun, 12 Jul 2026 23:34:56 +0100 Subject: [PATCH 6/6] New version of ready for users and current development plans --- README.md | 83 +++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 59 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 8b1210b..4bb6565 100644 --- a/README.md +++ b/README.md @@ -194,34 +194,69 @@ The immediate focus is proving that viewport tools can operate safely on scene o Current priorities: -- [x] Command model for editor changes -- [x] Undo/Redo on top of the command model -- [x] Command history panel -- [x] Multi-select -- [x] Multi-select move/delete -- [x] Tool mode state -- [x] Scale tool -- [x] Basic audio hooks +- [x] Initial implementation of dirty-state tracking for persistent editor-authored changes. +- [ ] Ensure dirty state remains accurate through execution, undo, redo, save, and load. +- [ ] Add a Ctrl+S shortcut using the existing scene save workflow. +- [ ] Add explicit runtime reset and saved-scene reload behaviour. +- [ ] Add consistent reset, reload, pause, and resume controls to the editor. +- [ ] Add authored object z-index ordering. +- [ ] Persist and restore object ordering through scene save files. +- [ ] Add basic editor actions for moving selected objects forward or backward in render order. -My development plan is tracked in [NOTES.md](NOTES.md), check the file for more details on what each of these points mean as well as what is included - this is what I will be working on in the next development phase. +My development plan is tracked in [NOTES.md](NOTES.md), check the file for more details on what each of these points mean as well as what is included - this is what I will be working on in my next development phase. ## Ready for users when... -I will consider Prune ready for users when the below is ready, this is the minimum I think users expect from a live editor/runtime prototype: - -- [x] Open app. -- [ ] Pick a scene type. (Deferred for now, initial scene type is hardcoded to Platformer, others in new dropdown.) -- [x] Select object in viewport. -- [x] Move with handle. -- [x] Resize with scale tool. -- [x] Duplicate it. -- [x] Delete it. -- [x] Undo/redo all of that. -- [x] Save. -- [x] Load. -- [x] Behaviour still works. -- [x] Runtime objects do not get accidentally edited or saved. -- [x] Basic audio hooks +The core editor/runtime loop is now working, but that does mean Prune is ready for you to invest any meaninful time in it just yet. + +I will consider Prune ready for early external users when someone unfamiliar with the codebase can create, edit, run, save, reload, and understand a small scene without modifying C++ or relying on undocumented project knowledge. + +### Core editor/runtime + +- [x] Open the application. +- [x] Select objects in the viewport. +- [x] Move authored objects using the move tool. +- [x] Resize authored objects using the scale tool. +- [x] Duplicate authored objects. +- [x] Delete authored objects. +- [x] Undo and redo editor changes. +- [x] Save and load scenes. +- [x] Preserve scene behaviour after loading. +- [x] Protect runtime-only objects from accidental editing and persistence. +- [x] Provide basic code-driven audio playback and scene audio hooks. + +### Editor workflow + +- [ ] Select a scene type when creating a new scene. +- [ ] Provide reliable new, open, save, save-as, reload, and reset workflows. +- [ ] Track unsaved authored changes accurately, including through undo and redo (undo/redo missing right now). +- [x] Show the current dirty state clearly in the editor. +- [ ] Support Ctrl+S for normal scene saving. +- [ ] Warn before closing, reloading, or replacing a scene with unsaved changes. +- [ ] Provide consistent pause, resume, runtime reset, and scene reload controls. +- [ ] Add explicit authored-object render ordering. +- [ ] Apply grid snapping consistently across applicable editor tools. +- [ ] Provide object locking or protection for authored objects that should not be changed accidentally. + +### Scene editing + +- [ ] Provide a usable asset workflow that does not require editing source code. +- [ ] Allow sprites and audio resources to be selected and configured through the editor. +- [ ] Expose useful authored-object behaviour through editor controls. +- [ ] Provide editor-facing event and reaction configuration, including audio playback. +- [ ] Support basic scene-wide configuration such as background and music. +- [ ] Add sufficient text rendering support for practical scenes. +- [ ] Introduce scene file versioning before external users create files that need long-term compatibility. + +### Proof Prune works and is usable + +- [ ] Provide at least one polished example for each supported scene type. +- [ ] Ensure errors such as missing assets and invalid scene files are reported clearly. +- [ ] Document how to build, launch, create, edit, run, save, and reload a scene. +- [ ] Ensure a new user can complete the basic workflow without reading the engine source. +- [ ] Provide at least one practical way to package or share a playable scene. (This may be a simple zip of the scene file and assets, or a small standalone executable.) + +*Until all or the majority of the above is checked, please consider Prune an active 2D game engine and editor prototype rather than something you can depend. ## Documentation stance