Initial project sync

This commit is contained in:
wxm
2026-07-06 22:55:14 -07:00
commit 8cca00d0da
1066 changed files with 58585 additions and 0 deletions
+107
View File
@@ -0,0 +1,107 @@
extends SceneTree
## 标题侧节奏音乐 + 标题背景「过去/现在」循环动画。
## 标题、难度选择、退出确认期间音乐持续播放;进入 gameplay 后停止,
## 由 RhythmManager.start() + AudioLayerController.restart_layers() 从 0 重播。
var failures: Array[String] = []
func _init() -> void:
_run.call_deferred()
func _run() -> void:
root.size = Vector2i(1152, 648)
_ensure_game_settings()
var flow: Node = load("res://autoload/game_flow_manager.gd").new()
flow.name = "FlowUnderTest"
flow.set("manage_scene", false)
root.add_child(flow)
await process_frame
# --- 标题音乐播放器:常驻处理、走 Music 总线 ---
var player := flow.get_node_or_null("TitleMusicPlayer") as AudioStreamPlayer
_expect(player != null, "flow owns a TitleMusicPlayer")
if player != null:
_expect(player.process_mode == Node.PROCESS_MODE_ALWAYS, "title music must survive the SceneTree pause")
_expect_equal(player.bus, &"Music", "title music routes through the Music bus")
# headless 下不加载 mp3,注入无限长的生成器流来验证播放开关。
player.stream = AudioStreamGenerator.new()
# --- 标题界面同时持有两个时代背景,「现在」画在「过去」上层 ---
var title_screen := (flow.get("_screens") as Dictionary).get(&"Title", null) as Control
_expect(title_screen != null, "title screen exists")
if title_screen != null:
var past := title_screen.get_node_or_null("SceneBackgroundPast") as TextureRect
var future := title_screen.get_node_or_null("SceneBackgroundFuture") as TextureRect
_expect(past != null, "title keeps the past-era background")
_expect(future != null, "title keeps the future-era background")
if past != null and future != null:
_expect(past.texture != null and future.texture != null, "both era textures load")
_expect(past.get_index() < future.get_index(), "the future era paints above the past era")
# --- 标题状态:音乐播放 + 时代循环动画运行 ---
flow.call("_set_state", &"Title")
flow.call("_update_title_music")
await process_frame
_expect(bool(flow.call("_should_play_title_music")), "title state wants the rhythm music")
if player != null:
_expect(player.playing, "title music plays on the title screen")
var tween := flow.get("_title_bg_tween") as Tween
_expect(tween != null and tween.is_valid(), "the era crossfade loop runs on the title screen")
# --- 难度选择:音乐延续,标题动画停止 ---
flow.call("_set_state", &"DifficultySelect")
await process_frame
_expect(bool(flow.call("_should_play_title_music")), "difficulty select keeps the rhythm music")
if player != null:
_expect(player.playing, "title music keeps playing on difficulty select")
_expect(flow.get("_title_bg_tween") == null, "the era loop stops once the title screen hides")
# --- 进入 gameplay:标题音乐停止 ---
flow.call("_set_state", &"Gameplay_FrontArea")
await process_frame
_expect(not bool(flow.call("_should_play_title_music")), "gameplay states drop the title music")
if player != null:
_expect(not player.playing, "title music stops when the run starts")
# --- 回到标题:音乐从头再播 ---
flow.call("_set_state", &"Title")
await process_frame
if player != null:
_expect(player.playing, "returning to title restarts the music")
flow.free()
_finish()
func _ensure_game_settings() -> Node:
var settings := root.get_node_or_null("GameSettings")
if settings != null:
return settings
settings = load("res://autoload/game_settings.gd").new()
settings.name = "GameSettings"
root.add_child(settings)
return settings
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 _finish() -> void:
paused = false
if failures.is_empty():
print("PASS title music and background loop")
quit(0)
else:
for failure: String in failures:
push_error(failure)
quit(1)