feat: define action completion costs

This commit is contained in:
Rijad Zuzo
2026-07-10 10:36:08 +02:00
parent 39989002ca
commit 0f7b12080e
19 changed files with 213 additions and 45 deletions
+9
View File
@@ -82,6 +82,15 @@ func _test_selection_and_execution_are_separate() -> void:
unfunded_selection.action_id == SimulationIds.ACTION_GATHER_WOOD,
"A guard should gather wood instead of selecting patrol when its material cost is unavailable"
)
_check(
(
unfunded_selection.rejections.has(SimulationIds.ACTION_PATROL)
and "Needs 1 Wood" in String(
unfunded_selection.rejections[SimulationIds.ACTION_PATROL]
)
),
"Selection should expose a readable definition-backed rejection reason"
)
func _test_target_resolution_and_travel_are_separate() -> void:
+10
View File
@@ -204,6 +204,16 @@ func _test_wood_work_requires_material() -> void:
),
"Patrol should not consume partial wood or create safety when its full cost cannot be paid"
)
var blocked_events: Array[EconomicEventRecord] = manager.get_npc_events(npc.id, 2)
_check(
(
not blocked_events.is_empty()
and StringName(blocked_events[0].data["event_type"])
== SimulationIds.EVENT_TASK_BLOCKED
and "needs 1 Wood" in String(blocked_events[0].data.get("action_name", ""))
),
"An unpaid completion cost should create a readable blocked-task fact"
)
manager.add_wood(1.5)
_check(
+10 -1
View File
@@ -63,7 +63,8 @@ func _run() -> void:
SimulationIds.ACTION_GATHER_FOOD,
-1.0,
"Test shortage reason",
{SimulationIds.ACTION_GATHER_FOOD: 4.5}
{SimulationIds.ACTION_GATHER_FOOD: 4.5, SimulationIds.ACTION_PATROL: -1000000.0},
{SimulationIds.ACTION_PATROL: "Needs 1 Wood (0.5 available)"}
)
manager.latest_decisions[npc.id] = test_decision
manager.emit_signal("npc_decision_recorded", npc, test_decision)
@@ -75,6 +76,14 @@ func _run() -> void:
and npc.npc_name in inspector_label.text,
"NPC inspector should show the selected villager's real decision reason"
)
_check(
(
"Unavailable" in inspector_label.text
and "Patrol — Needs 1 Wood" in inspector_label.text
and "-1000000" not in inspector_label.text
),
"NPC inspector should explain rejected actions without sentinel utility scores"
)
var carried_food: MeshInstance3D = visual.get_node("CarriedFood")
_check(not carried_food.visible, "NPC should begin without a carried-food prop")
npc.add_inventory(SimulationIds.RESOURCE_FOOD, 1.0)
+16
View File
@@ -65,6 +65,22 @@ func _test_definition_backed_behavior() -> void:
study.preferred_profession_id == SimulationIds.PROFESSION_SCHOLAR,
"Study should reference the stable scholar profession ID"
)
_check(
(
study.completion_cost_resource_id == SimulationIds.RESOURCE_WOOD
and is_equal_approx(study.completion_cost_amount, 1.0)
and study.has_completion_cost()
),
"Study should declare its completion cost through definition metadata"
)
var patrol := SimulationDefinitions.get_action(SimulationIds.ACTION_PATROL)
_check(
(
patrol.completion_cost_resource_id == SimulationIds.RESOURCE_WOOD
and is_equal_approx(patrol.completion_cost_amount, 1.0)
),
"Patrol should share the proven one-wood completion-cost contract"
)
var gather := SimulationDefinitions.get_action(SimulationIds.ACTION_GATHER_FOOD)
_check(
(