feat: let opportunity helpers act autonomously

This commit is contained in:
Rijad Zuzo
2026-07-16 10:58:48 +02:00
parent eaa15c076c
commit 0332c57a89
14 changed files with 253 additions and 30 deletions
+74
View File
@@ -28,6 +28,7 @@ func _initialize() -> void:
func _run() -> void:
_test_selection_and_execution_are_separate()
_test_opportunity_helper_selection_preserves_precedence()
_test_target_resolution_and_travel_are_separate()
if failures.is_empty():
@@ -91,6 +92,57 @@ func _test_selection_and_execution_are_separate() -> void:
)
func _test_opportunity_helper_selection_preserves_precedence() -> void:
var village := SimVillage.new()
village.food = 100.0
village.wood = 100.0
village.safety = 100.0
village.knowledge = 100.0
village.update_modifiers()
village.update_priorities()
var npc := SimNPC.new(702, "OpportunityHelper", SimulationIds.PROFESSION_WANDERER, 5.0, 5.0)
npc.hunger = 20.0
npc.energy = 80.0
var helper := _helper_result(npc.id, SimulationIds.ACTION_GATHER_WOOD)
var selector := ActionSelectionSystem.new()
var selection := selector.select_action(npc, village, 0.5, [npc], helper)
_check(
(
selection.action_id == SimulationIds.ACTION_GATHER_WOOD
and "Responding to village need" in selection.reason
and "Knows the need" in selection.reason
),
"The matching derived helper should choose its reported ordinary supply action"
)
npc.energy = 20.0
selection = selector.select_action(npc, village, 0.5, [npc], helper)
_check(
selection.action_id == SimulationIds.ACTION_REST,
"Urgent low energy should take precedence over an opportunity response"
)
npc.energy = 50.0
selection = selector.select_action(npc, village, 0.0, [npc], helper)
_check(
selection.action_id == SimulationIds.ACTION_SLEEP,
"The sleep schedule should take precedence over an opportunity response"
)
npc.energy = 80.0
npc.hunger = 90.0
selection = selector.select_action(npc, village, 0.5, [npc], helper)
_check(
selection.action_id == SimulationIds.ACTION_WITHDRAW_FOOD,
"Critical hunger should take precedence over an opportunity response"
)
npc.hunger = 20.0
helper = _helper_result(npc.id + 1, SimulationIds.ACTION_GATHER_WOOD)
selection = selector.select_action(npc, village, 0.5, [npc], helper)
_check(
"Responding to village need" not in selection.reason,
"A helper result must not redirect a different NPC"
)
func _test_target_resolution_and_travel_are_separate() -> void:
var adapter := FakeActiveWorld.new()
adapter.resource_candidates = [
@@ -185,6 +237,28 @@ func _on_travel_requested(_npc: SimNPC, target_position: Vector3) -> void:
travel_requests.append(target_position)
func _helper_result(helper_id: int, action_id: StringName) -> OpportunityHelperResult:
return (
OpportunityHelperResult
. new(
{
"opportunity_id": 9,
"helper_npc_id": helper_id,
"action_id": action_id,
"source_id": "",
"resource_id": SimulationIds.RESOURCE_WOOD,
"trigger_event_id": 14,
"trust": 0.7,
"familiarity": 0.8,
"uses_inventory": false,
"available_source_count": 1,
"profession_match": false,
"reason": "Knows the need; trust 0.70 toward Neighbour; has 1 finite source",
}
)
)
func _check(condition: bool, message: String) -> void:
if not condition:
failures.append(message)