feat: add simulation garden demo controls

This commit is contained in:
2026-07-07 15:56:24 +02:00
parent b0bad6c467
commit 4bfc09d5f6
9 changed files with 124 additions and 6 deletions
+3 -2
View File
@@ -466,8 +466,9 @@ represent a real chosen task.
distinct NPC colors and props, world labels identify each villager, and the
Tab-cycled inspector shows authoritative need values, task state, destination,
decision branch, and utility scores. The existing carried-food prop and village
panel expose immediate consequences. A cinematic/debug toggle and repeatable
demo reset remain Phase 6 capture tooling.
panel expose immediate consequences. `F10` toggles the debug/cinematic
presentation layer, while `F12` reloads the current scene as a repeatable
simulation-garden demo reset.
### Exit condition
+9 -3
View File
@@ -652,15 +652,21 @@ Completed after the architecture gate:
The practical next sequence is:
1. Add a cinematic/debug toggle and repeatable simulation-garden demo reset.
2. Expand resource discovery with many finite `ResourceNode` instances placed
1. Expand resource discovery with many finite `ResourceNode` instances placed
in meaningful foliage, animal-camp, berry, and village-stockpile contexts.
The first distance/safety/comfort scoring pass and small authored resource
spread exist; next, validate placement rules and grow the authored set
carefully rather than reintroducing abstract resource zones.
3. Expand persistence only when schedules, player state, or a real save menu
2. Expand persistence only when schedules, player state, or a real save menu
creates a concrete requirement.
Recently completed:
- `F10` cinematic/debug presentation toggle that hides development UI, world
labels, and NPC name/profession labels without changing simulation state;
- `F12` repeatable simulation-garden demo reset by reloading the current scene
with the same authored seed and starting state.
This order strengthens the simulation while regularly producing visible
progress suitable for public development updates.
+6
View File
@@ -10,6 +10,7 @@
[ext_resource type="Script" path="res://simulation/persistence/SaveSlotController.gd" id="10_save"]
[ext_resource type="PackedScene" path="res://world/jajce/JajceWorld.tscn" id="11_jajce"]
[ext_resource type="Script" path="res://world/ui/time_dial.gd" id="12_timedial"]
[ext_resource type="Script" path="res://world/demo/DemoController.gd" id="13_demo"]
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_o5qli"]
radius = 0.4
@@ -83,6 +84,11 @@ active_npcs_parent = NodePath("../ActiveNPCs")
script = ExtResource("6_7mycd")
simulation_manager = NodePath("../SimulationManager")
[node name="DemoController" type="Node" parent="." node_paths=PackedStringArray("ui", "active_npcs_parent")]
script = ExtResource("13_demo")
ui = NodePath("../UI")
active_npcs_parent = NodePath("../ActiveNPCs")
[node name="VillagePanel" type="PanelContainer" parent="UI" unique_id=482126250]
offset_left = 20.0
offset_top = 20.0
+9
View File
@@ -32,6 +32,7 @@ var path_pending := false
var is_dead_visual := false
var dead_material := StandardMaterial3D.new()
var debug_overlay_visible := true
func debug_log(message: String) -> void:
@@ -71,6 +72,7 @@ func _apply_profession_presentation() -> void:
profession_prop.mesh = _create_profession_prop_mesh()
profession_prop.visible = profession_prop.mesh != null
profession_label.text = "%s\n%s" % [npc_name, definition.display_name]
profession_label.visible = debug_overlay_visible
func _create_profession_prop_mesh() -> PrimitiveMesh:
@@ -112,6 +114,12 @@ func set_carried_food_visible(is_visible: bool) -> void:
carried_food_visual.visible = is_visible and not is_dead_visual
func set_debug_overlay_visible(is_visible: bool) -> void:
debug_overlay_visible = is_visible
if profession_label != null:
profession_label.visible = is_visible and not is_dead_visual
func set_target_position(pos: Vector3) -> void:
path_request_id += 1
var request_id := path_request_id
@@ -240,6 +248,7 @@ func apply_dead_visual_state() -> void:
is_dead_visual = true
carried_food_visual.visible = false
profession_prop.visible = false
profession_label.visible = false
path_request_id += 1
velocity = Vector3.ZERO
current_path = PackedVector3Array()
+5
View File
@@ -65,6 +65,11 @@ func _update_presentation() -> void:
label.text = text
func set_debug_label_enabled(is_enabled: bool) -> void:
debug_label_enabled = is_enabled
_update_presentation()
static func get_by_id(search_id: StringName):
for node in _all:
if node.site_id == search_id:
+67
View File
@@ -0,0 +1,67 @@
extends Node
@export var ui: CanvasLayer
@export var active_npcs_parent: Node
@export var debug_overlay_visible := true
var _refresh_accumulator := 0.0
func _ready() -> void:
_apply_debug_overlay_visibility()
func _process(delta: float) -> void:
_refresh_accumulator += delta
if _refresh_accumulator < 0.5:
return
_refresh_accumulator = 0.0
_apply_debug_overlay_visibility()
func _unhandled_key_input(event: InputEvent) -> void:
if not event is InputEventKey or not event.pressed or event.echo:
return
if event.keycode == KEY_F10:
toggle_debug_overlay()
get_viewport().set_input_as_handled()
elif event.keycode == KEY_F12:
reset_demo_scene()
get_viewport().set_input_as_handled()
func toggle_debug_overlay() -> void:
debug_overlay_visible = not debug_overlay_visible
_apply_debug_overlay_visibility()
print(
"[DemoController] %s"
% ("Debug overlay visible" if debug_overlay_visible else "Cinematic overlay hidden")
)
func reset_demo_scene() -> void:
print("[DemoController] Resetting simulation garden demo")
get_tree().reload_current_scene()
func _apply_debug_overlay_visibility() -> void:
if ui != null and ui.has_method("set_debug_overlay_visible"):
ui.set_debug_overlay_visible(debug_overlay_visible)
for node in ResourceNode.get_all():
if node.has_method("set_debug_label_enabled"):
node.set_debug_label_enabled(debug_overlay_visible)
for node in StorageNode.get_all():
if node.has_method("set_debug_label_enabled"):
node.set_debug_label_enabled(debug_overlay_visible)
for node in ActivitySite.get_all():
if node.has_method("set_debug_label_enabled"):
node.set_debug_label_enabled(debug_overlay_visible)
if active_npcs_parent == null:
return
for child in active_npcs_parent.get_children():
if child.has_method("set_debug_overlay_visible"):
child.set_debug_overlay_visible(debug_overlay_visible)
+9
View File
@@ -19,6 +19,7 @@ static var _all: Array[ResourceNode] = []
@export var comfort_distance := 18.0
@export var discovery_priority := 0.0
@export var hide_visual_when_depleted: bool = false
@export var debug_label_enabled := true
@onready var interaction_point: Marker3D = $InteractionPoint
@@ -121,6 +122,9 @@ func _update_presentation() -> void:
if not has_node("DebugLabel"):
return
var label := $DebugLabel as Label3D
label.visible = debug_label_enabled
if not debug_label_enabled:
return
var text := "%s\n%s %.1f" % [node_id, resource_id, get_amount_remaining()]
if state != null:
text += "\nrisk:%.2f comfort:%.0f" % [state.get_safety_risk(), state.get_comfort_distance()]
@@ -133,6 +137,11 @@ func _update_presentation() -> void:
label.text = text
func set_debug_label_enabled(is_enabled: bool) -> void:
debug_label_enabled = is_enabled
_update_presentation()
static func get_by_id(search_id: StringName) -> ResourceNode:
for node in _all:
if node.node_id == search_id:
+5
View File
@@ -91,6 +91,11 @@ func _update_presentation() -> void:
label.text = text
func set_debug_label_enabled(is_enabled: bool) -> void:
debug_label_enabled = is_enabled
_update_presentation()
static func get_by_id(search_id: StringName):
for node in _all:
if node.storage_id == search_id:
+11 -1
View File
@@ -9,6 +9,7 @@ const DEBUG_LOGS := false
var selected_npc_index := 0
var inspector_refresh_accumulator := 0.0
var debug_overlay_visible := true
func _ready() -> void:
@@ -88,6 +89,8 @@ func _on_state_restored() -> void:
func _refresh_npc_inspector() -> void:
if not debug_overlay_visible:
return
if simulation_manager == null or simulation_manager.npcs.is_empty():
npc_inspector_label.text = "No villagers available"
return
@@ -122,7 +125,7 @@ func _refresh_npc_inspector() -> void:
+ "Hunger: %.0f Energy: %.0f\n"
+ "Carrying food: %.0f\n\n"
+ "Why\n%s%s\n\n"
+ "[Tab] Next villager"
+ "[Tab] Next villager [F10] Cinematic/debug [F12] Demo reset"
) % [
npc.npc_name,
profession_name,
@@ -146,3 +149,10 @@ func _get_action_name(action_id: StringName) -> String:
if definition != null:
return definition.display_name
return String(action_id).capitalize()
func set_debug_overlay_visible(is_visible: bool) -> void:
debug_overlay_visible = is_visible
visible = is_visible
if is_visible:
_refresh_npc_inspector()