Follow the GDScript style guide in every script

- Types on every parameter, return value and variable, and snake_case names
  (the player name limit is the constant MAX_NAME_LENGTH).
- Spelling fixes: *_triggered, character_selected, separator, caught_errors.
  The server's catched_errors key stays as the server sends it.
- Signals are connected with signal.connect() instead of by name.
- Code is ordered signals, constants, exported vars, vars, @onready vars,
  functions, with @onready on one line and two blank lines between functions.
- Every script starts with a ## description. Template comments, old LOC notes
  and commented-out code are gone, apart from the local play code.
- Lines are at most 100 characters, apart from the local play path.
- The screenshot tour turns off the blinking text cursor so pictures match.
This commit is contained in:
Sansan committed 2026-10-06 11:53:26 +02:00
1 parent 674604425f
commit a0428e3475
45 files changed
+580 -589

No files matched your search

+2 -2
View File
@@ -145,13 +145,13 @@ text = "SoundtracksRemoved"
unique_name_in_owner = true unique_name_in_owner = true
layout_mode = 2 layout_mode = 2
[node name="CatchedErrorsVBox" type="VBoxContainer" parent="SyncPanel/ScrollContainer/SyncVBoxContainer" unique_id=63733711] [node name="CaughtErrorsVBox" type="VBoxContainer" parent="SyncPanel/ScrollContainer/SyncVBoxContainer" unique_id=63733711]
unique_name_in_owner = true unique_name_in_owner = true
visible = false visible = false
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
[node name="CatchedErrorsLabel" type="Label" parent="SyncPanel/ScrollContainer/SyncVBoxContainer/CatchedErrorsVBox" unique_id=1784389644] [node name="CaughtErrorsLabel" type="Label" parent="SyncPanel/ScrollContainer/SyncVBoxContainer/CaughtErrorsVBox" unique_id=1784389644]
layout_mode = 2 layout_mode = 2
text = "CatchedErrors" text = "CatchedErrors"
horizontal_alignment = 1 horizontal_alignment = 1
+8 -14
View File
@@ -1,18 +1,8 @@
class_name AboutPopup class_name AboutPopup
extends BasePopup extends BasePopup
## Shows the game version and what is new in each version.
@onready ## What is new in each version, newest first.
var about_panel: Panel = %AboutPanel
@onready
var version_label: Label = %VersionLabel
@onready
var whats_new_label: Label = %WhatsNewLabel
# Changelog as Dictionary with version as key and description as value
const WHATS_NEW: Dictionary = { const WHATS_NEW: Dictionary = {
"1.9.0": "Made improvements to the Search window "1.9.0": "Made improvements to the Search window
Made small changes to the inspiration list scrolling Made small changes to the inspiration list scrolling
@@ -51,15 +41,19 @@ const WHATS_NEW: Dictionary = {
"0.7-Beta": "Can now hop between songs" "0.7-Beta": "Can now hop between songs"
} }
@onready var about_panel: Panel = %AboutPanel
@onready var version_label: Label = %VersionLabel
@onready var whats_new_label: Label = %WhatsNewLabel
func open() -> void: func open() -> void:
super() super()
version_label.text = Settings.version version_label.text = Settings.version
# Build changelog text with version headers # Build changelog text with version headers
var changelog_text: String = "" var changelog_text: String = ""
for version: String in WHATS_NEW.keys(): for version: String in WHATS_NEW.keys():
changelog_text += "\n" + version + "\n" changelog_text += "\n" + version + "\n"
changelog_text += WHATS_NEW[version] + "\n" changelog_text += WHATS_NEW[version] + "\n"
whats_new_label.text = changelog_text whats_new_label.text = changelog_text
+1
View File
@@ -6,6 +6,7 @@ extends Control
## override [method open] or [method close] and call [code]super()[/code]. ## override [method open] or [method close] and call [code]super()[/code].
## The visible part of the popup. Clicks outside it close the popup. ## The visible part of the popup. Clicks outside it close the popup.
@export var panel: Control @export var panel: Control
+19 -18
View File
@@ -1,21 +1,30 @@
class_name CharacterCard class_name CharacterCard
extends Control extends Control
## One character in the character select popup: its picture and name. Clicking it picks
## the character.
@onready ## Emitted with the character's picture when the card is clicked.
var panel: Panel = $Panel
@onready
var character_image: TextureRect = %CharacterTextureRect
@onready
var character_name_label: Label = %CharacterName
signal new_character_selected(img_texture: ImageTexture) signal new_character_selected(img_texture: ImageTexture)
@onready var panel: Panel = $Panel
@onready var character_image: TextureRect = %CharacterTextureRect
@onready var character_name_label: Label = %CharacterName
func _input(event: InputEvent) -> void:
if visible && Settings.character_select_open:
if event is InputEventMouseButton && event.is_pressed() \
&& event.get_button_index() == MOUSE_BUTTON_LEFT:
var local_event: InputEvent = make_input_local(event)
if Rect2(Vector2(0, 0), panel.size).has_point(local_event.position):
GameLog.debug(character_name_label.text)
new_character_selected.emit(character_image.texture)
## Shows [param character_name] and fetches its picture from the server.
func set_character_name(character_name: String) -> void: func set_character_name(character_name: String) -> void:
panel.size = Vector2(240, 145) panel.size = Vector2(240, 145)
#margin.size = Vector2(240, 145)
#margin set margin 15, 10, 15, 0
character_name_label.text = character_name.split(".")[0] character_name_label.text = character_name.split(".")[0]
character_name_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER character_name_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
var image_fetched: Callable = func image_fetched(image: PackedByteArray) -> void: var image_fetched: Callable = func image_fetched(image: PackedByteArray) -> void:
@@ -37,11 +46,3 @@ func set_character_name(character_name: String) -> void:
character_image.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED character_image.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
character_image.texture = new_image character_image.texture = new_image
MusicServerClient.fetch_character_image(character_name, image_fetched) MusicServerClient.fetch_character_image(character_name, image_fetched)
func _input(event: InputEvent) -> void:
if self.visible == true && Settings.character_select_open == true:
if event is InputEventMouseButton && event.is_pressed() && event.get_button_index() == MOUSE_BUTTON_LEFT:
var evLocal: InputEvent = make_input_local(event)
if Rect2(Vector2(0, 0), panel.size).has_point(evLocal.position):
GameLog.debug(character_name_label.text)
new_character_selected.emit(character_image.texture)
+17 -18
View File
@@ -1,35 +1,31 @@
class_name CharacterSelectPopup class_name CharacterSelectPopup
extends BasePopup extends BasePopup
@onready ## Emitted with the picture of the character that was picked.
var character_panel: Panel = %CharacterPanel signal character_selected(texture: ImageTexture)
@onready
var title_label: Label = %TitleLabel
@onready
var character_grid: GridContainer = %CharacterGridContainer
@onready
var close_timer: Timer = %CloseTimer
var can_select_character: bool = false var can_select_character: bool = false
signal new_new_character_selected(file_name: String)
var character: PackedScene = preload("res://CharacterCard.tscn") var character: PackedScene = preload("res://CharacterCard.tscn")
@onready var character_panel: Panel = %CharacterPanel
@onready var title_label: Label = %TitleLabel
@onready var character_grid: GridContainer = %CharacterGridContainer
@onready var close_timer: Timer = %CloseTimer
func _ready() -> void: func _ready() -> void:
close_timer.timeout.connect(_on_close_timeout) close_timer.timeout.connect(_on_close_timeout)
func _on_close_timeout() -> void: func _on_close_timeout() -> void:
GameLog.debug("_on_close_timeout!") GameLog.debug("_on_close_timeout!")
Settings.character_select_open = false Settings.character_select_open = false
close_timer.stop() close_timer.stop()
func load_characters() -> void: func load_characters() -> void:
GameLog.debug("load_characters") GameLog.debug("load_characters")
var fetch_character_list: Callable = func fetch_character_list(list) -> void: var fetch_character_list: Callable = func fetch_character_list(list: Variant) -> void:
GameLog.debug("fetch_character_list") GameLog.debug("fetch_character_list")
Settings.delete_children(character_grid) Settings.delete_children(character_grid)
if typeof(list) == TYPE_ARRAY: if typeof(list) == TYPE_ARRAY:
@@ -40,9 +36,10 @@ func load_characters() -> void:
character_box.custom_minimum_size = Vector2(235, 145) character_box.custom_minimum_size = Vector2(235, 145)
character_box.size = Vector2(235, 145) character_box.size = Vector2(235, 145)
character_box.set_character_name(character_name) character_box.set_character_name(character_name)
character_box.connect("new_character_selected", _on_character_selected) character_box.new_character_selected.connect(_on_character_selected)
MusicServerClient.fetch_character_list(fetch_character_list) MusicServerClient.fetch_character_list(fetch_character_list)
func show_grid(player_name: String) -> void: func show_grid(player_name: String) -> void:
GameLog.debug("show_grid") GameLog.debug("show_grid")
title_label.text = "Select character for " + player_name title_label.text = "Select character for " + player_name
@@ -51,16 +48,18 @@ func show_grid(player_name: String) -> void:
can_select_character = true can_select_character = true
Settings.character_select_open = true Settings.character_select_open = true
func close() -> void: func close() -> void:
super() super()
Settings.character_select_open = false Settings.character_select_open = false
func _on_character_selected(texture: ImageTexture) -> void: func _on_character_selected(texture: ImageTexture) -> void:
GameLog.debug("_on_character_selected") GameLog.debug("_on_character_selected")
# Not close(): character_select_open stays true until close_timer fires, so the click # Not close(): character_select_open stays true until close_timer fires, so the click
# that picked the character does not also select a song behind it # that picked the character does not also select a song behind it
self.visible = false visible = false
if can_select_character: if can_select_character:
new_new_character_selected.emit(texture) character_selected.emit(texture)
close_timer.start() close_timer.start()
can_select_character = false can_select_character = false
+1 -1
View File
@@ -6,8 +6,8 @@ extends RefCounted
## Seconds before a request is given up and reported as failed. This covers the whole ## Seconds before a request is given up and reported as failed. This covers the whole
## download, so it is generous enough for long songs on a slow connection. ## download, so it is generous enough for long songs on a slow connection.
var timeout_seconds: float = 120.0
var timeout_seconds: float = 120.0
var _owner: Node var _owner: Node
+10 -9
View File
@@ -4,35 +4,36 @@ extends ScrollContainer
## by itself, at the speed set in the settings. ## by itself, at the speed set in the settings.
## The soundtracks shown, in order. ## The soundtracks shown, in order.
var soundtracks: Array = [] var soundtracks: Array = []
# Scroll direction: 1 = down, -1 = up # Scroll direction: 1 = down, -1 = up
var scroll_direction: int = 1 var scroll_direction: int = 1
@onready @onready var _rows: VBoxContainer = $VBoxContainer
var _rows: VBoxContainer = $VBoxContainer
# Called when the node enters the scene tree for the first time.
func _ready() -> void: func _ready() -> void:
scroll_direction = 1 scroll_direction = 1
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void: func _process(delta: float) -> void:
var speed_pixels_per_second: float = float(Settings.inspiration_list_speed) * 60.0 var speed_pixels_per_second: float = float(Settings.inspiration_list_speed) * 60.0
var scroll_amount: float = speed_pixels_per_second * delta * float(scroll_direction) var scroll_amount: float = speed_pixels_per_second * delta * float(scroll_direction)
var scroll_bar = get_v_scroll_bar() var scroll_bar: VScrollBar = get_v_scroll_bar()
# Don't scroll if list is empty or too short # Don't scroll if list is empty or too short
if scroll_bar.max_value <= 0: if scroll_bar.max_value <= 0:
return return
var current_pos = scroll_bar.value var current_pos: float = scroll_bar.value
if scroll_direction == 1: if scroll_direction == 1:
# Scrolling down # Scrolling down
if current_pos + scroll_bar.page < scroll_bar.max_value: if current_pos + scroll_bar.page < scroll_bar.max_value:
scroll_bar.value = min(current_pos + scroll_amount, scroll_bar.max_value - scroll_bar.page) scroll_bar.value = minf(current_pos + scroll_amount,
scroll_bar.max_value - scroll_bar.page)
else: else:
# Reached bottom, reverse direction # Reached bottom, reverse direction
scroll_direction = -1 scroll_direction = -1
@@ -40,7 +41,7 @@ func _process(delta: float) -> void:
else: else:
# Scrolling up # Scrolling up
if current_pos > 0: if current_pos > 0:
scroll_bar.value = max(current_pos + scroll_amount, 0) scroll_bar.value = maxf(current_pos + scroll_amount, 0)
else: else:
# Reached top, reverse direction # Reached top, reverse direction
scroll_direction = 1 scroll_direction = 1
@@ -50,7 +51,7 @@ func _process(delta: float) -> void:
## Fetches every soundtrack from the server and shows them. ## Fetches every soundtrack from the server and shows them.
func load_from_server() -> void: func load_from_server() -> void:
GameLog.debug("load_from_server") GameLog.debug("load_from_server")
var populate_list: Callable = func populate_list(array) -> void: var populate_list: Callable = func populate_list(array: Variant) -> void:
if typeof(array) == TYPE_ARRAY: if typeof(array) == TYPE_ARRAY:
show_soundtracks(array) show_soundtracks(array)
else: else:
+2
View File
@@ -16,6 +16,8 @@ var label: Label
## The item's text. ## The item's text.
var text: String: var text: String:
get: get:
return label.text return label.text
+8 -6
View File
@@ -1,19 +1,20 @@
class_name LogPopup class_name LogPopup
extends BasePopup extends BasePopup
## The game log: a row with the time for each thing that happens in the quiz, such as a
@onready ## player getting a point. Opened with Alt+L.
var log_panel: Panel = %LogPanel
@onready
var log_box: VBoxContainer = %LogVBoxContainer
var log_rows: Array var log_rows: Array
@onready var log_panel: Panel = %LogPanel
@onready var log_box: VBoxContainer = %LogVBoxContainer
func add_log_row(text: String) -> void: func add_log_row(text: String) -> void:
var log_row: String = get_time() + text var log_row: String = get_time() + text
log_rows.append(log_row) log_rows.append(log_row)
log_box.add_child(make_log_label(log_row)) log_box.add_child(make_log_label(log_row))
func make_log_label(row: String) -> Label: func make_log_label(row: String) -> Label:
var log_label: Label = Label.new() var log_label: Label = Label.new()
log_label.add_theme_font_size_override("font_size", 20) log_label.add_theme_font_size_override("font_size", 20)
@@ -21,6 +22,7 @@ func make_log_label(row: String) -> Label:
log_label.autowrap_mode = TextServer.AUTOWRAP_WORD log_label.autowrap_mode = TextServer.AUTOWRAP_WORD
return log_label return log_label
func get_time() -> String: func get_time() -> String:
var time: Dictionary = Time.get_time_dict_from_system() var time: Dictionary = Time.get_time_dict_from_system()
return "%02d:%02d:%02d " % [time.hour, time.minute, time.second] return "%02d:%02d:%02d " % [time.hour, time.minute, time.second]
+15 -34
View File
@@ -5,39 +5,20 @@ extends Control
## Everything that is not part of a quiz lives here, so other views can be added next ## Everything that is not part of a quiz lives here, so other views can be added next
## to the quiz later. ## to the quiz later.
@onready
var top_bar: TopBar = $TopBar
@onready
var quiz: Quiz = $Quiz
@onready
var search_popup: SearchPopup = $SearchPopup
@onready
var sync_popup: SyncPopup = %SyncPopup
@onready
var shortcuts_popup: ShortcutsPopup = %ShortcutsPopup
@onready
var qr_popup: QRPopup = %QRPopup
@onready
var about_popup: AboutPopup = %AboutPopup
@onready
var log_popup: LogPopup = %LogPopup
@onready
var statistic_popup: PopupPanel = $StatisticsPopupPanel
@onready
var statistic_label: Label = %StatisticsLabel
## Dims the window behind an open popup and keeps clicks from reaching the buttons under it. ## Dims the window behind an open popup and keeps clicks from reaching the buttons under it.
var popup_backdrop: PopupBackdrop var popup_backdrop: PopupBackdrop
@onready var top_bar: TopBar = $TopBar
@onready var quiz: Quiz = $Quiz
@onready var search_popup: SearchPopup = $SearchPopup
@onready var sync_popup: SyncPopup = %SyncPopup
@onready var shortcuts_popup: ShortcutsPopup = %ShortcutsPopup
@onready var qr_popup: QRPopup = %QRPopup
@onready var about_popup: AboutPopup = %AboutPopup
@onready var log_popup: LogPopup = %LogPopup
@onready var statistic_popup: PopupPanel = $StatisticsPopupPanel
@onready var statistic_label: Label = %StatisticsLabel
func _ready() -> void: func _ready() -> void:
popup_backdrop = PopupBackdrop.add_to(self, _popups()) popup_backdrop = PopupBackdrop.add_to(self, _popups())
@@ -50,8 +31,8 @@ func _ready() -> void:
top_bar.about_pressed.connect(show_about) top_bar.about_pressed.connect(show_about)
top_bar.qr_pressed.connect(show_qr) top_bar.qr_pressed.connect(show_qr)
sync_popup.connect("sync_started", _on_sync_started) sync_popup.sync_started.connect(_on_sync_started)
sync_popup.connect("sync_finished", _on_sync_finished) sync_popup.sync_finished.connect(_on_sync_finished)
quiz.server_changed.connect(search_popup.get_list_of_soundtracks) quiz.server_changed.connect(search_popup.get_list_of_soundtracks)
quiz.event_logged.connect(log_popup.add_log_row) quiz.event_logged.connect(log_popup.add_log_row)
@@ -63,9 +44,9 @@ func _input(event: InputEvent) -> void:
if event.alt_pressed && event.keycode == KEY_ENTER: if event.alt_pressed && event.keycode == KEY_ENTER:
GameLog.debug("Alt + Enter pressed") GameLog.debug("Alt + Enter pressed")
Settings.fullscreen = !Settings.fullscreen Settings.fullscreen = !Settings.fullscreen
if Settings.fullscreen == true: if Settings.fullscreen:
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN) DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
if Settings.fullscreen == false: if !Settings.fullscreen:
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
if event.alt_pressed && event.keycode == KEY_K: if event.alt_pressed && event.keycode == KEY_K:
shortcuts_popup.toggle() shortcuts_popup.toggle()
+2 -2
View File
@@ -3,12 +3,12 @@ extends PanelContainer
## The list of songs in the playlist, one row per song. ## The list of songs in the playlist, one row per song.
## Emitted when the row of song number [param song_no] is clicked. ## Emitted when the row of song number [param song_no] is clicked.
signal song_clicked(song_no: int) signal song_clicked(song_no: int)
var _song_list_object: PackedScene = preload("res://SongRow.tscn") var _song_list_object: PackedScene = preload("res://SongRow.tscn")
@onready @onready var _rows: VBoxContainer = %MusicList
var _rows: VBoxContainer = %MusicList
## Shows the songs in the playlist again, so it shows their latest state. ## Shows the songs in the playlist again, so it shows their latest state.
+41 -43
View File
@@ -3,76 +3,59 @@ extends Control
## The bar that plays the songs, with play, restart, progress and volume, and the ## The bar that plays the songs, with play, restart, progress and volume, and the
## auto repeat switch under it. ## auto repeat switch under it.
# LOC 20/1-24: 136 signal update_song_list
# LOC 12/6-24: 170 signal play_next_song
@onready
var play_button: TextureButton = %PlayTextureButton
@onready
var restart_button: TextureButton = %RestartTextureButton
@onready
var audio_player: AudioStreamPlayer = $AudioStreamPlayer
@onready
var sound_player: AudioStreamPlayer = $AudioSoundPlayer
@onready
var sound_effect_player: AudioStreamPlayer = $AudioSoundEffectPlayer
@onready
var progress_slider: HSlider = %MusicPlayerSlider
@onready
var volume_slider: VolumeSlider = %VolumeSlider
@onready
var music_time_label: Label = %MusicTimeLabel
@onready
var auto_repeat_button: CheckButton = $RepeatSongCheckButton
var play_icon: Texture = preload("res://icons/play_icon_light.svg") var play_icon: Texture = preload("res://icons/play_icon_light.svg")
var pause_icon: Texture = preload("res://icons/pause_icon_light.svg") var pause_icon: Texture = preload("res://icons/pause_icon_light.svg")
var songs: Array = [] var songs: Array = []
var is_changing: bool = false var is_changing: bool = false
var playback_position: float var playback_position: float
var stream: AudioStream var stream: AudioStream
var song_finished: bool = false var song_finished: bool = false
signal update_song_list @onready var play_button: TextureButton = %PlayTextureButton
signal play_next_song @onready var restart_button: TextureButton = %RestartTextureButton
@onready var audio_player: AudioStreamPlayer = $AudioStreamPlayer
@onready var sound_player: AudioStreamPlayer = $AudioSoundPlayer
@onready var sound_effect_player: AudioStreamPlayer = $AudioSoundEffectPlayer
@onready var progress_slider: HSlider = %MusicPlayerSlider
@onready var volume_slider: VolumeSlider = %VolumeSlider
@onready var music_time_label: Label = %MusicTimeLabel
@onready var auto_repeat_button: CheckButton = $RepeatSongCheckButton
# Called when the node enters the scene tree for the first time.
func _ready() -> void: func _ready() -> void:
play_button.pressed.connect(play_or_pause) play_button.pressed.connect(play_or_pause)
restart_button.pressed.connect(restart) restart_button.pressed.connect(restart)
progress_slider.drag_started.connect(_on_drag_started) progress_slider.drag_started.connect(_on_drag_started)
progress_slider.drag_ended.connect(_on_drag_ended) progress_slider.drag_ended.connect(_on_drag_ended)
audio_player.finished.connect(_on_finished) audio_player.finished.connect(_on_finished)
auto_repeat_button.pressed.connect(_on_auto_repeat_pressed) auto_repeat_button.pressed.connect(_on_auto_repeat_pressed)
func _on_auto_repeat_pressed() -> void: func _on_auto_repeat_pressed() -> void:
Settings.auto_repeat_song = !Settings.auto_repeat_song Settings.auto_repeat_song = !Settings.auto_repeat_song
func _process(_delta: float) -> void: func _process(_delta: float) -> void:
if audio_player.has_stream_playback() && !is_changing && !audio_player.stream_paused: if audio_player.has_stream_playback() && !is_changing && !audio_player.stream_paused:
if (!Settings.hide_beginning || audio_player.get_playback_position() > Settings.hide_for_seconds): if !Settings.hide_beginning \
|| audio_player.get_playback_position() > Settings.hide_for_seconds:
progress_slider.value = audio_player.get_playback_position() progress_slider.value = audio_player.get_playback_position()
if stream != null: if stream != null:
music_time_label.text = format_text(progress_slider.value, stream.get_length()) music_time_label.text = format_text(progress_slider.value, stream.get_length())
else: else:
music_time_label.text = format_text(progress_slider.value, 0.0) music_time_label.text = format_text(progress_slider.value, 0.0)
func format_time(time: float) -> String: func format_time(time: float) -> String:
var total_seconds: int = roundi(time) var total_seconds: int = roundi(time)
@warning_ignore("integer_division") @warning_ignore("integer_division")
return "%02d:%02d" % [total_seconds / 60, total_seconds % 60] return "%02d:%02d" % [total_seconds / 60, total_seconds % 60]
func format_text(part: float, total: float) -> String: func format_text(part: float, total: float) -> String:
if (Settings.hide_beginning && part < Settings.hide_for_seconds) && Settings.hide_length: if (Settings.hide_beginning && part < Settings.hide_for_seconds) && Settings.hide_length:
return "??:?? / ??:??" return "??:?? / ??:??"
@@ -83,6 +66,7 @@ func format_text(part: float, total: float) -> String:
else: else:
return format_time(part) + " / " + format_time(total) return format_time(part) + " / " + format_time(total)
func play_or_pause() -> void: func play_or_pause() -> void:
if song_finished: if song_finished:
restart() restart()
@@ -100,11 +84,13 @@ func play_or_pause() -> void:
else: else:
pause() pause()
func pause() -> void: func pause() -> void:
audio_player.stream_paused = true audio_player.stream_paused = true
playback_position = audio_player.get_playback_position() playback_position = audio_player.get_playback_position()
play_button.texture_normal = play_icon play_button.texture_normal = play_icon
func restart() -> void: func restart() -> void:
audio_player.stop() audio_player.stop()
audio_player.stream_paused = false audio_player.stream_paused = false
@@ -114,7 +100,8 @@ func restart() -> void:
audio_player.seek(playback_position) audio_player.seek(playback_position)
play_button.texture_normal = pause_icon play_button.texture_normal = pause_icon
audio_player.play() audio_player.play()
func _on_finished() -> void: func _on_finished() -> void:
play_button.texture_normal = play_icon play_button.texture_normal = play_icon
song_finished = true song_finished = true
@@ -122,26 +109,31 @@ func _on_finished() -> void:
play_next_song.emit() play_next_song.emit()
if Settings.auto_repeat_song: if Settings.auto_repeat_song:
restart() restart()
func _on_drag_started() -> void: func _on_drag_started() -> void:
is_changing = true is_changing = true
func _on_drag_ended(_changed: bool) -> void: func _on_drag_ended(_changed: bool) -> void:
audio_player.seek(progress_slider.value) audio_player.seek(progress_slider.value)
playback_position = progress_slider.value playback_position = progress_slider.value
is_changing = false is_changing = false
func seek(new_position: float) -> void: func seek(new_position: float) -> void:
progress_slider.value += new_position progress_slider.value += new_position
is_changing = true is_changing = true
audio_player.seek(progress_slider.value) audio_player.seek(progress_slider.value)
playback_position = progress_slider.value playback_position = progress_slider.value
is_changing = false is_changing = false
func change_volume(value: float) -> void: func change_volume(value: float) -> void:
volume_slider.value += value volume_slider.value += value
volume_slider.change_volume(volume_slider.value) volume_slider.change_volume(volume_slider.value)
func _on_point_triggered(point: String) -> void: func _on_point_triggered(point: String) -> void:
if point == "first": if point == "first":
var value: int = randi_range(0, 10) var value: int = randi_range(0, 10)
@@ -153,11 +145,13 @@ func _on_point_triggered(point: String) -> void:
play_sound(preload("res://sounds/intro_short.mp3")) play_sound(preload("res://sounds/intro_short.mp3"))
elif point == "match": elif point == "match":
play_sound(preload("res://sounds/sound0.mp3")) play_sound(preload("res://sounds/sound0.mp3"))
func play_sound_effect(sound_effect_name: AudioStream) -> void: func play_sound_effect(sound_effect_name: AudioStream) -> void:
sound_effect_player.stream = sound_effect_name sound_effect_player.stream = sound_effect_name
sound_effect_player.play() sound_effect_player.play()
func play_sound(sound_name: AudioStream) -> void: func play_sound(sound_name: AudioStream) -> void:
sound_player.stream = sound_name sound_player.stream = sound_name
audio_player.stop() audio_player.stop()
@@ -165,9 +159,8 @@ func play_sound(sound_name: AudioStream) -> void:
song_finished = true song_finished = true
play_button.texture_normal = play_icon play_button.texture_normal = play_icon
progress_slider.value = 0 progress_slider.value = 0
#if stream != null:
# music_time_label.text = format_text(progress_slider.value, stream.get_length())
func play_song(song: AudioStream) -> void: func play_song(song: AudioStream) -> void:
audio_player.stream = song audio_player.stream = song
audio_player.play() audio_player.play()
@@ -183,6 +176,7 @@ func play_song(song: AudioStream) -> void:
progress_slider.tick_count = round(stream.get_length() / 60) progress_slider.tick_count = round(stream.get_length() / 60)
music_time_label.text = format_text(progress_slider.value, stream.get_length()) music_time_label.text = format_text(progress_slider.value, stream.get_length())
func play_song_object(song_object_no: int) -> void: func play_song_object(song_object_no: int) -> void:
GameLog.debug("play_song_object") GameLog.debug("play_song_object")
if audio_player.is_playing(): if audio_player.is_playing():
@@ -210,6 +204,7 @@ func play_song_object(song_object_no: int) -> void:
Playlist.song_is_playing(song_object_no) Playlist.song_is_playing(song_object_no)
update_song_list.emit() update_song_list.emit()
func get_sound_test_song() -> void: func get_sound_test_song() -> void:
var test_sound: Callable = func test_sound(body: PackedByteArray) -> void: var test_sound: Callable = func test_sound(body: PackedByteArray) -> void:
var sound: AudioStream = AudioStreamMP3.new() var sound: AudioStream = AudioStreamMP3.new()
@@ -220,12 +215,14 @@ func get_sound_test_song() -> void:
##### LOCAL ##### LOCAL
var local_path: String = '/Users/sebastian/ResilioSync/Sorterat_test/Metal Gear Solid 4 - Guns of the Patriots/2-16 Metal Gear Saga.mp3' var local_path: String = '/Users/sebastian/ResilioSync/Sorterat_test/Metal Gear Solid 4 - Guns of the Patriots/2-16 Metal Gear Saga.mp3'
func load_mp3(_path: String) -> AudioStream: func load_mp3(_path: String) -> AudioStream:
var file: FileAccess = FileAccess.open(_path, FileAccess.READ) var file: FileAccess = FileAccess.open(_path, FileAccess.READ)
var sound: AudioStream = AudioStreamMP3.new() var sound: AudioStream = AudioStreamMP3.new()
sound.data = file.get_buffer(file.get_length()) sound.data = file.get_buffer(file.get_length())
return sound return sound
func play_local_song() -> void: func play_local_song() -> void:
if songs: if songs:
local_path = songs[0] local_path = songs[0]
@@ -233,5 +230,6 @@ func play_local_song() -> void:
GameLog.debug(FileAccess.file_exists(local_path)) GameLog.debug(FileAccess.file_exists(local_path))
play_sound(load_mp3(local_path)) play_sound(load_mp3(local_path))
func sound_test_local() -> void: func sound_test_local() -> void:
play_sound(preload("res://sounds/01. Opening.mp3")) play_sound(preload("res://sounds/01. Opening.mp3"))
+1
View File
@@ -7,6 +7,7 @@ extends Node
## without a running server. ## without a running server.
## Sends the actual HTTP requests. Swap it out in tests. ## Sends the actual HTTP requests. Swap it out in tests.
var transport: HttpTransport var transport: HttpTransport
+22 -12
View File
@@ -1,19 +1,28 @@
class_name PlayerNameField class_name PlayerNameField
extends TextEdit extends TextEdit
## The field for a new player's name. It keeps to one line of at most
## [constant MAX_NAME_LENGTH] characters, and reports Enter so the player can be added.
## Emitted when Enter is pressed while the field has focus.
signal enter_key_pressed signal enter_key_pressed
## Emitted when Alt+A is pressed.
signal close_pressed signal close_pressed
@export ## The longest name a player can have.
var LIMIT: int = 10 const MAX_NAME_LENGTH: int = 10
var current_text = ''
var cursor_line = 0
var cursor_column = 0
func _ready(): var current_text: String = ""
var cursor_line: int = 0
var cursor_column: int = 0
func _ready() -> void:
text_changed.connect(_on_text_changed) text_changed.connect(_on_text_changed)
func _input(event):
func _input(event: InputEvent) -> void:
if event is InputEventKey and event.pressed: if event is InputEventKey and event.pressed:
if event.alt_pressed && event.keycode == KEY_A: if event.alt_pressed && event.keycode == KEY_A:
close_pressed.emit() close_pressed.emit()
@@ -21,15 +30,16 @@ func _input(event):
if event.keycode == KEY_ENTER || event.keycode == KEY_KP_ENTER: if event.keycode == KEY_ENTER || event.keycode == KEY_KP_ENTER:
enter_key_pressed.emit() enter_key_pressed.emit()
func _on_text_changed():
func _on_text_changed() -> void:
if text.contains("\n"): if text.contains("\n"):
text = text.replace("\n", "") text = text.replace("\n", "")
var new_text : String = text var new_text: String = text
if new_text.length() > LIMIT: if new_text.length() > MAX_NAME_LENGTH:
text = current_text text = current_text
# when replacing the text, the cursor will get moved to the beginning of the # when replacing the text, the cursor will get moved to the beginning of the
# text, so move it back to where it was # text, so move it back to where it was
set_caret_line(cursor_line) set_caret_line(cursor_line)
set_caret_column(cursor_column) set_caret_column(cursor_column)
+23 -19
View File
@@ -1,40 +1,42 @@
extends RefCounted
class_name PlayerObject class_name PlayerObject
extends RefCounted
## A player in the quiz: name, score and character. The players are kept in
## [code]Settings.player_array[/code].
signal first_point_triggerd signal first_point_triggered
signal match_point_triggerd signal match_point_triggered
signal winner_triggerd signal winner_triggered
signal point_given_sound signal point_given_sound
signal player_point_given signal player_point_given
signal player_point_taken signal player_point_taken
## Unique id that never changes, unlike the player's position in Settings.player_array.
var id: String = _generate_uuid()
@export
var player_name: String
@export
var player_score: int
var is_first_point: bool = true
## Picture shown until the player picks a character. ## Picture shown until the player picks a character.
const NO_CHARACTER_PATH: String = "res://noCharacter.png" const NO_CHARACTER_PATH: String = "res://noCharacter.png"
@export var player_name: String
@export var player_score: int
## Unique id that never changes, unlike the player's position in Settings.player_array.
var id: String = _generate_uuid()
var is_first_point: bool = true
var character: Texture = load(NO_CHARACTER_PATH) var character: Texture = load(NO_CHARACTER_PATH)
func _init(new_name: String) -> void: func _init(new_name: String) -> void:
player_name = new_name player_name = new_name
player_score = 0 player_score = 0
## Returns a random version 4 UUID, e.g. "3f2b8c1e-9a4d-4e7f-b2c6-5d8e1f0a7b93". ## Returns a random version 4 UUID, e.g. "3f2b8c1e-9a4d-4e7f-b2c6-5d8e1f0a7b93".
static func _generate_uuid() -> String: static func _generate_uuid() -> String:
var bytes: PackedByteArray = Crypto.new().generate_random_bytes(16) var bytes: PackedByteArray = Crypto.new().generate_random_bytes(16)
bytes[6] = (bytes[6] & 0x0f) | 0x40 bytes[6] = (bytes[6] & 0x0f) | 0x40
bytes[8] = (bytes[8] & 0x3f) | 0x80 bytes[8] = (bytes[8] & 0x3f) | 0x80
var hex: String = bytes.hex_encode() var hex: String = bytes.hex_encode()
return "%s-%s-%s-%s-%s" % [hex.substr(0, 8), hex.substr(8, 4), hex.substr(12, 4), hex.substr(16, 4), hex.substr(20, 12)] return "%s-%s-%s-%s-%s" % [hex.substr(0, 8), hex.substr(8, 4), hex.substr(12, 4),
hex.substr(16, 4), hex.substr(20, 12)]
func add_point() -> void: func add_point() -> void:
if player_score < Settings.winning_score: if player_score < Settings.winning_score:
@@ -46,14 +48,15 @@ func add_point() -> void:
# With a low winning score the first point can also be match point or the win, # With a low winning score the first point can also be match point or the win,
# so the more important event is checked first # so the more important event is checked first
if player_score == Settings.winning_score: if player_score == Settings.winning_score:
winner_triggerd.emit() winner_triggered.emit()
elif player_score == Settings.winning_score - 1: elif player_score == Settings.winning_score - 1:
match_point_triggerd.emit() match_point_triggered.emit()
elif first_point: elif first_point:
first_point_triggerd.emit() first_point_triggered.emit()
else: else:
point_given_sound.emit() point_given_sound.emit()
func minus_point() -> void: func minus_point() -> void:
if player_score > 0: if player_score > 0:
player_score -= 1 player_score -= 1
@@ -61,6 +64,7 @@ func minus_point() -> void:
if player_score == 0: if player_score == 0:
is_first_point = true is_first_point = true
func reset_points() -> void: func reset_points() -> void:
player_score = 0 player_score = 0
is_first_point = true is_first_point = true
+19 -19
View File
@@ -1,21 +1,6 @@
class_name PlayerRow class_name PlayerRow
extends Control extends Control
@onready
var player_name_field: Label = %Name
@onready
var add_button: Button = %AddPoint
@onready
var minus_button: Button = %RemovePoint
@onready
var character: TextureButton = %Character
@onready
var remove_player: TextureButton = %RemovePlayer
signal change_character_clicked signal change_character_clicked
signal player_removed signal player_removed
@@ -26,7 +11,13 @@ var player_object: PlayerObject
## gives this player a point and Ctrl+Alt+<list_index + 1> takes one away. ## gives this player a point and Ctrl+Alt+<list_index + 1> takes one away.
var list_index: int var list_index: int
# Called when the node enters the scene tree for the first time. @onready var player_name_field: Label = %Name
@onready var add_button: Button = %AddPoint
@onready var minus_button: Button = %RemovePoint
@onready var character: TextureButton = %Character
@onready var remove_player: TextureButton = %RemovePlayer
func _ready() -> void: func _ready() -> void:
add_button.pressed.connect(add_point) add_button.pressed.connect(add_point)
add_button.set_shortcut(create_button_shortcut(list_index, false)) add_button.set_shortcut(create_button_shortcut(list_index, false))
@@ -37,42 +28,51 @@ func _ready() -> void:
update_score() update_score()
set_player_character() set_player_character()
func _process(delta: float) -> void: func _process(delta: float) -> void:
remove_player.visible = Settings.edit_players remove_player.visible = Settings.edit_players
func create_button_shortcut(index: int, ctrl_pressed: bool) -> Shortcut: func create_button_shortcut(index: int, ctrl_pressed: bool) -> Shortcut:
var button_shortcut: Shortcut = Shortcut.new() var button_shortcut: Shortcut = Shortcut.new()
var button_event : InputEventKey = InputEventKey.new() var button_event: InputEventKey = InputEventKey.new()
button_event.keycode = KEY_1 + index button_event.keycode = KEY_1 + index
button_event.alt_pressed = true button_event.alt_pressed = true
button_event.ctrl_pressed = ctrl_pressed button_event.ctrl_pressed = ctrl_pressed
button_shortcut.events.append(button_event) button_shortcut.events.append(button_event)
return button_shortcut return button_shortcut
func add_point() -> void: func add_point() -> void:
player_object.add_point() player_object.add_point()
update_score() update_score()
func minus_point() -> void: func minus_point() -> void:
player_object.minus_point() player_object.minus_point()
update_score() update_score()
func change_character() -> void: func change_character() -> void:
change_character_clicked.emit() change_character_clicked.emit()
func new_character_selected(character_texture: ImageTexture) -> void: func new_character_selected(character_texture: ImageTexture) -> void:
GameLog.debug("Back in player list with new") GameLog.debug("Back in player list with new")
player_object.character = character_texture player_object.character = character_texture
set_player_character() set_player_character()
func set_player_character() -> void: func set_player_character() -> void:
character.custom_minimum_size = Vector2(80, 40) character.custom_minimum_size = Vector2(80, 40)
character.ignore_texture_size = true character.ignore_texture_size = true
character.stretch_mode = TextureButton.STRETCH_KEEP_ASPECT_CENTERED character.stretch_mode = TextureButton.STRETCH_KEEP_ASPECT_CENTERED
character.texture_normal = player_object.character character.texture_normal = player_object.character
func update_score() -> void: func update_score() -> void:
if OS.has_feature("debug"): if OS.has_feature("debug"):
player_name_field.text = str(list_index + 1) + " " + player_object.player_name + ": " + str(player_object.player_score) player_name_field.text = "%d %s: %d" % [list_index + 1, player_object.player_name,
player_object.player_score]
else: else:
player_name_field.text = player_object.player_name + ": " + str(player_object.player_score) player_name_field.text = player_object.player_name + ": " + str(player_object.player_score)
+12 -18
View File
@@ -7,6 +7,7 @@ extends PanelContainer
## Emitted when a player has been added to [code]Settings.player_array[/code], before ## Emitted when a player has been added to [code]Settings.player_array[/code], before
## the list shows it. ## the list shows it.
signal player_added(player_object: PlayerObject) signal player_added(player_object: PlayerObject)
## Emitted when a player has been removed from [code]Settings.player_array[/code]. ## Emitted when a player has been removed from [code]Settings.player_array[/code].
@@ -17,26 +18,17 @@ signal change_character_requested(row: PlayerRow, player_name: String)
var _player_row: PackedScene = preload("res://PlayerRow.tscn") var _player_row: PackedScene = preload("res://PlayerRow.tscn")
@onready @onready var add_players_button: TextureButton = %AddPlayersButton
var add_players_button: TextureButton = %AddPlayersButton @onready var add_player_container: HBoxContainer = %AddPlayerContainer
@onready var add_player_button: Button = %AddPlayerButton
@onready @onready var new_player_name_field: PlayerNameField = %PlayerNameField
var add_player_container: HBoxContainer = %AddPlayerContainer @onready var players_list_container: VBoxContainer = %PlayersListVBoxContainer
@onready
var add_player_button: Button = %AddPlayerButton
@onready
var new_player_name_field: TextEdit = %PlayerNameField
@onready
var players_list_container: VBoxContainer = %PlayersListVBoxContainer
func _ready() -> void: func _ready() -> void:
add_players_button.pressed.connect(add_players) add_players_button.pressed.connect(add_players)
add_player_button.pressed.connect(add_player) add_player_button.pressed.connect(add_player)
new_player_name_field.connect("enter_key_pressed", add_player) new_player_name_field.enter_key_pressed.connect(add_player)
## Shows or hides the row for adding players, and the buttons for removing them. ## Shows or hides the row for adding players, and the buttons for removing them.
@@ -51,7 +43,8 @@ func add_players() -> void:
## already as many players as there can be. ## already as many players as there can be.
func add_player() -> void: func add_player() -> void:
GameLog.debug("add_player") GameLog.debug("add_player")
if new_player_name_field.text != "" && Settings.player_array.size() < Settings.MAX_NUMBER_OF_PLAYERS: if new_player_name_field.text != "" \
&& Settings.player_array.size() < Settings.MAX_NUMBER_OF_PLAYERS:
var new_player_object: PlayerObject = PlayerObject.new(new_player_name_field.text) var new_player_object: PlayerObject = PlayerObject.new(new_player_name_field.text)
new_player_name_field.text = "" new_player_name_field.text = ""
Settings.player_array.append(new_player_object) Settings.player_array.append(new_player_object)
@@ -72,8 +65,9 @@ func reload() -> void:
new_player.player_object = player_object new_player.player_object = player_object
new_player.list_index = counter new_player.list_index = counter
new_player.connect("change_character_clicked", _on_change_character_clicked.bind(new_player, player_object.player_name)) new_player.change_character_clicked.connect(
new_player.connect("player_removed", _on_player_removed.bind(player_object)) _on_change_character_clicked.bind(new_player, player_object.player_name))
new_player.player_removed.connect(_on_player_removed.bind(player_object))
players_list_container.add_child(new_player) players_list_container.add_child(new_player)
counter += 1 counter += 1
+26 -3
View File
@@ -1,26 +1,32 @@
extends Node extends Node
## The songs of the current quiz, in the order they were added, and which of them are
## played, answered and playing.
##
## Registered as the [code]Playlist[/code] autoload.
var song_object_array: Array[SongObject] = [] var song_object_array: Array[SongObject] = []
var latest_played_song: int = 0 var latest_played_song: int = 0
var currently_playing_song: int = -1 var currently_playing_song: int = -1
var first_song_downloaded: bool = false var first_song_downloaded: bool = false
var first_song_played: bool = false var first_song_played: bool = false
var number_of_tracks_to_preload: int = 1 var number_of_tracks_to_preload: int = 1
var is_song_downloading: bool = false var is_song_downloading: bool = false
func get_next_song() -> SongObject: func get_next_song() -> SongObject:
return song_object_array[latest_played_song] return song_object_array[latest_played_song]
func set_latest_played_song() -> void: func set_latest_played_song() -> void:
latest_played_song += 1 latest_played_song += 1
if latest_played_song > song_object_array.size() - 1: if latest_played_song > song_object_array.size() - 1:
latest_played_song = song_object_array.size() - 1 latest_played_song = song_object_array.size() - 1
func update_last_played_song() -> void: func update_last_played_song() -> void:
latest_played_song = song_object_array.size() latest_played_song = song_object_array.size()
func get_preloaded_songs() -> int: func get_preloaded_songs() -> int:
var number_of_tracks_preloaded: int = 0 var number_of_tracks_preloaded: int = 0
for song: SongObject in song_object_array: for song: SongObject in song_object_array:
@@ -28,9 +34,11 @@ func get_preloaded_songs() -> int:
number_of_tracks_preloaded += 1 number_of_tracks_preloaded += 1
return number_of_tracks_preloaded return number_of_tracks_preloaded
func should_preload_more_songs() -> bool: func should_preload_more_songs() -> bool:
return number_of_tracks_to_preload > get_preloaded_songs() return number_of_tracks_to_preload > get_preloaded_songs()
func song_is_answered(song_no: int = currently_playing_song) -> void: func song_is_answered(song_no: int = currently_playing_song) -> void:
# Nothing is playing yet (-1); a negative index would answer the last, unplayed song # Nothing is playing yet (-1); a negative index would answer the last, unplayed song
if song_no < 0 || song_no >= song_object_array.size(): if song_no < 0 || song_no >= song_object_array.size():
@@ -41,9 +49,11 @@ func song_is_answered(song_no: int = currently_playing_song) -> void:
GameLog.debug("add to stats: ", song_no) GameLog.debug("add to stats: ", song_no)
MusicServerClient.mark_song_played(song_no) MusicServerClient.mark_song_played(song_no)
func add_to_playlist(new_song_object: SongObject) -> void: func add_to_playlist(new_song_object: SongObject) -> void:
song_object_array.append(new_song_object) song_object_array.append(new_song_object)
func get_playlist() -> Array[SongObject]: func get_playlist() -> Array[SongObject]:
if Settings.is_debug: if Settings.is_debug:
return song_object_array return song_object_array
@@ -54,46 +64,59 @@ func get_playlist() -> Array[SongObject]:
temp_list.append(s) temp_list.append(s)
return temp_list return temp_list
func reset_playlist() -> void: func reset_playlist() -> void:
currently_playing_song = -1 currently_playing_song = -1
latest_played_song = 0 latest_played_song = 0
clear_song_list() clear_song_list()
func clear_song_list() -> void: func clear_song_list() -> void:
song_object_array.clear() song_object_array.clear()
func unset_is_playing() -> void: func unset_is_playing() -> void:
for song_object: SongObject in song_object_array: for song_object: SongObject in song_object_array:
song_object.is_playing = false song_object.is_playing = false
func set_currently_playing_song(value: int) -> void: func set_currently_playing_song(value: int) -> void:
currently_playing_song = value currently_playing_song = value
func get_current_soundtrack_title() -> String: func get_current_soundtrack_title() -> String:
return song_object_array[currently_playing_song].get_soundtrack_title() return song_object_array[currently_playing_song].get_soundtrack_title()
func get_current_song_title() -> String: func get_current_song_title() -> String:
return song_object_array[currently_playing_song].get_song_title() return song_object_array[currently_playing_song].get_song_title()
func add_point(player_id: String) -> void: func add_point(player_id: String) -> void:
song_object_array[currently_playing_song].add_point(player_id) song_object_array[currently_playing_song].add_point(player_id)
func remove_point(player_id: String) -> void: func remove_point(player_id: String) -> void:
song_object_array[currently_playing_song].remove_point(player_id) song_object_array[currently_playing_song].remove_point(player_id)
## Removes a player's points from every song. ## Removes a player's points from every song.
func remove_player_from_list(player_id: String) -> void: func remove_player_from_list(player_id: String) -> void:
for song_object: SongObject in song_object_array: for song_object: SongObject in song_object_array:
song_object.remove_point(player_id) song_object.remove_point(player_id)
func get_song(song_no: int) -> AudioStream: func get_song(song_no: int) -> AudioStream:
return song_object_array[song_no].song return song_object_array[song_no].song
func add_song_to_song_object(song_no: int, sound: AudioStream) -> void: func add_song_to_song_object(song_no: int, sound: AudioStream) -> void:
song_object_array[song_no].song = sound song_object_array[song_no].song = sound
func song_has_played(song_no: int = latest_played_song) -> void: func song_has_played(song_no: int = latest_played_song) -> void:
song_object_array[song_no].has_played = true song_object_array[song_no].has_played = true
func song_is_playing(song_no: int = latest_played_song) -> void: func song_is_playing(song_no: int = latest_played_song) -> void:
song_object_array[song_no].is_playing = true song_object_array[song_no].is_playing = true
+7 -9
View File
@@ -1,27 +1,25 @@
class_name QRPopup class_name QRPopup
extends BasePopup extends BasePopup
## Shows a QR code with the address of the search page on the MusicServer, so players
## can search for soundtracks on their phones.
@onready @onready var qr_panel: Panel = $QRPanel
var qr_panel: Panel = $QRPanel @onready var qr_code_rect: QRCodeRect = %QRCodeRect
@onready var url_label: Label = %URLLabel
@onready
var qr_code_rect: QRCodeRect = %QRCodeRect
@onready
var url_label: Label = %URLLabel
func _ready() -> void: func _ready() -> void:
qr_code_rect.light_module_color = Color.WHITE qr_code_rect.light_module_color = Color.WHITE
qr_code_rect.dark_module_color = Color.BLACK qr_code_rect.dark_module_color = Color.BLACK
qr_code_rect.quiet_zone_size = 4 qr_code_rect.quiet_zone_size = 4
# Generate QR code for the search URL # Generate QR code for the search URL
var url: String = Settings.default_path + "/search" var url: String = Settings.default_path + "/search"
qr_code_rect.data = url qr_code_rect.data = url
url_label.text = qr_code_rect.data url_label.text = qr_code_rect.data
GameLog.debug("QR code generated for: ", url) GameLog.debug("QR code generated for: ", url)
func open() -> void: func open() -> void:
super() super()
# Regenerate QR code in case settings changed # Regenerate QR code in case settings changed
+29 -54
View File
@@ -9,54 +9,13 @@ extends Control
## Emitted when another MusicServer is picked in the settings, after the quiz has ## Emitted when another MusicServer is picked in the settings, after the quiz has
## reloaded from it. ## reloaded from it.
signal server_changed signal server_changed
## Emitted with a line for the game log, e.g. when a player gets a point. ## Emitted with a line for the game log, e.g. when a player gets a point.
signal event_logged(text: String) signal event_logged(text: String)
@onready var songs: Array = []
var open_button: Button = $Open
@onready
var file_dialog: 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_popup: SettingsPopup = $SettingsPopup
@onready
var winner_popup: WinnerPopup = %WinnerPopup
@onready
var song_controls: SongControls = $SongControls
@onready
var song_loader: SongLoader = $SongLoader
@onready
var music_list_panel: MusicListPanel = $MusicListPanel
@onready
var character_select_popup: CharacterSelectPopup = %CharacterSelectPopup
@onready
var music_player: MusicPlayer = $MusicPlayer
@onready
var debug_label: Label = $DebugLabel
var songs: Array= []
var current_player_row: PlayerRow var current_player_row: PlayerRow
## True while statistics are off because a player won, until points are reset. ## True while statistics are off because a player won, until points are reset.
@@ -65,6 +24,21 @@ var stats_paused_by_win: bool = false
## Dims the window behind an open popup and keeps clicks from reaching the buttons under it. ## Dims the window behind an open popup and keeps clicks from reaching the buttons under it.
var popup_backdrop: PopupBackdrop var popup_backdrop: PopupBackdrop
@onready var open_button: Button = $Open
@onready var file_dialog: 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_popup: SettingsPopup = $SettingsPopup
@onready var winner_popup: WinnerPopup = %WinnerPopup
@onready var song_controls: SongControls = $SongControls
@onready var song_loader: SongLoader = $SongLoader
@onready var music_list_panel: MusicListPanel = $MusicListPanel
@onready var character_select_popup: CharacterSelectPopup = %CharacterSelectPopup
@onready var music_player: MusicPlayer = $MusicPlayer
@onready var debug_label: Label = $DebugLabel
func _ready() -> void: func _ready() -> void:
popup_backdrop = PopupBackdrop.add_to(self, _popups()) popup_backdrop = PopupBackdrop.add_to(self, _popups())
@@ -83,14 +57,14 @@ func _ready() -> void:
song_loader.song_list_loaded.connect(update_song_list) song_loader.song_list_loaded.connect(update_song_list)
song_loader.song_added.connect(_on_song_added) song_loader.song_added.connect(_on_song_added)
character_select_popup.connect("new_new_character_selected", _on_new_character_selected) character_select_popup.character_selected.connect(_on_new_character_selected)
music_list_panel.song_clicked.connect(song_object_clicked) music_list_panel.song_clicked.connect(song_object_clicked)
players_panel.player_added.connect(_on_player_added) players_panel.player_added.connect(_on_player_added)
players_panel.player_removed.connect(_on_player_removed) players_panel.player_removed.connect(_on_player_removed)
players_panel.change_character_requested.connect(_on_change_character_requested) players_panel.change_character_requested.connect(_on_change_character_requested)
music_player.connect("play_next_song", play_next_song) music_player.play_next_song.connect(play_next_song)
music_player.connect("update_song_list", update_song_list) music_player.update_song_list.connect(update_song_list)
settings_popup.connect("server_changed", _on_server_changed) settings_popup.server_changed.connect(_on_server_changed)
settings_popup.visibility_changed.connect(_on_settings_popup_visibility_changed) settings_popup.visibility_changed.connect(_on_settings_popup_visibility_changed)
open_button.pressed.connect(open) open_button.pressed.connect(open)
@@ -192,7 +166,7 @@ func update_song_list() -> void:
func song_object_clicked(song_no: int) -> void: func song_object_clicked(song_no: int) -> void:
GameLog.debug("Clicked Song Object: ", Playlist.song_object_array[song_no].to_string()) GameLog.debug("Clicked Song Object: ", Playlist.song_object_array[song_no].to_string())
if Settings.character_select_open == false: if !Settings.character_select_open:
if Playlist.get_song(song_no) == null: if Playlist.get_song(song_no) == null:
GameLog.debug("has no song, need to download") GameLog.debug("has no song, need to download")
var song_downloaded: Callable = func song_downloaded(body: PackedByteArray) -> void: var song_downloaded: Callable = func song_downloaded(body: PackedByteArray) -> void:
@@ -233,12 +207,12 @@ func show_settings() -> void:
# Each new player's points reach the music player, the log and the winner popup. # Each new player's points reach the music player, the log and the winner popup.
func _on_player_added(new_player_object: PlayerObject) -> void: func _on_player_added(new_player_object: PlayerObject) -> void:
new_player_object.connect("first_point_triggerd", music_player._on_point_triggered.bind("first")) new_player_object.first_point_triggered.connect(music_player._on_point_triggered.bind("first"))
new_player_object.connect("match_point_triggerd", music_player._on_point_triggered.bind("match")) new_player_object.match_point_triggered.connect(music_player._on_point_triggered.bind("match"))
new_player_object.connect("winner_triggerd", _on_player_won.bind(new_player_object)) new_player_object.winner_triggered.connect(_on_player_won.bind(new_player_object))
new_player_object.connect("point_given_sound", _on_make_point_given_sound) new_player_object.point_given_sound.connect(_on_make_point_given_sound)
new_player_object.connect("player_point_given", _on_point_given.bind(new_player_object)) new_player_object.player_point_given.connect(_on_point_given.bind(new_player_object))
new_player_object.connect("player_point_taken", _on_point_taken.bind(new_player_object)) new_player_object.player_point_taken.connect(_on_point_taken.bind(new_player_object))
func _on_point_given(player_object: PlayerObject) -> void: func _on_point_given(player_object: PlayerObject) -> void:
@@ -293,6 +267,7 @@ func _on_new_character_selected(character_texture: ImageTexture) -> void:
var local_path: String = '/Users/sebastian/ResilioSync/Sorterat_test/Metal Gear Solid 4 - Guns of the Patriots/2-16 Metal Gear Saga.mp3' 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: func open() -> void:
file_dialog.popup() file_dialog.popup()
+8 -5
View File
@@ -1,9 +1,12 @@
class_name ScrollToEndContainer class_name ScrollToEndContainer
extends ScrollContainer extends ScrollContainer
## A scroll container that scrolls to the end whenever its content grows, so the newest
## item is always in view.
var max_value = 0; var max_value: float = 0.0
func _draw():
if max_value != self.get_v_scroll_bar().max_value: func _draw() -> void:
max_value = self.get_v_scroll_bar().max_value if max_value != get_v_scroll_bar().max_value:
self.scroll_vertical = max_value max_value = get_v_scroll_bar().max_value
scroll_vertical = int(max_value)
+48 -38
View File
@@ -1,65 +1,65 @@
class_name SearchPopup class_name SearchPopup
extends BasePopup extends BasePopup
## Searches every soundtrack on the server as you type. Matches ignore case, spaces and
@onready ## special characters, and also find the letters in order with others in between.
var search_list: VBoxContainer = %SearchResults
@onready
var search_panel: Panel = %SearchPanel
@onready
var clear_button: Button = $ClearButton
@onready
var search_bar: TextEdit = $Searchbar
var soundtracks: Array = [] var soundtracks: Array = []
# Precomputed soundtrack data: array of dicts with original, lowercase, and cleaned fields ## Each soundtrack as a Dictionary with its original, lowercase and cleaned name.
var precomputed_soundtracks: Array = [] var precomputed_soundtracks: Array = []
# Regex cache: maps cleaned search terms to compiled RegEx objects ## Compiled patterns for the letters-in-order search, by cleaned search term.
var regex_cache: Dictionary = {} var regex_cache: Dictionary = {}
var label_pool: Array[Label] = [] var label_pool: Array[Label] = []
# Called when the node enters the scene tree for the first time. @onready var search_list: VBoxContainer = %SearchResults
@onready var search_panel: Panel = %SearchPanel
@onready var clear_button: Button = $ClearButton
@onready var search_bar: TextEdit = $Searchbar
func _ready() -> void: func _ready() -> void:
clear_button.pressed.connect(clear) clear_button.pressed.connect(clear)
search_bar.grab_focus() search_bar.grab_focus()
search_bar.text_changed.connect(search) search_bar.text_changed.connect(search)
visibility_changed.connect(focus) visibility_changed.connect(focus)
func focus() -> void: func focus() -> void:
if self.visible == true: if visible:
search_bar.grab_focus() search_bar.grab_focus()
clear() clear()
func close() -> void: func close() -> void:
clear() clear()
super() super()
func search() -> void: func search() -> void:
GameLog.debug(search_bar.text) GameLog.debug(search_bar.text)
var search_text: String = search_bar.text var search_text: String = search_bar.text
if search_text == "": if search_text == "":
show_all_soundtracks() show_all_soundtracks()
return return
perform_search(search_text) perform_search(search_text)
func show_all_soundtracks() -> void: func show_all_soundtracks() -> void:
# Hide all visible labels and add to pool # Hide all visible labels and add to pool
for child: Label in search_list.get_children(): for child: Label in search_list.get_children():
if child.visible: if child.visible:
child.visible = false child.visible = false
label_pool.append(child) label_pool.append(child)
# Show all soundtracks in order # Show all soundtracks in order
for soundtrack: String in soundtracks: for soundtrack: String in soundtracks:
add_soundtrack_reuse(soundtrack) add_soundtrack_reuse(soundtrack)
func perform_search(search_text: String) -> void: func perform_search(search_text: String) -> void:
# Hide all visible labels and add to pool # Hide all visible labels and add to pool
for child: Label in search_list.get_children(): for child: Label in search_list.get_children():
@@ -70,6 +70,7 @@ func perform_search(search_text: String) -> void:
for soundtrack: String in find_matches(search_text): for soundtrack: String in find_matches(search_text):
add_soundtrack_reuse(soundtrack) add_soundtrack_reuse(soundtrack)
## Returns the soundtracks matching [param search_text], best matches first: plain ## Returns the soundtracks matching [param search_text], best matches first: plain
## case-insensitive matches, then matches ignoring spaces and special characters, ## case-insensitive matches, then matches ignoring spaces and special characters,
## then matches with the letters in order but not next to each other. ## then matches with the letters in order but not next to each other.
@@ -81,25 +82,25 @@ func find_matches(search_text: String) -> Array[String]:
var matches: Array[String] = [] var matches: Array[String] = []
# Pass 1: Exact case-insensitive contains match on lowercase versions # Pass 1: Exact case-insensitive contains match on lowercase versions
for i in range(precomputed_soundtracks.size()): for i: int in range(precomputed_soundtracks.size()):
var st_data = precomputed_soundtracks[i] var st_data: Dictionary = precomputed_soundtracks[i]
if st_data["lowercase"].contains(search_text_lower): if st_data["lowercase"].contains(search_text_lower):
if not st_data["original"] in seen: if not st_data["original"] in seen:
seen[st_data["original"]] = true seen[st_data["original"]] = true
matches.append(st_data["original"]) matches.append(st_data["original"])
# Pass 2: Contains match on cleaned terms # Pass 2: Contains match on cleaned terms
for i in range(precomputed_soundtracks.size()): for i: int in range(precomputed_soundtracks.size()):
var st_data = precomputed_soundtracks[i] var st_data: Dictionary = precomputed_soundtracks[i]
if st_data["cleaned"].contains(clean_search_text): if st_data["cleaned"].contains(clean_search_text):
if not st_data["original"] in seen: if not st_data["original"] in seen:
seen[st_data["original"]] = true seen[st_data["original"]] = true
matches.append(st_data["original"]) matches.append(st_data["original"])
# Pass 3: Regex match on cleaned terms # Pass 3: Regex match on cleaned terms
var regex = compile_regex_cached(clean_search_text) var regex: RegEx = compile_regex_cached(clean_search_text)
for i in range(precomputed_soundtracks.size()): for i: int in range(precomputed_soundtracks.size()):
var st_data = precomputed_soundtracks[i] var st_data: Dictionary = precomputed_soundtracks[i]
if regex.search(st_data["cleaned"]): if regex.search(st_data["cleaned"]):
if not st_data["original"] in seen: if not st_data["original"] in seen:
seen[st_data["original"]] = true seen[st_data["original"]] = true
@@ -107,30 +108,35 @@ func find_matches(search_text: String) -> Array[String]:
return matches return matches
# Cleans a term by removing spaces and replacing special characters.
# Matches MusicServer's clean_term() function.
func clean_term(term: String) -> String:
return term.replace(" ", "").replace("é", "e").replace("+", "plus").replace("&", "and").replace("'n", "and").to_lower()
# Compiles a regex pattern with caching to avoid recompiling the same pattern multiple times. ## Cleans a term by removing spaces and replacing special characters.
## Matches MusicServer's clean_term() function.
func clean_term(term: String) -> String:
return term.replace(" ", "").replace("é", "e").replace("+", "plus").replace("&", "and") \
.replace("'n", "and").to_lower()
## Compiles the letters-in-order pattern for [param search_term], once per term.
func compile_regex_cached(search_term: String) -> RegEx: func compile_regex_cached(search_term: String) -> RegEx:
if not search_term in regex_cache: if not search_term in regex_cache:
var regex = RegEx.new() var regex: RegEx = RegEx.new()
var regText: String = ".*" var regex_text: String = ".*"
for letter: String in search_term: for letter: String in search_term:
# Escape regex metacharacters so searches like "Q*bert" or "(Remix)" work # Escape regex metacharacters so searches like "Q*bert" or "(Remix)" work
if "\\^$.|?*+()[]{}".contains(letter): if "\\^$.|?*+()[]{}".contains(letter):
regText += "\\" regex_text += "\\"
regText += letter + ".*" regex_text += letter + ".*"
regex.compile(regText) regex.compile(regex_text)
regex_cache[search_term] = regex regex_cache[search_term] = regex
return regex_cache[search_term] return regex_cache[search_term]
func add_soundtrack(soundtrack: String) -> void: func add_soundtrack(soundtrack: String) -> void:
# Backward compatibility - delegates to reuse version # Backward compatibility - delegates to reuse version
add_soundtrack_reuse(soundtrack) add_soundtrack_reuse(soundtrack)
# Optimized version that reuses label nodes from pool instead of creating new ones
## Shows [param soundtrack], reusing a hidden label from the pool when there is one.
func add_soundtrack_reuse(soundtrack: String) -> void: func add_soundtrack_reuse(soundtrack: String) -> void:
var label: Label var label: Label
if label_pool.size() > 0: if label_pool.size() > 0:
@@ -146,6 +152,7 @@ func add_soundtrack_reuse(soundtrack: String) -> void:
search_list.add_child(label) search_list.add_child(label)
label.visible = true label.visible = true
func check_if_soundtrack_exists(soundtrack: String) -> bool: func check_if_soundtrack_exists(soundtrack: String) -> bool:
var soundtrack_exists: bool = false var soundtrack_exists: bool = false
for child: Label in search_list.get_children(): for child: Label in search_list.get_children():
@@ -153,9 +160,10 @@ func check_if_soundtrack_exists(soundtrack: String) -> bool:
soundtrack_exists = true soundtrack_exists = true
return soundtrack_exists return soundtrack_exists
func get_list_of_soundtracks() -> void: func get_list_of_soundtracks() -> void:
GameLog.debug("get_list_of_soundtracks") GameLog.debug("get_list_of_soundtracks")
var handle_soundtracks: Callable = func handle_soundtracks(array) -> void: var handle_soundtracks: Callable = func handle_soundtracks(array: Variant) -> void:
if typeof(array) == TYPE_ARRAY: if typeof(array) == TYPE_ARRAY:
# Reuse existing labels: hide all and add to pool # Reuse existing labels: hide all and add to pool
for child: Label in search_list.get_children(): for child: Label in search_list.get_children():
@@ -168,6 +176,7 @@ func get_list_of_soundtracks() -> void:
GameLog.warning("Unexpected data") GameLog.warning("Unexpected data")
MusicServerClient.fetch_all_soundtracks_ordered(handle_soundtracks) MusicServerClient.fetch_all_soundtracks_ordered(handle_soundtracks)
## Replaces the searchable soundtracks and precomputes their search forms. ## Replaces the searchable soundtracks and precomputes their search forms.
func set_soundtracks(list: Array) -> void: func set_soundtracks(list: Array) -> void:
soundtracks = [] soundtracks = []
@@ -180,6 +189,7 @@ func set_soundtracks(list: Array) -> void:
"cleaned": clean_term(soundtrack) "cleaned": clean_term(soundtrack)
}) })
func clear() -> void: func clear() -> void:
search_bar.text = "" search_bar.text = ""
show_all_soundtracks() show_all_soundtracks()
+4 -4
View File
@@ -1,14 +1,14 @@
extends Node extends Node
## The game settings and the players, shared by the whole game.
##
## Registered as the [code]Settings[/code] autoload.
const MAX_NUMBER_OF_PLAYERS: int = 7 const MAX_NUMBER_OF_PLAYERS: int = 7
var default_path: String = "https://music.sanplex.xyz" var default_path: String = "https://music.sanplex.xyz"
var selected_server: int = 0 var selected_server: int = 0
var is_local: bool = false var is_local: bool = false
var is_debug: bool = false var is_debug: bool = false
var stop_after_current: bool = true var stop_after_current: bool = true
var auto_repeat_song: bool = true var auto_repeat_song: bool = true
var hide_next_track: bool = true var hide_next_track: bool = true
@@ -22,7 +22,6 @@ var hide_for_seconds: float = 5.0
var hide_beginning: bool = false var hide_beginning: bool = false
var hide_length: bool = false var hide_length: bool = false
var hide_ticks: bool = false var hide_ticks: bool = false
var player_array: Array[PlayerObject] var player_array: Array[PlayerObject]
var edit_players: bool = false var edit_players: bool = false
var currently_syncing: bool = false var currently_syncing: bool = false
@@ -32,6 +31,7 @@ var character_select_open: bool = false
## Releases are made from it, see RELEASING.md. ## Releases are made from it, see RELEASING.md.
var version: String = ProjectSettings.get_setting("application/config/version", "") var version: String = ProjectSettings.get_setting("application/config/version", "")
func delete_children(node: Node) -> void: func delete_children(node: Node) -> void:
for n: Node in node.get_children(): for n: Node in node.get_children():
node.remove_child(n) node.remove_child(n)
+62 -79
View File
@@ -1,82 +1,40 @@
class_name SettingsPopup class_name SettingsPopup
extends BasePopup extends BasePopup
## The settings: how songs are shown and played, the winning score, the song cache,
## which MusicServer to use and fullscreen.
@onready ## Emitted when another MusicServer is picked.
var settings_panel: Panel = %SettingsPanel
@onready
var stop_after_current_button: CheckButton = %StopAfterCurrentCheckButton
@onready
var hide_next_track_button: CheckButton = %HideNextTrackCheckButton
@onready
var add_to_database_button: CheckButton = %AddToDatabaseCheckButton
@onready
var low_played_button: CheckButton = %LowPlayedCheckButton
@onready
var hide_beginning_button: CheckButton = %HideBeginningCheckButton
@onready
var hide_length_button: CheckButton = %HideLengthCheckButton
@onready
var hide_ticks_button: CheckButton = %HideTicksCheckButton
@onready
var score_label: Label = %ScoreLabel
@onready
var lower_winning_score_button: Button = %LowerButton
@onready
var increase_winning_score_button: Button = %IncreaseButton
@onready
var cache_label: Label = %CacheLabel
@onready
var lower_cache_button: Button = %LowerCacheButton
@onready
var increase_cache_button: Button = %IncreaseCacheButton
@onready
var inspiration_speed_label: Label = %InspirationListSpeedLabel
@onready
var lower_inspiration_speed_button: Button = %LowerInspirationSpeedButton
@onready
var increase_inspiration_speed_button: Button = %IncreaseInspirationSpeedButton
@onready
var hide_for_seconds_label: Label = %HideForSecondsLabel
@onready
var lower_hide_for_seconds_button: Button = %LowerHideForSecondsButton
@onready
var increase_hide_for_seconds_button: Button = %IncreaseHideForSecondsButton
@onready
var select_server_button: OptionButton = %SelectServerButton
@onready
var fullscreen_button: CheckButton = %FullscreenButton
@onready
var local_button: CheckButton = %LocalButton
signal server_changed signal server_changed
# Called when the node enters the scene tree for the first time. @onready var settings_panel: Panel = %SettingsPanel
@onready var stop_after_current_button: CheckButton = %StopAfterCurrentCheckButton
@onready var hide_next_track_button: CheckButton = %HideNextTrackCheckButton
@onready var add_to_database_button: CheckButton = %AddToDatabaseCheckButton
@onready var low_played_button: CheckButton = %LowPlayedCheckButton
@onready var hide_beginning_button: CheckButton = %HideBeginningCheckButton
@onready var hide_length_button: CheckButton = %HideLengthCheckButton
@onready var hide_ticks_button: CheckButton = %HideTicksCheckButton
@onready var score_label: Label = %ScoreLabel
@onready var lower_winning_score_button: Button = %LowerButton
@onready var increase_winning_score_button: Button = %IncreaseButton
@onready var cache_label: Label = %CacheLabel
@onready var lower_cache_button: Button = %LowerCacheButton
@onready var increase_cache_button: Button = %IncreaseCacheButton
@onready var inspiration_speed_label: Label = %InspirationListSpeedLabel
@onready var lower_inspiration_speed_button: Button = %LowerInspirationSpeedButton
@onready var increase_inspiration_speed_button: Button = %IncreaseInspirationSpeedButton
@onready var hide_for_seconds_label: Label = %HideForSecondsLabel
@onready var lower_hide_for_seconds_button: Button = %LowerHideForSecondsButton
@onready var increase_hide_for_seconds_button: Button = %IncreaseHideForSecondsButton
@onready var select_server_button: OptionButton = %SelectServerButton
@onready var fullscreen_button: CheckButton = %FullscreenButton
@onready var local_button: CheckButton = %LocalButton
func _ready() -> void: func _ready() -> void:
fullscreen_button.button_pressed = Settings.fullscreen fullscreen_button.button_pressed = Settings.fullscreen
select_server_button.selected = Settings.selected_server select_server_button.selected = Settings.selected_server
stop_after_current_button.pressed.connect(stop_after_current) stop_after_current_button.pressed.connect(stop_after_current)
hide_next_track_button.pressed.connect(hide_next_track) hide_next_track_button.pressed.connect(hide_next_track)
add_to_database_button.pressed.connect(add_to_database) add_to_database_button.pressed.connect(add_to_database)
@@ -94,7 +52,7 @@ func _ready() -> void:
increase_hide_for_seconds_button.pressed.connect(increase_hide_for_seconds) increase_hide_for_seconds_button.pressed.connect(increase_hide_for_seconds)
fullscreen_button.pressed.connect(fullscreen) fullscreen_button.pressed.connect(fullscreen)
local_button.pressed.connect(local_play) local_button.pressed.connect(local_play)
stop_after_current_button.button_pressed = Settings.stop_after_current stop_after_current_button.button_pressed = Settings.stop_after_current
hide_next_track_button.button_pressed = Settings.hide_next_track hide_next_track_button.button_pressed = Settings.hide_next_track
add_to_database_button.button_pressed = Settings.add_to_stats add_to_database_button.button_pressed = Settings.add_to_stats
@@ -114,95 +72,117 @@ func _ready() -> void:
# Alt+Enter switches fullscreen from the main window, which resizes the window # Alt+Enter switches fullscreen from the main window, which resizes the window
get_window().size_changed.connect(_update_fullscreen_button) get_window().size_changed.connect(_update_fullscreen_button)
# Settings can change outside this popup, e.g. winning turns statistics off # Settings can change outside this popup, e.g. winning turns statistics off
func _on_visibility_changed() -> void: func _on_visibility_changed() -> void:
if visible: if visible:
add_to_database_button.button_pressed = Settings.add_to_stats add_to_database_button.button_pressed = Settings.add_to_stats
_update_fullscreen_button() _update_fullscreen_button()
func _update_fullscreen_button() -> void: func _update_fullscreen_button() -> void:
fullscreen_button.set_pressed_no_signal(Settings.fullscreen) fullscreen_button.set_pressed_no_signal(Settings.fullscreen)
func fullscreen() -> void: func fullscreen() -> void:
Settings.fullscreen = !Settings.fullscreen Settings.fullscreen = !Settings.fullscreen
if Settings.fullscreen == true: if Settings.fullscreen:
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN) DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
if Settings.fullscreen == false: if !Settings.fullscreen:
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED) DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
func local_play() -> void: func local_play() -> void:
Settings.play_local = !Settings.play_local Settings.play_local = !Settings.play_local
func stop_after_current() -> void: func stop_after_current() -> void:
Settings.stop_after_current = !Settings.stop_after_current Settings.stop_after_current = !Settings.stop_after_current
func hide_next_track() -> void: func hide_next_track() -> void:
Settings.hide_next_track = !Settings.hide_next_track Settings.hide_next_track = !Settings.hide_next_track
func add_to_database() -> void: func add_to_database() -> void:
Settings.add_to_stats = !Settings.add_to_stats Settings.add_to_stats = !Settings.add_to_stats
func low_played() -> void: func low_played() -> void:
Settings.use_low_played_mode = !Settings.use_low_played_mode Settings.use_low_played_mode = !Settings.use_low_played_mode
func hide_beginning() -> void: func hide_beginning() -> void:
Settings.hide_beginning = !Settings.hide_beginning Settings.hide_beginning = !Settings.hide_beginning
update_hide_for_seconds_enabled() update_hide_for_seconds_enabled()
func update_hide_for_seconds_enabled() -> void: func update_hide_for_seconds_enabled() -> void:
var enabled: bool = Settings.hide_beginning var enabled: bool = Settings.hide_beginning
%HideForSecondsHBoxContainer.visible = enabled %HideForSecondsHBoxContainer.visible = enabled
func hide_length() -> void: func hide_length() -> void:
Settings.hide_length = !Settings.hide_length Settings.hide_length = !Settings.hide_length
func hide_ticks() -> void: func hide_ticks() -> void:
Settings.hide_ticks = !Settings.hide_ticks Settings.hide_ticks = !Settings.hide_ticks
func lower_winning_score() -> void: func lower_winning_score() -> void:
Settings.winning_score = max(Settings.winning_score - 1, 1) Settings.winning_score = max(Settings.winning_score - 1, 1)
score_label.text = str(Settings.winning_score) score_label.text = str(Settings.winning_score)
func increase_winning_score() -> void: func increase_winning_score() -> void:
Settings.winning_score += 1 Settings.winning_score += 1
score_label.text = str(Settings.winning_score) score_label.text = str(Settings.winning_score)
func set_winning_score(new_score: int) -> void: func set_winning_score(new_score: int) -> void:
Settings.winning_score = new_score Settings.winning_score = new_score
score_label.text = str(Settings.winning_score) score_label.text = str(Settings.winning_score)
func lower_cache() -> void: func lower_cache() -> void:
Playlist.number_of_tracks_to_preload -= 1 Playlist.number_of_tracks_to_preload -= 1
if Playlist.number_of_tracks_to_preload < 1: if Playlist.number_of_tracks_to_preload < 1:
Playlist.number_of_tracks_to_preload = 1 Playlist.number_of_tracks_to_preload = 1
cache_label.text = str(Playlist.number_of_tracks_to_preload) cache_label.text = str(Playlist.number_of_tracks_to_preload)
func increase_cache() -> void: func increase_cache() -> void:
Playlist.number_of_tracks_to_preload += 1 Playlist.number_of_tracks_to_preload += 1
cache_label.text = str(Playlist.number_of_tracks_to_preload) cache_label.text = str(Playlist.number_of_tracks_to_preload)
func lower_inspiration_speed() -> void: func lower_inspiration_speed() -> void:
Settings.inspiration_list_speed -= 1 Settings.inspiration_list_speed -= 1
if Settings.inspiration_list_speed < 1: if Settings.inspiration_list_speed < 1:
Settings.inspiration_list_speed = 1 Settings.inspiration_list_speed = 1
inspiration_speed_label.text = str(Settings.inspiration_list_speed) inspiration_speed_label.text = str(Settings.inspiration_list_speed)
func increase_inspiration_speed() -> void: func increase_inspiration_speed() -> void:
Settings.inspiration_list_speed += 1 Settings.inspiration_list_speed += 1
inspiration_speed_label.text = str(Settings.inspiration_list_speed ) inspiration_speed_label.text = str(Settings.inspiration_list_speed )
func lower_hide_for_seconds() -> void: func lower_hide_for_seconds() -> void:
Settings.hide_for_seconds -= 1.0 Settings.hide_for_seconds -= 1.0
if Settings.hide_for_seconds < 0: if Settings.hide_for_seconds < 0:
Settings.hide_for_seconds = 0 Settings.hide_for_seconds = 0
hide_for_seconds_label.text = str(Settings.hide_for_seconds) hide_for_seconds_label.text = str(Settings.hide_for_seconds)
func increase_hide_for_seconds() -> void: func increase_hide_for_seconds() -> void:
Settings.hide_for_seconds += 1.0 Settings.hide_for_seconds += 1.0
if Settings.hide_for_seconds > 10.0: if Settings.hide_for_seconds > 10.0:
Settings.hide_for_seconds = 10.0 Settings.hide_for_seconds = 10.0
hide_for_seconds_label.text = str(Settings.hide_for_seconds) hide_for_seconds_label.text = str(Settings.hide_for_seconds)
func select_server(new_server: int) -> void: func select_server(new_server: int) -> void:
GameLog.debug("select_server") GameLog.debug("select_server")
Settings.default_path = select_server_button.get_item_text(new_server) Settings.default_path = select_server_button.get_item_text(new_server)
@@ -210,9 +190,11 @@ func select_server(new_server: int) -> void:
GameLog.debug("Settings.default_path: " + Settings.default_path) GameLog.debug("Settings.default_path: " + Settings.default_path)
GameLog.debug("Settings.selected_server: " + str(Settings.selected_server)) GameLog.debug("Settings.selected_server: " + str(Settings.selected_server))
GameLog.debug("new_server: " + str(new_server)) GameLog.debug("new_server: " + str(new_server))
GameLog.debug("select_server_button.get_item_text(new_server): " + select_server_button.get_item_text(new_server)) GameLog.debug("select_server_button.get_item_text(new_server): ",
select_server_button.get_item_text(new_server))
server_changed.emit() server_changed.emit()
func set_selected_server(new_server: int) -> void: func set_selected_server(new_server: int) -> void:
GameLog.debug("set_selected_server") GameLog.debug("set_selected_server")
select_server_button.select(new_server) select_server_button.select(new_server)
@@ -221,5 +203,6 @@ func set_selected_server(new_server: int) -> void:
GameLog.debug("Settings.default_path: " + Settings.default_path) GameLog.debug("Settings.default_path: " + Settings.default_path)
GameLog.debug("Settings.selected_server: " + str(Settings.selected_server)) GameLog.debug("Settings.selected_server: " + str(Settings.selected_server))
GameLog.debug("new_server: " + str(new_server)) GameLog.debug("new_server: " + str(new_server))
GameLog.debug("select_server_button.get_item_text(new_server): " + select_server_button.get_item_text(new_server)) GameLog.debug("select_server_button.get_item_text(new_server): ",
select_server_button.get_item_text(new_server))
server_changed.emit() server_changed.emit()
+10 -14
View File
@@ -1,11 +1,6 @@
class_name ShortcutsPopup class_name ShortcutsPopup
extends BasePopup extends BasePopup
## Lists the keyboard shortcuts. Opened with Alt+K or the Shortcuts button.
@onready
var shortcuts_vbox: VBoxContainer = %shortcutsVBox
@onready
var shortcuts_panel: Panel = %ShortcutsPanel
var shortcuts_dic: Dictionary = { var shortcuts_dic: Dictionary = {
"Alt + S": "Search", "Alt + S": "Search",
@@ -26,14 +21,17 @@ var shortcuts_dic: Dictionary = {
"Alt + Ctrl + 1 - 9": "Take point from player", "Alt + Ctrl + 1 - 9": "Take point from player",
} }
@onready var shortcuts_vbox: VBoxContainer = %shortcutsVBox
@onready var shortcuts_panel: Panel = %ShortcutsPanel
#Color(0.18, 0.18, 0.18) #Color(0.18, 0.18, 0.18)
#Color(0.302, 0.302, 0.302) #Color(0.302, 0.302, 0.302)
#Color(0.259, 0.259, 0.259) #Color(0.259, 0.259, 0.259)
func _ready() -> void: func _ready() -> void:
shortcuts_dic.sort() shortcuts_dic.sort()
Settings.delete_children(shortcuts_vbox) Settings.delete_children(shortcuts_vbox)
var title: Label = Label.new() var title: Label = Label.new()
title.add_theme_font_size_override("font_size", 20) title.add_theme_font_size_override("font_size", 20)
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
@@ -45,28 +43,26 @@ func _ready() -> void:
var panel: PanelContainer = PanelContainer.new() var panel: PanelContainer = PanelContainer.new()
var hbox: HBoxContainer = HBoxContainer.new() var hbox: HBoxContainer = HBoxContainer.new()
hbox.alignment = BoxContainer.ALIGNMENT_CENTER hbox.alignment = BoxContainer.ALIGNMENT_CENTER
var label: Label = Label.new() var label: Label = Label.new()
label.add_theme_font_size_override("font_size", 20) label.add_theme_font_size_override("font_size", 20)
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
label.text = shortcut label.text = shortcut
label.autowrap_mode = TextServer.AUTOWRAP_WORD label.autowrap_mode = TextServer.AUTOWRAP_WORD
label.size_flags_horizontal = Control.SIZE_EXPAND_FILL label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
var label2: Label = Label.new() var label2: Label = Label.new()
label2.add_theme_font_size_override("font_size", 20) label2.add_theme_font_size_override("font_size", 20)
label2.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER label2.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
label2.text = shortcuts_dic[shortcut] label2.text = shortcuts_dic[shortcut]
label2.autowrap_mode = TextServer.AUTOWRAP_WORD label2.autowrap_mode = TextServer.AUTOWRAP_WORD
label2.size_flags_horizontal = Control.SIZE_EXPAND_FILL label2.size_flags_horizontal = Control.SIZE_EXPAND_FILL
if count % 2 == 0: if count % 2 == 0:
var new_stylebox_normal: Resource = panel.get_theme_stylebox("panel").duplicate() var new_stylebox_normal: Resource = panel.get_theme_stylebox("panel").duplicate()
new_stylebox_normal.bg_color = Color(0.18, 0.18, 0.18) new_stylebox_normal.bg_color = Color(0.18, 0.18, 0.18)
panel.add_theme_stylebox_override("panel", new_stylebox_normal) panel.add_theme_stylebox_override("panel", new_stylebox_normal)
#label.add_theme_stylebox_override("normal", new_stylebox_normal)
#label2.add_theme_stylebox_override("normal", new_stylebox_normal)
hbox.add_child(label) hbox.add_child(label)
hbox.add_child(label2) hbox.add_child(label2)
panel.add_child(hbox) panel.add_child(hbox)
+2 -1
View File
@@ -8,6 +8,7 @@ extends Node
## Emitted when the songs already in the server playlist have been loaded into the ## Emitted when the songs already in the server playlist have been loaded into the
## Playlist. ## Playlist.
signal song_list_loaded signal song_list_loaded
## Emitted when a new song has been downloaded and added to the Playlist. ## Emitted when a new song has been downloaded and added to the Playlist.
@@ -21,7 +22,7 @@ var retry_seconds: float = 5.0
## Preloading waits for the list, because loading the list clears the playlist. ## Preloading waits for the list, because loading the list clears the playlist.
func load_song_list() -> void: func load_song_list() -> void:
GameLog.debug("load_song_list") GameLog.debug("load_song_list")
var song_list_fetched: Callable = func song_list_fetched(data) -> void: var song_list_fetched: Callable = func song_list_fetched(data: Variant) -> void:
Playlist.clear_song_list() Playlist.clear_song_list()
if typeof(data) == TYPE_ARRAY: if typeof(data) == TYPE_ARRAY:
for d: Dictionary in data: for d: Dictionary in data:
+12 -8
View File
@@ -3,53 +3,57 @@ extends RefCounted
class_name SongObject class_name SongObject
var song_number: int var song_number: int
var song_title: String var song_title: String
var soundtrack_title: String var soundtrack_title: String
## Ids of the players who got a point for this song. ## Ids of the players who got a point for this song.
var players_given_point: Array[String] = [] var players_given_point: Array[String] = []
var is_answered: bool = false var is_answered: bool = false
var has_played: bool = false var has_played: bool = false
var is_playing: bool = false var is_playing: bool = false
var song: AudioStream var song: AudioStream
func get_soundtrack_title() -> String: func get_soundtrack_title() -> String:
if is_answered: if is_answered:
return soundtrack_title return soundtrack_title
else: else:
return "????????" return "????????"
func get_song_title() -> String: func get_song_title() -> String:
if is_answered: if is_answered:
return song_title return song_title
else: else:
return "??????" return "??????"
func get_song_info() -> String: func get_song_info() -> String:
if Settings.is_debug: if Settings.is_debug:
var format_string: String = "%d. %s - %s | played: %s | answered: %s" var format_string: String = "%d. %s - %s | played: %s | answered: %s"
return format_string % [(song_number + 1), soundtrack_title, song_title, has_played, is_answered] return format_string % [(song_number + 1), soundtrack_title, song_title, has_played,
is_answered]
elif is_answered: elif is_answered:
var format_string: String = "%d. %s - %s" var format_string: String = "%d. %s - %s"
return format_string % [(song_number + 1), soundtrack_title, song_title] return format_string % [(song_number + 1), soundtrack_title, song_title]
else: else:
return "??? - ???" return "??? - ???"
func add_point(player_id: String) -> void: func add_point(player_id: String) -> void:
GameLog.debug("add_point") GameLog.debug("add_point")
if !players_given_point.has(player_id): if !players_given_point.has(player_id):
players_given_point.append(player_id) players_given_point.append(player_id)
func remove_point(player_id: String) -> void: func remove_point(player_id: String) -> void:
GameLog.debug("remove_point") GameLog.debug("remove_point")
players_given_point.erase(player_id) players_given_point.erase(player_id)
func _to_string() -> String: func _to_string() -> String:
var format_string: String = "song_number: %d | soundtrack_title: %s | song_title: %s | is_answered: %s | has_played: %s | is_playing: %s" var format_string: String = "song_number: %d | soundtrack_title: %s | song_title: %s" \
return format_string % [(song_number), soundtrack_title, song_title, is_answered, has_played, is_playing] + " | is_answered: %s | has_played: %s | is_playing: %s"
return format_string % [song_number, soundtrack_title, song_title, is_answered, has_played,
is_playing]
+7 -20
View File
@@ -1,33 +1,19 @@
class_name SongRow class_name SongRow
extends PanelContainer extends PanelContainer
## One song in the music list: its number, soundtrack and title, and a marker for
@onready ## each player who got a point for it.
var song_list_label: Label = %SongListLabel
@onready
var song_list_players: HBoxContainer = %SongListPlayers
var song_object: SongObject var song_object: SongObject
#Color(0.18, 0.18, 0.18) @onready var song_list_label: Label = %SongListLabel
#Color(0.302, 0.302, 0.302) @onready var song_list_players: HBoxContainer = %SongListPlayers
#Color(0.259, 0.259, 0.259)
# Called when the node enters the scene tree for the first time.
func _ready() -> void: func _ready() -> void:
#if song_object != null && song_object.song_number % 2 == 0:
# var new_stylebox_normal = self.get_theme_stylebox("panel").duplicate()
# new_stylebox_normal.bg_color = Color(0.18, 0.18, 0.18)
# self.add_theme_stylebox_override("panel", new_stylebox_normal)
# song_list_label.add_theme_stylebox_override("normal", new_stylebox_normal)
#else:
# var new_stylebox_normal = self.get_theme_stylebox("panel").duplicate()
# new_stylebox_normal.bg_color = Color(0.18, 0.18, 0.18)
# self.add_theme_stylebox_override("panel", new_stylebox_normal)
# song_list_label.add_theme_stylebox_override("normal", new_stylebox_normal)
set_label() set_label()
update_players() update_players()
func set_label() -> void: func set_label() -> void:
if song_object != null: if song_object != null:
var size_to_change: int = song_object.players_given_point.size() * 50 var size_to_change: int = song_object.players_given_point.size() * 50
@@ -36,6 +22,7 @@ func set_label() -> void:
song_list_label.add_theme_color_override("font_color", Color(1, 0.5, 0)) song_list_label.add_theme_color_override("font_color", Color(1, 0.5, 0))
song_list_label.text = song_object.get_song_info() song_list_label.text = song_object.get_song_info()
func update_players() -> void: func update_players() -> void:
Settings.delete_children(song_list_players) Settings.delete_children(song_list_players)
if song_object != null: if song_object != null:
+80 -92
View File
@@ -1,79 +1,49 @@
class_name SyncPopup class_name SyncPopup
extends BasePopup extends BasePopup
## Asks the server to sync its soundtracks with the music folder, shows the progress,
## and then lists what was added, changed or removed.
@onready ## Emitted when the server has started syncing.
var sync_panel: Panel = %SyncPanel
@onready
var sync_title_label: Label = %SyncTitleLabel
@onready
var no_changes_label: Label = %NoChangesLabel
@onready
var progress_hbox: HBoxContainer = %ProgressHBoxContainer
@onready
var sync_hbox: HBoxContainer = %SyncHBoxContainer
@onready
var sync_progress_bar: ProgressBar = %SyncProgressBar
@onready
var soundtracks_added_vbox: VBoxContainer = %SoundtracksAddedVBox
@onready
var soundtracks_added_separator: VSeparator = %SoundtracksAddedSeparator
@onready
var soundtracks_readded_vbox: VBoxContainer = %SoundtracksReAddedVBox
@onready
var soundtracks_readded_separator: VSeparator = %SoundtracksReAddedSeparator
@onready
var soundtracks_changed_title_vbox: VBoxContainer = %SoundtracksChangedTitleVBox
@onready
var soundtracks_changed_title_separator: VSeparator = %SoundtracksChangedTitleSeparator
@onready
var soundtracks_changed_content_vbox: VBoxContainer = %SoundtracksChangedContentVBox
@onready
var soundtracks_changed_content_separator: VSeparator = %SoundtracksChangedContentSeparator
@onready
var soundtracks_removed_vbox: VBoxContainer = %SoundtracksRemovedVBox
@onready
var soundtracks_removed_separator: VSeparator = %SoundtracksRemovedSeparator
@onready
var catched_errors_vbox: VBoxContainer = %CatchedErrorsVBox
@onready
var sync_time: Label = %SyncTime
@onready
var sync_timer: Timer = %SyncTimer
@onready
var restart_sync_button: Button = %RestartSyncButton
var changes: bool = false
var has_synced: bool = false
signal sync_started signal sync_started
## Emitted when the sync is done and its result is shown.
signal sync_finished signal sync_finished
var changes: bool = false
var has_synced: bool = false
@onready var sync_panel: Panel = %SyncPanel
@onready var sync_title_label: Label = %SyncTitleLabel
@onready var no_changes_label: Label = %NoChangesLabel
@onready var progress_hbox: HBoxContainer = %ProgressHBoxContainer
@onready var sync_hbox: HBoxContainer = %SyncHBoxContainer
@onready var sync_progress_bar: ProgressBar = %SyncProgressBar
@onready var soundtracks_added_vbox: VBoxContainer = %SoundtracksAddedVBox
@onready var soundtracks_added_separator: VSeparator = %SoundtracksAddedSeparator
@onready var soundtracks_readded_vbox: VBoxContainer = %SoundtracksReAddedVBox
@onready var soundtracks_readded_separator: VSeparator = %SoundtracksReAddedSeparator
@onready var soundtracks_changed_title_vbox: VBoxContainer = %SoundtracksChangedTitleVBox
@onready var soundtracks_changed_title_separator: VSeparator = %SoundtracksChangedTitleSeparator
@onready var soundtracks_changed_content_vbox: VBoxContainer = %SoundtracksChangedContentVBox
@onready var soundtracks_changed_content_separator: VSeparator = %SoundtracksChangedContentSeparator
@onready var soundtracks_removed_vbox: VBoxContainer = %SoundtracksRemovedVBox
@onready var soundtracks_removed_separator: VSeparator = %SoundtracksRemovedSeparator
@onready var caught_errors_vbox: VBoxContainer = %CaughtErrorsVBox
@onready var sync_time: Label = %SyncTime
@onready var sync_timer: Timer = %SyncTimer
@onready var restart_sync_button: Button = %RestartSyncButton
func _ready() -> void: func _ready() -> void:
sync_timer.timeout.connect(_on_sync_timeout) sync_timer.timeout.connect(_on_sync_timeout)
restart_sync_button.pressed.connect(start_sync) restart_sync_button.pressed.connect(start_sync)
restart_sync_button.visible = false restart_sync_button.visible = false
func show_section_if_has_data(data: Variant, vbox: VBoxContainer, separator: VSeparator, header_text: String) -> bool:
## Lists [param data] under [param header_text] in [param vbox] and returns true, or hides
## the column and its separator when there is nothing to list.
func show_section_if_has_data(data: Variant, vbox: VBoxContainer, separator: VSeparator,
header_text: String) -> bool:
if data != null && data.size() > 0: if data != null && data.size() > 0:
vbox.visible = true vbox.visible = true
make_list(data, vbox, header_text) make_list(data, vbox, header_text)
@@ -83,9 +53,10 @@ func show_section_if_has_data(data: Variant, vbox: VBoxContainer, separator: VSe
separator.visible = false separator.visible = false
return false return false
func start_sync() -> void: func start_sync() -> void:
GameLog.debug("start_sync") GameLog.debug("start_sync")
self.visible = true visible = true
if has_synced: if has_synced:
# Already synced, just show the results # Already synced, just show the results
show_sync_result() show_sync_result()
@@ -98,6 +69,7 @@ func start_sync() -> void:
if !Settings.currently_syncing: if !Settings.currently_syncing:
MusicServerClient.start_sync(sync_request_sent) MusicServerClient.start_sync(sync_request_sent)
func _on_sync_timeout() -> void: func _on_sync_timeout() -> void:
GameLog.debug("_on_sync_timeout!") GameLog.debug("_on_sync_timeout!")
var get_progress: Callable = func get_progress(data_received: Dictionary) -> void: var get_progress: Callable = func get_progress(data_received: Dictionary) -> void:
@@ -111,38 +83,46 @@ func _on_sync_timeout() -> void:
on_sync_finished(data_received) on_sync_finished(data_received)
MusicServerClient.fetch_sync_progress(get_progress) MusicServerClient.fetch_sync_progress(get_progress)
func on_sync_finished(data_received: Dictionary) -> void: func on_sync_finished(data_received: Dictionary) -> void:
has_synced = true has_synced = true
reset_sync_result() reset_sync_result()
if show_section_if_has_data(data_received.soundtracks_added, soundtracks_added_vbox, soundtracks_added_separator, "Added"): if show_section_if_has_data(data_received.soundtracks_added, soundtracks_added_vbox,
soundtracks_added_separator, "Added"):
changes = true changes = true
if show_section_if_has_data(data_received.soundtracks_re_added, soundtracks_readded_vbox, soundtracks_readded_separator, "Re-added"): if show_section_if_has_data(data_received.soundtracks_re_added, soundtracks_readded_vbox,
soundtracks_readded_separator, "Re-added"):
changes = true changes = true
if data_received.soundtracks_changed_title != null && data_received.soundtracks_changed_title.size() > 0: var changed_titles: Variant = data_received.soundtracks_changed_title
if changed_titles != null && changed_titles.size() > 0:
changes = true changes = true
Settings.delete_children(soundtracks_changed_title_vbox) Settings.delete_children(soundtracks_changed_title_vbox)
make_header_label(soundtracks_changed_title_vbox, "Changed Title") make_header_label(soundtracks_changed_title_vbox, "Changed Title")
var row: int = 0 var row: int = 0
for old_soundtrack_title: String in data_received.soundtracks_changed_title: for old_soundtrack_title: String in changed_titles:
var new_soundtrack_title: String = data_received.soundtracks_changed_title[old_soundtrack_title] var new_soundtrack_title: String = changed_titles[old_soundtrack_title]
soundtracks_changed_title_vbox.add_child(make_soundtrack_row( soundtracks_changed_title_vbox.add_child(make_soundtrack_row(
old_soundtrack_title + "\nchanged title to\n" + new_soundtrack_title, row)) old_soundtrack_title + "\nchanged title to\n" + new_soundtrack_title, row))
row += 1 row += 1
soundtracks_changed_title_vbox.visible = true soundtracks_changed_title_vbox.visible = true
if show_section_if_has_data(data_received.soundtracks_changed_content, soundtracks_changed_content_vbox, soundtracks_changed_content_separator, "Changed Content"): if show_section_if_has_data(data_received.soundtracks_changed_content,
soundtracks_changed_content_vbox, soundtracks_changed_content_separator,
"Changed Content"):
changes = true changes = true
if show_section_if_has_data(data_received.soundtracks_removed, soundtracks_removed_vbox, soundtracks_removed_separator, "Removed"): if show_section_if_has_data(data_received.soundtracks_removed, soundtracks_removed_vbox,
soundtracks_removed_separator, "Removed"):
changes = true changes = true
# The server's name for the errors it caught
if data_received.catched_errors != null && data_received.catched_errors.size() > 0: if data_received.catched_errors != null && data_received.catched_errors.size() > 0:
catched_errors_vbox.visible = true caught_errors_vbox.visible = true
make_list(data_received.catched_errors, catched_errors_vbox, "Errors") make_list(data_received.catched_errors, caught_errors_vbox, "Errors")
# Separators go only between visible columns, not after the last one # Separators go only between visible columns, not after the last one
var visible_sections: Array = [] var visible_sections: Array = []
if soundtracks_added_vbox.visible: if soundtracks_added_vbox.visible:
@@ -155,11 +135,11 @@ func on_sync_finished(data_received: Dictionary) -> void:
visible_sections.append("changed_content") visible_sections.append("changed_content")
if soundtracks_removed_vbox.visible: if soundtracks_removed_vbox.visible:
visible_sections.append("removed") visible_sections.append("removed")
# Show section separators only if there's a visible section after them # Show section separators only if there's a visible section after them
for i in range(visible_sections.size()): for i: int in range(visible_sections.size()):
var section = visible_sections[i] var section: String = visible_sections[i]
var separator_map = { var separator_map: Dictionary = {
"added": soundtracks_added_separator, "added": soundtracks_added_separator,
"readded": soundtracks_readded_separator, "readded": soundtracks_readded_separator,
"changed_title": soundtracks_changed_title_separator, "changed_title": soundtracks_changed_title_separator,
@@ -171,10 +151,11 @@ func on_sync_finished(data_received: Dictionary) -> void:
separator_map[section].visible = true separator_map[section].visible = true
else: else:
separator_map[section].visible = false separator_map[section].visible = false
if !changes: if !changes:
no_changes_label.visible = true no_changes_label.visible = true
sync_time.text = "Total time: " + data_received.total_time if data_received.has("total_time") else "" sync_time.text = "Total time: " + data_received.total_time \
if data_received.has("total_time") else ""
Settings.currently_syncing = false Settings.currently_syncing = false
sync_timer.stop() sync_timer.stop()
GameLog.debug("sync finished") GameLog.debug("sync finished")
@@ -182,9 +163,11 @@ func on_sync_finished(data_received: Dictionary) -> void:
restart_sync_button.visible = true restart_sync_button.visible = true
sync_finished.emit() sync_finished.emit()
func list_visible(vbox: VBoxContainer, seperator: VSeparator) -> void:
func list_visible(vbox: VBoxContainer, separator: VSeparator) -> void:
vbox.visible = true vbox.visible = true
seperator.visible = true separator.visible = true
func make_header_label(vbox: VBoxContainer, text: String) -> void: func make_header_label(vbox: VBoxContainer, text: String) -> void:
var label: Label = Label.new() var label: Label = Label.new()
@@ -194,6 +177,7 @@ func make_header_label(vbox: VBoxContainer, text: String) -> void:
label.text = text label.text = text
vbox.add_child(label) vbox.add_child(label)
# Items alternate between white and orange text as well as background shade # Items alternate between white and orange text as well as background shade
func make_soundtrack_row(text: String, row: int) -> ListRow: func make_soundtrack_row(text: String, row: int) -> ListRow:
var list_row: ListRow = ListRow.create(text, row, 18, HORIZONTAL_ALIGNMENT_CENTER) var list_row: ListRow = ListRow.create(text, row, 18, HORIZONTAL_ALIGNMENT_CENTER)
@@ -201,6 +185,7 @@ func make_soundtrack_row(text: String, row: int) -> ListRow:
list_row.label.add_theme_color_override("font_color", Color(1, 0.5, 0)) list_row.label.add_theme_color_override("font_color", Color(1, 0.5, 0))
return list_row return list_row
func reset_sync_result() -> void: func reset_sync_result() -> void:
sync_progress_bar.value = 0 sync_progress_bar.value = 0
sync_time.text = "" sync_time.text = ""
@@ -216,7 +201,8 @@ func reset_sync_result() -> void:
soundtracks_changed_content_separator.visible = false soundtracks_changed_content_separator.visible = false
soundtracks_removed_vbox.visible = false soundtracks_removed_vbox.visible = false
soundtracks_removed_separator.visible = false soundtracks_removed_separator.visible = false
catched_errors_vbox.visible = false caught_errors_vbox.visible = false
func make_list(list: Array, vbox: VBoxContainer, text: String) -> void: func make_list(list: Array, vbox: VBoxContainer, text: String) -> void:
Settings.delete_children(vbox) Settings.delete_children(vbox)
@@ -224,16 +210,18 @@ func make_list(list: Array, vbox: VBoxContainer, text: String) -> void:
for row: int in list.size(): for row: int in list.size():
vbox.add_child(make_soundtrack_row(list[row], row)) vbox.add_child(make_soundtrack_row(list[row], row))
func show_sync_result() -> void: func show_sync_result() -> void:
sync_title_label.text = "Sync Result" sync_title_label.text = "Sync Result"
progress_hbox.visible = false progress_hbox.visible = false
sync_hbox.visible = true sync_hbox.visible = true
restart_sync_button.visible = true restart_sync_button.visible = true
func show_sync_progress() -> void: func show_sync_progress() -> void:
sync_title_label.text = "Sync in Progress" sync_title_label.text = "Sync in Progress"
progress_hbox.visible = true progress_hbox.visible = true
no_changes_label.visible = false no_changes_label.visible = false
sync_hbox.visible = false sync_hbox.visible = false
catched_errors_vbox.visible = false caught_errors_vbox.visible = false
restart_sync_button.visible = false restart_sync_button.visible = false
+9
View File
@@ -87,6 +87,7 @@ static func prepare_autoloads() -> void:
func run(on_state: Callable) -> void: func run(on_state: Callable) -> void:
# The debug build lowers the winning score on startup; keep it out of reach # The debug build lowers the winning score on startup; keep it out of reach
Settings.winning_score = 20 Settings.winning_score = 20
_stop_caret_blink(_main)
await _settle() await _settle()
await on_state.call("startup", null) await on_state.call("startup", null)
@@ -194,6 +195,14 @@ func _call(method: String, args: Array = []) -> Variant:
return _main.callv(method, args) return _main.callv(method, args)
# A blinking text cursor would be shown in some pictures and not in others
func _stop_caret_blink(node: Node) -> void:
if node is TextEdit || node is LineEdit:
node.caret_blink = false
for child: Node in node.get_children():
_stop_caret_blink(child)
func _server() -> FakeHttpTransport: func _server() -> FakeHttpTransport:
return MusicServerClient.transport return MusicServerClient.transport
+8 -2
View File
@@ -58,9 +58,14 @@ const RENAMED_SCENES: Dictionary = {
"Player": "PlayerRow", "Player": "PlayerRow",
} }
## Nodes renamed inside a popup: old path from the main window, and new path. const SYNC_COLUMNS: String = "SyncPopup/SyncPanel/ScrollContainer/SyncVBoxContainer"
## Nodes renamed inside a popup: the path after the moves above, and the new path.
const RENAMED_PATHS: Dictionary = { const RENAMED_PATHS: Dictionary = {
"SearchPopup/ScrollContainer/VBoxContainer": "SearchPopup/ScrollContainer/SearchResults", "SearchPopup/ScrollContainer/VBoxContainer": "SearchPopup/ScrollContainer/SearchResults",
SYNC_COLUMNS + "/CatchedErrorsVBox": SYNC_COLUMNS + "/CaughtErrorsVBox",
SYNC_COLUMNS + "/CaughtErrorsVBox/CatchedErrorsLabel":
SYNC_COLUMNS + "/CaughtErrorsVBox/CaughtErrorsLabel",
} }
## How far a node may be from its old place, in pixels. ## How far a node may be from its old place, in pixels.
@@ -102,9 +107,10 @@ static func new_path(old_path: String) -> String:
var first: String = parts[0] var first: String = parts[0]
parts[0] = MOVED.get(first, first) parts[0] = MOVED.get(first, first)
var path: String = "/".join(parts) var path: String = "/".join(parts)
# In order, so a node inside a renamed node is found after its parent's rename
for renamed: String in RENAMED_PATHS: for renamed: String in RENAMED_PATHS:
if path == renamed || path.begins_with(renamed + "/"): if path == renamed || path.begins_with(renamed + "/"):
return RENAMED_PATHS[renamed] + path.trim_prefix(renamed) path = RENAMED_PATHS[renamed] + path.trim_prefix(renamed)
return path return path
+2 -1
View File
@@ -105,7 +105,8 @@ func test_rows_follow_player_order_and_numbering() -> void:
_add("Marle") _add("Marle")
_add("Lucca") _add("Lucca")
assert_array(_rows().map(func(row: PlayerRow) -> String: return row.player_object.player_name)) \ assert_array(_rows().map(
func(row: PlayerRow) -> String: return row.player_object.player_name)) \
.contains_exactly(["Crono", "Marle", "Lucca"]) .contains_exactly(["Crono", "Marle", "Lucca"])
assert_array(_rows().map(func(row: PlayerRow) -> int: return row.list_index)) \ assert_array(_rows().map(func(row: PlayerRow) -> int: return row.list_index)) \
.contains_exactly([0, 1, 2]) .contains_exactly([0, 1, 2])
+15 -8
View File
@@ -9,6 +9,12 @@ const MAIN_WINDOW_CHILDREN: Array[String] = [
"ShortcutsPopup", "StatisticsPopupPanel", "AboutPopup", "LogPopup", "ShortcutsPopup", "StatisticsPopupPanel", "AboutPopup", "LogPopup",
] ]
## The quiz buttons that are disabled while the server syncs.
const LOCKED_BY_SYNC: Array[String] = [
"QuizToolbar/SoundTestButton", "QuizToolbar/ResetPlaylistButton",
"SongControls/ShowAnswerButton", "SongControls/NextButton",
]
const TOP_BAR_CHILDREN: Array[String] = [ const TOP_BAR_CHILDREN: Array[String] = [
"TopPanelContainer", "Title", "SyncButton", "SearchButton", "StatisticsButton", "TopPanelContainer", "Title", "SyncButton", "SearchButton", "StatisticsButton",
"ShortcutsButton", "AboutButton", "QRButton", "ShortcutsButton", "AboutButton", "QRButton",
@@ -130,7 +136,8 @@ func test_points_from_the_player_list_reach_the_song_and_log() -> void:
(_player_row(crono).get_node("HBoxContainer/AddPoint") as BaseButton).pressed.emit() (_player_row(crono).get_node("HBoxContainer/AddPoint") as BaseButton).pressed.emit()
assert_array(Playlist.song_object_array[0].players_given_point).contains_exactly([crono.id]) assert_array(Playlist.song_object_array[0].players_given_point).contains_exactly([crono.id])
assert_int((_window.get_node("%LogPopup").get_node("%LogVBoxContainer") as Node).get_child_count()) \ var log_rows: Node = _window.get_node("%LogPopup").get_node("%LogVBoxContainer")
assert_int(log_rows.get_child_count()) \
.is_equal(1) .is_equal(1)
@@ -143,12 +150,13 @@ func test_character_button_in_the_player_list_picks_a_new_character() -> void:
assert_bool(character_select.visible).is_true() assert_bool(character_select.visible).is_true()
var texture: ImageTexture = ImageTexture.create_from_image( var texture: ImageTexture = ImageTexture.create_from_image(
Image.create(4, 4, false, Image.FORMAT_RGBA8)) Image.create(4, 4, false, Image.FORMAT_RGBA8))
character_select.new_new_character_selected.emit(texture) character_select.character_selected.emit(texture)
assert_object(crono.character).is_same(texture) assert_object(crono.character).is_same(texture)
func test_sound_test_button_fetches_the_sound_test_song() -> void: func test_sound_test_button_fetches_the_sound_test_song() -> void:
_server.respond_bytes("/music/soundTest", FileAccess.get_file_as_bytes("res://tests/data/short.mp3")) _server.respond_bytes("/music/soundTest",
FileAccess.get_file_as_bytes("res://tests/data/short.mp3"))
await _click_at(_center(_quiz.get_node("QuizToolbar/SoundTestButton"))) await _click_at(_center(_quiz.get_node("QuizToolbar/SoundTestButton")))
@@ -166,7 +174,8 @@ func test_loaded_song_list_shows_in_the_music_list() -> void:
_quiz.song_loader.load_song_list() _quiz.song_loader.load_song_list()
var rows: Array[Node] = _quiz.get_node("MusicListPanel/ScrollContainer/MusicList").get_children() var rows: Array[Node] = _quiz.get_node("MusicListPanel/ScrollContainer/MusicList") \
.get_children()
assert_array(rows.map(func(row: SongRow) -> int: return row.song_object.song_number)) \ assert_array(rows.map(func(row: SongRow) -> int: return row.song_object.song_number)) \
.contains_exactly([0, 1]) .contains_exactly([0, 1])
@@ -203,8 +212,7 @@ func test_sync_disables_quiz_buttons_until_finished() -> void:
_top_bar_button("SyncButton").pressed.emit() _top_bar_button("SyncButton").pressed.emit()
sync_popup.sync_timer.stop() sync_popup.sync_timer.stop()
for button_name: String in ["QuizToolbar/SoundTestButton", "QuizToolbar/ResetPlaylistButton", "SongControls/ShowAnswerButton", for button_name: String in LOCKED_BY_SYNC:
"SongControls/NextButton"]:
assert_bool((_quiz.get_node(button_name) as Button).disabled) \ assert_bool((_quiz.get_node(button_name) as Button).disabled) \
.override_failure_message(button_name + " not disabled").is_true() .override_failure_message(button_name + " not disabled").is_true()
assert_bool(_top_bar_button("SyncButton").disabled).is_false() assert_bool(_top_bar_button("SyncButton").disabled).is_false()
@@ -214,8 +222,7 @@ func test_sync_disables_quiz_buttons_until_finished() -> void:
sync_popup._on_sync_timeout() sync_popup._on_sync_timeout()
await _runner.simulate_frames(2) await _runner.simulate_frames(2)
for button_name: String in ["QuizToolbar/SoundTestButton", "QuizToolbar/ResetPlaylistButton", "SongControls/ShowAnswerButton", for button_name: String in LOCKED_BY_SYNC:
"SongControls/NextButton"]:
assert_bool((_quiz.get_node(button_name) as Button).disabled) \ assert_bool((_quiz.get_node(button_name) as Button).disabled) \
.override_failure_message(button_name + " still disabled").is_false() .override_failure_message(button_name + " still disabled").is_false()
# The search list in the main window and the quiz playlist are both reloaded # The search list in the main window and the quiz playlist are both reloaded
+9 -4
View File
@@ -1,7 +1,9 @@
extends GdUnitTestSuite extends GdUnitTestSuite
## Downloading songs into the Playlist ahead of time, against a fake server. ## Downloading songs into the Playlist ahead of time, against a fake server.
const SONG_INFO: Dictionary = {"SongNo": 0, "Soundtrack": "Chrono Trigger", "Song": "Corridors of Time"} const SONG_INFO: Dictionary = {
"SongNo": 0, "Soundtrack": "Chrono Trigger", "Song": "Corridors of Time",
}
var _snapshot: AutoloadSnapshot var _snapshot: AutoloadSnapshot
var _server: FakeHttpTransport var _server: FakeHttpTransport
@@ -32,7 +34,8 @@ func test_load_song_list_adds_songs_already_played_on_the_server() -> void:
_server.respond_json("/music/list", [ _server.respond_json("/music/list", [
{"SongNo": 0, "Soundtrack": "Chrono Trigger", "Song": "Corridors of Time"}, {"SongNo": 0, "Soundtrack": "Chrono Trigger", "Song": "Corridors of Time"},
]) ])
_server.respond_json("/music/info", {"SongNo": 1, "Soundtrack": "Final Fantasy VI", "Song": "Dancing Mad"}) _server.respond_json("/music/info",
{"SongNo": 1, "Soundtrack": "Final Fantasy VI", "Song": "Dancing Mad"})
var signals: GdUnitSignalAssert = assert_signal(monitor_signals(_loader)) var signals: GdUnitSignalAssert = assert_signal(monitor_signals(_loader))
_loader.load_song_list() _loader.load_song_list()
@@ -57,7 +60,8 @@ func test_fetch_song_downloads_queues_and_adds_the_song() -> void:
_loader.fetch_song() _loader.fetch_song()
await signals.is_emitted("song_added") await signals.is_emitted("song_added")
assert_array(_server.requested_paths).contains_exactly(["/music/rand", "/music/addQue", "/music/info"]) assert_array(_server.requested_paths) \
.contains_exactly(["/music/rand", "/music/addQue", "/music/info"])
var song: SongObject = Playlist.song_object_array[0] var song: SongObject = Playlist.song_object_array[0]
assert_str(song.song_title).is_equal("Corridors of Time") assert_str(song.song_title).is_equal("Corridors of Time")
assert_object(song.song).is_not_null() assert_object(song.song).is_not_null()
@@ -75,7 +79,8 @@ func test_fetch_song_keeps_going_until_the_cache_is_full() -> void:
func test_low_played_mode_uses_its_endpoint() -> void: func test_low_played_mode_uses_its_endpoint() -> void:
Settings.use_low_played_mode = true Settings.use_low_played_mode = true
_server.respond_bytes("/music/rand/low", FileAccess.get_file_as_bytes("res://tests/data/short.mp3")) _server.respond_bytes("/music/rand/low",
FileAccess.get_file_as_bytes("res://tests/data/short.mp3"))
_loader.fetch_song() _loader.fetch_song()
+4 -3
View File
@@ -1,4 +1,5 @@
extends GdUnitTestSuite extends GdUnitTestSuite
## The sync popup on its own: starting a sync, its progress, and how the result is shown.
var _server: FakeHttpTransport var _server: FakeHttpTransport
var _snapshot: AutoloadSnapshot var _snapshot: AutoloadSnapshot
@@ -132,7 +133,7 @@ func test_errors_are_listed() -> void:
_finish_sync(result) _finish_sync(result)
assert_bool(_node("CatchedErrorsVBox").visible).is_true() assert_bool(_node("CaughtErrorsVBox").visible).is_true()
func test_reopening_after_sync_shows_result_without_syncing_again() -> void: func test_reopening_after_sync_shows_result_without_syncing_again() -> void:
@@ -152,7 +153,7 @@ func test_errors_section_has_errors_header() -> void:
_finish_sync(result) _finish_sync(result)
assert_str((_node("CatchedErrorsVBox").get_child(0) as Label).text).is_equal("Errors") assert_str((_node("CaughtErrorsVBox").get_child(0) as Label).text).is_equal("Errors")
func test_long_names_wrap_instead_of_widening_the_window() -> void: func test_long_names_wrap_instead_of_widening_the_window() -> void:
@@ -173,7 +174,7 @@ func test_long_names_wrap_instead_of_widening_the_window() -> void:
var panel_width: float = (_window.get_node("SyncPanel") as Control).size.x var panel_width: float = (_window.get_node("SyncPanel") as Control).size.x
assert_float(_node("SyncHBoxContainer").get_combined_minimum_size().x) \ assert_float(_node("SyncHBoxContainer").get_combined_minimum_size().x) \
.is_less(panel_width) .is_less(panel_width)
assert_float(_node("CatchedErrorsVBox").get_combined_minimum_size().x) \ assert_float(_node("CaughtErrorsVBox").get_combined_minimum_size().x) \
.is_less(panel_width) .is_less(panel_width)
+1
View File
@@ -1,4 +1,5 @@
extends GdUnitTestSuite extends GdUnitTestSuite
## ListRow: text, alternating backgrounds and wrapping.
func _background(list_row: ListRow) -> Color: func _background(list_row: ListRow) -> Color:
+1
View File
@@ -1,4 +1,5 @@
extends GdUnitTestSuite extends GdUnitTestSuite
## How the music player shows a song's time, including the hidden parts.
var _music_player: MusicPlayer var _music_player: MusicPlayer
var _hide_beginning: bool var _hide_beginning: bool
+3 -1
View File
@@ -1,4 +1,5 @@
extends GdUnitTestSuite extends GdUnitTestSuite
## MusicServerClient: which paths it asks for and how it handles the answers.
var _server: FakeHttpTransport var _server: FakeHttpTransport
@@ -115,7 +116,8 @@ func test_mark_song_played_sends_put() -> void:
func test_character_name_is_url_encoded() -> void: func test_character_name_is_url_encoded() -> void:
MusicServerClient.fetch_character_image("Paper Mario.png", func(_body: PackedByteArray) -> void: pass) MusicServerClient.fetch_character_image("Paper Mario.png",
func(_body: PackedByteArray) -> void: pass)
assert_array(_server.requested_paths).contains_exactly(["/character?name=Paper%20Mario.png"]) assert_array(_server.requested_paths).contains_exactly(["/character?name=Paper%20Mario.png"])
+7 -6
View File
@@ -1,4 +1,5 @@
extends GdUnitTestSuite extends GdUnitTestSuite
## PlayerObject: points, and the first point, match point and winner signals.
var _winning_score: int var _winning_score: int
var _player: PlayerObject var _player: PlayerObject
@@ -20,7 +21,7 @@ func test_first_point_triggers_first_point_signal() -> void:
_player.add_point() _player.add_point()
assert_int(_player.player_score).is_equal(1) assert_int(_player.player_score).is_equal(1)
await assert_signal(_player).is_emitted("first_point_triggerd") await assert_signal(_player).is_emitted("first_point_triggered")
await assert_signal(_player).is_emitted("player_point_given") await assert_signal(_player).is_emitted("player_point_given")
@@ -30,7 +31,7 @@ func test_one_point_from_winning_triggers_match_point() -> void:
_player.add_point() _player.add_point()
await assert_signal(_player).is_emitted("match_point_triggerd") await assert_signal(_player).is_emitted("match_point_triggered")
func test_reaching_winning_score_triggers_winner() -> void: func test_reaching_winning_score_triggers_winner() -> void:
@@ -41,7 +42,7 @@ func test_reaching_winning_score_triggers_winner() -> void:
_player.add_point() _player.add_point()
assert_int(_player.player_score).is_equal(3) assert_int(_player.player_score).is_equal(3)
await assert_signal(_player).is_emitted("winner_triggerd") await assert_signal(_player).is_emitted("winner_triggered")
func test_score_does_not_go_past_winning_score() -> void: func test_score_does_not_go_past_winning_score() -> void:
@@ -91,7 +92,7 @@ func test_dropping_back_to_zero_allows_first_point_again() -> void:
_player.add_point() _player.add_point()
await assert_signal(_player).is_emitted("first_point_triggerd") await assert_signal(_player).is_emitted("first_point_triggered")
func test_reset_points() -> void: func test_reset_points() -> void:
@@ -125,7 +126,7 @@ func test_winning_score_of_one_is_won_with_first_point() -> void:
_player.add_point() _player.add_point()
await assert_signal(_player).is_emitted("winner_triggerd") await assert_signal(_player).is_emitted("winner_triggered")
func test_first_point_is_match_point_when_winning_score_is_two() -> void: func test_first_point_is_match_point_when_winning_score_is_two() -> void:
@@ -134,5 +135,5 @@ func test_first_point_is_match_point_when_winning_score_is_two() -> void:
_player.add_point() _player.add_point()
await assert_signal(_player).is_emitted("match_point_triggerd") await assert_signal(_player).is_emitted("match_point_triggered")
assert_bool(_player.is_first_point).is_false() assert_bool(_player.is_first_point).is_false()
+1
View File
@@ -1,4 +1,5 @@
extends GdUnitTestSuite extends GdUnitTestSuite
## Playlist: moving through the songs, answering them and keeping points.
var _server: FakeHttpTransport var _server: FakeHttpTransport
var _add_to_stats: bool var _add_to_stats: bool
+3 -1
View File
@@ -1,4 +1,5 @@
extends GdUnitTestSuite extends GdUnitTestSuite
## How the search popup finds and orders matching soundtracks.
const SearchWindowScript: GDScript = preload("res://search_popup.gd") const SearchWindowScript: GDScript = preload("res://search_popup.gd")
@@ -36,7 +37,8 @@ func test_ignores_spaces() -> void:
func test_and_matches_ampersand() -> void: func test_and_matches_ampersand() -> void:
assert_array(_search.find_matches("mario and luigi")).contains(["Mario & Luigi: Superstar Saga"]) assert_array(_search.find_matches("mario and luigi")) \
.contains(["Mario & Luigi: Superstar Saga"])
func test_accented_letters_match_plain_letters() -> void: func test_accented_letters_match_plain_letters() -> void:
+1
View File
@@ -1,4 +1,5 @@
extends GdUnitTestSuite extends GdUnitTestSuite
## Settings: the version number.
func test_version_comes_from_project_settings() -> void: func test_version_comes_from_project_settings() -> void:
+1
View File
@@ -1,4 +1,5 @@
extends GdUnitTestSuite extends GdUnitTestSuite
## SongObject: what it shows before and after the answer, and its points.
var _is_debug: bool var _is_debug: bool
var _song: SongObject var _song: SongObject
+12 -7
View File
@@ -1,19 +1,24 @@
class_name VolumeSlider class_name VolumeSlider
extends HSlider extends HSlider
## A slider that sets the volume of an audio bus.
@export ## The audio bus whose volume the slider sets.
var audio_bus_name := "music"
@onready @export var audio_bus_name: String = "music"
var _bus: int
func _ready(): var _bus: int
func _ready() -> void:
_bus = AudioServer.get_bus_index(audio_bus_name) _bus = AudioServer.get_bus_index(audio_bus_name)
value_changed.connect(_on_value_changed) value_changed.connect(_on_value_changed)
value = db_to_linear(AudioServer.get_bus_volume_db(_bus)) value = db_to_linear(AudioServer.get_bus_volume_db(_bus))
func _on_value_changed(changed_value: float) -> void:
AudioServer.set_bus_volume_db(_bus, linear_to_db(changed_value))
## Sets the bus volume to [param changed_value], from 0 (silent) to 1 (full).
func change_volume(changed_value: float) -> void: func change_volume(changed_value: float) -> void:
AudioServer.set_bus_volume_db(_bus, linear_to_db(changed_value)) AudioServer.set_bus_volume_db(_bus, linear_to_db(changed_value))
func _on_value_changed(changed_value: float) -> void:
AudioServer.set_bus_volume_db(_bus, linear_to_db(changed_value))
+5 -14
View File
@@ -1,17 +1,11 @@
class_name WinnerPopup class_name WinnerPopup
extends BasePopup extends BasePopup
## Shows who won, with their character, and where the other players placed.
@onready @onready var winner_panel: Panel = %WinnerPanel
var winner_panel: Panel = %WinnerPanel @onready var winner_picture: TextureRect = %WinnerPicture
@onready var winner_label: Label = %WinnerLabel
@onready @onready var players_list: VBoxContainer = %PlayersList
var winner_picture: TextureRect = %WinnerPicture
@onready
var winner_label: Label = %WinnerLabel
@onready
var players_list: VBoxContainer = %PlayersList
func _ready() -> void: func _ready() -> void:
@@ -54,6 +48,3 @@ func show_winner(winner: PlayerObject) -> void:
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
players_list.add_child(label) players_list.add_child(label)
placing += 1 placing += 1