162 lines
4.8 KiB
GDScript
162 lines
4.8 KiB
GDScript
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.profession = SimulationIds.PROFESSION_FARMER
|
|
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)
|