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:
1 parent
674604425f
commit
a0428e3475
45 files changed
+580
-589
No files matched your search
+2
-2
@@ -145,13 +145,13 @@ text = "SoundtracksRemoved"
|
||||
unique_name_in_owner = true
|
||||
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
|
||||
visible = false
|
||||
layout_mode = 2
|
||||
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
|
||||
text = "CatchedErrors"
|
||||
horizontal_alignment = 1
|
||||
|
||||
+8
-14
@@ -1,18 +1,8 @@
|
||||
class_name AboutPopup
|
||||
extends BasePopup
|
||||
## Shows the game version and what is new in each version.
|
||||
|
||||
@onready
|
||||
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
|
||||
## What is new in each version, newest first.
|
||||
const WHATS_NEW: Dictionary = {
|
||||
"1.9.0": "Made improvements to the Search window
|
||||
Made small changes to the inspiration list scrolling
|
||||
@@ -51,15 +41,19 @@ const WHATS_NEW: Dictionary = {
|
||||
"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:
|
||||
super()
|
||||
version_label.text = Settings.version
|
||||
|
||||
|
||||
# Build changelog text with version headers
|
||||
var changelog_text: String = ""
|
||||
for version: String in WHATS_NEW.keys():
|
||||
changelog_text += "\n" + version + "\n"
|
||||
changelog_text += WHATS_NEW[version] + "\n"
|
||||
|
||||
|
||||
whats_new_label.text = changelog_text
|
||||
@@ -6,6 +6,7 @@ extends Control
|
||||
## override [method open] or [method close] and call [code]super()[/code].
|
||||
|
||||
## The visible part of the popup. Clicks outside it close the popup.
|
||||
|
||||
@export var panel: Control
|
||||
|
||||
|
||||
|
||||
+19
-18
@@ -1,21 +1,30 @@
|
||||
class_name CharacterCard
|
||||
extends Control
|
||||
## One character in the character select popup: its picture and name. Clicking it picks
|
||||
## the character.
|
||||
|
||||
@onready
|
||||
var panel: Panel = $Panel
|
||||
|
||||
@onready
|
||||
var character_image: TextureRect = %CharacterTextureRect
|
||||
|
||||
@onready
|
||||
var character_name_label: Label = %CharacterName
|
||||
## Emitted with the character's picture when the card is clicked.
|
||||
|
||||
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:
|
||||
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.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
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.texture = new_image
|
||||
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
@@ -1,35 +1,31 @@
|
||||
class_name CharacterSelectPopup
|
||||
extends BasePopup
|
||||
|
||||
@onready
|
||||
var character_panel: Panel = %CharacterPanel
|
||||
|
||||
@onready
|
||||
var title_label: Label = %TitleLabel
|
||||
|
||||
@onready
|
||||
var character_grid: GridContainer = %CharacterGridContainer
|
||||
|
||||
@onready
|
||||
var close_timer: Timer = %CloseTimer
|
||||
## Emitted with the picture of the character that was picked.
|
||||
signal character_selected(texture: ImageTexture)
|
||||
|
||||
var can_select_character: bool = false
|
||||
|
||||
signal new_new_character_selected(file_name: String)
|
||||
|
||||
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:
|
||||
close_timer.timeout.connect(_on_close_timeout)
|
||||
|
||||
|
||||
func _on_close_timeout() -> void:
|
||||
GameLog.debug("_on_close_timeout!")
|
||||
Settings.character_select_open = false
|
||||
close_timer.stop()
|
||||
|
||||
|
||||
func load_characters() -> void:
|
||||
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")
|
||||
Settings.delete_children(character_grid)
|
||||
if typeof(list) == TYPE_ARRAY:
|
||||
@@ -40,9 +36,10 @@ func load_characters() -> void:
|
||||
character_box.custom_minimum_size = Vector2(235, 145)
|
||||
character_box.size = Vector2(235, 145)
|
||||
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)
|
||||
|
||||
|
||||
func show_grid(player_name: String) -> void:
|
||||
GameLog.debug("show_grid")
|
||||
title_label.text = "Select character for " + player_name
|
||||
@@ -51,16 +48,18 @@ func show_grid(player_name: String) -> void:
|
||||
can_select_character = true
|
||||
Settings.character_select_open = true
|
||||
|
||||
|
||||
func close() -> void:
|
||||
super()
|
||||
Settings.character_select_open = false
|
||||
|
||||
|
||||
func _on_character_selected(texture: ImageTexture) -> void:
|
||||
GameLog.debug("_on_character_selected")
|
||||
# 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
|
||||
self.visible = false
|
||||
visible = false
|
||||
if can_select_character:
|
||||
new_new_character_selected.emit(texture)
|
||||
character_selected.emit(texture)
|
||||
close_timer.start()
|
||||
can_select_character = false
|
||||
+1
-1
@@ -6,8 +6,8 @@ extends RefCounted
|
||||
|
||||
## 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.
|
||||
var timeout_seconds: float = 120.0
|
||||
|
||||
var timeout_seconds: float = 120.0
|
||||
var _owner: Node
|
||||
|
||||
|
||||
|
||||
+10
-9
@@ -4,35 +4,36 @@ extends ScrollContainer
|
||||
## by itself, at the speed set in the settings.
|
||||
|
||||
## The soundtracks shown, in order.
|
||||
|
||||
var soundtracks: Array = []
|
||||
|
||||
# Scroll direction: 1 = down, -1 = up
|
||||
var scroll_direction: int = 1
|
||||
|
||||
@onready
|
||||
var _rows: VBoxContainer = $VBoxContainer
|
||||
@onready var _rows: VBoxContainer = $VBoxContainer
|
||||
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
scroll_direction = 1
|
||||
|
||||
# Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
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_bar = get_v_scroll_bar()
|
||||
var scroll_bar: VScrollBar = get_v_scroll_bar()
|
||||
|
||||
# Don't scroll if list is empty or too short
|
||||
if scroll_bar.max_value <= 0:
|
||||
return
|
||||
|
||||
var current_pos = scroll_bar.value
|
||||
var current_pos: float = scroll_bar.value
|
||||
|
||||
if scroll_direction == 1:
|
||||
# Scrolling down
|
||||
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:
|
||||
# Reached bottom, reverse direction
|
||||
scroll_direction = -1
|
||||
@@ -40,7 +41,7 @@ func _process(delta: float) -> void:
|
||||
else:
|
||||
# Scrolling up
|
||||
if current_pos > 0:
|
||||
scroll_bar.value = max(current_pos + scroll_amount, 0)
|
||||
scroll_bar.value = maxf(current_pos + scroll_amount, 0)
|
||||
else:
|
||||
# Reached top, reverse direction
|
||||
scroll_direction = 1
|
||||
@@ -50,7 +51,7 @@ func _process(delta: float) -> void:
|
||||
## Fetches every soundtrack from the server and shows them.
|
||||
func load_from_server() -> void:
|
||||
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:
|
||||
show_soundtracks(array)
|
||||
else:
|
||||
|
||||
@@ -16,6 +16,8 @@ var label: Label
|
||||
|
||||
## The item's text.
|
||||
var text: String:
|
||||
|
||||
|
||||
get:
|
||||
return label.text
|
||||
|
||||
|
||||
+8
-6
@@ -1,19 +1,20 @@
|
||||
class_name LogPopup
|
||||
extends BasePopup
|
||||
|
||||
@onready
|
||||
var log_panel: Panel = %LogPanel
|
||||
|
||||
@onready
|
||||
var log_box: VBoxContainer = %LogVBoxContainer
|
||||
## The game log: a row with the time for each thing that happens in the quiz, such as a
|
||||
## player getting a point. Opened with Alt+L.
|
||||
|
||||
var log_rows: Array
|
||||
|
||||
@onready var log_panel: Panel = %LogPanel
|
||||
@onready var log_box: VBoxContainer = %LogVBoxContainer
|
||||
|
||||
|
||||
func add_log_row(text: String) -> void:
|
||||
var log_row: String = get_time() + text
|
||||
log_rows.append(log_row)
|
||||
log_box.add_child(make_log_label(log_row))
|
||||
|
||||
|
||||
func make_log_label(row: String) -> Label:
|
||||
var log_label: Label = Label.new()
|
||||
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
|
||||
return log_label
|
||||
|
||||
|
||||
func get_time() -> String:
|
||||
var time: Dictionary = Time.get_time_dict_from_system()
|
||||
return "%02d:%02d:%02d " % [time.hour, time.minute, time.second]
|
||||
+15
-34
@@ -5,39 +5,20 @@ extends Control
|
||||
## Everything that is not part of a quiz lives here, so other views can be added next
|
||||
## 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.
|
||||
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:
|
||||
popup_backdrop = PopupBackdrop.add_to(self, _popups())
|
||||
@@ -50,8 +31,8 @@ func _ready() -> void:
|
||||
top_bar.about_pressed.connect(show_about)
|
||||
top_bar.qr_pressed.connect(show_qr)
|
||||
|
||||
sync_popup.connect("sync_started", _on_sync_started)
|
||||
sync_popup.connect("sync_finished", _on_sync_finished)
|
||||
sync_popup.sync_started.connect(_on_sync_started)
|
||||
sync_popup.sync_finished.connect(_on_sync_finished)
|
||||
quiz.server_changed.connect(search_popup.get_list_of_soundtracks)
|
||||
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:
|
||||
GameLog.debug("Alt + Enter pressed")
|
||||
Settings.fullscreen = !Settings.fullscreen
|
||||
if Settings.fullscreen == true:
|
||||
if Settings.fullscreen:
|
||||
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
|
||||
if Settings.fullscreen == false:
|
||||
if !Settings.fullscreen:
|
||||
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
|
||||
if event.alt_pressed && event.keycode == KEY_K:
|
||||
shortcuts_popup.toggle()
|
||||
|
||||
+2
-2
@@ -3,12 +3,12 @@ extends PanelContainer
|
||||
## The list of songs in the playlist, one row per song.
|
||||
|
||||
## Emitted when the row of song number [param song_no] is clicked.
|
||||
|
||||
signal song_clicked(song_no: int)
|
||||
|
||||
var _song_list_object: PackedScene = preload("res://SongRow.tscn")
|
||||
|
||||
@onready
|
||||
var _rows: VBoxContainer = %MusicList
|
||||
@onready var _rows: VBoxContainer = %MusicList
|
||||
|
||||
|
||||
## Shows the songs in the playlist again, so it shows their latest state.
|
||||
|
||||
+41
-43
@@ -3,76 +3,59 @@ extends Control
|
||||
## The bar that plays the songs, with play, restart, progress and volume, and the
|
||||
## auto repeat switch under it.
|
||||
|
||||
# LOC 20/1-24: 136
|
||||
# LOC 12/6-24: 170
|
||||
|
||||
@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
|
||||
signal update_song_list
|
||||
signal play_next_song
|
||||
|
||||
var play_icon: Texture = preload("res://icons/play_icon_light.svg")
|
||||
var pause_icon: Texture = preload("res://icons/pause_icon_light.svg")
|
||||
|
||||
var songs: Array = []
|
||||
|
||||
var is_changing: bool = false
|
||||
var playback_position: float
|
||||
var stream: AudioStream
|
||||
var song_finished: bool = false
|
||||
|
||||
signal update_song_list
|
||||
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
|
||||
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
play_button.pressed.connect(play_or_pause)
|
||||
restart_button.pressed.connect(restart)
|
||||
progress_slider.drag_started.connect(_on_drag_started)
|
||||
progress_slider.drag_ended.connect(_on_drag_ended)
|
||||
|
||||
|
||||
audio_player.finished.connect(_on_finished)
|
||||
auto_repeat_button.pressed.connect(_on_auto_repeat_pressed)
|
||||
|
||||
|
||||
func _on_auto_repeat_pressed() -> void:
|
||||
Settings.auto_repeat_song = !Settings.auto_repeat_song
|
||||
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
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()
|
||||
if stream != null:
|
||||
music_time_label.text = format_text(progress_slider.value, stream.get_length())
|
||||
else:
|
||||
music_time_label.text = format_text(progress_slider.value, 0.0)
|
||||
|
||||
|
||||
func format_time(time: float) -> String:
|
||||
var total_seconds: int = roundi(time)
|
||||
@warning_ignore("integer_division")
|
||||
return "%02d:%02d" % [total_seconds / 60, total_seconds % 60]
|
||||
|
||||
|
||||
func format_text(part: float, total: float) -> String:
|
||||
if (Settings.hide_beginning && part < Settings.hide_for_seconds) && Settings.hide_length:
|
||||
return "??:?? / ??:??"
|
||||
@@ -83,6 +66,7 @@ func format_text(part: float, total: float) -> String:
|
||||
else:
|
||||
return format_time(part) + " / " + format_time(total)
|
||||
|
||||
|
||||
func play_or_pause() -> void:
|
||||
if song_finished:
|
||||
restart()
|
||||
@@ -100,11 +84,13 @@ func play_or_pause() -> void:
|
||||
else:
|
||||
pause()
|
||||
|
||||
|
||||
func pause() -> void:
|
||||
audio_player.stream_paused = true
|
||||
playback_position = audio_player.get_playback_position()
|
||||
play_button.texture_normal = play_icon
|
||||
|
||||
|
||||
func restart() -> void:
|
||||
audio_player.stop()
|
||||
audio_player.stream_paused = false
|
||||
@@ -114,7 +100,8 @@ func restart() -> void:
|
||||
audio_player.seek(playback_position)
|
||||
play_button.texture_normal = pause_icon
|
||||
audio_player.play()
|
||||
|
||||
|
||||
|
||||
func _on_finished() -> void:
|
||||
play_button.texture_normal = play_icon
|
||||
song_finished = true
|
||||
@@ -122,26 +109,31 @@ func _on_finished() -> void:
|
||||
play_next_song.emit()
|
||||
if Settings.auto_repeat_song:
|
||||
restart()
|
||||
|
||||
|
||||
|
||||
func _on_drag_started() -> void:
|
||||
is_changing = true
|
||||
|
||||
|
||||
func _on_drag_ended(_changed: bool) -> void:
|
||||
audio_player.seek(progress_slider.value)
|
||||
playback_position = progress_slider.value
|
||||
is_changing = false
|
||||
|
||||
|
||||
func seek(new_position: float) -> void:
|
||||
progress_slider.value += new_position
|
||||
is_changing = true
|
||||
audio_player.seek(progress_slider.value)
|
||||
playback_position = progress_slider.value
|
||||
is_changing = false
|
||||
|
||||
|
||||
|
||||
func change_volume(value: float) -> void:
|
||||
volume_slider.value += value
|
||||
volume_slider.change_volume(volume_slider.value)
|
||||
|
||||
|
||||
func _on_point_triggered(point: String) -> void:
|
||||
if point == "first":
|
||||
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"))
|
||||
elif point == "match":
|
||||
play_sound(preload("res://sounds/sound0.mp3"))
|
||||
|
||||
|
||||
|
||||
func play_sound_effect(sound_effect_name: AudioStream) -> void:
|
||||
sound_effect_player.stream = sound_effect_name
|
||||
sound_effect_player.play()
|
||||
|
||||
|
||||
func play_sound(sound_name: AudioStream) -> void:
|
||||
sound_player.stream = sound_name
|
||||
audio_player.stop()
|
||||
@@ -165,9 +159,8 @@ func play_sound(sound_name: AudioStream) -> void:
|
||||
song_finished = true
|
||||
play_button.texture_normal = play_icon
|
||||
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:
|
||||
audio_player.stream = song
|
||||
audio_player.play()
|
||||
@@ -183,6 +176,7 @@ func play_song(song: AudioStream) -> void:
|
||||
progress_slider.tick_count = round(stream.get_length() / 60)
|
||||
music_time_label.text = format_text(progress_slider.value, stream.get_length())
|
||||
|
||||
|
||||
func play_song_object(song_object_no: int) -> void:
|
||||
GameLog.debug("play_song_object")
|
||||
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)
|
||||
update_song_list.emit()
|
||||
|
||||
|
||||
func get_sound_test_song() -> void:
|
||||
var test_sound: Callable = func test_sound(body: PackedByteArray) -> void:
|
||||
var sound: AudioStream = AudioStreamMP3.new()
|
||||
@@ -220,12 +215,14 @@ func get_sound_test_song() -> void:
|
||||
##### LOCAL
|
||||
var local_path: String = '/Users/sebastian/ResilioSync/Sorterat_test/Metal Gear Solid 4 - Guns of the Patriots/2-16 Metal Gear Saga.mp3'
|
||||
|
||||
|
||||
func load_mp3(_path: String) -> AudioStream:
|
||||
var file: FileAccess = FileAccess.open(_path, FileAccess.READ)
|
||||
var sound: AudioStream = AudioStreamMP3.new()
|
||||
sound.data = file.get_buffer(file.get_length())
|
||||
return sound
|
||||
|
||||
|
||||
func play_local_song() -> void:
|
||||
if songs:
|
||||
local_path = songs[0]
|
||||
@@ -233,5 +230,6 @@ func play_local_song() -> void:
|
||||
GameLog.debug(FileAccess.file_exists(local_path))
|
||||
play_sound(load_mp3(local_path))
|
||||
|
||||
|
||||
func sound_test_local() -> void:
|
||||
play_sound(preload("res://sounds/01. Opening.mp3"))
|
||||
@@ -7,6 +7,7 @@ extends Node
|
||||
## without a running server.
|
||||
|
||||
## Sends the actual HTTP requests. Swap it out in tests.
|
||||
|
||||
var transport: HttpTransport
|
||||
|
||||
|
||||
|
||||
+22
-12
@@ -1,19 +1,28 @@
|
||||
class_name PlayerNameField
|
||||
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
|
||||
|
||||
## Emitted when Alt+A is pressed.
|
||||
signal close_pressed
|
||||
|
||||
@export
|
||||
var LIMIT: int = 10
|
||||
var current_text = ''
|
||||
var cursor_line = 0
|
||||
var cursor_column = 0
|
||||
## The longest name a player can have.
|
||||
const MAX_NAME_LENGTH: int = 10
|
||||
|
||||
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)
|
||||
|
||||
func _input(event):
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if event is InputEventKey and event.pressed:
|
||||
if event.alt_pressed && event.keycode == KEY_A:
|
||||
close_pressed.emit()
|
||||
@@ -21,15 +30,16 @@ func _input(event):
|
||||
if event.keycode == KEY_ENTER || event.keycode == KEY_KP_ENTER:
|
||||
enter_key_pressed.emit()
|
||||
|
||||
func _on_text_changed():
|
||||
|
||||
func _on_text_changed() -> void:
|
||||
if text.contains("\n"):
|
||||
text = text.replace("\n", "")
|
||||
|
||||
var new_text : String = text
|
||||
if new_text.length() > LIMIT:
|
||||
|
||||
var new_text: String = text
|
||||
if new_text.length() > MAX_NAME_LENGTH:
|
||||
text = current_text
|
||||
# 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_column(cursor_column)
|
||||
|
||||
|
||||
+23
-19
@@ -1,40 +1,42 @@
|
||||
extends RefCounted
|
||||
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 match_point_triggerd
|
||||
signal winner_triggerd
|
||||
signal first_point_triggered
|
||||
signal match_point_triggered
|
||||
signal winner_triggered
|
||||
signal point_given_sound
|
||||
signal player_point_given
|
||||
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.
|
||||
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)
|
||||
|
||||
|
||||
func _init(new_name: String) -> void:
|
||||
player_name = new_name
|
||||
player_score = 0
|
||||
|
||||
|
||||
## Returns a random version 4 UUID, e.g. "3f2b8c1e-9a4d-4e7f-b2c6-5d8e1f0a7b93".
|
||||
static func _generate_uuid() -> String:
|
||||
var bytes: PackedByteArray = Crypto.new().generate_random_bytes(16)
|
||||
bytes[6] = (bytes[6] & 0x0f) | 0x40
|
||||
bytes[8] = (bytes[8] & 0x3f) | 0x80
|
||||
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:
|
||||
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,
|
||||
# so the more important event is checked first
|
||||
if player_score == Settings.winning_score:
|
||||
winner_triggerd.emit()
|
||||
winner_triggered.emit()
|
||||
elif player_score == Settings.winning_score - 1:
|
||||
match_point_triggerd.emit()
|
||||
match_point_triggered.emit()
|
||||
elif first_point:
|
||||
first_point_triggerd.emit()
|
||||
first_point_triggered.emit()
|
||||
else:
|
||||
point_given_sound.emit()
|
||||
|
||||
|
||||
func minus_point() -> void:
|
||||
if player_score > 0:
|
||||
player_score -= 1
|
||||
@@ -61,6 +64,7 @@ func minus_point() -> void:
|
||||
if player_score == 0:
|
||||
is_first_point = true
|
||||
|
||||
|
||||
func reset_points() -> void:
|
||||
player_score = 0
|
||||
is_first_point = true
|
||||
+19
-19
@@ -1,21 +1,6 @@
|
||||
class_name PlayerRow
|
||||
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 player_removed
|
||||
|
||||
@@ -26,7 +11,13 @@ var player_object: PlayerObject
|
||||
## gives this player a point and Ctrl+Alt+<list_index + 1> takes one away.
|
||||
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:
|
||||
add_button.pressed.connect(add_point)
|
||||
add_button.set_shortcut(create_button_shortcut(list_index, false))
|
||||
@@ -37,42 +28,51 @@ func _ready() -> void:
|
||||
update_score()
|
||||
set_player_character()
|
||||
|
||||
|
||||
func _process(delta: float) -> void:
|
||||
remove_player.visible = Settings.edit_players
|
||||
|
||||
|
||||
func create_button_shortcut(index: int, ctrl_pressed: bool) -> Shortcut:
|
||||
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.alt_pressed = true
|
||||
button_event.ctrl_pressed = ctrl_pressed
|
||||
button_shortcut.events.append(button_event)
|
||||
return button_shortcut
|
||||
|
||||
|
||||
func add_point() -> void:
|
||||
player_object.add_point()
|
||||
update_score()
|
||||
|
||||
|
||||
func minus_point() -> void:
|
||||
player_object.minus_point()
|
||||
update_score()
|
||||
|
||||
|
||||
|
||||
func change_character() -> void:
|
||||
change_character_clicked.emit()
|
||||
|
||||
|
||||
func new_character_selected(character_texture: ImageTexture) -> void:
|
||||
GameLog.debug("Back in player list with new")
|
||||
player_object.character = character_texture
|
||||
set_player_character()
|
||||
|
||||
|
||||
func set_player_character() -> void:
|
||||
character.custom_minimum_size = Vector2(80, 40)
|
||||
character.ignore_texture_size = true
|
||||
character.stretch_mode = TextureButton.STRETCH_KEEP_ASPECT_CENTERED
|
||||
character.texture_normal = player_object.character
|
||||
|
||||
|
||||
func update_score() -> void:
|
||||
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:
|
||||
player_name_field.text = player_object.player_name + ": " + str(player_object.player_score)
|
||||
+12
-18
@@ -7,6 +7,7 @@ extends PanelContainer
|
||||
|
||||
## Emitted when a player has been added to [code]Settings.player_array[/code], before
|
||||
## the list shows it.
|
||||
|
||||
signal player_added(player_object: PlayerObject)
|
||||
|
||||
## 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")
|
||||
|
||||
@onready
|
||||
var add_players_button: TextureButton = %AddPlayersButton
|
||||
|
||||
@onready
|
||||
var add_player_container: HBoxContainer = %AddPlayerContainer
|
||||
|
||||
@onready
|
||||
var add_player_button: Button = %AddPlayerButton
|
||||
|
||||
@onready
|
||||
var new_player_name_field: TextEdit = %PlayerNameField
|
||||
|
||||
@onready
|
||||
var players_list_container: VBoxContainer = %PlayersListVBoxContainer
|
||||
@onready var add_players_button: TextureButton = %AddPlayersButton
|
||||
@onready var add_player_container: HBoxContainer = %AddPlayerContainer
|
||||
@onready var add_player_button: Button = %AddPlayerButton
|
||||
@onready var new_player_name_field: PlayerNameField = %PlayerNameField
|
||||
@onready var players_list_container: VBoxContainer = %PlayersListVBoxContainer
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
add_players_button.pressed.connect(add_players)
|
||||
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.
|
||||
@@ -51,7 +43,8 @@ func add_players() -> void:
|
||||
## already as many players as there can be.
|
||||
func add_player() -> void:
|
||||
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)
|
||||
new_player_name_field.text = ""
|
||||
Settings.player_array.append(new_player_object)
|
||||
@@ -72,8 +65,9 @@ func reload() -> void:
|
||||
new_player.player_object = player_object
|
||||
new_player.list_index = counter
|
||||
|
||||
new_player.connect("change_character_clicked", _on_change_character_clicked.bind(new_player, player_object.player_name))
|
||||
new_player.connect("player_removed", _on_player_removed.bind(player_object))
|
||||
new_player.change_character_clicked.connect(
|
||||
_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)
|
||||
counter += 1
|
||||
|
||||
+26
-3
@@ -1,26 +1,32 @@
|
||||
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 latest_played_song: int = 0
|
||||
var currently_playing_song: int = -1
|
||||
|
||||
var first_song_downloaded: bool = false
|
||||
var first_song_played: bool = false
|
||||
|
||||
var number_of_tracks_to_preload: int = 1
|
||||
var is_song_downloading: bool = false
|
||||
|
||||
|
||||
func get_next_song() -> SongObject:
|
||||
return song_object_array[latest_played_song]
|
||||
|
||||
|
||||
func set_latest_played_song() -> void:
|
||||
latest_played_song += 1
|
||||
if latest_played_song > song_object_array.size() - 1:
|
||||
latest_played_song = song_object_array.size() - 1
|
||||
|
||||
|
||||
func update_last_played_song() -> void:
|
||||
latest_played_song = song_object_array.size()
|
||||
|
||||
|
||||
func get_preloaded_songs() -> int:
|
||||
var number_of_tracks_preloaded: int = 0
|
||||
for song: SongObject in song_object_array:
|
||||
@@ -28,9 +34,11 @@ func get_preloaded_songs() -> int:
|
||||
number_of_tracks_preloaded += 1
|
||||
return number_of_tracks_preloaded
|
||||
|
||||
|
||||
func should_preload_more_songs() -> bool:
|
||||
return number_of_tracks_to_preload > get_preloaded_songs()
|
||||
|
||||
|
||||
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
|
||||
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)
|
||||
MusicServerClient.mark_song_played(song_no)
|
||||
|
||||
|
||||
func add_to_playlist(new_song_object: SongObject) -> void:
|
||||
song_object_array.append(new_song_object)
|
||||
|
||||
|
||||
func get_playlist() -> Array[SongObject]:
|
||||
if Settings.is_debug:
|
||||
return song_object_array
|
||||
@@ -54,46 +64,59 @@ func get_playlist() -> Array[SongObject]:
|
||||
temp_list.append(s)
|
||||
return temp_list
|
||||
|
||||
|
||||
func reset_playlist() -> void:
|
||||
currently_playing_song = -1
|
||||
latest_played_song = 0
|
||||
clear_song_list()
|
||||
|
||||
|
||||
|
||||
func clear_song_list() -> void:
|
||||
song_object_array.clear()
|
||||
|
||||
|
||||
func unset_is_playing() -> void:
|
||||
for song_object: SongObject in song_object_array:
|
||||
song_object.is_playing = false
|
||||
|
||||
|
||||
func set_currently_playing_song(value: int) -> void:
|
||||
currently_playing_song = value
|
||||
|
||||
|
||||
func get_current_soundtrack_title() -> String:
|
||||
return song_object_array[currently_playing_song].get_soundtrack_title()
|
||||
|
||||
|
||||
func get_current_song_title() -> String:
|
||||
return song_object_array[currently_playing_song].get_song_title()
|
||||
|
||||
|
||||
func add_point(player_id: String) -> void:
|
||||
song_object_array[currently_playing_song].add_point(player_id)
|
||||
|
||||
|
||||
func remove_point(player_id: String) -> void:
|
||||
song_object_array[currently_playing_song].remove_point(player_id)
|
||||
|
||||
|
||||
## Removes a player's points from every song.
|
||||
func remove_player_from_list(player_id: String) -> void:
|
||||
for song_object: SongObject in song_object_array:
|
||||
song_object.remove_point(player_id)
|
||||
|
||||
|
||||
func get_song(song_no: int) -> AudioStream:
|
||||
return song_object_array[song_no].song
|
||||
|
||||
|
||||
func add_song_to_song_object(song_no: int, sound: AudioStream) -> void:
|
||||
song_object_array[song_no].song = sound
|
||||
|
||||
|
||||
func song_has_played(song_no: int = latest_played_song) -> void:
|
||||
song_object_array[song_no].has_played = true
|
||||
|
||||
|
||||
func song_is_playing(song_no: int = latest_played_song) -> void:
|
||||
song_object_array[song_no].is_playing = true
|
||||
+7
-9
@@ -1,27 +1,25 @@
|
||||
class_name QRPopup
|
||||
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
|
||||
var qr_panel: Panel = $QRPanel
|
||||
|
||||
@onready
|
||||
var qr_code_rect: QRCodeRect = %QRCodeRect
|
||||
|
||||
@onready
|
||||
var url_label: Label = %URLLabel
|
||||
@onready var qr_panel: Panel = $QRPanel
|
||||
@onready var qr_code_rect: QRCodeRect = %QRCodeRect
|
||||
@onready var url_label: Label = %URLLabel
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
qr_code_rect.light_module_color = Color.WHITE
|
||||
qr_code_rect.dark_module_color = Color.BLACK
|
||||
qr_code_rect.quiet_zone_size = 4
|
||||
|
||||
|
||||
# Generate QR code for the search URL
|
||||
var url: String = Settings.default_path + "/search"
|
||||
qr_code_rect.data = url
|
||||
url_label.text = qr_code_rect.data
|
||||
GameLog.debug("QR code generated for: ", url)
|
||||
|
||||
|
||||
func open() -> void:
|
||||
super()
|
||||
# Regenerate QR code in case settings changed
|
||||
|
||||
@@ -9,54 +9,13 @@ extends Control
|
||||
|
||||
## Emitted when another MusicServer is picked in the settings, after the quiz has
|
||||
## reloaded from it.
|
||||
|
||||
signal server_changed
|
||||
|
||||
## Emitted with a line for the game log, e.g. when a player gets a point.
|
||||
signal event_logged(text: String)
|
||||
|
||||
@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
|
||||
|
||||
var songs: Array= []
|
||||
var songs: Array = []
|
||||
var current_player_row: PlayerRow
|
||||
|
||||
## 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.
|
||||
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:
|
||||
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_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)
|
||||
players_panel.player_added.connect(_on_player_added)
|
||||
players_panel.player_removed.connect(_on_player_removed)
|
||||
players_panel.change_character_requested.connect(_on_change_character_requested)
|
||||
music_player.connect("play_next_song", play_next_song)
|
||||
music_player.connect("update_song_list", update_song_list)
|
||||
settings_popup.connect("server_changed", _on_server_changed)
|
||||
music_player.play_next_song.connect(play_next_song)
|
||||
music_player.update_song_list.connect(update_song_list)
|
||||
settings_popup.server_changed.connect(_on_server_changed)
|
||||
settings_popup.visibility_changed.connect(_on_settings_popup_visibility_changed)
|
||||
|
||||
open_button.pressed.connect(open)
|
||||
@@ -192,7 +166,7 @@ func update_song_list() -> void:
|
||||
|
||||
func song_object_clicked(song_no: int) -> void:
|
||||
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:
|
||||
GameLog.debug("has no song, need to download")
|
||||
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.
|
||||
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.connect("match_point_triggerd", music_player._on_point_triggered.bind("match"))
|
||||
new_player_object.connect("winner_triggerd", _on_player_won.bind(new_player_object))
|
||||
new_player_object.connect("point_given_sound", _on_make_point_given_sound)
|
||||
new_player_object.connect("player_point_given", _on_point_given.bind(new_player_object))
|
||||
new_player_object.connect("player_point_taken", _on_point_taken.bind(new_player_object))
|
||||
new_player_object.first_point_triggered.connect(music_player._on_point_triggered.bind("first"))
|
||||
new_player_object.match_point_triggered.connect(music_player._on_point_triggered.bind("match"))
|
||||
new_player_object.winner_triggered.connect(_on_player_won.bind(new_player_object))
|
||||
new_player_object.point_given_sound.connect(_on_make_point_given_sound)
|
||||
new_player_object.player_point_given.connect(_on_point_given.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:
|
||||
@@ -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'
|
||||
|
||||
|
||||
func open() -> void:
|
||||
file_dialog.popup()
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
class_name ScrollToEndContainer
|
||||
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:
|
||||
max_value = self.get_v_scroll_bar().max_value
|
||||
self.scroll_vertical = max_value
|
||||
|
||||
func _draw() -> void:
|
||||
if max_value != get_v_scroll_bar().max_value:
|
||||
max_value = get_v_scroll_bar().max_value
|
||||
scroll_vertical = int(max_value)
|
||||
+48
-38
@@ -1,65 +1,65 @@
|
||||
class_name SearchPopup
|
||||
extends BasePopup
|
||||
|
||||
@onready
|
||||
var search_list: VBoxContainer = %SearchResults
|
||||
|
||||
@onready
|
||||
var search_panel: Panel = %SearchPanel
|
||||
|
||||
@onready
|
||||
var clear_button: Button = $ClearButton
|
||||
|
||||
@onready
|
||||
var search_bar: TextEdit = $Searchbar
|
||||
## Searches every soundtrack on the server as you type. Matches ignore case, spaces and
|
||||
## special characters, and also find the letters in order with others in between.
|
||||
|
||||
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 = []
|
||||
|
||||
# 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 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:
|
||||
clear_button.pressed.connect(clear)
|
||||
search_bar.grab_focus()
|
||||
search_bar.text_changed.connect(search)
|
||||
visibility_changed.connect(focus)
|
||||
|
||||
|
||||
func focus() -> void:
|
||||
if self.visible == true:
|
||||
if visible:
|
||||
search_bar.grab_focus()
|
||||
clear()
|
||||
|
||||
|
||||
func close() -> void:
|
||||
clear()
|
||||
super()
|
||||
|
||||
|
||||
func search() -> void:
|
||||
GameLog.debug(search_bar.text)
|
||||
var search_text: String = search_bar.text
|
||||
|
||||
|
||||
if search_text == "":
|
||||
show_all_soundtracks()
|
||||
return
|
||||
|
||||
|
||||
perform_search(search_text)
|
||||
|
||||
|
||||
func show_all_soundtracks() -> void:
|
||||
# Hide all visible labels and add to pool
|
||||
for child: Label in search_list.get_children():
|
||||
if child.visible:
|
||||
child.visible = false
|
||||
label_pool.append(child)
|
||||
|
||||
|
||||
# Show all soundtracks in order
|
||||
for soundtrack: String in soundtracks:
|
||||
add_soundtrack_reuse(soundtrack)
|
||||
|
||||
|
||||
func perform_search(search_text: String) -> void:
|
||||
# Hide all visible labels and add to pool
|
||||
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):
|
||||
add_soundtrack_reuse(soundtrack)
|
||||
|
||||
|
||||
## Returns the soundtracks matching [param search_text], best matches first: plain
|
||||
## case-insensitive matches, then matches ignoring spaces and special characters,
|
||||
## 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] = []
|
||||
|
||||
# Pass 1: Exact case-insensitive contains match on lowercase versions
|
||||
for i in range(precomputed_soundtracks.size()):
|
||||
var st_data = precomputed_soundtracks[i]
|
||||
for i: int in range(precomputed_soundtracks.size()):
|
||||
var st_data: Dictionary = precomputed_soundtracks[i]
|
||||
if st_data["lowercase"].contains(search_text_lower):
|
||||
if not st_data["original"] in seen:
|
||||
seen[st_data["original"]] = true
|
||||
matches.append(st_data["original"])
|
||||
|
||||
# Pass 2: Contains match on cleaned terms
|
||||
for i in range(precomputed_soundtracks.size()):
|
||||
var st_data = precomputed_soundtracks[i]
|
||||
for i: int in range(precomputed_soundtracks.size()):
|
||||
var st_data: Dictionary = precomputed_soundtracks[i]
|
||||
if st_data["cleaned"].contains(clean_search_text):
|
||||
if not st_data["original"] in seen:
|
||||
seen[st_data["original"]] = true
|
||||
matches.append(st_data["original"])
|
||||
|
||||
# Pass 3: Regex match on cleaned terms
|
||||
var regex = compile_regex_cached(clean_search_text)
|
||||
for i in range(precomputed_soundtracks.size()):
|
||||
var st_data = precomputed_soundtracks[i]
|
||||
var regex: RegEx = compile_regex_cached(clean_search_text)
|
||||
for i: int in range(precomputed_soundtracks.size()):
|
||||
var st_data: Dictionary = precomputed_soundtracks[i]
|
||||
if regex.search(st_data["cleaned"]):
|
||||
if not st_data["original"] in seen:
|
||||
seen[st_data["original"]] = true
|
||||
@@ -107,30 +108,35 @@ func find_matches(search_text: String) -> Array[String]:
|
||||
|
||||
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:
|
||||
if not search_term in regex_cache:
|
||||
var regex = RegEx.new()
|
||||
var regText: String = ".*"
|
||||
var regex: RegEx = RegEx.new()
|
||||
var regex_text: String = ".*"
|
||||
for letter: String in search_term:
|
||||
# Escape regex metacharacters so searches like "Q*bert" or "(Remix)" work
|
||||
if "\\^$.|?*+()[]{}".contains(letter):
|
||||
regText += "\\"
|
||||
regText += letter + ".*"
|
||||
regex.compile(regText)
|
||||
regex_text += "\\"
|
||||
regex_text += letter + ".*"
|
||||
regex.compile(regex_text)
|
||||
regex_cache[search_term] = regex
|
||||
return regex_cache[search_term]
|
||||
|
||||
|
||||
func add_soundtrack(soundtrack: String) -> void:
|
||||
# Backward compatibility - delegates to reuse version
|
||||
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:
|
||||
var label: Label
|
||||
if label_pool.size() > 0:
|
||||
@@ -146,6 +152,7 @@ func add_soundtrack_reuse(soundtrack: String) -> void:
|
||||
search_list.add_child(label)
|
||||
label.visible = true
|
||||
|
||||
|
||||
func check_if_soundtrack_exists(soundtrack: String) -> bool:
|
||||
var soundtrack_exists: bool = false
|
||||
for child: Label in search_list.get_children():
|
||||
@@ -153,9 +160,10 @@ func check_if_soundtrack_exists(soundtrack: String) -> bool:
|
||||
soundtrack_exists = true
|
||||
return soundtrack_exists
|
||||
|
||||
|
||||
func get_list_of_soundtracks() -> void:
|
||||
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:
|
||||
# Reuse existing labels: hide all and add to pool
|
||||
for child: Label in search_list.get_children():
|
||||
@@ -168,6 +176,7 @@ func get_list_of_soundtracks() -> void:
|
||||
GameLog.warning("Unexpected data")
|
||||
MusicServerClient.fetch_all_soundtracks_ordered(handle_soundtracks)
|
||||
|
||||
|
||||
## Replaces the searchable soundtracks and precomputes their search forms.
|
||||
func set_soundtracks(list: Array) -> void:
|
||||
soundtracks = []
|
||||
@@ -180,6 +189,7 @@ func set_soundtracks(list: Array) -> void:
|
||||
"cleaned": clean_term(soundtrack)
|
||||
})
|
||||
|
||||
|
||||
func clear() -> void:
|
||||
search_bar.text = ""
|
||||
show_all_soundtracks()
|
||||
|
||||
+4
-4
@@ -1,14 +1,14 @@
|
||||
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
|
||||
|
||||
var default_path: String = "https://music.sanplex.xyz"
|
||||
var selected_server: int = 0
|
||||
|
||||
var is_local: bool = false
|
||||
|
||||
var is_debug: bool = false
|
||||
|
||||
var stop_after_current: bool = true
|
||||
var auto_repeat_song: 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_length: bool = false
|
||||
var hide_ticks: bool = false
|
||||
|
||||
var player_array: Array[PlayerObject]
|
||||
var edit_players: 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.
|
||||
var version: String = ProjectSettings.get_setting("application/config/version", "")
|
||||
|
||||
|
||||
func delete_children(node: Node) -> void:
|
||||
for n: Node in node.get_children():
|
||||
node.remove_child(n)
|
||||
|
||||
+62
-79
@@ -1,82 +1,40 @@
|
||||
class_name SettingsPopup
|
||||
extends BasePopup
|
||||
## The settings: how songs are shown and played, the winning score, the song cache,
|
||||
## which MusicServer to use and fullscreen.
|
||||
|
||||
@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
|
||||
|
||||
## Emitted when another MusicServer is picked.
|
||||
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:
|
||||
fullscreen_button.button_pressed = Settings.fullscreen
|
||||
select_server_button.selected = Settings.selected_server
|
||||
|
||||
|
||||
stop_after_current_button.pressed.connect(stop_after_current)
|
||||
hide_next_track_button.pressed.connect(hide_next_track)
|
||||
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)
|
||||
fullscreen_button.pressed.connect(fullscreen)
|
||||
local_button.pressed.connect(local_play)
|
||||
|
||||
|
||||
stop_after_current_button.button_pressed = Settings.stop_after_current
|
||||
hide_next_track_button.button_pressed = Settings.hide_next_track
|
||||
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
|
||||
get_window().size_changed.connect(_update_fullscreen_button)
|
||||
|
||||
|
||||
# Settings can change outside this popup, e.g. winning turns statistics off
|
||||
func _on_visibility_changed() -> void:
|
||||
if visible:
|
||||
add_to_database_button.button_pressed = Settings.add_to_stats
|
||||
_update_fullscreen_button()
|
||||
|
||||
|
||||
func _update_fullscreen_button() -> void:
|
||||
fullscreen_button.set_pressed_no_signal(Settings.fullscreen)
|
||||
|
||||
|
||||
func fullscreen() -> void:
|
||||
Settings.fullscreen = !Settings.fullscreen
|
||||
if Settings.fullscreen == true:
|
||||
if Settings.fullscreen:
|
||||
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
|
||||
if Settings.fullscreen == false:
|
||||
if !Settings.fullscreen:
|
||||
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
|
||||
|
||||
|
||||
func local_play() -> void:
|
||||
Settings.play_local = !Settings.play_local
|
||||
|
||||
|
||||
|
||||
func stop_after_current() -> void:
|
||||
Settings.stop_after_current = !Settings.stop_after_current
|
||||
|
||||
|
||||
func hide_next_track() -> void:
|
||||
Settings.hide_next_track = !Settings.hide_next_track
|
||||
|
||||
|
||||
|
||||
func add_to_database() -> void:
|
||||
Settings.add_to_stats = !Settings.add_to_stats
|
||||
|
||||
|
||||
|
||||
func low_played() -> void:
|
||||
Settings.use_low_played_mode = !Settings.use_low_played_mode
|
||||
|
||||
|
||||
func hide_beginning() -> void:
|
||||
Settings.hide_beginning = !Settings.hide_beginning
|
||||
update_hide_for_seconds_enabled()
|
||||
|
||||
|
||||
func update_hide_for_seconds_enabled() -> void:
|
||||
var enabled: bool = Settings.hide_beginning
|
||||
%HideForSecondsHBoxContainer.visible = enabled
|
||||
|
||||
|
||||
func hide_length() -> void:
|
||||
Settings.hide_length = !Settings.hide_length
|
||||
|
||||
|
||||
func hide_ticks() -> void:
|
||||
Settings.hide_ticks = !Settings.hide_ticks
|
||||
|
||||
|
||||
func lower_winning_score() -> void:
|
||||
Settings.winning_score = max(Settings.winning_score - 1, 1)
|
||||
score_label.text = str(Settings.winning_score)
|
||||
|
||||
|
||||
func increase_winning_score() -> void:
|
||||
Settings.winning_score += 1
|
||||
score_label.text = str(Settings.winning_score)
|
||||
|
||||
|
||||
func set_winning_score(new_score: int) -> void:
|
||||
Settings.winning_score = new_score
|
||||
score_label.text = str(Settings.winning_score)
|
||||
|
||||
|
||||
func lower_cache() -> void:
|
||||
Playlist.number_of_tracks_to_preload -= 1
|
||||
if Playlist.number_of_tracks_to_preload < 1:
|
||||
Playlist.number_of_tracks_to_preload = 1
|
||||
cache_label.text = str(Playlist.number_of_tracks_to_preload)
|
||||
|
||||
|
||||
func increase_cache() -> void:
|
||||
Playlist.number_of_tracks_to_preload += 1
|
||||
cache_label.text = str(Playlist.number_of_tracks_to_preload)
|
||||
|
||||
|
||||
func lower_inspiration_speed() -> void:
|
||||
Settings.inspiration_list_speed -= 1
|
||||
if Settings.inspiration_list_speed < 1:
|
||||
Settings.inspiration_list_speed = 1
|
||||
inspiration_speed_label.text = str(Settings.inspiration_list_speed)
|
||||
|
||||
|
||||
func increase_inspiration_speed() -> void:
|
||||
Settings.inspiration_list_speed += 1
|
||||
inspiration_speed_label.text = str(Settings.inspiration_list_speed )
|
||||
|
||||
|
||||
func lower_hide_for_seconds() -> void:
|
||||
Settings.hide_for_seconds -= 1.0
|
||||
if Settings.hide_for_seconds < 0:
|
||||
Settings.hide_for_seconds = 0
|
||||
hide_for_seconds_label.text = str(Settings.hide_for_seconds)
|
||||
|
||||
|
||||
func increase_hide_for_seconds() -> void:
|
||||
Settings.hide_for_seconds += 1.0
|
||||
if Settings.hide_for_seconds > 10.0:
|
||||
Settings.hide_for_seconds = 10.0
|
||||
hide_for_seconds_label.text = str(Settings.hide_for_seconds)
|
||||
|
||||
|
||||
func select_server(new_server: int) -> void:
|
||||
GameLog.debug("select_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.selected_server: " + str(Settings.selected_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()
|
||||
|
||||
|
||||
func set_selected_server(new_server: int) -> void:
|
||||
GameLog.debug("set_selected_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.selected_server: " + str(Settings.selected_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()
|
||||
+10
-14
@@ -1,11 +1,6 @@
|
||||
class_name ShortcutsPopup
|
||||
extends BasePopup
|
||||
|
||||
@onready
|
||||
var shortcuts_vbox: VBoxContainer = %shortcutsVBox
|
||||
|
||||
@onready
|
||||
var shortcuts_panel: Panel = %ShortcutsPanel
|
||||
## Lists the keyboard shortcuts. Opened with Alt+K or the Shortcuts button.
|
||||
|
||||
var shortcuts_dic: Dictionary = {
|
||||
"Alt + S": "Search",
|
||||
@@ -26,14 +21,17 @@ var shortcuts_dic: Dictionary = {
|
||||
"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.302, 0.302, 0.302)
|
||||
#Color(0.259, 0.259, 0.259)
|
||||
|
||||
func _ready() -> void:
|
||||
shortcuts_dic.sort()
|
||||
Settings.delete_children(shortcuts_vbox)
|
||||
|
||||
|
||||
var title: Label = Label.new()
|
||||
title.add_theme_font_size_override("font_size", 20)
|
||||
title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
@@ -45,28 +43,26 @@ func _ready() -> void:
|
||||
var panel: PanelContainer = PanelContainer.new()
|
||||
var hbox: HBoxContainer = HBoxContainer.new()
|
||||
hbox.alignment = BoxContainer.ALIGNMENT_CENTER
|
||||
|
||||
|
||||
var label: Label = Label.new()
|
||||
label.add_theme_font_size_override("font_size", 20)
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
label.text = shortcut
|
||||
label.autowrap_mode = TextServer.AUTOWRAP_WORD
|
||||
label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
|
||||
|
||||
var label2: Label = Label.new()
|
||||
label2.add_theme_font_size_override("font_size", 20)
|
||||
label2.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
label2.text = shortcuts_dic[shortcut]
|
||||
label2.autowrap_mode = TextServer.AUTOWRAP_WORD
|
||||
label2.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
|
||||
|
||||
if count % 2 == 0:
|
||||
var new_stylebox_normal: Resource = panel.get_theme_stylebox("panel").duplicate()
|
||||
new_stylebox_normal.bg_color = Color(0.18, 0.18, 0.18)
|
||||
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(label2)
|
||||
panel.add_child(hbox)
|
||||
|
||||
+2
-1
@@ -8,6 +8,7 @@ extends Node
|
||||
|
||||
## Emitted when the songs already in the server playlist have been loaded into the
|
||||
## Playlist.
|
||||
|
||||
signal song_list_loaded
|
||||
|
||||
## 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.
|
||||
func load_song_list() -> void:
|
||||
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()
|
||||
if typeof(data) == TYPE_ARRAY:
|
||||
for d: Dictionary in data:
|
||||
|
||||
+12
-8
@@ -3,53 +3,57 @@ extends RefCounted
|
||||
class_name SongObject
|
||||
|
||||
var song_number: int
|
||||
|
||||
var song_title: String
|
||||
|
||||
var soundtrack_title: String
|
||||
|
||||
## Ids of the players who got a point for this song.
|
||||
var players_given_point: Array[String] = []
|
||||
|
||||
var is_answered: bool = false
|
||||
|
||||
var has_played: bool = false
|
||||
|
||||
var is_playing: bool = false
|
||||
|
||||
var song: AudioStream
|
||||
|
||||
|
||||
func get_soundtrack_title() -> String:
|
||||
if is_answered:
|
||||
return soundtrack_title
|
||||
else:
|
||||
return "????????"
|
||||
|
||||
|
||||
func get_song_title() -> String:
|
||||
if is_answered:
|
||||
return song_title
|
||||
else:
|
||||
return "??????"
|
||||
|
||||
|
||||
func get_song_info() -> String:
|
||||
if Settings.is_debug:
|
||||
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:
|
||||
var format_string: String = "%d. %s - %s"
|
||||
return format_string % [(song_number + 1), soundtrack_title, song_title]
|
||||
else:
|
||||
return "??? - ???"
|
||||
|
||||
|
||||
func add_point(player_id: String) -> void:
|
||||
GameLog.debug("add_point")
|
||||
if !players_given_point.has(player_id):
|
||||
players_given_point.append(player_id)
|
||||
|
||||
|
||||
func remove_point(player_id: String) -> void:
|
||||
GameLog.debug("remove_point")
|
||||
players_given_point.erase(player_id)
|
||||
|
||||
|
||||
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"
|
||||
return format_string % [(song_number), soundtrack_title, song_title, is_answered, has_played, is_playing]
|
||||
var format_string: String = "song_number: %d | soundtrack_title: %s | song_title: %s" \
|
||||
+ " | 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
@@ -1,33 +1,19 @@
|
||||
class_name SongRow
|
||||
extends PanelContainer
|
||||
|
||||
@onready
|
||||
var song_list_label: Label = %SongListLabel
|
||||
|
||||
@onready
|
||||
var song_list_players: HBoxContainer = %SongListPlayers
|
||||
## One song in the music list: its number, soundtrack and title, and a marker for
|
||||
## each player who got a point for it.
|
||||
|
||||
var song_object: SongObject
|
||||
|
||||
#Color(0.18, 0.18, 0.18)
|
||||
#Color(0.302, 0.302, 0.302)
|
||||
#Color(0.259, 0.259, 0.259)
|
||||
@onready var song_list_label: Label = %SongListLabel
|
||||
@onready var song_list_players: HBoxContainer = %SongListPlayers
|
||||
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
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()
|
||||
update_players()
|
||||
|
||||
|
||||
func set_label() -> void:
|
||||
if song_object != null:
|
||||
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.text = song_object.get_song_info()
|
||||
|
||||
|
||||
func update_players() -> void:
|
||||
Settings.delete_children(song_list_players)
|
||||
if song_object != null:
|
||||
|
||||
+80
-92
@@ -1,79 +1,49 @@
|
||||
class_name SyncPopup
|
||||
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
|
||||
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
|
||||
## Emitted when the server has started syncing.
|
||||
|
||||
signal sync_started
|
||||
## Emitted when the sync is done and its result is shown.
|
||||
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:
|
||||
sync_timer.timeout.connect(_on_sync_timeout)
|
||||
restart_sync_button.pressed.connect(start_sync)
|
||||
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:
|
||||
vbox.visible = true
|
||||
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
|
||||
return false
|
||||
|
||||
|
||||
func start_sync() -> void:
|
||||
GameLog.debug("start_sync")
|
||||
self.visible = true
|
||||
visible = true
|
||||
if has_synced:
|
||||
# Already synced, just show the results
|
||||
show_sync_result()
|
||||
@@ -98,6 +69,7 @@ func start_sync() -> void:
|
||||
if !Settings.currently_syncing:
|
||||
MusicServerClient.start_sync(sync_request_sent)
|
||||
|
||||
|
||||
func _on_sync_timeout() -> void:
|
||||
GameLog.debug("_on_sync_timeout!")
|
||||
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)
|
||||
MusicServerClient.fetch_sync_progress(get_progress)
|
||||
|
||||
|
||||
func on_sync_finished(data_received: Dictionary) -> void:
|
||||
has_synced = true
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
Settings.delete_children(soundtracks_changed_title_vbox)
|
||||
make_header_label(soundtracks_changed_title_vbox, "Changed Title")
|
||||
var row: int = 0
|
||||
for old_soundtrack_title: String in data_received.soundtracks_changed_title:
|
||||
var new_soundtrack_title: String = data_received.soundtracks_changed_title[old_soundtrack_title]
|
||||
for old_soundtrack_title: String in changed_titles:
|
||||
var new_soundtrack_title: String = changed_titles[old_soundtrack_title]
|
||||
soundtracks_changed_title_vbox.add_child(make_soundtrack_row(
|
||||
old_soundtrack_title + "\nchanged title to\n" + new_soundtrack_title, row))
|
||||
row += 1
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
|
||||
# The server's name for the errors it caught
|
||||
if data_received.catched_errors != null && data_received.catched_errors.size() > 0:
|
||||
catched_errors_vbox.visible = true
|
||||
make_list(data_received.catched_errors, catched_errors_vbox, "Errors")
|
||||
|
||||
caught_errors_vbox.visible = true
|
||||
make_list(data_received.catched_errors, caught_errors_vbox, "Errors")
|
||||
|
||||
# Separators go only between visible columns, not after the last one
|
||||
var visible_sections: Array = []
|
||||
if soundtracks_added_vbox.visible:
|
||||
@@ -155,11 +135,11 @@ func on_sync_finished(data_received: Dictionary) -> void:
|
||||
visible_sections.append("changed_content")
|
||||
if soundtracks_removed_vbox.visible:
|
||||
visible_sections.append("removed")
|
||||
|
||||
|
||||
# Show section separators only if there's a visible section after them
|
||||
for i in range(visible_sections.size()):
|
||||
var section = visible_sections[i]
|
||||
var separator_map = {
|
||||
for i: int in range(visible_sections.size()):
|
||||
var section: String = visible_sections[i]
|
||||
var separator_map: Dictionary = {
|
||||
"added": soundtracks_added_separator,
|
||||
"readded": soundtracks_readded_separator,
|
||||
"changed_title": soundtracks_changed_title_separator,
|
||||
@@ -171,10 +151,11 @@ func on_sync_finished(data_received: Dictionary) -> void:
|
||||
separator_map[section].visible = true
|
||||
else:
|
||||
separator_map[section].visible = false
|
||||
|
||||
|
||||
if !changes:
|
||||
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
|
||||
sync_timer.stop()
|
||||
GameLog.debug("sync finished")
|
||||
@@ -182,9 +163,11 @@ func on_sync_finished(data_received: Dictionary) -> void:
|
||||
restart_sync_button.visible = true
|
||||
sync_finished.emit()
|
||||
|
||||
func list_visible(vbox: VBoxContainer, seperator: VSeparator) -> void:
|
||||
|
||||
func list_visible(vbox: VBoxContainer, separator: VSeparator) -> void:
|
||||
vbox.visible = true
|
||||
seperator.visible = true
|
||||
separator.visible = true
|
||||
|
||||
|
||||
func make_header_label(vbox: VBoxContainer, text: String) -> void:
|
||||
var label: Label = Label.new()
|
||||
@@ -194,6 +177,7 @@ func make_header_label(vbox: VBoxContainer, text: String) -> void:
|
||||
label.text = text
|
||||
vbox.add_child(label)
|
||||
|
||||
|
||||
# Items alternate between white and orange text as well as background shade
|
||||
func make_soundtrack_row(text: String, row: int) -> ListRow:
|
||||
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))
|
||||
return list_row
|
||||
|
||||
|
||||
func reset_sync_result() -> void:
|
||||
sync_progress_bar.value = 0
|
||||
sync_time.text = ""
|
||||
@@ -216,7 +201,8 @@ func reset_sync_result() -> void:
|
||||
soundtracks_changed_content_separator.visible = false
|
||||
soundtracks_removed_vbox.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:
|
||||
Settings.delete_children(vbox)
|
||||
@@ -224,16 +210,18 @@ func make_list(list: Array, vbox: VBoxContainer, text: String) -> void:
|
||||
for row: int in list.size():
|
||||
vbox.add_child(make_soundtrack_row(list[row], row))
|
||||
|
||||
|
||||
func show_sync_result() -> void:
|
||||
sync_title_label.text = "Sync Result"
|
||||
progress_hbox.visible = false
|
||||
sync_hbox.visible = true
|
||||
restart_sync_button.visible = true
|
||||
|
||||
|
||||
|
||||
func show_sync_progress() -> void:
|
||||
sync_title_label.text = "Sync in Progress"
|
||||
progress_hbox.visible = true
|
||||
no_changes_label.visible = false
|
||||
sync_hbox.visible = false
|
||||
catched_errors_vbox.visible = false
|
||||
caught_errors_vbox.visible = false
|
||||
restart_sync_button.visible = false
|
||||
@@ -87,6 +87,7 @@ static func prepare_autoloads() -> void:
|
||||
func run(on_state: Callable) -> void:
|
||||
# The debug build lowers the winning score on startup; keep it out of reach
|
||||
Settings.winning_score = 20
|
||||
_stop_caret_blink(_main)
|
||||
await _settle()
|
||||
await on_state.call("startup", null)
|
||||
|
||||
@@ -194,6 +195,14 @@ func _call(method: String, args: Array = []) -> Variant:
|
||||
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:
|
||||
return MusicServerClient.transport
|
||||
|
||||
|
||||
@@ -58,9 +58,14 @@ const RENAMED_SCENES: Dictionary = {
|
||||
"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 = {
|
||||
"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.
|
||||
@@ -102,9 +107,10 @@ static func new_path(old_path: String) -> String:
|
||||
var first: String = parts[0]
|
||||
parts[0] = MOVED.get(first, first)
|
||||
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:
|
||||
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
|
||||
|
||||
|
||||
|
||||
@@ -105,7 +105,8 @@ func test_rows_follow_player_order_and_numbering() -> void:
|
||||
_add("Marle")
|
||||
_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"])
|
||||
assert_array(_rows().map(func(row: PlayerRow) -> int: return row.list_index)) \
|
||||
.contains_exactly([0, 1, 2])
|
||||
|
||||
@@ -9,6 +9,12 @@ const MAIN_WINDOW_CHILDREN: Array[String] = [
|
||||
"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] = [
|
||||
"TopPanelContainer", "Title", "SyncButton", "SearchButton", "StatisticsButton",
|
||||
"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()
|
||||
|
||||
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)
|
||||
|
||||
|
||||
@@ -143,12 +150,13 @@ func test_character_button_in_the_player_list_picks_a_new_character() -> void:
|
||||
assert_bool(character_select.visible).is_true()
|
||||
var texture: ImageTexture = ImageTexture.create_from_image(
|
||||
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)
|
||||
|
||||
|
||||
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")))
|
||||
|
||||
@@ -166,7 +174,8 @@ func test_loaded_song_list_shows_in_the_music_list() -> void:
|
||||
|
||||
_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)) \
|
||||
.contains_exactly([0, 1])
|
||||
|
||||
@@ -203,8 +212,7 @@ func test_sync_disables_quiz_buttons_until_finished() -> void:
|
||||
_top_bar_button("SyncButton").pressed.emit()
|
||||
sync_popup.sync_timer.stop()
|
||||
|
||||
for button_name: String in ["QuizToolbar/SoundTestButton", "QuizToolbar/ResetPlaylistButton", "SongControls/ShowAnswerButton",
|
||||
"SongControls/NextButton"]:
|
||||
for button_name: String in LOCKED_BY_SYNC:
|
||||
assert_bool((_quiz.get_node(button_name) as Button).disabled) \
|
||||
.override_failure_message(button_name + " not disabled").is_true()
|
||||
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()
|
||||
await _runner.simulate_frames(2)
|
||||
|
||||
for button_name: String in ["QuizToolbar/SoundTestButton", "QuizToolbar/ResetPlaylistButton", "SongControls/ShowAnswerButton",
|
||||
"SongControls/NextButton"]:
|
||||
for button_name: String in LOCKED_BY_SYNC:
|
||||
assert_bool((_quiz.get_node(button_name) as Button).disabled) \
|
||||
.override_failure_message(button_name + " still disabled").is_false()
|
||||
# The search list in the main window and the quiz playlist are both reloaded
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
extends GdUnitTestSuite
|
||||
## 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 _server: FakeHttpTransport
|
||||
@@ -32,7 +34,8 @@ func test_load_song_list_adds_songs_already_played_on_the_server() -> void:
|
||||
_server.respond_json("/music/list", [
|
||||
{"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))
|
||||
|
||||
_loader.load_song_list()
|
||||
@@ -57,7 +60,8 @@ func test_fetch_song_downloads_queues_and_adds_the_song() -> void:
|
||||
_loader.fetch_song()
|
||||
|
||||
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]
|
||||
assert_str(song.song_title).is_equal("Corridors of Time")
|
||||
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:
|
||||
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()
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
extends GdUnitTestSuite
|
||||
## The sync popup on its own: starting a sync, its progress, and how the result is shown.
|
||||
|
||||
var _server: FakeHttpTransport
|
||||
var _snapshot: AutoloadSnapshot
|
||||
@@ -132,7 +133,7 @@ func test_errors_are_listed() -> void:
|
||||
|
||||
_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:
|
||||
@@ -152,7 +153,7 @@ func test_errors_section_has_errors_header() -> void:
|
||||
|
||||
_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:
|
||||
@@ -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
|
||||
assert_float(_node("SyncHBoxContainer").get_combined_minimum_size().x) \
|
||||
.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)
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
extends GdUnitTestSuite
|
||||
## ListRow: text, alternating backgrounds and wrapping.
|
||||
|
||||
|
||||
func _background(list_row: ListRow) -> Color:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
extends GdUnitTestSuite
|
||||
## How the music player shows a song's time, including the hidden parts.
|
||||
|
||||
var _music_player: MusicPlayer
|
||||
var _hide_beginning: bool
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
extends GdUnitTestSuite
|
||||
## MusicServerClient: which paths it asks for and how it handles the answers.
|
||||
|
||||
var _server: FakeHttpTransport
|
||||
|
||||
@@ -115,7 +116,8 @@ func test_mark_song_played_sends_put() -> 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"])
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
extends GdUnitTestSuite
|
||||
## PlayerObject: points, and the first point, match point and winner signals.
|
||||
|
||||
var _winning_score: int
|
||||
var _player: PlayerObject
|
||||
@@ -20,7 +21,7 @@ func test_first_point_triggers_first_point_signal() -> void:
|
||||
_player.add_point()
|
||||
|
||||
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")
|
||||
|
||||
|
||||
@@ -30,7 +31,7 @@ func test_one_point_from_winning_triggers_match_point() -> void:
|
||||
|
||||
_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:
|
||||
@@ -41,7 +42,7 @@ func test_reaching_winning_score_triggers_winner() -> void:
|
||||
_player.add_point()
|
||||
|
||||
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:
|
||||
@@ -91,7 +92,7 @@ func test_dropping_back_to_zero_allows_first_point_again() -> void:
|
||||
|
||||
_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:
|
||||
@@ -125,7 +126,7 @@ func test_winning_score_of_one_is_won_with_first_point() -> void:
|
||||
|
||||
_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:
|
||||
@@ -134,5 +135,5 @@ func test_first_point_is_match_point_when_winning_score_is_two() -> void:
|
||||
|
||||
_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()
|
||||
@@ -1,4 +1,5 @@
|
||||
extends GdUnitTestSuite
|
||||
## Playlist: moving through the songs, answering them and keeping points.
|
||||
|
||||
var _server: FakeHttpTransport
|
||||
var _add_to_stats: bool
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
extends GdUnitTestSuite
|
||||
## How the search popup finds and orders matching soundtracks.
|
||||
|
||||
const SearchWindowScript: GDScript = preload("res://search_popup.gd")
|
||||
|
||||
@@ -36,7 +37,8 @@ func test_ignores_spaces() -> 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:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
extends GdUnitTestSuite
|
||||
## Settings: the version number.
|
||||
|
||||
|
||||
func test_version_comes_from_project_settings() -> void:
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
extends GdUnitTestSuite
|
||||
## SongObject: what it shows before and after the answer, and its points.
|
||||
|
||||
var _is_debug: bool
|
||||
var _song: SongObject
|
||||
|
||||
+12
-7
@@ -1,19 +1,24 @@
|
||||
class_name VolumeSlider
|
||||
extends HSlider
|
||||
## A slider that sets the volume of an audio bus.
|
||||
|
||||
@export
|
||||
var audio_bus_name := "music"
|
||||
## The audio bus whose volume the slider sets.
|
||||
|
||||
@onready
|
||||
var _bus: int
|
||||
@export var audio_bus_name: String = "music"
|
||||
|
||||
func _ready():
|
||||
var _bus: int
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_bus = AudioServer.get_bus_index(audio_bus_name)
|
||||
value_changed.connect(_on_value_changed)
|
||||
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:
|
||||
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
@@ -1,17 +1,11 @@
|
||||
class_name WinnerPopup
|
||||
extends BasePopup
|
||||
## Shows who won, with their character, and where the other players placed.
|
||||
|
||||
@onready
|
||||
var winner_panel: Panel = %WinnerPanel
|
||||
|
||||
@onready
|
||||
var winner_picture: TextureRect = %WinnerPicture
|
||||
|
||||
@onready
|
||||
var winner_label: Label = %WinnerLabel
|
||||
|
||||
@onready
|
||||
var players_list: VBoxContainer = %PlayersList
|
||||
@onready var winner_panel: Panel = %WinnerPanel
|
||||
@onready var winner_picture: TextureRect = %WinnerPicture
|
||||
@onready var winner_label: Label = %WinnerLabel
|
||||
@onready var players_list: VBoxContainer = %PlayersList
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
@@ -54,6 +48,3 @@ func show_winner(winner: PlayerObject) -> void:
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
players_list.add_child(label)
|
||||
placing += 1
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user