feat: add better priorities setting

This commit is contained in:
Rijad Zuzo
2026-06-21 14:41:19 +02:00
parent 052b36a4e7
commit eef2068747
3 changed files with 191 additions and 49 deletions
+32 -25
View File
@@ -2,7 +2,7 @@ extends CharacterBody3D
signal arrived_at_target(sim_id: int) signal arrived_at_target(sim_id: int)
const DEBUG_LOGS := false const DEBUG_LOGS := true
@export var move_speed := 3.0 @export var move_speed := 3.0
@export var rotation_speed := 8.0 @export var rotation_speed := 8.0
@@ -18,6 +18,7 @@ var profession: String = ""
var current_path: PackedVector3Array = [] var current_path: PackedVector3Array = []
var path_index := 0 var path_index := 0
var current_target := Vector3.INF var current_target := Vector3.INF
var arrival_distance := 0.6
func debug_log(message: String) -> void: func debug_log(message: String) -> void:
if DEBUG_LOGS: if DEBUG_LOGS:
@@ -30,28 +31,15 @@ func setup_from_sim(npc: SimNPC) -> void:
name = "NPC_%s_%s" % [sim_id, npc_name] name = "NPC_%s_%s" % [sim_id, npc_name]
func set_target_position(pos: Vector3) -> void: func set_target_position(pos: Vector3) -> void:
if current_target.distance_to(pos) < 0.1:
return
current_target = pos current_target = pos
has_reported_arrival = false has_reported_arrival = false
nav_agent.target_position = pos
await get_tree().physics_frame if global_position.distance_to(pos) <= arrival_distance:
current_path = PackedVector3Array()
current_path = NavigationServer3D.map_get_path( path_index = 0
get_world_3d().navigation_map, debug_log("Already close to target, will report arrival")
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:
return return
current_target = pos
nav_agent.target_position = pos nav_agent.target_position = pos
await get_tree().physics_frame await get_tree().physics_frame
@@ -65,18 +53,26 @@ func set_target_position(pos: Vector3) -> void:
path_index = 0 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: 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(): if current_path.is_empty() or path_index >= current_path.size():
velocity = Vector3.ZERO velocity = Vector3.ZERO
move_and_slide() 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 return
var next_pos := current_path[path_index] 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) var target_angle := atan2(direction.x, direction.z)
rotation.y = lerp_angle(rotation.y, target_angle, rotation_speed * delta) 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)
+104 -20
View File
@@ -1,6 +1,8 @@
class_name SimNPC class_name SimNPC
extends RefCounted extends RefCounted
@export var DEBUG_LOGS := true
const TASK_STATE_IDLE := "idle" const TASK_STATE_IDLE := "idle"
const TASK_STATE_TRAVELING := "traveling" const TASK_STATE_TRAVELING := "traveling"
const TASK_STATE_WORKING := "working" const TASK_STATE_WORKING := "working"
@@ -79,34 +81,116 @@ func choose_new_task(village: SimVillage) -> void:
set_task("rest", 4.0) set_task("rest", 4.0)
return return
if village.food < 15.0: var best_task := choose_best_work_task(village)
set_task("gather_food", 5.0)
return
if village.wood < 10.0: match best_task:
set_task("gather_wood", 5.0) "gather_food":
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":
set_task("gather_food", 5.0) set_task("gather_food", 5.0)
"woodcutter": "gather_wood":
set_task("gather_wood", 5.0) set_task("gather_wood", 5.0)
"guard": "patrol":
set_task("patrol", 6.0) set_task("patrol", 6.0)
"scholar": "study":
set_task("study", 6.0) set_task("study", 6.0)
_: _:
set_task("wander", 4.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: func set_task(task: String, duration: float) -> void:
current_task = task current_task = task
task_duration = duration task_duration = duration
+55 -4
View File
@@ -13,6 +13,11 @@ var wood_modifier := 1.0
var safety_modifier := 1.0 var safety_modifier := 1.0
var knowledge_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: func update_modifiers() -> void:
food_modifier = 1.0 food_modifier = 1.0
wood_modifier = 1.0 wood_modifier = 1.0
@@ -34,15 +39,44 @@ func update_modifiers() -> void:
knowledge_modifier 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: func apply_npc_task(npc: SimNPC) -> void:
var productivity := 1.0 var productivity := 1.0
if npc.energy < 20.0: if npc.energy < 20.0:
productivity = 0.25 productivity = 0.25
elif npc.energy < 40.0: elif npc.energy < 40.0:
productivity = 0.5 productivity = 0.5
match npc.current_task: match npc.current_task:
"gather_food": "gather_food":
food += 2.0 * productivity food += 2.0 * productivity
@@ -54,8 +88,9 @@ func apply_npc_task(npc: SimNPC) -> void:
knowledge += 1.0 * productivity knowledge += 1.0 * productivity
"eat": "eat":
food -= 1.0 food -= 1.0
npc.hunger = max(npc.hunger - 35.0, 0.0)
"rest": "rest":
pass npc.energy = min(npc.energy + 35.0, 100.0)
"wander": "wander":
safety -= 0.2 safety -= 0.2
@@ -63,14 +98,30 @@ func apply_npc_task(npc: SimNPC) -> void:
wood = max(wood, 0.0) wood = max(wood, 0.0)
safety = clamp(safety, 0.0, 100.0) safety = clamp(safety, 0.0, 100.0)
knowledge = max(knowledge, 0.0) knowledge = max(knowledge, 0.0)
update_modifiers() update_modifiers()
update_priorities()
func get_summary() -> String: 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(food),
round(wood), round(wood),
round(safety), 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: func debug_log(message: String) -> void: