diff --git a/docs/BUILD_IN_PUBLIC_PLAN.md b/docs/BUILD_IN_PUBLIC_PLAN.md index 771637d..3e914c1 100644 --- a/docs/BUILD_IN_PUBLIC_PLAN.md +++ b/docs/BUILD_IN_PUBLIC_PLAN.md @@ -632,10 +632,15 @@ Completed: 10. `Jajce Lookdev 01` capture and review: a reproducible capture tool writes the daytime lookdev baseline to `docs/baselines/jajce_lookdev_01.png`, and the review notes the current strengths and visual follow-ups. +11. `Simulation Garden 01` capture and review: a reproducible runtime capture + tool writes debug and cinematic `main.tscn` baselines to `docs/baselines/` + and verifies live NPC visuals in the terrain-backed village. Next: -1. Capture “Simulation Garden 01.” +1. Improve the runtime first-read: camera staging, readable path/landmark + composition, and diegetic task communication without adding new simulation + mechanics. Do not start with GIS data, a full city, a large asset pack, or more NPC mechanics. The next proof is a beautiful stage for the systems that already diff --git a/docs/LEARNING_ROADMAP.md b/docs/LEARNING_ROADMAP.md index 0c9bc60..ade57b4 100644 --- a/docs/LEARNING_ROADMAP.md +++ b/docs/LEARNING_ROADMAP.md @@ -680,6 +680,9 @@ Recently completed: that do not reach their requested targets. - `Jajce Lookdev 01` is captured and reviewed with a project-owned capture script and a daytime baseline image under `docs/baselines/`. +- `Simulation Garden 01` is captured and reviewed from `main.tscn` with paired + debug/cinematic baselines that verify live NPC visuals in the Terrain3D-backed + village. This order strengthens the simulation while regularly producing visible progress suitable for public development updates. diff --git a/docs/PROJECT_CONTEXT.md b/docs/PROJECT_CONTEXT.md index b5f32fc..beca44e 100644 --- a/docs/PROJECT_CONTEXT.md +++ b/docs/PROJECT_CONTEXT.md @@ -198,6 +198,9 @@ study, and patrol target typed activity sites. The migration is documented in - **Lookdev baseline:** `Jajce Lookdev 01` is captured at `docs/baselines/jajce_lookdev_01.png` with `tools/capture_jajce_lookdev.gd` +- **Runtime baseline:** `Simulation Garden 01` is captured as paired debug and + cinematic `main.tscn` images under `docs/baselines/` with + `tools/capture_simulation_garden.gd` - **Terrain compatibility floor:** Godot 4.4 according to the bundled extension - **Version control:** Git - **Primary branch:** `main` @@ -336,8 +339,8 @@ distance with resource `safety_risk`, `comfort_distance`, and keep the old greybox ground out of runtime physics, require paths to reach their targets, and compare required navigation paths against the Terrain3D height field; -- a look-development scene and camera. -- a reproducible daytime lookdev capture and review note under +- a look-development scene and camera; +- reproducible lookdev and runtime capture scripts plus review notes under `docs/baselines/`. This is a runtime integration proof, not the final terrain composition. @@ -549,8 +552,8 @@ These are expected prototype constraints, not necessarily isolated bugs: playable-loop pass, so future terrain sculpting should rerun the bake tool and recheck reachability rather than hand-editing navigation polygons. - Terrain3D and the bounded Jajce beauty baseline now run in the main game - scene; `Jajce Lookdev 01` is captured, while the runtime `Simulation Garden - 01` capture remains the next presentation checkpoint. + scene; `Jajce Lookdev 01` and the runtime `Simulation Garden 01` are captured + as reproducible presentation baselines. - Combat, companions, factions, politics, trade, rumours, quests, persistence, and regional travel do not yet exist. - Placeholder geometry is sufficient for debugging but not for build-in-public @@ -768,7 +771,8 @@ eating consumes it. Each successful transfer creates a persisted structured event, and active NPCs visibly carry food through presentation derived from their inventory. The bounded save-slot slice is also complete, including validation, atomic replacement recovery, and active-visual rebuilding. -Terrain3D runtime integration and the simulation-garden beauty pass are next. +Terrain3D runtime integration, the bounded Jajce beauty pass, and the first +simulation-garden runtime capture are complete. The coherent visual slice can then aim for: diff --git a/docs/baselines/SIMULATION_GARDEN_01.md b/docs/baselines/SIMULATION_GARDEN_01.md new file mode 100644 index 0000000..8cb944a --- /dev/null +++ b/docs/baselines/SIMULATION_GARDEN_01.md @@ -0,0 +1,44 @@ +# Simulation Garden 01 + +Captured: 2026-07-08 + +## Files + +- `docs/baselines/simulation_garden_01_debug.png` +- `docs/baselines/simulation_garden_01_cinematic.png` +- `tools/capture_simulation_garden.gd` + +## Capture Command + +Run from the project root with the normal renderer: + +```powershell +$env:APPDATA = 'C:\Users\Rijad\Documents\Simulation Game\logs\quality\godot_profile' +$env:LOCALAPPDATA = 'C:\Users\Rijad\Documents\Simulation Game\logs\quality\godot_profile' +& 'C:\Users\Rijad\Downloads\tools\Godot_v4.7-stable_win64.exe\Godot_v4.7-stable_win64_console.exe' --path 'C:\Users\Rijad\Documents\Simulation Game' --script res://tools/capture_simulation_garden.gd +``` + +The script warms up `main.tscn`, verifies that the simulation and active NPC +visuals exist, captures the F10 debug presentation, then captures the cinematic +presentation with development labels and UI hidden. + +## Review + +The milestone is valid as a runtime integration checkpoint: the same Jajce +Terrain3D world is running in `main.tscn`, NPCs are alive in the terrain-backed +village, the selected-NPC reason inspector is fed by real task scoring, and the +cinematic/debug toggle produces a shareable view without changing simulation +state. + +The frame also makes the next presentation risks plain: + +- the camera still favors verification over composition; +- placeholder characters, houses, and work props remain useful but visibly + prototype-grade; +- terrain texture variation reads better than the old flat map, but paths, + landmarks, water, and foreground silhouettes need stronger first-read staging; +- labels explain the system well, while the cinematic view still needs more + diegetic task communication. + +Use this baseline as the first runtime comparison image before adding more +systems or expanding the terrain. diff --git a/docs/baselines/simulation_garden_01_cinematic.png b/docs/baselines/simulation_garden_01_cinematic.png new file mode 100644 index 0000000..decf237 Binary files /dev/null and b/docs/baselines/simulation_garden_01_cinematic.png differ diff --git a/docs/baselines/simulation_garden_01_debug.png b/docs/baselines/simulation_garden_01_debug.png new file mode 100644 index 0000000..c9790d5 Binary files /dev/null and b/docs/baselines/simulation_garden_01_debug.png differ diff --git a/tools/capture_simulation_garden.gd b/tools/capture_simulation_garden.gd new file mode 100644 index 0000000..0256f65 --- /dev/null +++ b/tools/capture_simulation_garden.gd @@ -0,0 +1,106 @@ +extends SceneTree + +const SCENE_PATH := "res://main.tscn" +const DEBUG_OUTPUT_PATH := "res://docs/baselines/simulation_garden_01_debug.png" +const CINEMATIC_OUTPUT_PATH := "res://docs/baselines/simulation_garden_01_cinematic.png" +const CAPTURE_SIZE := Vector2i(1280, 720) +const WARMUP_FRAMES := 540 + + +func _initialize() -> void: + call_deferred("_run") + + +func _run() -> void: + root.size = CAPTURE_SIZE + var scene := load(SCENE_PATH) as PackedScene + if scene == null: + push_error("Could not load %s" % SCENE_PATH) + quit(1) + return + + var main_scene := scene.instantiate() + var simulation_manager := main_scene.get_node("SimulationManager") + simulation_manager.debug_logs = false + + root.add_child(main_scene) + await process_frame + for _frame in WARMUP_FRAMES: + await process_frame + + var active_npcs := main_scene.get_node("ActiveNPCs") + var demo_controller := main_scene.get_node("DemoController") + if simulation_manager.npcs.is_empty() or active_npcs.get_child_count() == 0: + push_error("Simulation Garden capture needs active NPCs") + quit(1) + return + + if demo_controller.has_method("toggle_debug_overlay"): + if not demo_controller.debug_overlay_visible: + demo_controller.toggle_debug_overlay() + + var debug_analysis := _capture(DEBUG_OUTPUT_PATH) + if debug_analysis.is_empty(): + quit(1) + return + + if demo_controller.has_method("toggle_debug_overlay") and demo_controller.debug_overlay_visible: + demo_controller.toggle_debug_overlay() + await process_frame + for _frame in 30: + await process_frame + + var cinematic_analysis := _capture(CINEMATIC_OUTPUT_PATH) + if cinematic_analysis.is_empty(): + quit(1) + return + + print( + "[TOOL] Saved Simulation Garden captures | debug %s | cinematic %s" + % [debug_analysis, cinematic_analysis] + ) + quit(0) + + +func _capture(output_path: String) -> Dictionary: + var image := root.get_texture().get_image() + if image == null or image.is_empty(): + push_error("Simulation Garden capture produced no image") + return {} + + var analysis := _analyze_image(image) + if analysis.get("range", 0.0) < 0.05: + push_error("Simulation Garden capture is too uniform: %s" % analysis) + return {} + + var error := image.save_png(output_path) + if error != OK: + push_error("Could not save %s: %s" % [output_path, error_string(error)]) + return {} + + return analysis + + +func _analyze_image(image: Image) -> Dictionary: + var min_luma := INF + var max_luma := -INF + var total_luma := 0.0 + var samples := 0 + var step_x = maxi(1, image.get_width() / 64) + var step_y = maxi(1, image.get_height() / 36) + + for y in range(0, image.get_height(), step_y): + for x in range(0, image.get_width(), step_x): + var color := image.get_pixel(x, y) + var luma := color.r * 0.2126 + color.g * 0.7152 + color.b * 0.0722 + min_luma = minf(min_luma, luma) + max_luma = maxf(max_luma, luma) + total_luma += luma + samples += 1 + + return { + "size": "%sx%s" % [image.get_width(), image.get_height()], + "samples": samples, + "average_luma": total_luma / samples, + "range": max_luma - min_luma, + }