205 lines
7.6 KiB
GDScript
205 lines
7.6 KiB
GDScript
extends SceneTree
|
|
|
|
var failures: Array[String] = []
|
|
|
|
|
|
func _init() -> void:
|
|
_run.call_deferred()
|
|
|
|
|
|
func _run() -> void:
|
|
await process_frame
|
|
_check_resources_load()
|
|
await _check_runner_upcoming_and_triggered_once()
|
|
await _check_runner_broadcasts_empty_action_and_unknown_events()
|
|
_check_stage7_chart_resource_shape()
|
|
_check_stage9_chart_resource_shape()
|
|
_finish()
|
|
|
|
|
|
func _check_resources_load() -> void:
|
|
for path: String in [
|
|
"res://resources/chart_event.gd",
|
|
"res://resources/chart_track.gd",
|
|
"res://resources/beat_chart.gd",
|
|
"res://scenes/chart/chart_runner.gd",
|
|
]:
|
|
_expect(load(path) != null, "%s should load" % path)
|
|
|
|
|
|
func _check_runner_upcoming_and_triggered_once() -> void:
|
|
var runner := _make_runner(_make_chart())
|
|
var upcoming: Array[StringName] = []
|
|
var triggered: Array[StringName] = []
|
|
runner.connect("chart_event_upcoming", func(event: Resource, _time_to_event: float) -> void:
|
|
upcoming.append(_event_identity(event))
|
|
)
|
|
runner.connect("chart_event_triggered", func(event: Resource) -> void:
|
|
triggered.append(_event_identity(event))
|
|
)
|
|
|
|
runner.call("update_for_song_time", 0.49)
|
|
_expect_array(upcoming, [&"boss_combo_1"], "Boss action upcoming should expose action_id at lead window")
|
|
_expect(triggered.is_empty(), "No event should trigger before event time")
|
|
|
|
runner.call("update_for_song_time", 1.0)
|
|
runner.call("update_for_song_time", 1.1)
|
|
_expect_array(triggered, [&"boss_combo_1"], "Boss action should trigger once")
|
|
|
|
runner.call("update_for_song_time", 2.99)
|
|
runner.call("update_for_song_time", 3.0)
|
|
runner.call("update_for_song_time", 3.2)
|
|
_expect(upcoming.count(&"boss_lunging_stab") == 1, "Special boss action upcoming should fire once")
|
|
_expect(triggered.count(&"boss_lunging_stab") == 1, "Special boss action should trigger once")
|
|
runner.queue_free()
|
|
await process_frame
|
|
|
|
|
|
func _check_runner_broadcasts_empty_action_and_unknown_events() -> void:
|
|
var bus := _ensure_event_bus()
|
|
var mirrored: Array[StringName] = []
|
|
bus.connect("chart_event_triggered", func(event: Resource) -> void:
|
|
mirrored.append(_event_identity(event))
|
|
)
|
|
var chart_script: Script = load("res://resources/beat_chart.gd")
|
|
var track_script: Script = load("res://resources/chart_track.gd")
|
|
var chart: Resource = chart_script.new()
|
|
var track: Resource = track_script.new()
|
|
chart.set("chart_id", &"wide_channel_chart")
|
|
track.set("track_id", &"boss_special")
|
|
track.set("track_type", &"boss")
|
|
track.set("events", [
|
|
_make_event(1, &"time_anchor", &"Boss", 0.0, &""),
|
|
_make_event(2, &"phase_warning", &"", 0.0, &""),
|
|
])
|
|
chart.set("tracks", [track])
|
|
var runner := _make_runner(chart)
|
|
runner.call("update_for_song_time", 0.5)
|
|
_expect_array(mirrored, [&"time_anchor"], "ChartRunner should broadcast unknown empty-action events")
|
|
runner.call("update_for_song_time", 1.0)
|
|
_expect_array(mirrored, [&"time_anchor", &"phase_warning"], "ChartRunner should keep the future event wide channel")
|
|
runner.queue_free()
|
|
await process_frame
|
|
|
|
|
|
func _check_stage7_chart_resource_shape() -> void:
|
|
var chart: Resource = load("res://resources/charts/stage7_boss_opening.tres")
|
|
_expect(chart != null, "Stage-7 boss opening chart should load")
|
|
if chart == null:
|
|
return
|
|
var track_ids: Array[StringName] = []
|
|
for track: Resource in chart.get("tracks"):
|
|
track_ids.append(StringName(str(track.get("track_id"))))
|
|
for event: Resource in track.get("events"):
|
|
if StringName(str(event.get("event_type"))) == &"enemy_action":
|
|
_expect_equal(event.get("target_id"), &"Boss", "Boss chart action target_id should match ActorsContainer/Boss")
|
|
_expect(track_ids.has(&"track_boss_melee"), "Boss chart should split melee events into their own track")
|
|
_expect(track_ids.has(&"track_boss_ranged"), "Boss chart should split ranged events into their own track")
|
|
_expect(track_ids.has(&"track_boss_special"), "Boss chart should reserve a special track")
|
|
|
|
|
|
func _check_stage9_chart_resource_shape() -> void:
|
|
var chart: Resource = load("res://resources/charts/stage9_boss_duel.tres")
|
|
_expect(chart != null, "Stage-9 boss duel chart should load")
|
|
if chart == null:
|
|
return
|
|
_expect_equal(chart.get("chart_id"), &"stage9_boss_duel", "Stage-9 chart id")
|
|
_expect(int(chart.get("total_beats")) >= 64, "Stage-9 chart should cover a full 16-bar duel at 4/4")
|
|
var track_ids: Array[StringName] = []
|
|
var action_ids: Array[StringName] = []
|
|
for track: Resource in chart.get("tracks"):
|
|
track_ids.append(StringName(str(track.get("track_id"))))
|
|
for event: Resource in track.get("events"):
|
|
if StringName(str(event.get("event_type"))) != &"enemy_action":
|
|
continue
|
|
_expect_equal(event.get("target_id"), &"Boss", "Stage-9 Boss event target_id should match ActorsContainer/Boss")
|
|
action_ids.append(StringName(str(event.get("action_id"))))
|
|
_expect(track_ids.has(&"track_boss_melee"), "Stage-9 chart should keep melee track")
|
|
_expect(track_ids.has(&"track_boss_ranged"), "Stage-9 chart should keep ranged track")
|
|
_expect(track_ids.has(&"track_boss_special"), "Stage-9 chart should keep special track")
|
|
for required_action: StringName in [&"boss_combo_1", &"boss_combo_2", &"boss_combo_3", &"boss_lunging_stab", &"boss_shoot_1", &"boss_shoot_2", &"boss_2way_shoot", &"boss_retreat_dash"]:
|
|
_expect(action_ids.has(required_action), "Stage-9 chart should include %s" % required_action)
|
|
|
|
|
|
func _make_event(beat: int, event_type: StringName, target_id := &"", lead_beats := 1.0, action_id := &"") -> Resource:
|
|
var event_script: Script = load("res://resources/chart_event.gd")
|
|
var event: Resource = event_script.new()
|
|
event.set("beat_index", beat)
|
|
event.set("event_type", event_type)
|
|
event.set("target_id", target_id)
|
|
event.set("lead_beats", lead_beats)
|
|
event.set("action_id", action_id)
|
|
return event
|
|
|
|
|
|
func _make_chart() -> Resource:
|
|
var chart_script: Script = load("res://resources/beat_chart.gd")
|
|
var track_script: Script = load("res://resources/chart_track.gd")
|
|
var chart: Resource = chart_script.new()
|
|
var melee: Resource = track_script.new()
|
|
var special: Resource = track_script.new()
|
|
chart.set("chart_id", &"test_boss_chart")
|
|
melee.set("track_id", &"track_boss_melee")
|
|
melee.set("track_type", &"boss")
|
|
melee.set("events", [
|
|
_make_event(2, &"enemy_action", &"Boss", 1.0, &"boss_combo_1"),
|
|
])
|
|
special.set("track_id", &"track_boss_special")
|
|
special.set("track_type", &"boss")
|
|
special.set("events", [
|
|
_make_event(6, &"enemy_action", &"Boss", 2.0, &"boss_lunging_stab"),
|
|
])
|
|
chart.set("tracks", [melee, special])
|
|
return chart
|
|
|
|
|
|
func _make_runner(chart: Resource) -> Node:
|
|
var runner_script: Script = load("res://scenes/chart/chart_runner.gd")
|
|
var runner: Node = runner_script.new()
|
|
runner.set("beat_time_override", 0.5)
|
|
runner.call("set_chart", chart)
|
|
root.add_child(runner)
|
|
return runner
|
|
|
|
|
|
func _ensure_event_bus() -> Node:
|
|
var existing := root.get_node_or_null("EventBus")
|
|
if existing != null:
|
|
return existing
|
|
var bus: Node = load("res://autoload/event_bus.gd").new()
|
|
bus.name = "EventBus"
|
|
root.add_child(bus)
|
|
return bus
|
|
|
|
|
|
func _event_identity(event: Resource) -> StringName:
|
|
var action_id := StringName(str(event.get("action_id")))
|
|
if not action_id.is_empty():
|
|
return action_id
|
|
return StringName(str(event.get("event_type")))
|
|
|
|
|
|
func _expect(condition: bool, label: String) -> void:
|
|
if not condition:
|
|
failures.append(label)
|
|
|
|
|
|
func _expect_equal(actual: Variant, expected: Variant, label: String) -> void:
|
|
if actual != expected:
|
|
failures.append("%s: expected %s, got %s" % [label, expected, actual])
|
|
|
|
|
|
func _expect_array(actual: Array, expected: Array, label: String) -> void:
|
|
if actual != expected:
|
|
failures.append("%s: expected %s, got %s" % [label, expected, actual])
|
|
|
|
|
|
func _finish() -> void:
|
|
if failures.is_empty():
|
|
print("PASS chart layer")
|
|
quit(0)
|
|
else:
|
|
for failure: String in failures:
|
|
push_error(failure)
|
|
quit(1)
|