extends SceneTree var failures: Array[String] = [] func _initialize() -> void: call_deferred("_run") func _run() -> void: _test_enemy_definitions() _test_player_downing() _test_defend_selection() _finish() func _test_enemy_definitions() -> void: var raider: EnemyDefinition = SimulationEnemies.get_enemy(&"enemy_raider") var wolf: EnemyDefinition = SimulationEnemies.get_enemy(&"enemy_wolf") _check( ( raider != null and wolf != null and raider.kind == SimulationIds.COMBATANT_KIND_RAIDER and wolf.kind == SimulationIds.COMBATANT_KIND_WOLF and raider.weapon_id == SimulationIds.ITEM_SWORD and wolf.weapon_id == SimulationIds.ITEM_CLAW ), "Enemy definitions should describe raiders and wolves as data" ) var manager := _create_manager(991) var wolf_id: StringName = manager.spawn_wolf(Vector3.ZERO) var wolf_combatant: CombatantStateRecord = manager.get_combatant(wolf_id) _check( ( is_equal_approx(wolf_combatant.get_health(), wolf.health) and wolf_combatant.get_weapon_id() == wolf.weapon_id ), "Spawned enemies should inherit their definition's health and weapon" ) manager.free() func _test_player_downing() -> void: var manager := _create_manager(992) var player_state: PlayerStateRecord = manager.get_player_state() _check( is_equal_approx(player_state.get_health(), 100.0) and not player_state.is_downed(), "The player should begin healthy and standing" ) var wolf_id: StringName = manager.spawn_wolf(Vector3.ZERO) manager.update_player_combatant(Vector3.ZERO) for _tick in 3: manager.simulate_tick() _check( player_state.get_health() < 100.0 and not player_state.is_downed(), "A hungry wolf should wound the player during a hunt" ) var downed := false for _tick in 60: manager.simulate_tick() if player_state.is_downed(): downed = true break _check(downed, "A prolonged wolf attack should eventually down the player") if downed: for _tick in 22: manager.simulate_tick() _check(not player_state.is_downed(), "The downed player should recover and stand again") var player_combatant: CombatantStateRecord = manager.get_combatant(&"player_combatant") _check( ( player_state.get_health() > 0.0 and player_combatant != null and player_combatant.is_alive() and is_equal_approx(player_combatant.get_health(), player_state.get_health()) ), "Recovery should revive both authoritative player records at matching health" ) manager.update_player_combatant(Vector3(100.0, 0.0, 100.0)) manager.spawn_wolf(Vector3(100.0, 0.0, 100.0)) var recovered_health := player_state.get_health() manager.simulate_tick() _check( player_state.get_health() < recovered_health, "A recovered player should become targetable and vulnerable again" ) manager.free() func _test_defend_selection() -> void: var manager := _create_manager(993) manager.clock.elapsed_ticks = 100 var tribe: FactionStateRecord = manager.get_faction(SimulationIds.FACTION_TRIBE) tribe.set_war_plan(SimulationIds.WAR_PLAN_RAIDING, 1) var defender: SimNPC = manager.npcs[0] defender.strength = 9.0 defender.hunger = 20.0 defender.energy = 90.0 defender.is_starving = false defender.mourning_ticks = 0 defender.inventory.clear() defender.task_state = SimNPC.TASK_STATE_IDLE defender.task_complete = true manager.call("_select_action_if_idle", defender, SimNPC.TASK_STATE_IDLE) _check( ( defender.current_task == SimulationIds.ACTION_DEFEND and manager.conflict_system.is_raiding() ), "A strong idle villager should rally to defend during an active raid" ) var worker: SimNPC = manager.npcs[1] worker.strength = 1.0 worker.hunger = 20.0 worker.energy = 90.0 worker.is_starving = false worker.mourning_ticks = 0 worker.inventory.clear() worker.task_state = SimNPC.TASK_STATE_IDLE worker.task_complete = true manager.call("_select_action_if_idle", worker, SimNPC.TASK_STATE_IDLE) _check( worker.current_task != SimulationIds.ACTION_DEFEND, "A weak non-guard villager should not be conscripted as a defender" ) manager.free() func _create_manager(seed_value: int = 991) -> Node: var manager: Node = load("res://simulation/SimulationManager.gd").new() manager.simulation_seed = seed_value manager.debug_logs = false var home_positions: Array[Vector3] = [ Vector3(0.0, 0.0, 0.0), Vector3(2.0, 0.0, 0.0), Vector3(10.0, 0.0, 0.0), Vector3(12.0, 0.0, 0.0), Vector3(30.0, 0.0, 30.0), Vector3(40.0, 0.0, 40.0), ] manager.home_positions = home_positions root.add_child(manager) manager.set_process(false) return manager func _finish() -> void: if failures.is_empty(): print("[TEST] Combat patterns passed: enemy data, player downing, defend duty") quit(0) return for failure in failures: push_error("[TEST] " + failure) quit(1) func _check(condition: bool, message: String) -> void: if not condition: failures.append(message)