Split now playing, the quiz buttons and song loading out of the quiz

NowPlaying, QuizToolbar and SongControls become their own scenes, the auto
repeat switch moves into the music player scene, and downloading songs moves
into a SongLoader node. The quiz only wires the parts together. Every node
keeps its place on screen.

Screenshots of every UI state are pixel-identical to before, the layout test
still matches the original baseline, and each new part has its own tests.
This commit is contained in:
Sansan committed 2026-10-06 11:11:18 +02:00
1 parent bad4b3ad8b
commit e94c75e84d
32 files changed
+822 -300

No files matched your search

+29 -121
View File
@@ -21,25 +21,13 @@ var fileDialog: FileDialog = $FileDialog
var inspiration_list: InspirationList = $InspirationList
@onready
var soundtrack_label: Label = $VBoxContainer/SoundtrackLabel
@onready
var song_label: Label = $VBoxContainer/SongLabel
var now_playing: NowPlaying = $NowPlaying
@onready
var players_panel: PlayersPanel = $PlayersPanel
@onready
var reset_playlist_button: Button = $ResetPlaylistButton
@onready
var reset_points_button: Button = $ResetPointsButton
@onready
var sound_test_button: Button = $SoundTestButton
@onready
var settings_button: Button = $SettingsButton
var toolbar: QuizToolbar = $QuizToolbar
@onready
var settings_window: Control = $SettingsWindow
@@ -48,10 +36,10 @@ var settings_window: Control = $SettingsWindow
var winner_window: Control = %WinnerWindow
@onready
var show_answer_button: Button = $ShowAnswerButton
var song_controls: SongControls = $SongControls
@onready
var next_button: Button = $NextButton
var song_loader: SongLoader = $SongLoader
@onready
var music_list_panel: MusicListPanel = $MusicListPanel
@@ -60,10 +48,7 @@ var music_list_panel: MusicListPanel = $MusicListPanel
var new_character_select: Control = %NewCharacterSelect
@onready
var auto_repeat_song_button: CheckButton = $RepeatSongCheckButton
@onready
var music_player_container: PanelContainer = $MusicPlayer
var music_player_container: MusicPlayer = $MusicPlayer
@onready
var log_window: Control = %Log
@@ -80,9 +65,6 @@ var stats_paused_by_win: bool = false
## Dims the window behind an open popup and keeps clicks from reaching the buttons under it.
var popup_backdrop: PopupBackdrop
## Seconds to wait before retrying a song download step that failed.
var song_fetch_retry_seconds: float = 5.0
func _ready() -> void:
popup_backdrop = PopupBackdrop.add_to(self, _popups())
@@ -92,12 +74,14 @@ func _ready() -> void:
Settings.is_debug = true
settings_window.set_winning_score(5)
settings_window.set_selected_server(4)
next_button.pressed.connect(play_next_song)
show_answer_button.pressed.connect(show_answer_pressed)
sound_test_button.pressed.connect(music_player_container.get_sound_test_song)
settings_button.pressed.connect(show_settings)
reset_playlist_button.pressed.connect(reset_playlist)
reset_points_button.pressed.connect(reset_points)
song_controls.next_pressed.connect(play_next_song)
song_controls.show_answer_pressed.connect(show_answer_pressed)
toolbar.sound_test_pressed.connect(music_player_container.get_sound_test_song)
toolbar.settings_pressed.connect(show_settings)
toolbar.reset_playlist_pressed.connect(reset_playlist)
toolbar.reset_points_pressed.connect(reset_points)
song_loader.song_list_loaded.connect(update_song_list)
song_loader.song_added.connect(_on_song_added)
new_character_select.connect("new_new_character_selected", _on_new_character_selected)
music_list_panel.song_clicked.connect(song_object_clicked)
@@ -110,10 +94,9 @@ func _ready() -> void:
settings_window.visibility_changed.connect(_on_settings_window_visibility_changed)
open_button.pressed.connect(open)
auto_repeat_song_button.pressed.connect(repeat_song)
inspiration_list.load_from_server()
fetch_song_list_at_start()
song_loader.load_song_list()
new_character_select.load_characters()
@@ -140,18 +123,15 @@ func _input(event: InputEvent) -> void:
## Disables the buttons that use the song list while the server syncs it.
func lock_for_sync() -> void:
sound_test_button.disabled = true
reset_playlist_button.disabled = true
show_answer_button.disabled = true
next_button.disabled = true
toolbar.set_locked(true)
song_controls.set_locked(true)
## Reloads from the server after a sync and starts a new game.
func on_sync_finished() -> void:
refresh_from_server()
sound_test_button.disabled = false
reset_playlist_button.disabled = false
show_answer_button.disabled = false
toolbar.set_locked(false)
song_controls.set_locked(false)
reset_points()
@@ -170,41 +150,12 @@ func _on_server_changed() -> void:
server_changed.emit()
func repeat_song() -> void:
Settings.auto_repeat_song = !Settings.auto_repeat_song
func show_answer_pressed() -> void:
print("show_answer_pressed")
Playlist.song_is_answered()
update_song_list()
# Loads the songs already in the server playlist, then starts preloading new ones.
# Preloading waits for the list, because loading the list clears the playlist.
func fetch_song_list_at_start() -> void:
print("fetch_song_list_at_start")
var fetch_song_list_at_start_done: Callable = func fetch_songs_at_start_done(data) -> void:
Playlist.clear_song_list()
if typeof(data) == TYPE_ARRAY:
for d: Dictionary in data:
var new_song_object: SongObject = SongObject.new()
new_song_object.song_number = d.SongNo
new_song_object.soundtrack_title = d.Soundtrack
new_song_object.song_title = d.Song
new_song_object.is_answered = true
new_song_object.has_played = true
Playlist.add_to_playlist(new_song_object)
elif data != null:
print("Unexpected data")
Playlist.update_last_played_song()
update_song_list()
if !Playlist.is_song_downloading:
fetch_song()
MusicServerClient.fetch_song_list(fetch_song_list_at_start_done,
_retry_later.bind(fetch_song_list_at_start))
func play_next_song() -> void:
print("play_next_song ", Playlist.latest_played_song)
if Playlist.first_song_played:
@@ -212,9 +163,8 @@ func play_next_song() -> void:
Playlist.first_song_played = true
music_player_container.play_song_object(Playlist.latest_played_song)
if !(Playlist.get_preloaded_songs() > 0):
next_button.disabled = true
if !Playlist.is_song_downloading:
fetch_song()
song_controls.set_next_enabled(false)
song_loader.fetch_song_if_idle()
func _popups() -> Array[PopupWindow]:
@@ -224,63 +174,21 @@ func _popups() -> Array[PopupWindow]:
# The cache size can be changed in the settings. Waiting until the settings are closed
# avoids starting downloads while the user is still clicking the number up and down.
func _on_settings_window_visibility_changed() -> void:
if !settings_window.visible && !Playlist.is_song_downloading && Playlist.should_preload_more_songs():
fetch_song()
if !settings_window.visible:
song_loader.fill_cache()
func fetch_song() -> void:
print("fetch_song")
Playlist.is_song_downloading = true
_download_random_song(SongObject.new())
# Fetching a song takes three requests. If one fails, only that step is retried, so
# the server and the game keep the same songs in their playlists.
func _download_random_song(new_song_object: SongObject) -> void:
var song_downloaded: Callable = func song_downloaded(body: PackedByteArray) -> void:
print("song_downloaded")
var sound: AudioStream = AudioStreamMP3.new()
sound.data = body
new_song_object.song = sound
_add_song_to_queue(new_song_object)
MusicServerClient.fetch_random_song(Settings.use_low_played_mode, song_downloaded,
_retry_later.bind(_download_random_song.bind(new_song_object)))
func _add_song_to_queue(new_song_object: SongObject) -> void:
MusicServerClient.add_to_queue(_fetch_song_info.bind(new_song_object),
_retry_later.bind(_add_song_to_queue.bind(new_song_object)))
func _fetch_song_info(new_song_object: SongObject) -> void:
var song_info_fetched: Callable = func song_info_fetched(data_received: Dictionary) -> void:
print("song_info_fetched")
new_song_object.song_number = data_received.SongNo
new_song_object.soundtrack_title = data_received.Soundtrack
new_song_object.song_title = data_received.Song
print("new_song_object: ", new_song_object.to_string())
Playlist.add_to_playlist(new_song_object)
next_button.disabled = false
if Playlist.should_preload_more_songs():
fetch_song()
else:
Playlist.is_song_downloading = false
if Settings.is_debug:
update_song_list()
MusicServerClient.fetch_song_info(song_info_fetched,
_retry_later.bind(_fetch_song_info.bind(new_song_object)))
func _retry_later(step: Callable) -> void:
print("Song download failed, retrying in %s seconds" % song_fetch_retry_seconds)
get_tree().create_timer(song_fetch_retry_seconds).timeout.connect(step)
func _on_song_added() -> void:
song_controls.set_next_enabled(true)
if Settings.is_debug:
update_song_list()
func update_song_list() -> void:
print("update_song_list")
if Playlist.currently_playing_song >= 0:
soundtrack_label.text = Playlist.get_current_soundtrack_title()
song_label.text = Playlist.get_current_song_title()
now_playing.show_song(Playlist.get_current_soundtrack_title(),
Playlist.get_current_song_title())
music_list_panel.refresh()
@@ -304,7 +212,7 @@ func reset_playlist() -> void:
print("reset_playlist")
var playlist_reset_done: Callable = func playlist_reset_done() -> void:
Playlist.reset_playlist()
fetch_song_list_at_start()
song_loader.load_song_list()
MusicServerClient.reset_playlist(playlist_reset_done)