diff --git a/CMakeLists.txt b/CMakeLists.txt index 41eb905..d4b9db8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,8 @@ add_executable(Prune src/prune/core/time.cpp src/prune/core/input.cpp + src/prune/audio/audio_system.cpp + src/prune/editor/editor_command.cpp src/prune/editor/editor_actions.cpp diff --git a/DECISIONS.md b/DECISIONS.md index 4f74182..c4f5298 100644 --- a/DECISIONS.md +++ b/DECISIONS.md @@ -892,3 +892,80 @@ Initial tool modes are deliberately limited to Select and Move. Select keeps sel The active tool is transient editor state. It lives in the generic world scene state so viewport interaction and UI can share it, but it is not saved into scene files. + +## Decision — Add basic audio hooks before final event management design + +### Context + +Prune needs basic sound playback now for clear runtime feedback in the existing sample scenes: + +Simple Shooter firing and enemy destruction. +Platformer jumping and player hit/death feedback. +Artillery firing and explosion/player hit feedback. + +The broader event/reaction system is not designed yet. Long term, events may drive multiple reaction types, including sounds, animations, sprite changes, UI effects, screen shake, object spawning, and scene-specific behaviours. + +Adding a full authored event system now would be premature. The editor UI, asset model, serialisation shape, and scene semantics are not ready for that decision. + +### Decision + +Add a minimal audio hook path now, built on lightweight scene event ids. + +Scenes may emit simple event ids such as: + +player_fired +player_jumped +enemy_destroyed +player_hit +round_reset + +A small audio layer maps those event ids to hard-coded sound resources and plays them when received. + +Scene behaviour must not know: + +- Which sound file is used. +- How sound is loaded. +- How sound is mixed. +- Whether audio is enabled. +- Which future reaction types may also respond to the same event. + +The app/runtime layer is responsible for passing emitted scene events to the audio system. + +- Initial constraints +- Use hard-coded event-to-sound mappings only. +- Keep sound resources small and explicit. +- Keep the audio enable/disable toggle global. +- Do not add authored event UI yet. +- Do not serialise event bindings yet. +- Do not introduce a full asset browser or asset registry for this pass. +- Do not make scene behaviour call audio playback directly. + +#### Consequences + +This gives Prune useful runtime feedback immediately without committing to the final event architecture. + +The current audio hook path is intentionally disposable at the mapping level but not at the boundary level. The important boundary is that scenes emit events and another system consumes them. + +Later, the hard-coded audio mapping can be replaced by a more general event reaction system, for example: + +player_fired + - Play sound: shoot.wav + - Play animation: muzzle_flash + - Spawn object: projectile + - Trigger UI effect: screen_shake + +### Rejected alternative + +Direct sound calls inside scene behaviour + +Rejected because it would couple scene behaviour to audio playback and make later event-driven reactions harder. + +#### Bad direction: + +Platformer jump code -> play jump.wav directly + +#### Preferred direction: + +Platformer jump code -> emit player_jumped +Audio system -> maps player_jumped to jump.wav +Future reaction system -> may also map player_jumped to animation, particles, UI effects, or scene-specific responses diff --git a/NOTES.md b/NOTES.md index f37d94e..dfc15b3 100644 --- a/NOTES.md +++ b/NOTES.md @@ -1,192 +1,124 @@ -# Current Phase — Immediate hardening before next tools +# Current Phase — Short-term editor/runtime hardening ## Goal -Make Prune feel like a real editor rather than a set of game type scene slices with panels attached. +Turn the current editor/runtime foundation into something that behaves more like a typical game engine/editor. -All the previous development proved that multiple scene types can share the same editor/runtime while owning their own behaviour, tools, panels, inspectors, object semantics, defaults, and save data. +The previous phase proved the important foundations: editor commands, undo/redo, multi-select, explicit tool modes, scale, and the first audio/event hook path. The next phase should now harden the everyday editor behaviour around saving, reset/reload, ordering, and scene state tracking. -During the next development phase, we should now prove that the editor can safely manipulate scene-owned objects without bypassing those scene rules. +This phase is not about adding new systems. It is about removing rough edges that will be awkward to deal with later. -## Guiding rule +Prune should know when a scene has changed, what can be saved, what can be reset, what order objects render in, and whether runtime state is being viewed or rebuilt. These behaviours should not be inferred accidentally from object shape, current UI selection, or temporary scene runtime state. -Editor tools must act through explicit editor semantics, not through incidental object shape. +## Targets -A selected object is not just a rectangle. It may be authored, runtime-created, selectable, editable, movable, persistent, solid, hazardous, scene-specific, or protected. +### 1. Dirty state tracking -## Must do first - -- [x] Keep player/controller ownership scene-specific unless a genuinely shared abstraction appears. -- [x] Introduce an editor command/change model before implementing undo/redo. -- [x] Keep undo/redo editor-only at first. -- [x] Keep scene activation separate from default scene creation so loading cannot be overwritten by `on_enter()`. - -## Phase targets - -### 1. Editor command/change model - -Create the smallest useful model for editor-authored changes. - -Initial command candidates: - -- [x] Move object in scene -- [x] Create object -- [x] Delete object -- [x] Rename object -- [x] Change object size -- [x] Change object rendering type (rectangle/colour) -- [x] Change object colour -- [x] Change object sprite -- [x] Viewport movement/zoom (no zoom yet) -- [x] Record state snapshots not delta changes. - -Avoid runtime/gameplay commands for now. A projectile moving, an enemy spawning, or an artillery round ending should not enter editor undo history, we just care about what the user did in the editor. - -### 2. Undo/redo - -Build undo/redo on top of the command model, not as custom reversal logic scattered through tools and panels. +Track whether the current scene has unsaved editor-authored changes. Initial scope: -- [x] Command history buffer panel with list of past commands -- [x] Move transform tool from scene/tools to editor/tools -- [x] Viewport object movement -- [x] Object creation -- [x] Object deletion -- [x] Simple inspector edits +* [ ] Mark scene dirty when editor commands are executed. +* [ ] 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. Out of scope for the first pass: -- Runtime object history -- Gameplay state rewind -- Save/load history restoration -- Multi-scene history - -### 3. Delete and duplicate workflow - -Add normal editor actions once command history exists. - -Initial scope: - -- [x] Delete selected authored object -- [x] Duplicate selected authored object -- [x] Select the duplicated object -- [x] Offset duplicates slightly so the result is visible - -This gives undo/redo useful behaviour to prove. +* Save confirmation modal when closing Prune. +* Save confirmation modal when switching scenes. +* Per-scene dirty tracking across multiple open scenes. +* Autosave. -### 4. Multi-select +### 2. CTRL-S save shortcut -Add multi-select after the single-object command path is stable. +Add a normal editor save shortcut once dirty state exists. Initial scope: -- [x] Selection set instead of one selected id -- [x] Shift-click to add/remove from the selection set -- [x] Clear selection on empty viewport click -- [x] Outliner multi-select support -- [x] Visible outline for every selected object -- [x] Combined selection bounds in the viewport -- [x] Multi-selection drag handle on the selection bounds -- [x] Selection count in editor UI -- [x] Move selected objects as one editor command -- [x] Delete selected deletable objects as one editor command - -Multi-select now supports grouped viewport move and grouped delete. Grouped inspector/property edits remain deferred because they need more specific UX and command labels. - -### 5. Tool mode state +* [ ] CTRL-S saves the current scene when a scene file path is known. +* [ ] CTRL-S uses the same save path as the existing save workflow. +* [ ] Successful save clears dirty state. +* [ ] Failed save leaves dirty state unchanged. +* [ ] Avoid triggering save repeatedly while the key is held. -Status: implemented. +Out of scope for the first pass: -Introduce an explicit editor tool mode before adding more viewport tools. +* Save As shortcut. +* Recent files. +* Save failure recovery UI beyond a simple message/log entry. -Suggested initial tools: +### 3. Runtime reset and reload -```cpp -enum class EditorTool { - Select, - Move, - Scale, - Rotate -}; -``` +Add explicit runtime reset/reload behaviour rather than relying only on pause/resume. -This avoids hiding editor behaviour inside whichever handle happened to be clicked. +Initial scope: -Current implementation: +* [ ] Reset runtime state for the active scene without destroying authored scene data. +* [ ] Reload the scene from saved data when a backing file exists. +* [ ] Preserve the distinction between editor-authored objects and runtime-only objects. +* [ ] Clear runtime-only objects during reset/reload. +* [ ] Re-enter scene runtime cleanly after reset/reload. +* [ ] Make behaviour consistent across Simple Shooter, Platformer, and Artillery. +* [ ] New toolbar in the editor for reset/reload/pause/resume. -- [x] Active tool state lives in generic world scene state. -- [x] A fixed viewport palette exposes Select and Move as text tool buttons. -- [x] The viewport palette also exposes scene-owned creation actions such as Wall, Platform, Hazard, and Terrain Line. -- [x] Select keeps the existing selection and move-handle behaviour. -- [x] Move allows direct body dragging for movable authored objects. -- [x] Scale and rotate remain unimplemented until their interaction rules are explicit. +Out of scope for the first pass: -### 6. Scale tool +* Save prompts before reload. +* Runtime state snapshots. +* Gameplay rewind. -Implement scale before rotate. +### 4. Z-index ordering -Scale fits the current object model because Prune already has width/height and rectangle bounds. +Add authored object ordering so render order is deliberate and editable. Initial scope: -- [x] Single selected authored object -- [x] Corner or edge handle -- [x] Minimum size clamp -- [x] Inspector updates immediately -- [x] Undo/redo command recorded when the drag completes +* [ ] Add an authored z-index/order field to scene objects. +* [ ] Render authored objects using explicit ordering. +* [ ] Persist ordering in scene save data. +* [ ] Restore ordering on load. +* [ ] Add basic editor actions for moving selected object up/down in order. +* [ ] Keep runtime-only ordering behaviour simple and deterministic. -### 7. Basic audio and event hooks - -Add audio through events. - -Preferred direction: - -- Scenes emit lightweight event ids such as `player_fired`, `enemy_destroyed`, `player_hit`, `round_reset`. -- A small audio layer maps event ids to sounds. -- Scene behaviour does not know how sound is played. - -Initial scope: +Out of scope for the first pass: -- [ ] One or two hard-coded sound resources -- [ ] Fire event -- [ ] Hit/destroy event -- [ ] Global enable/disable toggle - -## Short term, in no particular order - -- Z-Index ordering -- Runtime reset and reload, not just pause/resume -- CTRL-S to save scene -- Dirty state tracking for scene - -## Medium term, in no particular order - -- Rotate tool -- Background image support -- Simplified asset management -- Text rendering -- Behaviour toggles for authored objects -- Polished sample scenes -- Asset browser -- Animated sprites/facing support -- Scene file versioning -- Grid snapping support on all tools and UI option, not in settings -- Object locking and protection -- More robust collision shapes and collision options -- Scene layering, rendering and collision etc. - -## Long term, in no particular order - -- Grouping -- Input mapping and rebinding -- Scene settings panel for background, music, and other scene-wide options -- Game UI panels -- Pathfinding support -- Full audio mixer -- Prefabs/templates -- Native playable export -- WebAssembly playable export -- Card scene -- Puzzle scene -- Many more scene tools +* Full layer system. +* Separate render/collision layers. +* Drag-and-drop ordering in the outliner. + +## Follow on development targets + +### Medium term, in no particular order + +* Rotate tool +* Background image support +* Simplified asset management +* Text rendering +* Behaviour toggles for authored objects +* Polished sample scenes +* Event management and event-driven reactions (play audio, spawn objects, play animation, screen effects, etc.) +* Asset browser +* Animated sprites/facing support +* Scene file versioning +* Grid snapping support on all tools and UI option, not in settings +* Object locking and protection +* More robust collision shapes and collision options +* Scene layering, rendering and collision etc. + +### Long term, in no particular order + +* Grouping +* Input mapping and rebinding +* Scene settings panel for background, music, and other scene-wide options +* Game UI panels +* Pathfinding support +* Full audio mixer +* Prefabs/templates +* Native playable export +* WebAssembly playable export +* Card scene +* Puzzle scene +* Many more scene tools diff --git a/README.md b/README.md index 962cf84..d85f685 100644 --- a/README.md +++ b/README.md @@ -196,7 +196,7 @@ Current priorities: - [x] Multi-select move/delete - [x] Tool mode state - [x] Scale tool -- [ ] Basic audio hooks +- [x] Basic audio hooks 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. @@ -209,13 +209,14 @@ I will consider Prune ready for users when the below is ready, this is the minim - [x] Select object in viewport. - [x] Move with handle. - [x] Resize with scale tool. -- [ ] Duplicate it. -- [ ] Delete it. +- [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 ## Documentation stance @@ -315,6 +316,7 @@ build/Debug/Prune.exe - [SDL2_image](https://github.com/libsdl-org/SDL_image) — sprite loading - [The Cherno](https://www.youtube.com/@TheCherno) — C++ game engine architecture and editor design inspiration - [Kenny](https://kenney.nl/) — free game assets for prototyping +- [jsfxr](https://sfxr.me/) - free sound effect generator for prototyping ## Licence diff --git a/assets/sound-effects/explosion.wav b/assets/sound-effects/explosion.wav new file mode 100644 index 0000000..67cecb7 Binary files /dev/null and b/assets/sound-effects/explosion.wav differ diff --git a/assets/sound-effects/jump.wav b/assets/sound-effects/jump.wav new file mode 100644 index 0000000..e0f0fef Binary files /dev/null and b/assets/sound-effects/jump.wav differ diff --git a/assets/sound-effects/shoot.wav b/assets/sound-effects/shoot.wav new file mode 100644 index 0000000..96bd3a6 Binary files /dev/null and b/assets/sound-effects/shoot.wav differ diff --git a/src/prune/app/app.cpp b/src/prune/app/app.cpp index 540374f..a55c767 100644 --- a/src/prune/app/app.cpp +++ b/src/prune/app/app.cpp @@ -12,6 +12,7 @@ #include "backends/imgui_impl_sdlrenderer2.h" #include "prune/app/app.hpp" +#include "prune/audio/audio_system.hpp" #include "prune/core/time.hpp" #include "prune/scene/scene_factory.hpp" #include "prune/tooling/theme.hpp" @@ -25,6 +26,8 @@ namespace prune { init_sdl(); m_window = std::make_unique(config.window); + init_audio(); + m_input = std::make_unique(); m_time = std::make_unique