149 lines
4.9 KiB
GDScript3
149 lines
4.9 KiB
GDScript3
|
|
extends SceneTree
|
||
|
|
|
||
|
|
# Headless test: run with
|
||
|
|
# godot --headless -s res://tests/test_music_theme.gd
|
||
|
|
# Forgejo #148: the main theme plays all the time.
|
||
|
|
#
|
||
|
|
# "All the time" is the requirement, so what is checked is CONTINUITY rather
|
||
|
|
# than "a sound was started once": the same player has to survive the main
|
||
|
|
# menu becoming the game, the game becoming a planet surface, and the
|
||
|
|
# surface becoming the game again - and there must never be two of it,
|
||
|
|
# because a second copy is the theme playing over itself.
|
||
|
|
#
|
||
|
|
# Covers:
|
||
|
|
# A. loading a scene starts the theme
|
||
|
|
# B. it is parented to the tree root, not to the scene - the thing that
|
||
|
|
# makes it survive change_scene_to_file at all
|
||
|
|
# C. the stream loops, so "all the time" outlasts one play-through of it
|
||
|
|
# D. it keeps playing while a dialog has the game stopped (#128)
|
||
|
|
# E. swapping scenes neither restarts it nor stacks a second one up
|
||
|
|
|
||
|
|
var game_state: Node
|
||
|
|
var scene: Node
|
||
|
|
var player: AudioStreamPlayer
|
||
|
|
var stage: int = 0
|
||
|
|
var elapsed: float = 0.0
|
||
|
|
|
||
|
|
|
||
|
|
func _initialize() -> void:
|
||
|
|
# GameState is an autoload: a bare GameState.<instance member> does not
|
||
|
|
# compile in a SceneTree script, so the node is fetched by name instead.
|
||
|
|
game_state = root.get_node("GameState")
|
||
|
|
game_state.reset_new_game()
|
||
|
|
game_state.intro_active = false
|
||
|
|
game_state.reset_quests()
|
||
|
|
|
||
|
|
|
||
|
|
func _fail(message: String) -> bool:
|
||
|
|
print("TEST_MUSIC_THEME: FAIL (%s)" % message)
|
||
|
|
quit(1)
|
||
|
|
return true
|
||
|
|
|
||
|
|
|
||
|
|
func _players() -> Array:
|
||
|
|
var found: Array = []
|
||
|
|
for node in root.get_children():
|
||
|
|
if node is AudioStreamPlayer:
|
||
|
|
found.append(node)
|
||
|
|
return found
|
||
|
|
|
||
|
|
|
||
|
|
func _load(path: String) -> void:
|
||
|
|
scene = load(path).instantiate()
|
||
|
|
root.add_child(scene)
|
||
|
|
current_scene = scene
|
||
|
|
|
||
|
|
|
||
|
|
func _process(delta: float) -> bool:
|
||
|
|
elapsed += delta
|
||
|
|
match stage:
|
||
|
|
0:
|
||
|
|
if not _players().is_empty():
|
||
|
|
return _fail("something was already playing before any scene loaded")
|
||
|
|
_load("res://world/Main.tscn")
|
||
|
|
elapsed = 0.0
|
||
|
|
stage = 1
|
||
|
|
return false
|
||
|
|
1:
|
||
|
|
# The player is added deferred, so it arrives a frame later.
|
||
|
|
if elapsed < 0.4:
|
||
|
|
return false
|
||
|
|
# --- A and B. it exists, at the root ---------------------
|
||
|
|
var players: Array = _players()
|
||
|
|
if players.size() != 1:
|
||
|
|
return _fail("%d music players at the root, expected 1" % players.size())
|
||
|
|
player = players[0]
|
||
|
|
if player.get_parent() != root:
|
||
|
|
return _fail("the music player hangs off %s - it would die with the scene" \
|
||
|
|
% player.get_parent())
|
||
|
|
if not player.playing:
|
||
|
|
return _fail("the theme is not playing")
|
||
|
|
if player.stream == null:
|
||
|
|
return _fail("the music player has no stream on it")
|
||
|
|
print("theme playing from %s, parented to the tree root" % player.stream.resource_path)
|
||
|
|
|
||
|
|
# --- C. and it comes round again -------------------------
|
||
|
|
if not ("loop" in player.stream) or not player.stream.loop:
|
||
|
|
return _fail("the theme does not loop - it would fall silent after one play")
|
||
|
|
if player.stream.get_length() < 10.0:
|
||
|
|
return _fail("the theme is %.1fs long - that is not a main theme" \
|
||
|
|
% player.stream.get_length())
|
||
|
|
print("%.0fs of theme, looping" % player.stream.get_length())
|
||
|
|
|
||
|
|
# --- D. and a dialog does not stop it --------------------
|
||
|
|
var narration: Control = scene.get_node("UI/NarrationBanner")
|
||
|
|
narration.say("Stopping everything else.", "TEST")
|
||
|
|
if not paused:
|
||
|
|
return _fail("the dialog did not stop the game, so this proves nothing")
|
||
|
|
if not player.can_process():
|
||
|
|
return _fail("the music stops whenever a dialog is up")
|
||
|
|
print("a dialog stops the game and the music keeps playing")
|
||
|
|
while narration.dismiss():
|
||
|
|
pass
|
||
|
|
|
||
|
|
elapsed = 0.0
|
||
|
|
stage = 2
|
||
|
|
return false
|
||
|
|
2:
|
||
|
|
if elapsed < 0.3:
|
||
|
|
return false
|
||
|
|
# --- E. through a scene change ---------------------------
|
||
|
|
game_state.planet_name = "Music Test"
|
||
|
|
game_state.planet_id = "music:test"
|
||
|
|
game_state.planet_definition_id = "rocky"
|
||
|
|
scene.free()
|
||
|
|
_load("res://surface/PlanetSurface.tscn")
|
||
|
|
elapsed = 0.0
|
||
|
|
stage = 3
|
||
|
|
return false
|
||
|
|
3:
|
||
|
|
if elapsed < 0.4:
|
||
|
|
return false
|
||
|
|
var players2: Array = _players()
|
||
|
|
if players2.size() != 1:
|
||
|
|
return _fail("%d music players after a scene change - the theme is playing over itself" \
|
||
|
|
% players2.size())
|
||
|
|
if players2[0] != player:
|
||
|
|
return _fail("the scene change replaced the music player, restarting the theme")
|
||
|
|
if not player.playing:
|
||
|
|
return _fail("the theme stopped when the scene changed")
|
||
|
|
print("scene changed to a planet surface: same player, still playing")
|
||
|
|
|
||
|
|
scene.free()
|
||
|
|
_load("res://world/Main.tscn")
|
||
|
|
elapsed = 0.0
|
||
|
|
stage = 4
|
||
|
|
return false
|
||
|
|
4:
|
||
|
|
if elapsed < 0.4:
|
||
|
|
return false
|
||
|
|
if _players().size() != 1 or _players()[0] != player:
|
||
|
|
return _fail("coming back from a surface left %d players" % _players().size())
|
||
|
|
if not player.playing:
|
||
|
|
return _fail("the theme stopped on the way back from a surface")
|
||
|
|
print("and back again: still one player, still the same one")
|
||
|
|
|
||
|
|
print("TEST_MUSIC_THEME: PASS")
|
||
|
|
quit(0)
|
||
|
|
return true
|
||
|
|
return false
|