Files
MusicPlayer/quiz.gd
T
Sansan e94c75e84d 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.
2026-10-06 11:11:18 +02:00

326 lines
10 KiB
GDScript

class_name Quiz
extends Control
## The quiz: the song that is playing, the playlist, the players and their points, and
## the popups opened from here (settings, character select, winner and log).
##
## The main window shows the quiz below its top bar. Pictures and buttons keep the
## positions they had in the main window, so the quiz fills the whole window and lets
## clicks through where it has nothing.
## Emitted when another MusicServer is picked in the settings, after the quiz has
## reloaded from it.
signal server_changed
@onready
var open_button: Button = $Open
@onready
var fileDialog: FileDialog = $FileDialog
@onready
var inspiration_list: InspirationList = $InspirationList
@onready
var now_playing: NowPlaying = $NowPlaying
@onready
var players_panel: PlayersPanel = $PlayersPanel
@onready
var toolbar: QuizToolbar = $QuizToolbar
@onready
var settings_window: Control = $SettingsWindow
@onready
var winner_window: Control = %WinnerWindow
@onready
var song_controls: SongControls = $SongControls
@onready
var song_loader: SongLoader = $SongLoader
@onready
var music_list_panel: MusicListPanel = $MusicListPanel
@onready
var new_character_select: Control = %NewCharacterSelect
@onready
var music_player_container: MusicPlayer = $MusicPlayer
@onready
var log_window: Control = %Log
@onready
var debug_label: Label = $DebugLabel
var songs: Array= []
var current_player: Node
## True while statistics are off because a player won, until points are reset.
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
func _ready() -> void:
popup_backdrop = PopupBackdrop.add_to(self, _popups())
if OS.has_feature("debug"):
print("is_debug")
debug_label.visible = true
Settings.is_debug = true
settings_window.set_winning_score(5)
settings_window.set_selected_server(4)
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)
players_panel.player_added.connect(_on_player_added)
players_panel.player_removed.connect(_on_player_removed)
players_panel.change_character_requested.connect(_on_player_change_character_clicked)
music_player_container.connect("play_next_song", play_next_song)
music_player_container.connect("update_song_list", update_song_list)
settings_window.connect("server_changed", _on_server_changed)
settings_window.visibility_changed.connect(_on_settings_window_visibility_changed)
open_button.pressed.connect(open)
inspiration_list.load_from_server()
song_loader.load_song_list()
new_character_select.load_characters()
func _input(event: InputEvent) -> void:
if event is InputEventKey and event.pressed:
if event.alt_pressed && event.keycode == KEY_UP:
print("Alt + UP pressed")
music_player_container.change_volume(0.05)
if event.alt_pressed && event.keycode == KEY_DOWN:
print("Alt + DOWN pressed")
music_player_container.change_volume(-0.05)
if event.alt_pressed && event.ctrl_pressed && event.keycode == KEY_LEFT:
print("Ctrl + Alt + LEFT pressed")
music_player_container.restart()
elif event.alt_pressed && event.keycode == KEY_LEFT:
print("Alt + LEFT pressed")
music_player_container.seek(-5)
if event.alt_pressed && event.keycode == KEY_RIGHT:
print("Alt + RIGHT pressed")
music_player_container.seek(5)
if event.alt_pressed && event.keycode == KEY_L:
log_window.toggle()
## Disables the buttons that use the song list while the server syncs it.
func lock_for_sync() -> void:
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()
toolbar.set_locked(false)
song_controls.set_locked(false)
reset_points()
## Reloads the soundtracks, playlist and characters from the server.
func refresh_from_server() -> void:
print("refresh_from_server")
inspiration_list.load_from_server()
reset_playlist()
new_character_select.load_characters()
func _on_server_changed() -> void:
print("_on_server_changed")
refresh_from_server()
reset_points()
server_changed.emit()
func show_answer_pressed() -> void:
print("show_answer_pressed")
Playlist.song_is_answered()
update_song_list()
func play_next_song() -> void:
print("play_next_song ", Playlist.latest_played_song)
if Playlist.first_song_played:
Playlist.set_latest_played_song()
Playlist.first_song_played = true
music_player_container.play_song_object(Playlist.latest_played_song)
if !(Playlist.get_preloaded_songs() > 0):
song_controls.set_next_enabled(false)
song_loader.fetch_song_if_idle()
func _popups() -> Array[PopupWindow]:
return [settings_window, new_character_select, winner_window, log_window]
# 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:
song_loader.fill_cache()
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:
now_playing.show_song(Playlist.get_current_soundtrack_title(),
Playlist.get_current_song_title())
music_list_panel.refresh()
func song_object_clicked(song_no: int) -> void:
print("Clicked Song Object: ", Playlist.song_object_array[song_no].to_string())
if Settings.character_select_open == false:
if Playlist.get_song(song_no) == null:
print("has no song, need to download")
var song_downloaded: Callable = func song_downloaded(body: PackedByteArray) -> void:
var sound: AudioStream = AudioStreamMP3.new()
sound.data = body
Playlist.add_song_to_song_object(song_no, sound)
music_player_container.play_song_object(song_no)
MusicServerClient.fetch_song(song_no, song_downloaded)
else:
print("song already downloaded")
music_player_container.play_song_object(song_no)
func reset_playlist() -> void:
print("reset_playlist")
var playlist_reset_done: Callable = func playlist_reset_done() -> void:
Playlist.reset_playlist()
song_loader.load_song_list()
MusicServerClient.reset_playlist(playlist_reset_done)
func reset_points() -> void:
print("reset_points")
for player_objects: PlayerObject in Settings.player_array:
player_objects.reset_points()
players_panel.reload()
# A new game starts, so turn statistics back on if winning turned them off
if stats_paused_by_win:
Settings.add_to_stats = true
stats_paused_by_win = false
func show_settings() -> void:
print("show_settings")
settings_window.open()
# Each new player's points reach the music player, the log and the winner window.
func _on_player_added(new_player_object: PlayerObject) -> void:
new_player_object.connect("first_point_triggerd", music_player_container._on_point_triggered.bind("first"))
new_player_object.connect("match_point_triggerd", music_player_container._on_point_triggered.bind("match"))
new_player_object.connect("winner_triggerd", _on_player_won.bind(new_player_object))
new_player_object.connect("point_given_sound", _on_make_point_given_sound)
new_player_object.connect("player_point_given", _on_point_given.bind(new_player_object))
new_player_object.connect("player_point_taken", _on_point_taken.bind(new_player_object))
func _on_point_given(player_object: PlayerObject) -> void:
print("_on_point_given")
log_window.add_log_row(player_object.player_name + " got a point")
if Playlist.currently_playing_song >= 0:
Playlist.add_point(player_object.id)
update_song_list()
func _on_make_point_given_sound() -> void:
music_player_container.play_sound_effect(preload("res://sounds/itemopen.wav"))
func _on_point_taken(player_object: PlayerObject) -> void:
print("_on_point_taken")
log_window.add_log_row(player_object.player_name + " lost a point")
music_player_container.play_sound_effect(preload("res://sounds/itemequip.wav"))
if Playlist.currently_playing_song >= 0:
Playlist.remove_point(player_object.id)
update_song_list()
func _on_player_won(player_object: PlayerObject) -> void:
print("_on_player_won")
winner_window.show_winner(player_object)
music_player_container.play_song(preload("res://sounds/winning.mp3"))
# Songs played after the game is won don't count towards the statistics
if Settings.add_to_stats:
Settings.add_to_stats = false
stats_paused_by_win = true
func _on_player_removed(player_object: PlayerObject) -> void:
Playlist.remove_player_from_list(player_object.id)
update_song_list()
func _on_player_change_character_clicked(new_player: Player, player_name: String) -> void:
print("_on_player_change_character_clicked")
current_player = new_player
print("Change character for: " + player_name)
new_character_select.show_grid(player_name)
func _on_new_character_selected(character_texture: ImageTexture) -> void:
print("_on_new_character_selected")
current_player.new_character_selected(character_texture)
###Local
var local_path: String = '/Users/sebastian/ResilioSync/Sorterat_test/Metal Gear Solid 4 - Guns of the Patriots/2-16 Metal Gear Saga.mp3'
func open() -> void:
fileDialog.popup()
func _on_file_dialog_dir_selected(path: String) -> void:
print(path)
dir_contents(path)
func dir_contents(path: String) -> void:
var dir: DirAccess = DirAccess.open(path)
if dir:
dir.list_dir_begin()
var file_name: String = dir.get_next()
songs.clear()
var soundtracks: Array = []
while file_name != "":
if dir.current_is_dir():
#print("Found directory: " + file_name)
soundtracks.append(file_name)
else:
#print("Found file: " + file_name)
if file_name.ends_with(".mp3"):
songs.append(path + "/" + file_name)
file_name = dir.get_next()
inspiration_list.show_soundtracks(soundtracks)
else:
print("An error occurred when trying to access the path.")