Files
gamedev-the-steward/tests/simulation_definitions_test.gd
T
admin 4604dc6d5a feat: add NPC schedules with sleep, meal, and work periods
Add ACTION_SLEEP with home-position targeting and energy restoration. Embed schedule periods (SLEEP/MEAL/WORK/DISCRETIONARY) in ActionSelectionSystem with TimeOfDay from SimulationClock. NPCs sleep at night when energy is low, eat during meal periods when hungry, and work/discretionary the rest of the cycle. Home position stored per NPC and serialized through NPCStateRecord with backward-compatible fallback. Cycle duration persisted through save/restore. Includes headless test for sleep period, work period, meal hunger, energy restoration, serialization round-trip, and home targeting without world adapter.
2026-07-08 17:44:39 +02:00

130 lines
4.0 KiB
GDScript

extends SceneTree
var failures: Array[String] = []
func _initialize() -> void:
call_deferred("_run")
func _run() -> void:
_test_registry_integrity()
_test_definition_backed_behavior()
_test_state_reference_validation()
_test_id_round_trip()
if failures.is_empty():
print("[TEST] Simulation definitions passed")
quit(0)
return
for failure in failures:
push_error("[TEST] " + failure)
quit(1)
func _test_registry_integrity() -> void:
var errors := SimulationDefinitions.validate()
_check(errors.is_empty(), "Definition registry should validate: %s" % [errors])
var action_ids: Array[StringName] = []
for definition in SimulationDefinitions.get_actions():
action_ids.append(definition.action_id)
_check(
SimulationDefinitions.get_action(definition.action_id) == definition,
"Action lookup should return '%s'" % definition.action_id
)
_check(action_ids.size() == 10, "Registry should contain all ten executable prototype actions")
var profession_ids := SimulationDefinitions.get_profession_ids()
_check(profession_ids.size() == 5, "Registry should contain all five prototype professions")
var profession_colors := {}
for profession_id in profession_ids:
var profession := SimulationDefinitions.get_profession(profession_id)
_check(
profession != null,
"Profession lookup should return '%s'" % profession_id
)
if profession != null:
profession_colors[profession.visual_color.to_html()] = true
_check(
profession_colors.size() == profession_ids.size(),
"Each profession should have a distinct definition-backed visual color"
)
func _test_definition_backed_behavior() -> void:
var npc := SimNPC.new(500, "DefinitionWorker", SimulationIds.PROFESSION_SCHOLAR, 5.0, 5.0)
npc.set_task(SimulationIds.ACTION_STUDY)
var study := SimulationDefinitions.get_action(SimulationIds.ACTION_STUDY)
_check(
is_equal_approx(npc.task_duration, study.default_duration),
"SimNPC should use the action definition's default duration"
)
_check(
study.preferred_profession_id == SimulationIds.PROFESSION_SCHOLAR,
"Study should reference the stable scholar profession ID"
)
var gather := SimulationDefinitions.get_action(SimulationIds.ACTION_GATHER_FOOD)
_check(
(
gather.target_type == SimulationIds.TARGET_RESOURCE
and gather.resource_action_id == SimulationIds.ACTION_GATHER_FOOD
),
"Gather-food target metadata should come from its definition"
)
func _test_state_reference_validation() -> void:
var npc := SimNPC.new(501, "ValidationWorker", SimulationIds.PROFESSION_FARMER, 5.0, 5.0)
var valid_data := NPCStateRecord.capture(npc).to_dictionary()
var invalid_profession := valid_data.duplicate(true)
invalid_profession["profession"] = "missing_profession"
_check(
NPCStateRecord.from_dictionary(invalid_profession) == null,
"NPC state should reject an unknown profession ID"
)
var invalid_action := valid_data.duplicate(true)
invalid_action["current_task"] = "missing_action"
_check(
NPCStateRecord.from_dictionary(invalid_action) == null,
"NPC state should reject an unknown action ID"
)
func _test_id_round_trip() -> void:
var manager: Node = load("res://simulation/SimulationManager.gd").new()
manager.debug_logs = false
root.add_child(manager)
manager.set_process(false)
var json_state: String = manager.serialize_state()
var restored: Node = load("res://simulation/SimulationManager.gd").new()
restored.debug_logs = false
root.add_child(restored)
restored.set_process(false)
_check(
restored.restore_state_from_json(json_state),
"Definition-backed IDs should restore from serialized state"
)
for npc in restored.npcs:
_check(
typeof(npc.profession) == TYPE_STRING_NAME, "Profession should restore as StringName"
)
_check(
typeof(npc.current_task) == TYPE_STRING_NAME,
"Current action should restore as StringName"
)
_check(
SimulationDefinitions.get_profession(npc.profession) != null,
"Restored profession should resolve through the registry"
)
manager.free()
restored.free()
func _check(condition: bool, message: String) -> void:
if not condition:
failures.append(message)