feat: add simulation garden demo controls
This commit is contained in:
@@ -466,8 +466,9 @@ represent a real chosen task.
|
|||||||
distinct NPC colors and props, world labels identify each villager, and the
|
distinct NPC colors and props, world labels identify each villager, and the
|
||||||
Tab-cycled inspector shows authoritative need values, task state, destination,
|
Tab-cycled inspector shows authoritative need values, task state, destination,
|
||||||
decision branch, and utility scores. The existing carried-food prop and village
|
decision branch, and utility scores. The existing carried-food prop and village
|
||||||
panel expose immediate consequences. A cinematic/debug toggle and repeatable
|
panel expose immediate consequences. `F10` toggles the debug/cinematic
|
||||||
demo reset remain Phase 6 capture tooling.
|
presentation layer, while `F12` reloads the current scene as a repeatable
|
||||||
|
simulation-garden demo reset.
|
||||||
|
|
||||||
### Exit condition
|
### Exit condition
|
||||||
|
|
||||||
|
|||||||
@@ -652,15 +652,21 @@ Completed after the architecture gate:
|
|||||||
|
|
||||||
The practical next sequence is:
|
The practical next sequence is:
|
||||||
|
|
||||||
1. Add a cinematic/debug toggle and repeatable simulation-garden demo reset.
|
1. Expand resource discovery with many finite `ResourceNode` instances placed
|
||||||
2. Expand resource discovery with many finite `ResourceNode` instances placed
|
|
||||||
in meaningful foliage, animal-camp, berry, and village-stockpile contexts.
|
in meaningful foliage, animal-camp, berry, and village-stockpile contexts.
|
||||||
The first distance/safety/comfort scoring pass and small authored resource
|
The first distance/safety/comfort scoring pass and small authored resource
|
||||||
spread exist; next, validate placement rules and grow the authored set
|
spread exist; next, validate placement rules and grow the authored set
|
||||||
carefully rather than reintroducing abstract resource zones.
|
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.
|
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
|
This order strengthens the simulation while regularly producing visible
|
||||||
progress suitable for public development updates.
|
progress suitable for public development updates.
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
[ext_resource type="Script" path="res://simulation/persistence/SaveSlotController.gd" id="10_save"]
|
[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="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/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"]
|
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_o5qli"]
|
||||||
radius = 0.4
|
radius = 0.4
|
||||||
@@ -83,6 +84,11 @@ active_npcs_parent = NodePath("../ActiveNPCs")
|
|||||||
script = ExtResource("6_7mycd")
|
script = ExtResource("6_7mycd")
|
||||||
simulation_manager = NodePath("../SimulationManager")
|
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]
|
[node name="VillagePanel" type="PanelContainer" parent="UI" unique_id=482126250]
|
||||||
offset_left = 20.0
|
offset_left = 20.0
|
||||||
offset_top = 20.0
|
offset_top = 20.0
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ var path_pending := false
|
|||||||
|
|
||||||
var is_dead_visual := false
|
var is_dead_visual := false
|
||||||
var dead_material := StandardMaterial3D.new()
|
var dead_material := StandardMaterial3D.new()
|
||||||
|
var debug_overlay_visible := true
|
||||||
|
|
||||||
|
|
||||||
func debug_log(message: String) -> void:
|
func debug_log(message: String) -> void:
|
||||||
@@ -71,6 +72,7 @@ func _apply_profession_presentation() -> void:
|
|||||||
profession_prop.mesh = _create_profession_prop_mesh()
|
profession_prop.mesh = _create_profession_prop_mesh()
|
||||||
profession_prop.visible = profession_prop.mesh != null
|
profession_prop.visible = profession_prop.mesh != null
|
||||||
profession_label.text = "%s\n%s" % [npc_name, definition.display_name]
|
profession_label.text = "%s\n%s" % [npc_name, definition.display_name]
|
||||||
|
profession_label.visible = debug_overlay_visible
|
||||||
|
|
||||||
|
|
||||||
func _create_profession_prop_mesh() -> PrimitiveMesh:
|
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
|
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:
|
func set_target_position(pos: Vector3) -> void:
|
||||||
path_request_id += 1
|
path_request_id += 1
|
||||||
var request_id := path_request_id
|
var request_id := path_request_id
|
||||||
@@ -240,6 +248,7 @@ func apply_dead_visual_state() -> void:
|
|||||||
is_dead_visual = true
|
is_dead_visual = true
|
||||||
carried_food_visual.visible = false
|
carried_food_visual.visible = false
|
||||||
profession_prop.visible = false
|
profession_prop.visible = false
|
||||||
|
profession_label.visible = false
|
||||||
path_request_id += 1
|
path_request_id += 1
|
||||||
velocity = Vector3.ZERO
|
velocity = Vector3.ZERO
|
||||||
current_path = PackedVector3Array()
|
current_path = PackedVector3Array()
|
||||||
|
|||||||
@@ -65,6 +65,11 @@ func _update_presentation() -> void:
|
|||||||
label.text = text
|
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):
|
static func get_by_id(search_id: StringName):
|
||||||
for node in _all:
|
for node in _all:
|
||||||
if node.site_id == search_id:
|
if node.site_id == search_id:
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -19,6 +19,7 @@ static var _all: Array[ResourceNode] = []
|
|||||||
@export var comfort_distance := 18.0
|
@export var comfort_distance := 18.0
|
||||||
@export var discovery_priority := 0.0
|
@export var discovery_priority := 0.0
|
||||||
@export var hide_visual_when_depleted: bool = false
|
@export var hide_visual_when_depleted: bool = false
|
||||||
|
@export var debug_label_enabled := true
|
||||||
|
|
||||||
@onready var interaction_point: Marker3D = $InteractionPoint
|
@onready var interaction_point: Marker3D = $InteractionPoint
|
||||||
|
|
||||||
@@ -121,6 +122,9 @@ func _update_presentation() -> void:
|
|||||||
if not has_node("DebugLabel"):
|
if not has_node("DebugLabel"):
|
||||||
return
|
return
|
||||||
var label := $DebugLabel as Label3D
|
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()]
|
var text := "%s\n%s %.1f" % [node_id, resource_id, get_amount_remaining()]
|
||||||
if state != null:
|
if state != null:
|
||||||
text += "\nrisk:%.2f comfort:%.0f" % [state.get_safety_risk(), state.get_comfort_distance()]
|
text += "\nrisk:%.2f comfort:%.0f" % [state.get_safety_risk(), state.get_comfort_distance()]
|
||||||
@@ -133,6 +137,11 @@ func _update_presentation() -> void:
|
|||||||
label.text = text
|
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:
|
static func get_by_id(search_id: StringName) -> ResourceNode:
|
||||||
for node in _all:
|
for node in _all:
|
||||||
if node.node_id == search_id:
|
if node.node_id == search_id:
|
||||||
|
|||||||
@@ -91,6 +91,11 @@ func _update_presentation() -> void:
|
|||||||
label.text = text
|
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):
|
static func get_by_id(search_id: StringName):
|
||||||
for node in _all:
|
for node in _all:
|
||||||
if node.storage_id == search_id:
|
if node.storage_id == search_id:
|
||||||
|
|||||||
+11
-1
@@ -9,6 +9,7 @@ const DEBUG_LOGS := false
|
|||||||
|
|
||||||
var selected_npc_index := 0
|
var selected_npc_index := 0
|
||||||
var inspector_refresh_accumulator := 0.0
|
var inspector_refresh_accumulator := 0.0
|
||||||
|
var debug_overlay_visible := true
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
@@ -88,6 +89,8 @@ func _on_state_restored() -> void:
|
|||||||
|
|
||||||
|
|
||||||
func _refresh_npc_inspector() -> void:
|
func _refresh_npc_inspector() -> void:
|
||||||
|
if not debug_overlay_visible:
|
||||||
|
return
|
||||||
if simulation_manager == null or simulation_manager.npcs.is_empty():
|
if simulation_manager == null or simulation_manager.npcs.is_empty():
|
||||||
npc_inspector_label.text = "No villagers available"
|
npc_inspector_label.text = "No villagers available"
|
||||||
return
|
return
|
||||||
@@ -122,7 +125,7 @@ func _refresh_npc_inspector() -> void:
|
|||||||
+ "Hunger: %.0f Energy: %.0f\n"
|
+ "Hunger: %.0f Energy: %.0f\n"
|
||||||
+ "Carrying food: %.0f\n\n"
|
+ "Carrying food: %.0f\n\n"
|
||||||
+ "Why\n%s%s\n\n"
|
+ "Why\n%s%s\n\n"
|
||||||
+ "[Tab] Next villager"
|
+ "[Tab] Next villager [F10] Cinematic/debug [F12] Demo reset"
|
||||||
) % [
|
) % [
|
||||||
npc.npc_name,
|
npc.npc_name,
|
||||||
profession_name,
|
profession_name,
|
||||||
@@ -146,3 +149,10 @@ func _get_action_name(action_id: StringName) -> String:
|
|||||||
if definition != null:
|
if definition != null:
|
||||||
return definition.display_name
|
return definition.display_name
|
||||||
return String(action_id).capitalize()
|
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()
|
||||||
|
|||||||
Reference in New Issue
Block a user