From c37eb9bd3dc575ef7a14bde00e4bbe7300f908c1 Mon Sep 17 00:00:00 2001
From: Rijad Zuzo <rijad.zuzo@matriq.ch>
Date: Fri, 19 Jun 2026 10:42:11 +0200
Subject: [PATCH] feat: add behaviour events from the simulation results

---
 main.tscn                       | 19 ++++++++++++++-----
 player/player.gd                |  6 +++++-
 simulation/SimulationManager.gd | 31 ++++++++++++++++++++-----------
 world/world_view_manager.gd     | 33 ++++++++++++++++++++++++---------
 4 files changed, 63 insertions(+), 26 deletions(-)

diff --git a/main.tscn b/main.tscn
index f347ecd..e4abf60 100644
--- a/main.tscn
+++ b/main.tscn
@@ -1,4 +1,4 @@
-[gd_scene load_steps=13 format=3 uid="uid://rs3hv73svbpa"]
+[gd_scene load_steps=14 format=3 uid="uid://rs3hv73svbpa"]
 
 [ext_resource type="Script" uid="uid://4mg67crlim53" path="res://player/player.gd" id="1_h2yge"]
 [ext_resource type="Script" uid="uid://ii8ai801jur2" path="res://player/camera_rig.gd" id="2_1bvp3"]
@@ -7,14 +7,16 @@
 [ext_resource type="PackedScene" uid="uid://dhxxyprqflotq" path="res://player/npc/NpcVisual.tscn" id="5_lquwl"]
 
 [sub_resource type="NavigationMesh" id="NavigationMesh_lquwl"]
-vertices = PackedVector3Array(-14.5, 0.4, -14.5, -14.5, 0.4, 14.5, 14.5, 0.4, 14.5, 14.5, 0.4, -14.5)
-polygons = [PackedInt32Array(3, 2, 0), PackedInt32Array(0, 2, 1)]
+vertices = PackedVector3Array(-14.5, 0.5, 0, -1, 0.5, 0, -1, 0.5, -0.75, 0, 0.5, -1, 0, 0.5, -14.5, -14.5, 0.5, -14.5, 0.75, 0.5, -1, 0.75, 0.75, -0.5, 14.5, 0.5, -0.5, 14.5, 0.5, -14.5, 0.75, 0.75, 0, 0, 1, 0, 0, 0.75, 0.75, -0.5, 0.75, 0.75, -0.5, 0.5, 14.5, 14.5, 0.5, 14.5, -1, 0.5, 0.75, -14.5, 0.5, 14.5)
+polygons = [PackedInt32Array(2, 1, 0), PackedInt32Array(3, 2, 4), PackedInt32Array(4, 2, 5), PackedInt32Array(5, 2, 0), PackedInt32Array(6, 3, 4), PackedInt32Array(8, 7, 6), PackedInt32Array(8, 6, 9), PackedInt32Array(9, 6, 4), PackedInt32Array(12, 11, 10), PackedInt32Array(10, 7, 8), PackedInt32Array(14, 13, 12), PackedInt32Array(10, 8, 12), PackedInt32Array(12, 8, 14), PackedInt32Array(14, 8, 15), PackedInt32Array(0, 1, 16), PackedInt32Array(16, 13, 14), PackedInt32Array(14, 17, 16), PackedInt32Array(16, 17, 0)]
 
 [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_o5qli"]
 albedo_color = Color(0.16862746, 0.52156866, 0.30588236, 1)
 
 [sub_resource type="BoxMesh" id="BoxMesh_sgp6g"]
 
+[sub_resource type="BoxShape3D" id="BoxShape3D_lquwl"]
+
 [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_o5qli"]
 radius = 0.4
 height = 1.7
@@ -41,13 +43,20 @@ light_energy = 2.0
 [node name="NavigationRegion3D" type="NavigationRegion3D" parent="World"]
 navigation_mesh = SubResource("NavigationMesh_lquwl")
 
-[node name="Ground" type="MeshInstance3D" parent="World/NavigationRegion3D"]
+[node name="GroundRoot" type="StaticBody3D" parent="World/NavigationRegion3D"]
+
+[node name="GroundMesh" type="MeshInstance3D" parent="World/NavigationRegion3D/GroundRoot"]
 transform = Transform3D(30, 0, 0, 0, 0.1, 0, 0, 0, 30, 0, -0.05, 0)
 material_override = SubResource("StandardMaterial3D_o5qli")
 mesh = SubResource("BoxMesh_sgp6g")
-skeleton = NodePath("../..")
+skeleton = NodePath("../../..")
+
+[node name="GroundCollision" type="CollisionShape3D" parent="World/NavigationRegion3D/GroundRoot"]
+transform = Transform3D(30, 0, 0, 0, 0.1, 0, 0, 0, 30, 0, 0.18380833, 0)
+shape = SubResource("BoxShape3D_lquwl")
 
 [node name="Player" type="CharacterBody3D" parent="." node_paths=PackedStringArray("camera_rig")]
+transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.05, 0)
 script = ExtResource("1_h2yge")
 camera_rig = NodePath("../CameraRig")
 
diff --git a/player/player.gd b/player/player.gd
index c403eb9..d0f1f8d 100644
--- a/player/player.gd
+++ b/player/player.gd
@@ -28,7 +28,11 @@ func _physics_process(delta: float) -> void:
 
 	velocity.x = move_toward(velocity.x, target_velocity.x, acceleration * delta)
 	velocity.z = move_toward(velocity.z, target_velocity.z, acceleration * delta)
-	velocity.y = 0.0
+	
+	if not is_on_floor():
+		velocity.y -= 30.0 * delta
+	else:
+		velocity.y = 0.0
 
 	move_and_slide()
 
diff --git a/simulation/SimulationManager.gd b/simulation/SimulationManager.gd
index 7afa7f0..8e097ff 100644
--- a/simulation/SimulationManager.gd
+++ b/simulation/SimulationManager.gd
@@ -1,5 +1,8 @@
 extends Node
 
+signal npc_task_changed(npc: SimNPC, old_task: String, new_task: String)
+const DEBUG_LOGS := true
+
 var npcs: Array[SimNPC] = []
 var tick_interval := 1.0
 var tick_timer := 0.0
@@ -47,15 +50,21 @@ func simulate_tick() -> void:
 	print("--- Tick ", tick_count, " ---")
 
 	for npc in npcs:
+		var old_task := npc.current_task
 		npc.simulate_tick()
-		print(
-			npc.npc_name,
-			" | ",
-			npc.profession,
-			" | task: ",
-			npc.current_task,
-			" | hunger: ",
-			round(npc.hunger),
-			" | energy: ",
-			round(npc.energy)
-		)
+		
+		if old_task != npc.current_task:
+			npc_task_changed.emit(npc, old_task, npc.current_task)
+		
+		if DEBUG_LOGS:
+			print(
+				npc.npc_name,
+				" | ",
+				npc.profession,
+				" | task: ",
+				npc.current_task,
+				" | hunger: ",
+				round(npc.hunger),
+				" | energy: ",
+				round(npc.energy)
+			)
diff --git a/world/world_view_manager.gd b/world/world_view_manager.gd
index f4b8f8a..6c20b5d 100644
--- a/world/world_view_manager.gd
+++ b/world/world_view_manager.gd
@@ -20,16 +20,18 @@ func debug_log(message: String) -> void:
 		print("[WorldViewManager] ", message)
 
 func _ready() -> void:
-	call_deferred("spawn_initial_npcs")
+	call_deferred("initialize_world_view")
 
-var target_update_timer := 0.0
-var target_update_interval := 0.5
-
-func _process(delta: float) -> void:
-	target_update_timer += delta
-	if target_update_timer >= target_update_interval:
-		target_update_timer = 0.0
-		update_npc_targets()
+func initialize_world_view() -> void:
+	await get_tree().physics_frame
+	spawn_initial_npcs()
+	
+	if simulation_manager.has_signal("npc_task_changed"):
+		simulation_manager.npc_task_changed.connect(_on_npc_task_changed)
+	else:
+		push_error("WorldViewManager: SimulationManager has no npc_task_changed signal")
+	
+	update_npc_targets()
 
 func spawn_initial_npcs() -> void:
 	if simulation_manager == null:
@@ -96,3 +98,16 @@ func get_target_for_task(task: String) -> Marker3D:
 			return food_zone
 		_:
 			return rest_zone
+
+func _on_npc_task_changed(npc: SimNPC, old_task: String, new_task: String) -> void:
+	if DEBUG_LOGS:
+		debug_log("%s changed task: %s -> %s" % [npc.npc_name, old_task, new_task])
+	
+	if not active_npc_visuals.has(npc.id):
+		return
+	
+	var visual = active_npc_visuals[npc.id]
+	var target := get_target_for_task(new_task)
+	
+	if target != null:
+		visual.set_target_position(target.global_position)
