From eef2068747ae62192767e366cc6913fa8f7c78f1 Mon Sep 17 00:00:00 2001 From: Rijad Zuzo Date: Sun, 21 Jun 2026 14:41:19 +0200 Subject: [PATCH] feat: add better priorities setting --- player/npc/NpcVisual.gd | 57 ++++++++++-------- simulation/SimNPC.gd | 124 ++++++++++++++++++++++++++++++++------- simulation/SimVillage.gd | 59 +++++++++++++++++-- 3 files changed, 191 insertions(+), 49 deletions(-) diff --git a/player/npc/NpcVisual.gd b/player/npc/NpcVisual.gd index b7cd605..4b22fb7 100644 --- a/player/npc/NpcVisual.gd +++ b/player/npc/NpcVisual.gd @@ -2,7 +2,7 @@ extends CharacterBody3D signal arrived_at_target(sim_id: int) -const DEBUG_LOGS := false +const DEBUG_LOGS := true @export var move_speed := 3.0 @export var rotation_speed := 8.0 @@ -18,6 +18,7 @@ var profession: String = "" var current_path: PackedVector3Array = [] var path_index := 0 var current_target := Vector3.INF +var arrival_distance := 0.6 func debug_log(message: String) -> void: if DEBUG_LOGS: @@ -30,28 +31,15 @@ func setup_from_sim(npc: SimNPC) -> void: name = "NPC_%s_%s" % [sim_id, npc_name] func set_target_position(pos: Vector3) -> void: - if current_target.distance_to(pos) < 0.1: - return - current_target = pos has_reported_arrival = false - nav_agent.target_position = pos - await get_tree().physics_frame - - current_path = NavigationServer3D.map_get_path( - get_world_3d().navigation_map, - global_position, - pos, - true - ) - - path_index = 0 - debug_log("New target: %s Path points: %s" % [pos, current_path.size()]) - if current_target.distance_to(pos) < 0.1: + if global_position.distance_to(pos) <= arrival_distance: + current_path = PackedVector3Array() + path_index = 0 + debug_log("Already close to target, will report arrival") return - current_target = pos nav_agent.target_position = pos await get_tree().physics_frame @@ -65,18 +53,26 @@ func set_target_position(pos: Vector3) -> void: path_index = 0 - debug_log("New target: %s Path points: %s" % [pos, current_path.size()]) + if current_path.is_empty(): + debug_log("Path is empty. Will report arrival/failure fallback.") + else: + debug_log("New target: %s Path points: %s" % [pos, current_path.size()]) func _physics_process(delta: float) -> void: + if current_target == Vector3.INF: + velocity = Vector3.ZERO + move_and_slide() + return + + if global_position.distance_to(current_target) <= arrival_distance: + _report_arrival_once() + velocity = Vector3.ZERO + move_and_slide() + return + if current_path.is_empty() or path_index >= current_path.size(): velocity = Vector3.ZERO move_and_slide() - - if not has_reported_arrival and current_target != Vector3.INF: - has_reported_arrival = true - debug_log("Arrived at target") - arrived_at_target.emit(sim_id) - return var next_pos := current_path[path_index] @@ -97,3 +93,14 @@ func _physics_process(delta: float) -> void: var target_angle := atan2(direction.x, direction.z) rotation.y = lerp_angle(rotation.y, target_angle, rotation_speed * delta) + +func _report_arrival_once() -> void: + if has_reported_arrival: + return + + has_reported_arrival = true + current_path = PackedVector3Array() + path_index = 0 + + debug_log("Arrived at target") + arrived_at_target.emit(sim_id) diff --git a/simulation/SimNPC.gd b/simulation/SimNPC.gd index e218040..84d04a7 100644 --- a/simulation/SimNPC.gd +++ b/simulation/SimNPC.gd @@ -1,6 +1,8 @@ class_name SimNPC extends RefCounted +@export var DEBUG_LOGS := true + const TASK_STATE_IDLE := "idle" const TASK_STATE_TRAVELING := "traveling" const TASK_STATE_WORKING := "working" @@ -79,34 +81,116 @@ func choose_new_task(village: SimVillage) -> void: set_task("rest", 4.0) return - if village.food < 15.0: - set_task("gather_food", 5.0) - return + var best_task := choose_best_work_task(village) - if village.wood < 10.0: - set_task("gather_wood", 5.0) - return - - if village.safety < 40.0 and profession == "guard": - set_task("patrol", 6.0) - return - - if village.knowledge < 10.0 and profession == "scholar": - set_task("study", 6.0) - return - - match profession: - "farmer": + match best_task: + "gather_food": set_task("gather_food", 5.0) - "woodcutter": + "gather_wood": set_task("gather_wood", 5.0) - "guard": + "patrol": set_task("patrol", 6.0) - "scholar": + "study": set_task("study", 6.0) _: set_task("wander", 4.0) +func choose_best_work_task(village: SimVillage) -> String: + var food_score := calculate_task_score( + "gather_food", + village.food_priority, + village.food, + 15.0, + 30.0, + 3.0, + 1.5, + "farmer" + ) + + var wood_score := calculate_task_score( + "gather_wood", + village.wood_priority, + village.wood, + 10.0, + 20.0, + 2.5, + 1.0, + "woodcutter" + ) + + var safety_score := calculate_task_score( + "patrol", + village.safety_priority, + village.safety, + 30.0, + 50.0, + 3.0, + 1.5, + "guard" + ) + + var knowledge_score := calculate_task_score( + "study", + village.knowledge_priority, + village.knowledge, + 10.0, + 25.0, + 1.5, + 0.75, + "scholar" + ) + + var best_task := "gather_food" + var best_score := food_score + + if wood_score > best_score: + best_task = "gather_wood" + best_score = wood_score + + if safety_score > best_score: + best_task = "patrol" + best_score = safety_score + + if knowledge_score > best_score: + best_task = "study" + best_score = knowledge_score + + return best_task + +func calculate_task_score( + task_name: String, + base_priority: float, + resource_amount: float, + critical_threshold: float, + low_threshold: float, + critical_bonus: float, + low_bonus: float, + preferred_profession: String +) -> float: + var score := base_priority + + if resource_amount < critical_threshold: + score += critical_bonus + elif resource_amount < low_threshold: + score += low_bonus + + if profession == preferred_profession: + score += 2.0 + + score += randf_range(0.0, 0.5) + + if DEBUG_LOGS: + print( + "[SimNPC] ", + npc_name, + " score ", + task_name, + " = ", + score + ) + + return score + func set_task(task: String, duration: float) -> void: current_task = task task_duration = duration diff --git a/simulation/SimVillage.gd b/simulation/SimVillage.gd index 392f353..360bfd4 100644 --- a/simulation/SimVillage.gd +++ b/simulation/SimVillage.gd @@ -13,6 +13,11 @@ var wood_modifier := 1.0 var safety_modifier := 1.0 var knowledge_modifier := 1.0 +var food_priority := 1.0 +var wood_priority := 1.0 +var safety_priority := 1.0 +var knowledge_priority := 1.0 + func update_modifiers() -> void: food_modifier = 1.0 wood_modifier = 1.0 @@ -34,15 +39,44 @@ func update_modifiers() -> void: knowledge_modifier ] ) + +func update_priorities() -> void: + food_priority = 1.0 + wood_priority = 1.0 + safety_priority = 1.0 + knowledge_priority = 1.0 + if food < 10.0: + food_priority = 3.0 + elif food < 20.0: + food_priority = 2.0 + + if wood < 5.0: + wood_priority = 2.5 + elif wood < 15.0: + wood_priority = 1.75 + + if safety < 25.0: + safety_priority = 3.0 + elif safety < 50.0: + safety_priority = 1.5 + + if knowledge < 5.0: + knowledge_priority = 1.75 + elif knowledge < 15.0: + knowledge_priority = 1.25 + + if DEBUG_LOGS: + print(get_priority_summary()) func apply_npc_task(npc: SimNPC) -> void: var productivity := 1.0 + if npc.energy < 20.0: productivity = 0.25 elif npc.energy < 40.0: productivity = 0.5 - + match npc.current_task: "gather_food": food += 2.0 * productivity @@ -54,8 +88,9 @@ func apply_npc_task(npc: SimNPC) -> void: knowledge += 1.0 * productivity "eat": food -= 1.0 + npc.hunger = max(npc.hunger - 35.0, 0.0) "rest": - pass + npc.energy = min(npc.energy + 35.0, 100.0) "wander": safety -= 0.2 @@ -63,14 +98,30 @@ func apply_npc_task(npc: SimNPC) -> void: wood = max(wood, 0.0) safety = clamp(safety, 0.0, 100.0) knowledge = max(knowledge, 0.0) + update_modifiers() + update_priorities() func get_summary() -> String: - return "Village | food: %s | wood: %s | safety: %s | knowledge: %s" % [ + + return "Village | food: %s | wood: %s | safety: %s | knowledge: %s | priorities F/W/S/K: %s/%s/%s/%s" % [ round(food), round(wood), round(safety), - round(knowledge) + round(knowledge), + food_priority, + wood_priority, + safety_priority, + knowledge_priority + ] + + +func get_priority_summary() -> String: + return "Priorities | food: %s | wood: %s | safety: %s | knowledge: %s" % [ + food_priority, + wood_priority, + safety_priority, + knowledge_priority ] func debug_log(message: String) -> void: