Compare commits
14
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a7bf38c5f9 | ||
|
|
5519afb5f6 | ||
|
|
d77b72159d | ||
|
|
a0428e3475 | ||
|
|
674604425f | ||
|
|
390da577d7 | ||
|
|
b7d67b573e | ||
|
|
5831e1e73d | ||
|
|
5cdec5cc8f | ||
|
|
e94c75e84d | ||
|
|
bad4b3ad8b | ||
|
|
8c1987807c | ||
|
|
3c7b4e5c9d | ||
|
|
75c59eb223 |
No files matched your search
@@ -1,38 +0,0 @@
|
||||
extends ScrollContainer
|
||||
|
||||
# Scroll direction: 1 = down, -1 = up
|
||||
var scroll_direction: int = 1
|
||||
|
||||
# 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()
|
||||
|
||||
# Don't scroll if list is empty or too short
|
||||
if scroll_bar.max_value <= 0:
|
||||
return
|
||||
|
||||
var current_pos = 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)
|
||||
else:
|
||||
# Reached bottom, reverse direction
|
||||
scroll_direction = -1
|
||||
scroll_bar.value = scroll_bar.max_value - scroll_bar.page
|
||||
else:
|
||||
# Scrolling up
|
||||
if current_pos > 0:
|
||||
scroll_bar.value = max(current_pos + scroll_amount, 0)
|
||||
else:
|
||||
# Reached top, reverse direction
|
||||
scroll_direction = 1
|
||||
scroll_bar.value = 0
|
||||
-572
@@ -1,572 +0,0 @@
|
||||
extends Control
|
||||
|
||||
# 601 LOC 11/9 - 2023
|
||||
# 628 LOC 15/9 - 2023
|
||||
# 631 LOC 16/9 - 2023
|
||||
# 554 LOC 30/5 - 2024
|
||||
# 551 LOC 3/6 - 2024
|
||||
# 386 LOC 12/6 - 2024
|
||||
|
||||
##TODO
|
||||
# 15. Fix for local play
|
||||
|
||||
@onready
|
||||
var open_button: Button = $Open
|
||||
|
||||
@onready
|
||||
var fileDialog: FileDialog = $FileDialog
|
||||
|
||||
@onready
|
||||
var inspiration_scroll: ScrollContainer = $ScrollContainer
|
||||
|
||||
@onready
|
||||
var inspiration_list: VBoxContainer = $ScrollContainer/VBoxContainer
|
||||
|
||||
@onready
|
||||
var soundtrack_label: Label = $VBoxContainer/SoundtrackLabel
|
||||
|
||||
@onready
|
||||
var song_label: Label = $VBoxContainer/SongLabel
|
||||
|
||||
@onready
|
||||
var add_player_container: HBoxContainer = $Players/VBoxContainer/AddPlayerContainer
|
||||
|
||||
@onready
|
||||
var add_players_button: TextureButton = $Players/VBoxContainer/HBoxContainer/AddPlayersButton
|
||||
|
||||
@onready
|
||||
var add_player_button: Button = $Players/VBoxContainer/AddPlayerContainer/AddPlayerButton
|
||||
|
||||
@onready
|
||||
var new_player_name_field: TextEdit = $Players/VBoxContainer/AddPlayerContainer/PlayerNameField
|
||||
|
||||
@onready
|
||||
var reset_playlist_button: Button = $ResetPlaylistButton
|
||||
|
||||
@onready
|
||||
var reset_points_button: Button = $ResetPointsButton
|
||||
|
||||
@onready
|
||||
var sound_test_button: Button = $SoundTestButton
|
||||
|
||||
@onready
|
||||
var sync_button: Button = $SyncButton
|
||||
|
||||
@onready
|
||||
var sync_window: Control = %SyncWindow
|
||||
|
||||
@onready
|
||||
var settings_button: Button = $SettingsButton
|
||||
|
||||
@onready
|
||||
var settings_window: Control = $SettingsWindow
|
||||
|
||||
@onready
|
||||
var shortcuts_window: Control = %ShortcutsWindow
|
||||
|
||||
@onready
|
||||
var qr_window: Control = %QRWindow
|
||||
|
||||
@onready
|
||||
var winner_window: Control = %WinnerWindow
|
||||
|
||||
@onready
|
||||
var qr_button: Button = $QRButton
|
||||
|
||||
@onready
|
||||
var statistics_button: Button = $StatisticsButton
|
||||
|
||||
@onready
|
||||
var statistic_popup: PopupPanel = $StatisticsPopupPanel
|
||||
|
||||
@onready
|
||||
var statistic_label: Label = $StatisticsPopupPanel/StatisticsLabel
|
||||
|
||||
@onready
|
||||
var shortcuts_button: Button = $ShortcutsButton
|
||||
|
||||
@onready
|
||||
var about_button: Button = $AboutButton
|
||||
|
||||
@onready
|
||||
var about_window: Control = %AboutWindow
|
||||
|
||||
@onready
|
||||
var show_answer_button: Button = $ShowAnswerButton
|
||||
|
||||
@onready
|
||||
var next_button: Button = $NextButton
|
||||
|
||||
@onready
|
||||
var music_list_scroll: ScrollContainer = $MusicListPanel/ScrollContainer
|
||||
|
||||
@onready
|
||||
var music_list: VBoxContainer = $MusicListPanel/ScrollContainer/MusicList
|
||||
|
||||
@onready
|
||||
var player_container: VBoxContainer = $Players/VBoxContainer
|
||||
|
||||
@onready
|
||||
var players_list_container: VBoxContainer = $Players/VBoxContainer/ScrollContainer/PlayersListVBoxContainer
|
||||
|
||||
@onready
|
||||
var new_character_select: Control = %NewCharacterSelect
|
||||
|
||||
@onready
|
||||
var search_button: Button = $SearchButton
|
||||
|
||||
@onready
|
||||
var search_view: Control= $Search
|
||||
|
||||
@onready
|
||||
var auto_repeat_song_button: CheckButton = $RepeatSongCheckButton
|
||||
|
||||
@onready
|
||||
var music_player_container: PanelContainer = $MusicPlayer
|
||||
|
||||
@onready
|
||||
var log_window: Control = %Log
|
||||
|
||||
@onready
|
||||
var debug_label: Label = $DebugLabel
|
||||
|
||||
var player: PackedScene = preload("res://Player.tscn")
|
||||
var song_list_object: PackedScene = preload("res://song_list_object.tscn")
|
||||
|
||||
var songs: Array= []
|
||||
var soundtracks: Array = []
|
||||
var current_player: Node
|
||||
|
||||
## True while statistics are off because a player won, until points are reset.
|
||||
var stats_paused_by_win: bool = false
|
||||
|
||||
## Dims the window behind an open popup and keeps clicks from reaching the buttons under it.
|
||||
var popup_backdrop: ColorRect
|
||||
|
||||
## Seconds to wait before retrying a song download step that failed.
|
||||
var song_fetch_retry_seconds: float = 5.0
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready() -> void:
|
||||
_setup_popup_backdrop()
|
||||
var debug: bool = OS.has_feature("debug")
|
||||
var is_mac: bool = OS.has_feature("macos")
|
||||
if debug:
|
||||
print("is_debug")
|
||||
debug_label.visible = true
|
||||
Settings.is_debug = true
|
||||
settings_window.set_winning_score(5)
|
||||
settings_window.set_selected_server(4)
|
||||
if is_mac:
|
||||
print("is_mac")
|
||||
next_button.pressed.connect(play_next_song)
|
||||
show_answer_button.pressed.connect(show_answer_pressed)
|
||||
search_button.pressed.connect(show_search)
|
||||
sync_button.pressed.connect(sync_soundtracks)
|
||||
sound_test_button.pressed.connect(music_player_container.get_sound_test_song)
|
||||
statistics_button.pressed.connect(get_statistics)
|
||||
shortcuts_button.pressed.connect(show_shortcuts)
|
||||
about_button.pressed.connect(show_about)
|
||||
settings_button.pressed.connect(show_settings)
|
||||
qr_button.pressed.connect(show_qr)
|
||||
reset_playlist_button.pressed.connect(reset_playlist)
|
||||
reset_points_button.pressed.connect(reset_points)
|
||||
|
||||
new_character_select.connect("new_new_character_selected", _on_new_character_selected)
|
||||
new_player_name_field.connect("enter_key_pressed", add_player)
|
||||
music_player_container.connect("play_next_song", play_next_song)
|
||||
music_player_container.connect("update_song_list", update_song_list)
|
||||
settings_window.connect("server_changed", server_updated)
|
||||
settings_window.visibility_changed.connect(_on_settings_window_visibility_changed)
|
||||
sync_window.connect("sync_started", _on_sync_started)
|
||||
sync_window.connect("sync_finished", _on_sync_finished)
|
||||
|
||||
|
||||
add_players_button.pressed.connect(add_players)
|
||||
add_player_button.pressed.connect(add_player)
|
||||
open_button.pressed.connect(open)
|
||||
auto_repeat_song_button.pressed.connect(repeat_song)
|
||||
|
||||
get_suggestion_list()
|
||||
fetch_song_list_at_start()
|
||||
search_view.get_list_of_soundtracks()
|
||||
new_character_select.load_characters()
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if event is InputEventKey and event.pressed:
|
||||
if event.alt_pressed && event.keycode == KEY_UP:
|
||||
print("Alt + UP pressed")
|
||||
music_player_container.change_volume(0.05)
|
||||
if event.alt_pressed && event.keycode == KEY_DOWN:
|
||||
print("Alt + DOWN pressed")
|
||||
music_player_container.change_volume(-0.05)
|
||||
if event.alt_pressed && event.ctrl_pressed && event.keycode == KEY_LEFT:
|
||||
print("Ctrl + Alt + LEFT pressed")
|
||||
music_player_container.restart()
|
||||
elif event.alt_pressed && event.keycode == KEY_LEFT:
|
||||
print("Alt + LEFT pressed")
|
||||
music_player_container.seek(-5)
|
||||
if event.alt_pressed && event.keycode == KEY_RIGHT:
|
||||
print("Alt + RIGHT pressed")
|
||||
music_player_container.seek(5)
|
||||
if event.alt_pressed && event.keycode == KEY_ENTER:
|
||||
print("Alt + Enter pressed")
|
||||
Settings.fullscreen = !Settings.fullscreen
|
||||
settings_window.fullscreen_button.button_pressed = Settings.fullscreen
|
||||
if Settings.fullscreen == true:
|
||||
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
|
||||
if Settings.fullscreen == false:
|
||||
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
|
||||
if event.alt_pressed && event.keycode == KEY_K:
|
||||
shortcuts_window.toggle()
|
||||
if event.alt_pressed && event.keycode == KEY_L:
|
||||
log_window.toggle()
|
||||
|
||||
func server_updated() -> void:
|
||||
print("server_updated")
|
||||
update_from_server()
|
||||
reset_points()
|
||||
|
||||
func update_from_server() -> void:
|
||||
print("update_from_server")
|
||||
search_view.get_list_of_soundtracks()
|
||||
get_suggestion_list()
|
||||
reset_playlist()
|
||||
new_character_select.load_characters()
|
||||
|
||||
func repeat_song() -> void:
|
||||
Settings.auto_repeat_song = !Settings.auto_repeat_song
|
||||
|
||||
func show_answer_pressed() -> void:
|
||||
print("show_answer_pressed")
|
||||
Playlist.song_is_answered()
|
||||
update_song_list()
|
||||
|
||||
# Loads the songs already in the server playlist, then starts preloading new ones.
|
||||
# Preloading waits for the list, because loading the list clears the playlist.
|
||||
func fetch_song_list_at_start() -> void:
|
||||
print("fetch_song_list_at_start")
|
||||
var fetch_song_list_at_start_done: Callable = func fetch_songs_at_start_done(data) -> void:
|
||||
Playlist.clear_song_list()
|
||||
if typeof(data) == TYPE_ARRAY:
|
||||
for d: Dictionary in data:
|
||||
var new_song_object: SongObject = SongObject.new()
|
||||
new_song_object.song_number = d.SongNo
|
||||
new_song_object.soundtrack_title = d.Soundtrack
|
||||
new_song_object.song_title = d.Song
|
||||
new_song_object.is_answered = true
|
||||
new_song_object.has_played = true
|
||||
Playlist.add_to_playlist(new_song_object)
|
||||
elif data != null:
|
||||
print("Unexpected data")
|
||||
Playlist.update_last_played_song()
|
||||
update_song_list()
|
||||
if !Playlist.is_song_downloading:
|
||||
fetch_song()
|
||||
MusicServerClient.fetch_song_list(fetch_song_list_at_start_done,
|
||||
_retry_later.bind(fetch_song_list_at_start))
|
||||
|
||||
func play_next_song() -> void:
|
||||
print("play_next_song ", Playlist.latest_played_song)
|
||||
if Playlist.first_song_played:
|
||||
Playlist.set_latest_played_song()
|
||||
Playlist.first_song_played = true
|
||||
music_player_container.play_song_object(Playlist.latest_played_song)
|
||||
if !(Playlist.get_preloaded_songs() > 0):
|
||||
next_button.disabled = true
|
||||
if !Playlist.is_song_downloading:
|
||||
fetch_song()
|
||||
|
||||
#not first "/music/addPlayed"
|
||||
#"/music/rand"
|
||||
#"/music/addQue"
|
||||
#"/music/info"
|
||||
func _popups() -> Array[PopupWindow]:
|
||||
return [search_view, sync_window, settings_window, qr_window, new_character_select,
|
||||
shortcuts_window, about_window, winner_window, log_window]
|
||||
|
||||
# The backdrop sits above the main window and below every popup. Popups close
|
||||
# themselves when clicked outside, and the backdrop keeps that click from reaching
|
||||
# the buttons behind them.
|
||||
func _setup_popup_backdrop() -> void:
|
||||
popup_backdrop = ColorRect.new()
|
||||
popup_backdrop.name = "PopupBackdrop"
|
||||
popup_backdrop.color = Color(0, 0, 0, 0.55)
|
||||
popup_backdrop.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
add_child(popup_backdrop)
|
||||
popup_backdrop.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
for popup: PopupWindow in _popups():
|
||||
move_child(popup, -1)
|
||||
popup.visibility_changed.connect(_update_popup_backdrop)
|
||||
_update_popup_backdrop()
|
||||
|
||||
func _update_popup_backdrop() -> void:
|
||||
popup_backdrop.visible = _popups().any(func(popup: PopupWindow) -> bool: return popup.visible)
|
||||
|
||||
# The cache size can be changed in the settings. Waiting until the settings are closed
|
||||
# avoids starting downloads while the user is still clicking the number up and down.
|
||||
func _on_settings_window_visibility_changed() -> void:
|
||||
if !settings_window.visible && !Playlist.is_song_downloading && Playlist.should_preload_more_songs():
|
||||
fetch_song()
|
||||
|
||||
func fetch_song() -> void:
|
||||
print("fetch_song")
|
||||
Playlist.is_song_downloading = true
|
||||
_download_random_song(SongObject.new())
|
||||
|
||||
# Fetching a song takes three requests. If one fails, only that step is retried, so
|
||||
# the server and the game keep the same songs in their playlists.
|
||||
func _download_random_song(new_song_object: SongObject) -> void:
|
||||
var song_downloaded: Callable = func song_downloaded(body: PackedByteArray) -> void:
|
||||
print("song_downloaded")
|
||||
var sound: AudioStream = AudioStreamMP3.new()
|
||||
sound.data = body
|
||||
new_song_object.song = sound
|
||||
_add_song_to_queue(new_song_object)
|
||||
MusicServerClient.fetch_random_song(Settings.use_low_played_mode, song_downloaded,
|
||||
_retry_later.bind(_download_random_song.bind(new_song_object)))
|
||||
|
||||
func _add_song_to_queue(new_song_object: SongObject) -> void:
|
||||
MusicServerClient.add_to_queue(_fetch_song_info.bind(new_song_object),
|
||||
_retry_later.bind(_add_song_to_queue.bind(new_song_object)))
|
||||
|
||||
func _fetch_song_info(new_song_object: SongObject) -> void:
|
||||
var song_info_fetched: Callable = func song_info_fetched(data_received: Dictionary) -> void:
|
||||
print("song_info_fetched")
|
||||
new_song_object.song_number = data_received.SongNo
|
||||
new_song_object.soundtrack_title = data_received.Soundtrack
|
||||
new_song_object.song_title = data_received.Song
|
||||
print("new_song_object: ", new_song_object.to_string())
|
||||
Playlist.add_to_playlist(new_song_object)
|
||||
next_button.disabled = false
|
||||
if Playlist.should_preload_more_songs():
|
||||
fetch_song()
|
||||
else:
|
||||
Playlist.is_song_downloading = false
|
||||
if Settings.is_debug:
|
||||
update_song_list()
|
||||
MusicServerClient.fetch_song_info(song_info_fetched,
|
||||
_retry_later.bind(_fetch_song_info.bind(new_song_object)))
|
||||
|
||||
func _retry_later(step: Callable) -> void:
|
||||
print("Song download failed, retrying in %s seconds" % song_fetch_retry_seconds)
|
||||
get_tree().create_timer(song_fetch_retry_seconds).timeout.connect(step)
|
||||
|
||||
func update_song_list() -> void:
|
||||
print("update_song_list")
|
||||
if Playlist.currently_playing_song >= 0:
|
||||
soundtrack_label.text = Playlist.get_current_soundtrack_title()
|
||||
song_label.text = Playlist.get_current_song_title()
|
||||
Settings.delete_children(music_list)
|
||||
for s: SongObject in Playlist.get_playlist():
|
||||
var new_song_list_object: SongListObject = song_list_object.instantiate()
|
||||
new_song_list_object.song_object = s
|
||||
new_song_list_object.gui_input.connect(song_object_clicked.bind(s.song_number))
|
||||
music_list.add_child(new_song_list_object)
|
||||
|
||||
func song_object_clicked(event: InputEvent, song_no: int) -> void:
|
||||
if (event is InputEventMouseButton && event.pressed && event.button_index == MOUSE_BUTTON_LEFT):
|
||||
print("Clicked Song Object: ", Playlist.song_object_array[song_no].to_string())
|
||||
if Settings.character_select_open == false:
|
||||
if Playlist.get_song(song_no) == null:
|
||||
print("has no song, need to download")
|
||||
var song_downloaded: Callable = func song_downloaded(body: PackedByteArray) -> void:
|
||||
var sound: AudioStream = AudioStreamMP3.new()
|
||||
sound.data = body
|
||||
Playlist.add_song_to_song_object(song_no, sound)
|
||||
music_player_container.play_song_object(song_no)
|
||||
MusicServerClient.fetch_song(song_no, song_downloaded)
|
||||
else:
|
||||
print("song already downloaded")
|
||||
music_player_container.play_song_object(song_no)
|
||||
|
||||
func reset_playlist() -> void:
|
||||
print("reset_playlist")
|
||||
var playlist_reset_done: Callable = func playlist_reset_done() -> void:
|
||||
Playlist.reset_playlist()
|
||||
fetch_song_list_at_start()
|
||||
|
||||
MusicServerClient.reset_playlist(playlist_reset_done)
|
||||
|
||||
func reset_points() -> void:
|
||||
print("reset_points")
|
||||
for player_objects: PlayerObject in Settings.player_array:
|
||||
player_objects.reset_points()
|
||||
load_players()
|
||||
# A new game starts, so turn statistics back on if winning turned them off
|
||||
if stats_paused_by_win:
|
||||
Settings.add_to_stats = true
|
||||
stats_paused_by_win = false
|
||||
|
||||
func show_about() -> void:
|
||||
print("show_about")
|
||||
about_window.open()
|
||||
|
||||
func show_shortcuts() -> void:
|
||||
print("show_shortcuts")
|
||||
shortcuts_window.open()
|
||||
|
||||
func show_settings() -> void:
|
||||
print("show_settings")
|
||||
settings_window.open()
|
||||
|
||||
func show_qr() -> void:
|
||||
print("show_qr")
|
||||
qr_window.open()
|
||||
|
||||
func get_statistics() -> void:
|
||||
print("get_statistics")
|
||||
statistic_popup.visible = true
|
||||
statistic_label.text = "Total amount of soundtracks in the playlist: " + str(soundtracks.size())
|
||||
|
||||
func sync_soundtracks() -> void:
|
||||
print("sync_soundtracks")
|
||||
sync_window.start_sync()
|
||||
|
||||
func _on_sync_started() -> void:
|
||||
print("_on_sync_started")
|
||||
sync_button.disabled = false
|
||||
sound_test_button.disabled = true
|
||||
reset_playlist_button.disabled = true
|
||||
show_answer_button.disabled = true
|
||||
next_button.disabled = true
|
||||
|
||||
func _on_sync_finished() -> void:
|
||||
print("_on_sync_finished")
|
||||
update_from_server()
|
||||
sound_test_button.disabled = false
|
||||
reset_playlist_button.disabled = false
|
||||
show_answer_button.disabled = false
|
||||
reset_points()
|
||||
|
||||
func get_suggestion_list() -> void:
|
||||
print("get_suggestion_list")
|
||||
var populate_list: Callable = func populate_list(array) -> void:
|
||||
if typeof(array) == TYPE_ARRAY:
|
||||
soundtracks = []
|
||||
Settings.delete_children(inspiration_list)
|
||||
soundtracks.append_array(array)
|
||||
for i: int in soundtracks.size():
|
||||
inspiration_list.add_child(ListRow.create(soundtracks[i], i, 20))
|
||||
else:
|
||||
print("Unexpected data")
|
||||
MusicServerClient.fetch_all_soundtracks(populate_list)
|
||||
|
||||
func add_players() -> void:
|
||||
print("add_players")
|
||||
Settings.edit_players = !Settings.edit_players
|
||||
add_player_container.visible = !add_player_container.visible
|
||||
new_player_name_field.grab_focus()
|
||||
|
||||
func add_player() -> void:
|
||||
print("add_player")
|
||||
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)
|
||||
new_player_object.connect("first_point_triggerd", music_player_container._on_point_triggered.bind("first"))
|
||||
new_player_object.connect("match_point_triggerd", music_player_container._on_point_triggered.bind("match"))
|
||||
new_player_object.connect("winner_triggerd", _on_player_won.bind(new_player_object))
|
||||
new_player_object.connect("point_given_sound", _on_make_point_given_sound)
|
||||
new_player_object.connect("player_point_given", _on_point_given.bind(new_player_object))
|
||||
new_player_object.connect("player_point_taken", _on_point_taken.bind(new_player_object))
|
||||
load_players()
|
||||
|
||||
func load_players() -> void:
|
||||
print("load_players")
|
||||
Settings.delete_children(players_list_container)
|
||||
var counter: int = 0
|
||||
for player_object: PlayerObject in Settings.player_array:
|
||||
print(player_object.player_name)
|
||||
|
||||
var new_player: Player = player.instantiate()
|
||||
new_player.player_object = player_object
|
||||
new_player.list_index = counter
|
||||
|
||||
new_player.connect("change_character_clicked", _on_player_change_character_clicked.bind(new_player, player_object.player_name))
|
||||
new_player.connect("player_removed", _on_player_removed.bind(player_object))
|
||||
|
||||
players_list_container.add_child(new_player)
|
||||
counter += 1
|
||||
|
||||
func _on_point_given(player_object: PlayerObject) -> void:
|
||||
print("_on_point_given")
|
||||
log_window.add_log_row(player_object.player_name + " got a point")
|
||||
if Playlist.currently_playing_song >= 0:
|
||||
Playlist.add_point(player_object.id)
|
||||
update_song_list()
|
||||
|
||||
func _on_make_point_given_sound() -> void:
|
||||
music_player_container.play_sound_effect(preload("res://sounds/itemopen.wav"))
|
||||
|
||||
func _on_point_taken(player_object: PlayerObject) -> void:
|
||||
print("_on_point_taken")
|
||||
log_window.add_log_row(player_object.player_name + " lost a point")
|
||||
music_player_container.play_sound_effect(preload("res://sounds/itemequip.wav"))
|
||||
if Playlist.currently_playing_song >= 0:
|
||||
Playlist.remove_point(player_object.id)
|
||||
update_song_list()
|
||||
|
||||
func _on_player_won(player_object: PlayerObject) -> void:
|
||||
print("_on_player_won")
|
||||
winner_window.show_winner(player_object)
|
||||
music_player_container.play_song(preload("res://sounds/winning.mp3"))
|
||||
# Songs played after the game is won don't count towards the statistics
|
||||
if Settings.add_to_stats:
|
||||
Settings.add_to_stats = false
|
||||
stats_paused_by_win = true
|
||||
|
||||
func _on_player_removed(player_object: PlayerObject) -> void:
|
||||
print("_on_player_removed ", player_object.player_name)
|
||||
Settings.player_array.erase(player_object)
|
||||
load_players()
|
||||
Playlist.remove_player_from_list(player_object.id)
|
||||
update_song_list()
|
||||
|
||||
func _on_player_change_character_clicked(new_player: Player, player_name: String) -> void:
|
||||
print("_on_player_change_character_clicked")
|
||||
current_player = new_player
|
||||
print("Change character for: " + player_name)
|
||||
new_character_select.show_grid(player_name)
|
||||
|
||||
func _on_new_character_selected(character_texture: ImageTexture) -> void:
|
||||
print("_on_new_character_selected")
|
||||
current_player.new_character_selected(character_texture)
|
||||
|
||||
func show_search() -> void:
|
||||
print("show_search")
|
||||
search_view.toggle()
|
||||
|
||||
|
||||
###Local
|
||||
|
||||
var local_path: String = '/Users/sebastian/ResilioSync/Sorterat_test/Metal Gear Solid 4 - Guns of the Patriots/2-16 Metal Gear Saga.mp3'
|
||||
|
||||
func open() -> void:
|
||||
fileDialog.popup()
|
||||
|
||||
func _on_file_dialog_dir_selected(path: String) -> void:
|
||||
print(path)
|
||||
dir_contents(path)
|
||||
|
||||
func dir_contents(path: String) -> void:
|
||||
var dir: DirAccess = DirAccess.open(path)
|
||||
if dir:
|
||||
dir.list_dir_begin()
|
||||
var file_name: String = dir.get_next()
|
||||
songs.clear()
|
||||
soundtracks = []
|
||||
while file_name != "":
|
||||
if dir.current_is_dir():
|
||||
#print("Found directory: " + file_name)
|
||||
soundtracks.append(file_name)
|
||||
else:
|
||||
#print("Found file: " + file_name)
|
||||
if file_name.ends_with(".mp3"):
|
||||
songs.append(path + "/" + file_name)
|
||||
file_name = dir.get_next()
|
||||
else:
|
||||
print("An error occurred when trying to access the path.")
|
||||
|
||||
for i: int in soundtracks.size():
|
||||
inspiration_list.add_child(ListRow.create(soundtracks[i], i, 20))
|
||||
-502
@@ -1,502 +0,0 @@
|
||||
[gd_scene format=3 uid="uid://xwq863o6uvsu"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://b8f4g15cas2j2" path="res://MainWindow.gd" id="1_eu0t5"]
|
||||
[ext_resource type="Script" uid="uid://elgn7gkxgagq" path="res://InsperationScrollContainer.gd" id="2_gxtxm"]
|
||||
[ext_resource type="Texture2D" uid="uid://bcfmpd7h512ef" path="res://icons/person_add_light.svg" id="5_31tjv"]
|
||||
[ext_resource type="PackedScene" uid="uid://ds15cgsf8vpvc" path="res://MusicPlayer.tscn" id="5_emn36"]
|
||||
[ext_resource type="PackedScene" uid="uid://cdy4kvemwaiom" path="res://new_character_select.tscn" id="6_dhrvg"]
|
||||
[ext_resource type="Script" uid="uid://gf1bk6xbe3j" path="res://MusicListScrollContainer.gd" id="7_dj026"]
|
||||
[ext_resource type="Script" uid="uid://cgakje8gb37tf" path="res://PlayerNameField.gd" id="7_qsdfy"]
|
||||
[ext_resource type="Theme" uid="uid://rxexo3ur85as" path="res://LightGrayTheme.tres" id="7_wxbv6"]
|
||||
[ext_resource type="PackedScene" uid="uid://bxydgil1yifps" path="res://SearchWindow.tscn" id="9_5ijvr"]
|
||||
[ext_resource type="PackedScene" uid="uid://dpdem7pdxweb5" path="res://SyncWindow.tscn" id="10_yxw0b"]
|
||||
[ext_resource type="PackedScene" uid="uid://dldpeo5y3l5hq" path="res://SettingsWindow.tscn" id="11_k62u5"]
|
||||
[ext_resource type="PackedScene" uid="uid://bd1by80q1v27e" path="res://shortcutsWindow.tscn" id="13_8eebo"]
|
||||
[ext_resource type="PackedScene" uid="uid://bijh5h5yrivm3" path="res://Log.tscn" id="14_26rwn"]
|
||||
[ext_resource type="PackedScene" uid="uid://btupbowehiyyu" path="res://QRWindow.tscn" id="15_qr_window"]
|
||||
[ext_resource type="PackedScene" uid="uid://cp54idaudb8dp" path="res://AboutWindow.tscn" id="16_about_window"]
|
||||
[ext_resource type="PackedScene" uid="uid://cxlsmxnrp0agh" path="res://WinnerWindow.tscn" id="17_winner_window"]
|
||||
[ext_resource type="Theme" path="res://popup_theme.tres" id="popup_theme"]
|
||||
|
||||
[sub_resource type="LabelSettings" id="LabelSettings_ychxr"]
|
||||
font_size = 25
|
||||
|
||||
[sub_resource type="LabelSettings" id="LabelSettings_qspbu"]
|
||||
font_size = 50
|
||||
|
||||
[sub_resource type="LabelSettings" id="LabelSettings_3m52w"]
|
||||
font_size = 35
|
||||
|
||||
[sub_resource type="InputEventKey" id="InputEventKey_03bm3"]
|
||||
device = -1
|
||||
alt_pressed = true
|
||||
keycode = 65
|
||||
unicode = 63743
|
||||
|
||||
[sub_resource type="Shortcut" id="Shortcut_jafqj"]
|
||||
events = [SubResource("InputEventKey_03bm3")]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_qk7sj"]
|
||||
bg_color = Color(0.180392, 0.180392, 0.180392, 1)
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_k1ygi"]
|
||||
bg_color = Color(0.180392, 0.180392, 0.180392, 1)
|
||||
|
||||
[sub_resource type="InputEventKey" id="InputEventKey_06rl4"]
|
||||
device = -1
|
||||
alt_pressed = true
|
||||
keycode = 86
|
||||
unicode = 8249
|
||||
|
||||
[sub_resource type="Shortcut" id="Shortcut_a7fvb"]
|
||||
events = [SubResource("InputEventKey_06rl4")]
|
||||
|
||||
[sub_resource type="InputEventKey" id="InputEventKey_7dnqw"]
|
||||
device = -1
|
||||
alt_pressed = true
|
||||
keycode = 67
|
||||
unicode = 231
|
||||
|
||||
[sub_resource type="Shortcut" id="Shortcut_d6fml"]
|
||||
events = [SubResource("InputEventKey_7dnqw")]
|
||||
|
||||
[sub_resource type="InputEventKey" id="InputEventKey_ujjlu"]
|
||||
device = -1
|
||||
alt_pressed = true
|
||||
keycode = 83
|
||||
|
||||
[sub_resource type="Shortcut" id="Shortcut_fbju4"]
|
||||
events = [SubResource("InputEventKey_ujjlu")]
|
||||
|
||||
[node name="Control" type="Control" unique_id=407149696]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_eu0t5")
|
||||
|
||||
[node name="TopPanelContainer" type="PanelContainer" parent="." unique_id=663404711]
|
||||
layout_mode = 0
|
||||
offset_right = 1920.0
|
||||
offset_bottom = 56.0
|
||||
|
||||
[node name="Title" type="Label" parent="." unique_id=1187907861]
|
||||
layout_mode = 0
|
||||
offset_left = 64.0
|
||||
offset_top = 12.0
|
||||
offset_right = 369.0
|
||||
offset_bottom = 50.0
|
||||
size_flags_horizontal = 0
|
||||
text = "Music Player Randomizer"
|
||||
label_settings = SubResource("LabelSettings_ychxr")
|
||||
|
||||
[node name="Open" type="Button" parent="." unique_id=165052329]
|
||||
visible = false
|
||||
layout_mode = 0
|
||||
offset_left = 1296.0
|
||||
offset_top = 66.0
|
||||
offset_right = 1346.0
|
||||
offset_bottom = 97.0
|
||||
text = "Open"
|
||||
|
||||
[node name="FileDialog" type="FileDialog" parent="." unique_id=485771559]
|
||||
title = "Open a Directory"
|
||||
initial_position = 2
|
||||
size = Vector2i(616, 400)
|
||||
ok_button_text = "Select Current Folder"
|
||||
file_mode = 2
|
||||
access = 2
|
||||
show_hidden_files = true
|
||||
|
||||
[node name="MusicListPanel" type="PanelContainer" parent="." unique_id=2011292375]
|
||||
layout_mode = 0
|
||||
offset_left = 64.0
|
||||
offset_top = 384.0
|
||||
offset_right = 1152.0
|
||||
offset_bottom = 896.0
|
||||
theme = ExtResource("7_wxbv6")
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="MusicListPanel" unique_id=707750459]
|
||||
layout_mode = 2
|
||||
follow_focus = true
|
||||
script = ExtResource("7_dj026")
|
||||
|
||||
[node name="MusicList" type="VBoxContainer" parent="MusicListPanel/ScrollContainer" unique_id=373398568]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="." unique_id=498283761]
|
||||
layout_mode = 0
|
||||
offset_left = 1216.0
|
||||
offset_top = 128.0
|
||||
offset_right = 1857.0
|
||||
offset_bottom = 512.0
|
||||
horizontal_scroll_mode = 0
|
||||
vertical_scroll_mode = 3
|
||||
script = ExtResource("2_gxtxm")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="ScrollContainer" unique_id=1215162949]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=42468498]
|
||||
layout_mode = 0
|
||||
offset_left = 64.0
|
||||
offset_top = 64.0
|
||||
offset_right = 1152.0
|
||||
offset_bottom = 384.0
|
||||
|
||||
[node name="SoundtrackLabel" type="Label" parent="VBoxContainer" unique_id=173147793]
|
||||
custom_minimum_size = Vector2(1, 1)
|
||||
layout_mode = 2
|
||||
label_settings = SubResource("LabelSettings_qspbu")
|
||||
horizontal_alignment = 1
|
||||
autowrap_mode = 2
|
||||
|
||||
[node name="SongLabel" type="Label" parent="VBoxContainer" unique_id=1687490027]
|
||||
custom_minimum_size = Vector2(1, 1)
|
||||
layout_mode = 2
|
||||
label_settings = SubResource("LabelSettings_3m52w")
|
||||
horizontal_alignment = 1
|
||||
autowrap_mode = 2
|
||||
|
||||
[node name="MusicPlayer" parent="." unique_id=1990459832 instance=ExtResource("5_emn36")]
|
||||
layout_mode = 0
|
||||
anchors_preset = 0
|
||||
anchor_left = 0.0
|
||||
anchor_top = 0.0
|
||||
anchor_right = 0.0
|
||||
anchor_bottom = 0.0
|
||||
offset_left = 65.0
|
||||
offset_top = 992.0
|
||||
offset_right = 1856.0
|
||||
offset_bottom = 1024.0
|
||||
grow_horizontal = 1
|
||||
grow_vertical = 1
|
||||
|
||||
[node name="Players" type="PanelContainer" parent="." unique_id=1708375945]
|
||||
layout_mode = 0
|
||||
offset_left = 1216.0
|
||||
offset_top = 576.0
|
||||
offset_right = 1857.0
|
||||
offset_bottom = 977.0
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="Players" unique_id=270084211]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="Players/VBoxContainer" unique_id=513047023]
|
||||
custom_minimum_size = Vector2(2.08165e-12, 40)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="Players/VBoxContainer/HBoxContainer" unique_id=1331357386]
|
||||
custom_minimum_size = Vector2(2.08165e-12, 40)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 1
|
||||
text = "Players"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="AddPlayersButton" type="TextureButton" parent="Players/VBoxContainer/HBoxContainer" unique_id=634410148]
|
||||
custom_minimum_size = Vector2(2.08165e-12, 40)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 8
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
shortcut = SubResource("Shortcut_jafqj")
|
||||
texture_normal = ExtResource("5_31tjv")
|
||||
|
||||
[node name="AddPlayerContainer" type="HBoxContainer" parent="Players/VBoxContainer" unique_id=1371948739]
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(2.08165e-12, 40)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Panel" type="Panel" parent="Players/VBoxContainer/AddPlayerContainer" unique_id=1569854056]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_qk7sj")
|
||||
|
||||
[node name="PlayerNameField" type="TextEdit" parent="Players/VBoxContainer/AddPlayerContainer" unique_id=332535327]
|
||||
custom_minimum_size = Vector2(2.08165e-12, 40)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
placeholder_text = "Player name"
|
||||
script = ExtResource("7_qsdfy")
|
||||
|
||||
[node name="AddPlayerButton" type="Button" parent="Players/VBoxContainer/AddPlayerContainer" unique_id=245001313]
|
||||
custom_minimum_size = Vector2(2.08165e-12, 40)
|
||||
layout_mode = 2
|
||||
text = "Add"
|
||||
|
||||
[node name="Panel2" type="Panel" parent="Players/VBoxContainer/AddPlayerContainer" unique_id=60215448]
|
||||
custom_minimum_size = Vector2(2.08165e-12, 40)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_k1ygi")
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="Players/VBoxContainer" unique_id=1000000001]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 15
|
||||
horizontal_scroll_mode = 0
|
||||
|
||||
[node name="PlayersListVBoxContainer" type="VBoxContainer" parent="Players/VBoxContainer/ScrollContainer" unique_id=1000000002]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 15
|
||||
|
||||
[node name="ResetPlaylistButton" type="Button" parent="." unique_id=1754155620]
|
||||
layout_mode = 0
|
||||
offset_left = 1360.0
|
||||
offset_top = 64.0
|
||||
offset_right = 1471.0
|
||||
offset_bottom = 95.0
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
text = "Reset playlist"
|
||||
|
||||
[node name="ResetPointsButton" type="Button" parent="." unique_id=942880776]
|
||||
layout_mode = 0
|
||||
offset_left = 1488.0
|
||||
offset_top = 64.0
|
||||
offset_right = 1599.0
|
||||
offset_bottom = 95.0
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
text = "Reset points"
|
||||
|
||||
[node name="SyncButton" type="Button" parent="." unique_id=1228313556]
|
||||
layout_mode = 0
|
||||
offset_left = 1616.0
|
||||
offset_top = 64.0
|
||||
offset_right = 1759.0
|
||||
offset_bottom = 95.0
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
text = "Sync soundtracks"
|
||||
|
||||
[node name="SoundTestButton" type="Button" parent="." unique_id=1332971879]
|
||||
layout_mode = 0
|
||||
offset_left = 1776.0
|
||||
offset_top = 64.0
|
||||
offset_right = 1887.0
|
||||
offset_bottom = 95.0
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
text = "Sound test"
|
||||
|
||||
[node name="ShowAnswerButton" type="Button" parent="." unique_id=415388039]
|
||||
layout_mode = 0
|
||||
offset_left = 65.0
|
||||
offset_top = 944.0
|
||||
offset_right = 176.0
|
||||
offset_bottom = 975.0
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
shortcut = SubResource("Shortcut_a7fvb")
|
||||
text = "Show answer"
|
||||
|
||||
[node name="NextButton" type="Button" parent="." unique_id=1744703488]
|
||||
layout_mode = 0
|
||||
offset_left = 193.0
|
||||
offset_top = 944.0
|
||||
offset_right = 368.0
|
||||
offset_bottom = 975.0
|
||||
focus_mode = 0
|
||||
disabled = true
|
||||
action_mode = 0
|
||||
shortcut = SubResource("Shortcut_d6fml")
|
||||
text = "Randomize new track"
|
||||
|
||||
[node name="SearchButton" type="Button" parent="." unique_id=1782453581]
|
||||
layout_mode = 0
|
||||
offset_left = 1354.0
|
||||
offset_top = 8.0
|
||||
offset_right = 1424.0
|
||||
offset_bottom = 48.0
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
shortcut = SubResource("Shortcut_fbju4")
|
||||
text = "Search"
|
||||
|
||||
[node name="SettingsButton" type="Button" parent="." unique_id=1193400323]
|
||||
layout_mode = 0
|
||||
offset_left = 1442.0
|
||||
offset_top = 8.0
|
||||
offset_right = 1512.0
|
||||
offset_bottom = 48.0
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
text = "Settings"
|
||||
|
||||
[node name="StatisticsButton" type="Button" parent="." unique_id=2045021269]
|
||||
layout_mode = 0
|
||||
offset_left = 1530.0
|
||||
offset_top = 8.0
|
||||
offset_right = 1600.0
|
||||
offset_bottom = 48.0
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
text = "Statistics"
|
||||
|
||||
[node name="ShortcutsButton" type="Button" parent="." unique_id=2066498226]
|
||||
layout_mode = 0
|
||||
offset_left = 1626.0
|
||||
offset_top = 8.0
|
||||
offset_right = 1696.0
|
||||
offset_bottom = 48.0
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
text = "Shortcuts"
|
||||
|
||||
[node name="AboutButton" type="Button" parent="." unique_id=1057035253]
|
||||
layout_mode = 0
|
||||
offset_left = 1722.0
|
||||
offset_top = 8.0
|
||||
offset_right = 1792.0
|
||||
offset_bottom = 48.0
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
text = "About"
|
||||
|
||||
[node name="QRButton" type="Button" parent="." unique_id=2021908559]
|
||||
layout_mode = 0
|
||||
offset_left = 1802.0
|
||||
offset_top = 8.0
|
||||
offset_right = 1872.0
|
||||
offset_bottom = 48.0
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
text = "QR"
|
||||
|
||||
[node name="Search" parent="." unique_id=1661250375 instance=ExtResource("9_5ijvr")]
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
offset_left = 56.0
|
||||
offset_top = 96.0
|
||||
offset_right = 56.0
|
||||
offset_bottom = 96.0
|
||||
|
||||
[node name="SyncWindow" parent="." unique_id=182499699 instance=ExtResource("10_yxw0b")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
offset_left = 210.0
|
||||
offset_top = 153.0
|
||||
offset_right = 210.0
|
||||
offset_bottom = 153.0
|
||||
|
||||
[node name="SettingsWindow" parent="." unique_id=1323246978 instance=ExtResource("11_k62u5")]
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
offset_left = 720.0
|
||||
offset_top = 153.0
|
||||
offset_right = 720.0
|
||||
offset_bottom = 153.0
|
||||
|
||||
[node name="QRWindow" parent="." unique_id=589733821 instance=ExtResource("15_qr_window")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
offset_left = 720.0
|
||||
offset_top = 153.0
|
||||
offset_right = 720.0
|
||||
offset_bottom = 153.0
|
||||
|
||||
[node name="NewCharacterSelect" parent="." unique_id=1679856322 instance=ExtResource("6_dhrvg")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
offset_left = 405.0
|
||||
offset_top = 165.0
|
||||
offset_right = 1557.0
|
||||
offset_bottom = 805.0
|
||||
|
||||
[node name="ShortcutsWindow" parent="." unique_id=1513416242 instance=ExtResource("13_8eebo")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
offset_left = 562.0
|
||||
offset_top = 160.0
|
||||
offset_right = 1262.0
|
||||
offset_bottom = 860.0
|
||||
|
||||
[node name="StatisticsPopupPanel" type="PopupPanel" parent="." unique_id=298311407]
|
||||
theme = ExtResource("popup_theme")
|
||||
oversampling_override = 1.0
|
||||
initial_position = 2
|
||||
size = Vector2i(450, 100)
|
||||
|
||||
[node name="Label" type="Label" parent="StatisticsPopupPanel" unique_id=548316493]
|
||||
offset_left = 4.0
|
||||
offset_top = 4.0
|
||||
offset_right = 446.0
|
||||
offset_bottom = 96.0
|
||||
text = "Statistics"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="StatisticsLabel" type="Label" parent="StatisticsPopupPanel" unique_id=58226477]
|
||||
offset_left = 4.0
|
||||
offset_top = 4.0
|
||||
offset_right = 446.0
|
||||
offset_bottom = 96.0
|
||||
text = "Total amount of soundtracks in the playlist: 9000"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="AboutWindow" parent="." unique_id=141261008 instance=ExtResource("16_about_window")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
offset_left = 720.0
|
||||
offset_top = 153.0
|
||||
offset_right = 720.0
|
||||
offset_bottom = 153.0
|
||||
|
||||
[node name="WinnerWindow" parent="." unique_id=999999999 instance=ExtResource("17_winner_window")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
offset_left = 720.0
|
||||
offset_top = 153.0
|
||||
offset_right = 720.0
|
||||
offset_bottom = 153.0
|
||||
|
||||
[node name="RepeatSongCheckButton" type="CheckButton" parent="." unique_id=776835053]
|
||||
layout_mode = 0
|
||||
offset_left = 64.0
|
||||
offset_top = 1032.0
|
||||
offset_right = 208.0
|
||||
offset_bottom = 1063.0
|
||||
focus_mode = 0
|
||||
button_pressed = true
|
||||
action_mode = 0
|
||||
text = "Auto repeat"
|
||||
|
||||
[node name="DebugLabel" type="Label" parent="." unique_id=1959221257]
|
||||
visible = false
|
||||
layout_mode = 0
|
||||
offset_left = 974.0
|
||||
offset_top = 904.0
|
||||
offset_right = 1014.0
|
||||
offset_bottom = 927.0
|
||||
scale = Vector2(3, 3)
|
||||
text = "DEBUG"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
autowrap_mode = 2
|
||||
|
||||
[node name="Log" parent="." unique_id=1289881388 instance=ExtResource("14_26rwn")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
offset_left = 562.0
|
||||
offset_top = 132.0
|
||||
offset_right = 1262.0
|
||||
offset_bottom = 832.0
|
||||
|
||||
[connection signal="dir_selected" from="FileDialog" to="." method="_on_file_dialog_dir_selected"]
|
||||
@@ -1,8 +0,0 @@
|
||||
extends ScrollContainer
|
||||
|
||||
var max_value = 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
|
||||
+1
-1
@@ -5,7 +5,7 @@ Releases are made from the version in `project.godot`
|
||||
same version in the About window.
|
||||
|
||||
1. Bump the version, e.g. from `1.9.0` to `1.10.0`, and add a `WHATS_NEW` entry in
|
||||
`AboutWindow.gd`.
|
||||
`popups/about_popup.gd`.
|
||||
2. Merge to `main` and push.
|
||||
|
||||
The `Build` workflow (`.gitea/workflows/gitea-build.yaml`) then:
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ pass.
|
||||
| Folder | Contents |
|
||||
|---|---|
|
||||
| `tests/unit/` | Logic without scenes: playlist, songs, player scoring, time formatting, search, the server client |
|
||||
| `tests/integration/` | Real scenes (`MainWindow.tscn`, `SyncWindow.tscn`) run against a fake server, including the point keybinds |
|
||||
| `tests/integration/` | Real scenes (`MainWindow.tscn`, `SyncPopup.tscn`) run against a fake server, including the point keybinds |
|
||||
| `tests/fakes/` | `FakeHttpTransport`, `AutoloadSnapshot` and the `MainWindowTestSuite` base class |
|
||||
| `tests/data/` | Files the fake server sends, e.g. a short MP3 |
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
extends Node
|
||||
## Writes messages about what the game is doing, for finding problems.
|
||||
##
|
||||
## Registered as the [code]GameLog[/code] autoload. Use [method debug] instead of
|
||||
## [code]print()[/code]: the messages show in debug builds and the editor, and are left
|
||||
## out of released games. Problems the player may notice go through [method warning].
|
||||
|
||||
|
||||
## Prints [param parts] joined together, like [code]print()[/code], in debug builds only.
|
||||
func debug(...parts: Array) -> void:
|
||||
if OS.is_debug_build():
|
||||
print(_join(parts))
|
||||
|
||||
|
||||
## Reports a problem in the editor's debugger and the console, in every build.
|
||||
func warning(...parts: Array) -> void:
|
||||
push_warning(_join(parts))
|
||||
|
||||
|
||||
func _join(parts: Array) -> String:
|
||||
return "".join(parts.map(func(part: Variant) -> String: return str(part)))
|
||||
@@ -0,0 +1 @@
|
||||
uid://c37mlo6svk03q
|
||||
@@ -0,0 +1,59 @@
|
||||
class_name ListRow
|
||||
extends PanelContainer
|
||||
## One item in a list of names, such as the inspiration list or a sync result column.
|
||||
##
|
||||
## Every row has its own background, alternating between two shades, so it is clear
|
||||
## where an item starts even when its text wraps over several lines.
|
||||
|
||||
const EVEN_BACKGROUND: Color = Color(1, 1, 1, 0.05)
|
||||
const ODD_BACKGROUND: Color = Color(1, 1, 1, 0.11)
|
||||
|
||||
static var _even_style: StyleBoxFlat
|
||||
static var _odd_style: StyleBoxFlat
|
||||
|
||||
## The label showing the item's text.
|
||||
var label: Label
|
||||
|
||||
## The item's text.
|
||||
var text: String:
|
||||
|
||||
|
||||
get:
|
||||
return label.text
|
||||
|
||||
|
||||
## Creates the row for item number [param row] (counting from 0) in a list.
|
||||
static func create(
|
||||
item_text: String,
|
||||
row: int,
|
||||
font_size: int,
|
||||
alignment: HorizontalAlignment = HORIZONTAL_ALIGNMENT_LEFT,
|
||||
) -> ListRow:
|
||||
var list_row: ListRow = ListRow.new()
|
||||
list_row.add_theme_stylebox_override("panel", _style(row % 2 != 0))
|
||||
list_row.label = Label.new()
|
||||
list_row.label.add_theme_font_size_override("font_size", font_size)
|
||||
list_row.label.text = item_text
|
||||
list_row.label.horizontal_alignment = alignment
|
||||
list_row.label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
list_row.add_child(list_row.label)
|
||||
return list_row
|
||||
|
||||
|
||||
# Rows share two styles instead of each creating its own
|
||||
static func _style(odd: bool) -> StyleBoxFlat:
|
||||
if _even_style == null:
|
||||
_even_style = _make_style(EVEN_BACKGROUND)
|
||||
_odd_style = _make_style(ODD_BACKGROUND)
|
||||
return _odd_style if odd else _even_style
|
||||
|
||||
|
||||
static func _make_style(background: Color) -> StyleBoxFlat:
|
||||
var style: StyleBoxFlat = StyleBoxFlat.new()
|
||||
style.bg_color = background
|
||||
style.set_corner_radius_all(4)
|
||||
style.content_margin_left = 8
|
||||
style.content_margin_right = 8
|
||||
style.content_margin_top = 2
|
||||
style.content_margin_bottom = 2
|
||||
return style
|
||||
File renamed without changes.
@@ -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
|
||||
File renamed without changes.
@@ -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,22 +34,26 @@ 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():
|
||||
return
|
||||
print("show_answer_pressed song_object: ", song_object_array[song_no]._to_string())
|
||||
GameLog.debug("show_answer_pressed song_object: ", song_object_array[song_no]._to_string())
|
||||
song_object_array[song_no].is_answered = true
|
||||
if Settings.add_to_stats:
|
||||
print("add to stats: ", song_no)
|
||||
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
|
||||
File renamed without changes.
@@ -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)
|
||||
File renamed without changes.
@@ -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:
|
||||
print("add_point")
|
||||
GameLog.debug("add_point")
|
||||
if !players_given_point.has(player_id):
|
||||
players_given_point.append(player_id)
|
||||
|
||||
|
||||
func remove_point(player_id: String) -> void:
|
||||
print("remove_point")
|
||||
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]
|
||||
File renamed without changes.
@@ -1,42 +0,0 @@
|
||||
class_name HttpTransport
|
||||
extends RefCounted
|
||||
## Sends HTTP requests for [code]MusicServerClient[/code] using [HTTPRequest] nodes.
|
||||
##
|
||||
## Tests subclass this to answer requests without a network connection.
|
||||
|
||||
## 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 _owner: Node
|
||||
|
||||
|
||||
func _init(owner: Node) -> void:
|
||||
_owner = owner
|
||||
|
||||
|
||||
## Requests [param url] and calls [param on_complete] with
|
||||
## [code](response_code: int, body: PackedByteArray)[/code]. Connection errors and
|
||||
## timeouts are reported with response code 0.
|
||||
func request(url: String, method: int, on_complete: Callable) -> void:
|
||||
var http_request: HTTPRequest = HTTPRequest.new()
|
||||
http_request.timeout = timeout_seconds
|
||||
_owner.add_child(http_request)
|
||||
http_request.request_completed.connect(
|
||||
func(
|
||||
result: int,
|
||||
response_code: int,
|
||||
_headers: PackedStringArray,
|
||||
body: PackedByteArray,
|
||||
) -> void:
|
||||
http_request.queue_free()
|
||||
on_complete.call(response_code if result == HTTPRequest.RESULT_SUCCESS else 0, body)
|
||||
)
|
||||
var headers: PackedStringArray = []
|
||||
if method == HTTPClient.METHOD_PUT:
|
||||
headers.append("Content-Type: application/json")
|
||||
var error: int = http_request.request(url, headers, method)
|
||||
if error != OK:
|
||||
http_request.queue_free()
|
||||
push_error("Could not send request to %s (error %d)" % [url, error])
|
||||
on_complete.call(0, PackedByteArray())
|
||||
+17
-17
@@ -5,20 +5,20 @@ extends SceneTree
|
||||
|
||||
|
||||
func _initialize() -> void:
|
||||
var args: PackedStringArray = OS.get_cmdline_user_args()
|
||||
if args.size() != 3:
|
||||
printerr("Usage: godot --headless -s render_icon.gd -- <input.svg> <output.png> <size>")
|
||||
quit(1)
|
||||
return
|
||||
var svg: String = FileAccess.get_file_as_string(args[0])
|
||||
var source: Image = Image.new()
|
||||
# Load at 1x to read the SVG's own size, then scale so the result is <size> pixels
|
||||
var error: int = source.load_svg_from_string(svg)
|
||||
if error == OK:
|
||||
var image: Image = Image.new()
|
||||
error = image.load_svg_from_string(svg, float(args[2]) / source.get_width())
|
||||
if error == OK:
|
||||
error = image.save_png(args[1])
|
||||
if error != OK:
|
||||
printerr("Could not render %s to %s (error %d)" % [args[0], args[1], error])
|
||||
quit(error)
|
||||
var args: PackedStringArray = OS.get_cmdline_user_args()
|
||||
if args.size() != 3:
|
||||
printerr("Usage: godot --headless -s render_icon.gd -- <input.svg> <output.png> <size>")
|
||||
quit(1)
|
||||
return
|
||||
var svg: String = FileAccess.get_file_as_string(args[0])
|
||||
var source: Image = Image.new()
|
||||
# Load at 1x to read the SVG's own size, then scale so the result is <size> pixels
|
||||
var error: int = source.load_svg_from_string(svg)
|
||||
if error == OK:
|
||||
var image: Image = Image.new()
|
||||
error = image.load_svg_from_string(svg, float(args[2]) / source.get_width())
|
||||
if error == OK:
|
||||
error = image.save_png(args[1])
|
||||
if error != OK:
|
||||
printerr("Could not render %s to %s (error %d)" % [args[0], args[1], error])
|
||||
quit(error)
|
||||
-57
@@ -1,57 +0,0 @@
|
||||
class_name ListRow
|
||||
extends PanelContainer
|
||||
## One item in a list of names, such as the inspiration list or a sync result column.
|
||||
##
|
||||
## Every row has its own background, alternating between two shades, so it is clear
|
||||
## where an item starts even when its text wraps over several lines.
|
||||
|
||||
const EVEN_BACKGROUND: Color = Color(1, 1, 1, 0.05)
|
||||
const ODD_BACKGROUND: Color = Color(1, 1, 1, 0.11)
|
||||
|
||||
static var _even_style: StyleBoxFlat
|
||||
static var _odd_style: StyleBoxFlat
|
||||
|
||||
## The label showing the item's text.
|
||||
var label: Label
|
||||
|
||||
## The item's text.
|
||||
var text: String:
|
||||
get:
|
||||
return label.text
|
||||
|
||||
|
||||
## Creates the row for item number [param row] (counting from 0) in a list.
|
||||
static func create(
|
||||
item_text: String,
|
||||
row: int,
|
||||
font_size: int,
|
||||
alignment: HorizontalAlignment = HORIZONTAL_ALIGNMENT_LEFT,
|
||||
) -> ListRow:
|
||||
var list_row: ListRow = ListRow.new()
|
||||
list_row.add_theme_stylebox_override("panel", _style(row % 2 != 0))
|
||||
list_row.label = Label.new()
|
||||
list_row.label.add_theme_font_size_override("font_size", font_size)
|
||||
list_row.label.text = item_text
|
||||
list_row.label.horizontal_alignment = alignment
|
||||
list_row.label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
list_row.add_child(list_row.label)
|
||||
return list_row
|
||||
|
||||
|
||||
# Rows share two styles instead of each creating its own
|
||||
static func _style(odd: bool) -> StyleBoxFlat:
|
||||
if _even_style == null:
|
||||
_even_style = _make_style(EVEN_BACKGROUND)
|
||||
_odd_style = _make_style(ODD_BACKGROUND)
|
||||
return _odd_style if odd else _even_style
|
||||
|
||||
|
||||
static func _make_style(background: Color) -> StyleBoxFlat:
|
||||
var style: StyleBoxFlat = StyleBoxFlat.new()
|
||||
style.bg_color = background
|
||||
style.set_corner_radius_all(4)
|
||||
style.content_margin_left = 8
|
||||
style.content_margin_right = 8
|
||||
style.content_margin_top = 2
|
||||
style.content_margin_bottom = 2
|
||||
return style
|
||||
@@ -0,0 +1,102 @@
|
||||
[gd_scene format=3 uid="uid://xwq863o6uvsu"]
|
||||
|
||||
[ext_resource type="PackedScene" path="res://main/TopBar.tscn" id="top_bar_scene"]
|
||||
[ext_resource type="PackedScene" path="res://quiz/Quiz.tscn" id="quiz_scene"]
|
||||
[ext_resource type="Script" uid="uid://b8f4g15cas2j2" path="res://main/main_window.gd" id="1_eu0t5"]
|
||||
[ext_resource type="PackedScene" uid="uid://bxydgil1yifps" path="res://popups/SearchPopup.tscn" id="9_5ijvr"]
|
||||
[ext_resource type="PackedScene" uid="uid://dpdem7pdxweb5" path="res://popups/SyncPopup.tscn" id="10_yxw0b"]
|
||||
[ext_resource type="PackedScene" uid="uid://bd1by80q1v27e" path="res://popups/ShortcutsPopup.tscn" id="13_8eebo"]
|
||||
[ext_resource type="PackedScene" uid="uid://btupbowehiyyu" path="res://popups/QRPopup.tscn" id="15_qr_window"]
|
||||
[ext_resource type="PackedScene" uid="uid://cp54idaudb8dp" path="res://popups/AboutPopup.tscn" id="16_about_window"]
|
||||
[ext_resource type="Theme" path="res://themes/popup_theme.tres" id="popup_theme"]
|
||||
[ext_resource type="PackedScene" uid="uid://bijh5h5yrivm3" path="res://popups/LogPopup.tscn" id="14_26rwn"]
|
||||
|
||||
[node name="MainWindow" type="Control" unique_id=407149696]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
script = ExtResource("1_eu0t5")
|
||||
|
||||
[node name="TopBar" parent="." unique_id=1734261695 instance=ExtResource("top_bar_scene")]
|
||||
|
||||
[node name="Quiz" parent="." unique_id=1357908642 instance=ExtResource("quiz_scene")]
|
||||
|
||||
[node name="SearchPopup" parent="." unique_id=1661250375 instance=ExtResource("9_5ijvr")]
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
offset_left = 56.0
|
||||
offset_top = 96.0
|
||||
offset_right = 56.0
|
||||
offset_bottom = 96.0
|
||||
|
||||
[node name="SyncPopup" parent="." unique_id=182499699 instance=ExtResource("10_yxw0b")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
offset_left = 210.0
|
||||
offset_top = 153.0
|
||||
offset_right = 210.0
|
||||
offset_bottom = 153.0
|
||||
|
||||
[node name="QRPopup" parent="." unique_id=589733821 instance=ExtResource("15_qr_window")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
offset_left = 720.0
|
||||
offset_top = 153.0
|
||||
offset_right = 720.0
|
||||
offset_bottom = 153.0
|
||||
|
||||
[node name="ShortcutsPopup" parent="." unique_id=1513416242 instance=ExtResource("13_8eebo")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
offset_left = 562.0
|
||||
offset_top = 160.0
|
||||
offset_right = 1262.0
|
||||
offset_bottom = 860.0
|
||||
|
||||
[node name="StatisticsPopupPanel" type="PopupPanel" parent="." unique_id=298311407]
|
||||
theme = ExtResource("popup_theme")
|
||||
oversampling_override = 1.0
|
||||
initial_position = 2
|
||||
size = Vector2i(450, 100)
|
||||
|
||||
[node name="Label" type="Label" parent="StatisticsPopupPanel" unique_id=548316493]
|
||||
offset_left = 4.0
|
||||
offset_top = 4.0
|
||||
offset_right = 446.0
|
||||
offset_bottom = 96.0
|
||||
text = "Statistics"
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="StatisticsLabel" type="Label" parent="StatisticsPopupPanel" unique_id=58226477]
|
||||
unique_name_in_owner = true
|
||||
offset_left = 4.0
|
||||
offset_top = 4.0
|
||||
offset_right = 446.0
|
||||
offset_bottom = 96.0
|
||||
text = "Total amount of soundtracks in the playlist: 9000"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="AboutPopup" parent="." unique_id=141261008 instance=ExtResource("16_about_window")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
offset_left = 720.0
|
||||
offset_top = 153.0
|
||||
offset_right = 720.0
|
||||
offset_bottom = 153.0
|
||||
|
||||
[node name="LogPopup" parent="." unique_id=1289881388 instance=ExtResource("14_26rwn")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
offset_left = 562.0
|
||||
offset_top = 132.0
|
||||
offset_right = 1262.0
|
||||
offset_bottom = 832.0
|
||||
@@ -0,0 +1,100 @@
|
||||
[gd_scene format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://main/top_bar.gd" id="top_bar_script"]
|
||||
|
||||
[sub_resource type="LabelSettings" id="LabelSettings_ychxr"]
|
||||
font_size = 25
|
||||
|
||||
[sub_resource type="InputEventKey" id="InputEventKey_ujjlu"]
|
||||
device = -1
|
||||
alt_pressed = true
|
||||
keycode = 83
|
||||
|
||||
[sub_resource type="Shortcut" id="Shortcut_fbju4"]
|
||||
events = [SubResource("InputEventKey_ujjlu")]
|
||||
|
||||
[node name="TopBar" type="Control" unique_id=1489162753]
|
||||
layout_mode = 3
|
||||
anchors_preset = 10
|
||||
anchor_right = 1.0
|
||||
offset_bottom = 56.0
|
||||
grow_horizontal = 2
|
||||
mouse_filter = 2
|
||||
script = ExtResource("top_bar_script")
|
||||
|
||||
[node name="TopPanelContainer" type="PanelContainer" parent="." unique_id=663404711]
|
||||
layout_mode = 0
|
||||
offset_right = 1920.0
|
||||
offset_bottom = 56.0
|
||||
|
||||
[node name="Title" type="Label" parent="." unique_id=1187907861]
|
||||
layout_mode = 0
|
||||
offset_left = 64.0
|
||||
offset_top = 12.0
|
||||
offset_right = 369.0
|
||||
offset_bottom = 50.0
|
||||
size_flags_horizontal = 0
|
||||
text = "Music Player Randomizer"
|
||||
label_settings = SubResource("LabelSettings_ychxr")
|
||||
|
||||
[node name="SyncButton" type="Button" parent="." unique_id=1228313556]
|
||||
layout_mode = 0
|
||||
offset_left = 1442.0
|
||||
offset_top = 8.0
|
||||
offset_right = 1512.0
|
||||
offset_bottom = 48.0
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
text = "Sync"
|
||||
|
||||
[node name="SearchButton" type="Button" parent="." unique_id=1782453581]
|
||||
layout_mode = 0
|
||||
offset_left = 1354.0
|
||||
offset_top = 8.0
|
||||
offset_right = 1424.0
|
||||
offset_bottom = 48.0
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
shortcut = SubResource("Shortcut_fbju4")
|
||||
text = "Search"
|
||||
|
||||
[node name="StatisticsButton" type="Button" parent="." unique_id=2045021269]
|
||||
layout_mode = 0
|
||||
offset_left = 1530.0
|
||||
offset_top = 8.0
|
||||
offset_right = 1600.0
|
||||
offset_bottom = 48.0
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
text = "Statistics"
|
||||
|
||||
[node name="ShortcutsButton" type="Button" parent="." unique_id=2066498226]
|
||||
layout_mode = 0
|
||||
offset_left = 1626.0
|
||||
offset_top = 8.0
|
||||
offset_right = 1696.0
|
||||
offset_bottom = 48.0
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
text = "Shortcuts"
|
||||
|
||||
[node name="AboutButton" type="Button" parent="." unique_id=1057035253]
|
||||
layout_mode = 0
|
||||
offset_left = 1722.0
|
||||
offset_top = 8.0
|
||||
offset_right = 1792.0
|
||||
offset_bottom = 48.0
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
text = "About"
|
||||
|
||||
[node name="QRButton" type="Button" parent="." unique_id=2021908559]
|
||||
layout_mode = 0
|
||||
offset_left = 1802.0
|
||||
offset_top = 8.0
|
||||
offset_right = 1872.0
|
||||
offset_bottom = 48.0
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
text = "QR"
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
class_name MainWindow
|
||||
extends Control
|
||||
## The main window: the top bar, the popups opened from it and the quiz below it.
|
||||
##
|
||||
## Everything that is not part of a quiz lives here, so other views can be added next
|
||||
## to the quiz later.
|
||||
|
||||
## 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())
|
||||
if OS.has_feature("macos"):
|
||||
GameLog.debug("is_mac")
|
||||
top_bar.search_pressed.connect(show_search)
|
||||
top_bar.sync_pressed.connect(sync_soundtracks)
|
||||
top_bar.statistics_pressed.connect(get_statistics)
|
||||
top_bar.shortcuts_pressed.connect(show_shortcuts)
|
||||
top_bar.about_pressed.connect(show_about)
|
||||
top_bar.qr_pressed.connect(show_qr)
|
||||
|
||||
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)
|
||||
|
||||
search_popup.get_list_of_soundtracks()
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if event is InputEventKey and event.pressed:
|
||||
if event.alt_pressed && event.keycode == KEY_ENTER:
|
||||
GameLog.debug("Alt + Enter pressed")
|
||||
Settings.fullscreen = !Settings.fullscreen
|
||||
if Settings.fullscreen:
|
||||
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_FULLSCREEN)
|
||||
if !Settings.fullscreen:
|
||||
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
|
||||
if event.alt_pressed && event.keycode == KEY_K:
|
||||
shortcuts_popup.toggle()
|
||||
if event.alt_pressed && event.keycode == KEY_L:
|
||||
log_popup.toggle()
|
||||
|
||||
|
||||
func _popups() -> Array[BasePopup]:
|
||||
return [search_popup, sync_popup, qr_popup, shortcuts_popup, about_popup, log_popup]
|
||||
|
||||
|
||||
func show_search() -> void:
|
||||
GameLog.debug("show_search")
|
||||
search_popup.toggle()
|
||||
|
||||
|
||||
func show_about() -> void:
|
||||
GameLog.debug("show_about")
|
||||
about_popup.open()
|
||||
|
||||
|
||||
func show_shortcuts() -> void:
|
||||
GameLog.debug("show_shortcuts")
|
||||
shortcuts_popup.open()
|
||||
|
||||
|
||||
func show_qr() -> void:
|
||||
GameLog.debug("show_qr")
|
||||
qr_popup.open()
|
||||
|
||||
|
||||
func get_statistics() -> void:
|
||||
GameLog.debug("get_statistics")
|
||||
statistic_popup.visible = true
|
||||
statistic_label.text = "Total amount of soundtracks in the playlist: " \
|
||||
+ str(search_popup.soundtracks.size())
|
||||
|
||||
|
||||
func sync_soundtracks() -> void:
|
||||
GameLog.debug("sync_soundtracks")
|
||||
sync_popup.start_sync()
|
||||
|
||||
|
||||
func _on_sync_started() -> void:
|
||||
GameLog.debug("_on_sync_started")
|
||||
top_bar.sync_button.disabled = false
|
||||
quiz.lock_for_sync()
|
||||
|
||||
|
||||
func _on_sync_finished() -> void:
|
||||
GameLog.debug("_on_sync_finished")
|
||||
search_popup.get_list_of_soundtracks()
|
||||
quiz.on_sync_finished()
|
||||
File renamed without changes.
@@ -0,0 +1,22 @@
|
||||
class_name TopBar
|
||||
extends Control
|
||||
## The bar along the top of the main window, with the title and the buttons for things
|
||||
## that are not part of a quiz.
|
||||
|
||||
signal search_pressed
|
||||
signal sync_pressed
|
||||
signal statistics_pressed
|
||||
signal shortcuts_pressed
|
||||
signal about_pressed
|
||||
signal qr_pressed
|
||||
|
||||
@onready var sync_button: Button = $SyncButton
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
$SearchButton.pressed.connect(search_pressed.emit)
|
||||
sync_button.pressed.connect(sync_pressed.emit)
|
||||
$StatisticsButton.pressed.connect(statistics_pressed.emit)
|
||||
$ShortcutsButton.pressed.connect(shortcuts_pressed.emit)
|
||||
$AboutButton.pressed.connect(about_pressed.emit)
|
||||
$QRButton.pressed.connect(qr_pressed.emit)
|
||||
@@ -0,0 +1 @@
|
||||
uid://bb2ukabiec1vv
|
||||
@@ -1,10 +1,10 @@
|
||||
[gd_scene load_steps=10 format=3 uid="uid://ds15cgsf8vpvc"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://d1v0rquma6nt8" path="res://MusicPlayer.gd" id="1_t24ra"]
|
||||
[ext_resource type="Script" uid="uid://d1v0rquma6nt8" path="res://player/music_player.gd" id="1_t24ra"]
|
||||
[ext_resource type="AudioStream" uid="uid://n2g8jddr85h2" path="res://sounds/01. Opening.mp3" id="2_xti80"]
|
||||
[ext_resource type="Texture2D" uid="uid://comxqfiykp54f" path="res://icons/play_icon_light.svg" id="3_6g308"]
|
||||
[ext_resource type="Texture2D" uid="uid://ccb6rvbldlgdg" path="res://icons/reload_light_icon.svg" id="4_jleuo"]
|
||||
[ext_resource type="PackedScene" uid="uid://w400rnew7453" path="res://volume_slider.tscn" id="5_iifuj"]
|
||||
[ext_resource type="PackedScene" uid="uid://w400rnew7453" path="res://player/VolumeSlider.tscn" id="5_iifuj"]
|
||||
|
||||
[sub_resource type="InputEventKey" id="InputEventKey_r4qo3"]
|
||||
device = -1
|
||||
@@ -26,19 +26,11 @@ unicode = 90
|
||||
[sub_resource type="Shortcut" id="Shortcut_i7swq"]
|
||||
events = [SubResource("InputEventKey_2nvce")]
|
||||
|
||||
[node name="MusicPlayer" type="PanelContainer"]
|
||||
anchors_preset = 8
|
||||
anchor_left = 0.5
|
||||
anchor_top = 0.5
|
||||
anchor_right = 0.5
|
||||
anchor_bottom = 0.5
|
||||
offset_left = -121.0
|
||||
offset_top = -15.5
|
||||
offset_right = 121.0
|
||||
offset_bottom = 16.5
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
size_flags_vertical = 4
|
||||
[node name="MusicPlayer" type="Control"]
|
||||
layout_mode = 3
|
||||
offset_right = 1792.0
|
||||
offset_bottom = 71.0
|
||||
mouse_filter = 2
|
||||
script = ExtResource("1_t24ra")
|
||||
|
||||
[node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."]
|
||||
@@ -53,11 +45,18 @@ bus = &"music"
|
||||
stream = ExtResource("2_xti80")
|
||||
bus = &"music"
|
||||
|
||||
[node name="MusicPlayerContainer" type="HBoxContainer" parent="."]
|
||||
[node name="PlayerBar" type="PanelContainer" parent="."]
|
||||
layout_mode = 0
|
||||
offset_left = 1.0
|
||||
offset_right = 1792.0
|
||||
offset_bottom = 32.0
|
||||
|
||||
[node name="MusicPlayerContainer" type="HBoxContainer" parent="PlayerBar"]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
|
||||
[node name="PlayTextureButton" type="TextureButton" parent="MusicPlayerContainer"]
|
||||
[node name="PlayTextureButton" type="TextureButton" parent="PlayerBar/MusicPlayerContainer"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(32, 32)
|
||||
layout_mode = 2
|
||||
focus_mode = 0
|
||||
@@ -67,7 +66,8 @@ texture_normal = ExtResource("3_6g308")
|
||||
ignore_texture_size = true
|
||||
stretch_mode = 0
|
||||
|
||||
[node name="RestartTextureButton" type="TextureButton" parent="MusicPlayerContainer"]
|
||||
[node name="RestartTextureButton" type="TextureButton" parent="PlayerBar/MusicPlayerContainer"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(32, 32)
|
||||
layout_mode = 2
|
||||
focus_mode = 0
|
||||
@@ -78,17 +78,30 @@ ignore_texture_size = true
|
||||
stretch_mode = 0
|
||||
flip_h = true
|
||||
|
||||
[node name="MusicPlayerSlider" type="HSlider" parent="MusicPlayerContainer"]
|
||||
[node name="MusicPlayerSlider" type="HSlider" parent="PlayerBar/MusicPlayerContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 4
|
||||
scrollable = false
|
||||
|
||||
[node name="MusicTimeLabel" type="Label" parent="MusicPlayerContainer"]
|
||||
[node name="MusicTimeLabel" type="Label" parent="PlayerBar/MusicPlayerContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "0:00 / 0:00"
|
||||
|
||||
[node name="VolumeSlider" parent="MusicPlayerContainer" instance=ExtResource("5_iifuj")]
|
||||
[node name="VolumeSlider" parent="PlayerBar/MusicPlayerContainer" instance=ExtResource("5_iifuj")]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(100, 0)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 4
|
||||
|
||||
[node name="RepeatSongCheckButton" type="CheckButton" parent="."]
|
||||
layout_mode = 0
|
||||
offset_top = 40.0
|
||||
offset_right = 144.0
|
||||
offset_bottom = 71.0
|
||||
focus_mode = 0
|
||||
button_pressed = true
|
||||
action_mode = 0
|
||||
text = "Auto repeat"
|
||||
@@ -1,6 +1,6 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://w400rnew7453"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://bgviu7smansj1" path="res://volume_slider.gd" id="1_1aufb"]
|
||||
[ext_resource type="Script" uid="uid://bgviu7smansj1" path="res://player/volume_slider.gd" id="1_1aufb"]
|
||||
|
||||
[node name="VolumeSlider" type="HSlider"]
|
||||
offset_right = 8.0
|
||||
@@ -1,69 +1,61 @@
|
||||
class_name MusicPlayer
|
||||
extends PanelContainer
|
||||
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 = $MusicPlayerContainer/PlayTextureButton
|
||||
|
||||
@onready
|
||||
var restart_button: TextureButton = $MusicPlayerContainer/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 = $MusicPlayerContainer/MusicPlayerSlider
|
||||
|
||||
@onready
|
||||
var volume_slider: HSlider = $MusicPlayerContainer/VolumeSlider
|
||||
|
||||
@onready
|
||||
var music_time_label: Label = $MusicPlayerContainer/MusicTimeLabel
|
||||
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 "??:?? / ??:??"
|
||||
@@ -74,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()
|
||||
@@ -82,7 +75,7 @@ func play_or_pause() -> void:
|
||||
audio_player.stream_paused = false
|
||||
song_finished = false
|
||||
audio_player.seek(playback_position)
|
||||
print("continue")
|
||||
GameLog.debug("continue")
|
||||
progress_slider.max_value = round(stream.get_length())
|
||||
if Settings.hide_ticks:
|
||||
progress_slider.tick_count = 0
|
||||
@@ -91,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
|
||||
@@ -105,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
|
||||
@@ -113,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)
|
||||
@@ -144,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()
|
||||
@@ -156,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()
|
||||
@@ -174,8 +176,9 @@ 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:
|
||||
print("play_song_object")
|
||||
GameLog.debug("play_song_object")
|
||||
if audio_player.is_playing():
|
||||
audio_player.stop()
|
||||
await get_tree().create_timer(0.5).timeout
|
||||
@@ -195,12 +198,13 @@ func play_song_object(song_object_no: int) -> void:
|
||||
Playlist.set_currently_playing_song(song_object_no)
|
||||
Playlist.unset_is_playing()
|
||||
if !Settings.hide_next_track:
|
||||
print("Show answer now!!")
|
||||
GameLog.debug("Show answer now!!")
|
||||
Playlist.song_is_answered(song_object_no)
|
||||
Playlist.song_has_played(song_object_no)
|
||||
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()
|
||||
@@ -211,18 +215,21 @@ 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]
|
||||
print(local_path)
|
||||
print(FileAccess.file_exists(local_path))
|
||||
GameLog.debug(local_path)
|
||||
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"))
|
||||
File renamed without changes.
@@ -1,18 +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))
|
||||
File renamed without changes.
@@ -1,9 +1,9 @@
|
||||
[gd_scene format=3 uid="uid://cp54idaudb8dp"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://b7bh0of7olbcb" path="res://AboutWindow.gd" id="1_about_script"]
|
||||
[ext_resource type="Theme" path="res://popup_theme.tres" id="popup_theme"]
|
||||
[ext_resource type="Script" uid="uid://b7bh0of7olbcb" path="res://popups/about_popup.gd" id="1_about_script"]
|
||||
[ext_resource type="Theme" path="res://themes/popup_theme.tres" id="popup_theme"]
|
||||
|
||||
[node name="AboutControl" type="Control" unique_id=1626010355 node_paths=PackedStringArray("panel")]
|
||||
[node name="AboutPopup" type="Control" unique_id=1626010355 node_paths=PackedStringArray("panel")]
|
||||
theme = ExtResource("popup_theme")
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
@@ -1,12 +1,12 @@
|
||||
[gd_scene load_steps=4 format=3 uid="uid://c7ecjj2af6usf"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://i35b3scbpwpu" path="res://character.gd" id="1_pjt0s"]
|
||||
[ext_resource type="Script" uid="uid://i35b3scbpwpu" path="res://popups/character_card.gd" id="1_pjt0s"]
|
||||
[ext_resource type="Texture2D" uid="uid://cv3hdd6bsruau" path="res://icon.svg" id="2_pjt0s"]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_pjt0s"]
|
||||
bg_color = Color(0.302, 0.302, 0.302, 1)
|
||||
|
||||
[node name="Control" type="Control"]
|
||||
[node name="CharacterCard" type="Control"]
|
||||
layout_mode = 3
|
||||
anchors_preset = 0
|
||||
script = ExtResource("1_pjt0s")
|
||||
@@ -1,12 +1,12 @@
|
||||
[gd_scene load_steps=3 format=3 uid="uid://cdy4kvemwaiom"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cvffg87wr6r3y" path="res://new_character_select.gd" id="1_h4tdp"]
|
||||
[ext_resource type="Theme" path="res://popup_theme.tres" id="popup_theme"]
|
||||
[ext_resource type="Script" uid="uid://cvffg87wr6r3y" path="res://popups/character_select_popup.gd" id="1_h4tdp"]
|
||||
[ext_resource type="Theme" path="res://themes/popup_theme.tres" id="popup_theme"]
|
||||
|
||||
[sub_resource type="LabelSettings" id="LabelSettings_p4iny"]
|
||||
font_size = 25
|
||||
|
||||
[node name="Control" type="Control" node_paths=PackedStringArray("panel")]
|
||||
[node name="CharacterSelectPopup" type="Control" node_paths=PackedStringArray("panel")]
|
||||
theme = ExtResource("popup_theme")
|
||||
layout_mode = 3
|
||||
anchors_preset = 0
|
||||
@@ -1,9 +1,9 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://bijh5h5yrivm3"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://cw41y87l64qo7" path="res://log.gd" id="1_q72ua"]
|
||||
[ext_resource type="Theme" path="res://popup_theme.tres" id="popup_theme"]
|
||||
[ext_resource type="Script" uid="uid://cw41y87l64qo7" path="res://popups/log_popup.gd" id="1_q72ua"]
|
||||
[ext_resource type="Theme" path="res://themes/popup_theme.tres" id="popup_theme"]
|
||||
|
||||
[node name="Control" type="Control" node_paths=PackedStringArray("panel")]
|
||||
[node name="LogPopup" type="Control" node_paths=PackedStringArray("panel")]
|
||||
theme = ExtResource("popup_theme")
|
||||
custom_minimum_size = Vector2(700, 700)
|
||||
layout_mode = 3
|
||||
@@ -1,10 +1,10 @@
|
||||
[gd_scene format=3 uid="uid://btupbowehiyyu"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://bsa6xg5xos6h7" path="res://QRWindow.gd" id="1_qr_script"]
|
||||
[ext_resource type="Script" uid="uid://bsa6xg5xos6h7" path="res://popups/qr_popup.gd" id="1_qr_script"]
|
||||
[ext_resource type="Script" uid="uid://dynulhnyq5hpq" path="res://addons/kenyoni/qr_code/qr_code_rect.gd" id="2_jpsxt"]
|
||||
[ext_resource type="Theme" path="res://popup_theme.tres" id="popup_theme"]
|
||||
[ext_resource type="Theme" path="res://themes/popup_theme.tres" id="popup_theme"]
|
||||
|
||||
[node name="QRControl" type="Control" unique_id=1996957585 node_paths=PackedStringArray("panel")]
|
||||
[node name="QRPopup" type="Control" unique_id=1996957585 node_paths=PackedStringArray("panel")]
|
||||
theme = ExtResource("popup_theme")
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
@@ -22,6 +22,7 @@ offset_right = 450.0
|
||||
offset_bottom = 520.0
|
||||
|
||||
[node name="QRCodeRect" type="TextureRect" parent="QRPanel" unique_id=1054378438]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 0
|
||||
offset_left = 20.0
|
||||
offset_top = 20.0
|
||||
@@ -1,9 +1,9 @@
|
||||
[gd_scene format=3 uid="uid://bxydgil1yifps"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://bg5yo08638600" path="res://SearchWindow.gd" id="1_m7bij"]
|
||||
[ext_resource type="Theme" path="res://popup_theme.tres" id="popup_theme"]
|
||||
[ext_resource type="Script" uid="uid://bg5yo08638600" path="res://popups/search_popup.gd" id="1_m7bij"]
|
||||
[ext_resource type="Theme" path="res://themes/popup_theme.tres" id="popup_theme"]
|
||||
|
||||
[node name="Control" type="Control" unique_id=616238617 node_paths=PackedStringArray("panel")]
|
||||
[node name="SearchPopup" type="Control" unique_id=616238617 node_paths=PackedStringArray("panel")]
|
||||
theme = ExtResource("popup_theme")
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
@@ -47,6 +47,7 @@ offset_right = 576.0
|
||||
offset_bottom = 384.0
|
||||
scale = Vector2(2, 2)
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="ScrollContainer" unique_id=1567403144]
|
||||
[node name="SearchResults" type="VBoxContainer" parent="ScrollContainer" unique_id=1567403144]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
@@ -1,12 +1,12 @@
|
||||
[gd_scene format=3 uid="uid://dldpeo5y3l5hq"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://dukqyjoduf1af" path="res://SettingsWindow.gd" id="1_bt55j"]
|
||||
[ext_resource type="Theme" path="res://popup_theme.tres" id="popup_theme"]
|
||||
[ext_resource type="Script" uid="uid://dukqyjoduf1af" path="res://popups/settings_popup.gd" id="1_bt55j"]
|
||||
[ext_resource type="Theme" path="res://themes/popup_theme.tres" id="popup_theme"]
|
||||
|
||||
[sub_resource type="LabelSettings" id="LabelSettings_3xrlm"]
|
||||
font_size = 25
|
||||
|
||||
[node name="SettingsControl" type="Control" unique_id=1298840328 node_paths=PackedStringArray("panel")]
|
||||
[node name="SettingsPopup" type="Control" unique_id=1298840328 node_paths=PackedStringArray("panel")]
|
||||
theme = ExtResource("popup_theme")
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
@@ -1,9 +1,9 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://bd1by80q1v27e"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://rolypd0cqd6h" path="res://shortcuts_window.gd" id="1_12pxi"]
|
||||
[ext_resource type="Theme" path="res://popup_theme.tres" id="popup_theme"]
|
||||
[ext_resource type="Script" uid="uid://rolypd0cqd6h" path="res://popups/shortcuts_popup.gd" id="1_12pxi"]
|
||||
[ext_resource type="Theme" path="res://themes/popup_theme.tres" id="popup_theme"]
|
||||
|
||||
[node name="ShorcutsControl" type="Control" node_paths=PackedStringArray("panel")]
|
||||
[node name="ShortcutsPopup" type="Control" node_paths=PackedStringArray("panel")]
|
||||
theme = ExtResource("popup_theme")
|
||||
custom_minimum_size = Vector2(700, 700)
|
||||
layout_mode = 3
|
||||
@@ -1,12 +1,12 @@
|
||||
[gd_scene format=3 uid="uid://dpdem7pdxweb5"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://c3ejukwth512a" path="res://sync_window.gd" id="1_8ba47"]
|
||||
[ext_resource type="Theme" path="res://popup_theme.tres" id="popup_theme"]
|
||||
[ext_resource type="Script" uid="uid://c3ejukwth512a" path="res://popups/sync_popup.gd" id="1_8ba47"]
|
||||
[ext_resource type="Theme" path="res://themes/popup_theme.tres" id="popup_theme"]
|
||||
|
||||
[sub_resource type="LabelSettings" id="LabelSettings_anbfi"]
|
||||
font_size = 25
|
||||
|
||||
[node name="Control2" type="Control" unique_id=1302914356 node_paths=PackedStringArray("panel")]
|
||||
[node name="SyncPopup" type="Control" unique_id=1302914356 node_paths=PackedStringArray("panel")]
|
||||
theme = ExtResource("popup_theme")
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
@@ -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
|
||||
@@ -1,13 +1,13 @@
|
||||
[gd_scene format=3 uid="uid://cxlsmxnrp0agh"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://dxpb22xbwf3t1" path="res://winner_window.gd" id="1_winner_script"]
|
||||
[ext_resource type="Script" uid="uid://dxpb22xbwf3t1" path="res://popups/winner_popup.gd" id="1_winner_script"]
|
||||
[ext_resource type="Texture2D" uid="uid://r4as0nmtoa7p" path="res://noCharacter.png" id="2_no_character"]
|
||||
[ext_resource type="Theme" path="res://popup_theme.tres" id="popup_theme"]
|
||||
[ext_resource type="Theme" path="res://themes/popup_theme.tres" id="popup_theme"]
|
||||
|
||||
[sub_resource type="LabelSettings" id="LabelSettings_hr75l"]
|
||||
font_size = 35
|
||||
|
||||
[node name="WinnerControl" type="Control" unique_id=1111111111 node_paths=PackedStringArray("panel")]
|
||||
[node name="WinnerPopup" type="Control" unique_id=1111111111 node_paths=PackedStringArray("panel")]
|
||||
theme = ExtResource("popup_theme")
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
@@ -1,18 +1,22 @@
|
||||
extends PopupWindow
|
||||
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 = {
|
||||
"2.0.0": "Reworked all popups, they dim the background and close when clicking outside
|
||||
Fixed blurry text and the layout of the player list
|
||||
Moved Sync to the top row and Settings next to the reset buttons
|
||||
The inspiration and sync lists now show where each item starts
|
||||
The sync result now looks right with any number of columns
|
||||
Failed song downloads are now retried instead of getting stuck
|
||||
The game no longer waits forever when the server does not answer
|
||||
The song cache now fills up to the cache size in the settings
|
||||
Statistics are turned back on when a new game starts
|
||||
Fixed bugs with low winning scores and Show answer before a song is playing
|
||||
The log now shows the time on every row
|
||||
New app icon and a menu entry on Linux
|
||||
Reorganized the code and added automated tests",
|
||||
"1.9.0": "Made improvements to the Search window
|
||||
Made small changes to the inspiration list scrolling
|
||||
Made small changes to the player list
|
||||
@@ -50,15 +54,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
|
||||
File renamed without changes.
@@ -1,4 +1,4 @@
|
||||
class_name PopupWindow
|
||||
class_name BasePopup
|
||||
extends Control
|
||||
## A window shown on top of the main window, such as Settings or About.
|
||||
##
|
||||
@@ -6,32 +6,33 @@ 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
|
||||
|
||||
|
||||
## Shows the popup.
|
||||
func open() -> void:
|
||||
visible = true
|
||||
visible = true
|
||||
|
||||
|
||||
## Hides the popup.
|
||||
func close() -> void:
|
||||
visible = false
|
||||
visible = false
|
||||
|
||||
|
||||
## Opens the popup if it is closed and closes it if it is open.
|
||||
func toggle() -> void:
|
||||
if visible:
|
||||
close()
|
||||
else:
|
||||
open()
|
||||
if visible:
|
||||
close()
|
||||
else:
|
||||
open()
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if !visible || !(event is InputEventMouseButton) || !event.pressed:
|
||||
return
|
||||
var click: InputEventMouseButton = panel.make_input_local(event)
|
||||
if !Rect2(Vector2.ZERO, panel.size).has_point(click.position):
|
||||
close()
|
||||
# The click only closes the popup; it must not also press what is behind it
|
||||
get_viewport().set_input_as_handled()
|
||||
if !visible || !(event is InputEventMouseButton) || !event.pressed:
|
||||
return
|
||||
var click: InputEventMouseButton = panel.make_input_local(event)
|
||||
if !Rect2(Vector2.ZERO, panel.size).has_point(click.position):
|
||||
close()
|
||||
# The click only closes the popup; it must not also press what is behind it
|
||||
get_viewport().set_input_as_handled()
|
||||
File renamed without changes.
@@ -1,31 +1,41 @@
|
||||
class_name Character
|
||||
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:
|
||||
var img: Image = Image.new()
|
||||
var err: Error = img.load_png_from_buffer(image)
|
||||
if err != OK:
|
||||
print(err)
|
||||
GameLog.debug("Character image is not a PNG, trying JPG: ", error_string(err))
|
||||
var err2: Error = img.load_jpg_from_buffer(image)
|
||||
if err2 != OK:
|
||||
print(err2)
|
||||
GameLog.warning("Could not load the image for ", character_name, ": ",
|
||||
error_string(err2))
|
||||
var new_image: ImageTexture = ImageTexture.new()
|
||||
new_image.set_image(img)
|
||||
character_image.custom_minimum_size = Vector2(200, 100)
|
||||
@@ -36,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):
|
||||
print(character_name_label.text)
|
||||
new_character_selected.emit(character_image.texture)
|
||||
File renamed without changes.
@@ -1,65 +1,65 @@
|
||||
extends PopupWindow
|
||||
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
|
||||
var character: PackedScene = preload("res://popups/CharacterCard.tscn")
|
||||
|
||||
signal new_new_character_selected(file_name: String)
|
||||
@onready var character_panel: Panel = %CharacterPanel
|
||||
@onready var title_label: Label = %TitleLabel
|
||||
@onready var character_grid: GridContainer = %CharacterGridContainer
|
||||
@onready var close_timer: Timer = %CloseTimer
|
||||
|
||||
var character: PackedScene = preload("res://character.tscn")
|
||||
|
||||
func _ready() -> void:
|
||||
close_timer.timeout.connect(_on_close_timeout)
|
||||
|
||||
|
||||
func _on_close_timeout() -> void:
|
||||
print("_on_close_timeout!")
|
||||
GameLog.debug("_on_close_timeout!")
|
||||
Settings.character_select_open = false
|
||||
close_timer.stop()
|
||||
|
||||
|
||||
func load_characters() -> void:
|
||||
print("load_characters")
|
||||
var fetch_character_list: Callable = func fetch_character_list(list) -> void:
|
||||
print("fetch_character_list")
|
||||
GameLog.debug("load_characters")
|
||||
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:
|
||||
for character_name: String in list:
|
||||
print("character_name ", character_name)
|
||||
var character_box: Character = character.instantiate()
|
||||
GameLog.debug("character_name ", character_name)
|
||||
var character_box: CharacterCard = character.instantiate()
|
||||
character_grid.add_child(character_box)
|
||||
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:
|
||||
print("show_grid")
|
||||
GameLog.debug("show_grid")
|
||||
title_label.text = "Select character for " + player_name
|
||||
close_timer.stop()
|
||||
open()
|
||||
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:
|
||||
print("_on_character_selected")
|
||||
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
|
||||
File renamed without changes.
@@ -1,18 +1,20 @@
|
||||
extends PopupWindow
|
||||
|
||||
@onready
|
||||
var log_panel: Panel = %LogPanel
|
||||
|
||||
@onready
|
||||
var log_box: VBoxContainer = %LogVBoxContainer
|
||||
class_name LogPopup
|
||||
extends BasePopup
|
||||
## 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)
|
||||
@@ -20,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]
|
||||
File renamed without changes.
@@ -0,0 +1,31 @@
|
||||
class_name PopupBackdrop
|
||||
extends ColorRect
|
||||
## Dims everything behind an open popup and keeps clicks from reaching the buttons
|
||||
## under it.
|
||||
##
|
||||
## The backdrop sits above the rest of its parent and below the popups it belongs to.
|
||||
## Popups close themselves when clicked outside, and the backdrop keeps that click from
|
||||
## reaching the buttons behind them.
|
||||
|
||||
var _popups: Array[BasePopup] = []
|
||||
|
||||
|
||||
## Adds a backdrop to [param parent] and moves [param popups] above it. The backdrop
|
||||
## shows while any of them is open.
|
||||
static func add_to(parent: Control, popups: Array[BasePopup]) -> PopupBackdrop:
|
||||
var backdrop: PopupBackdrop = PopupBackdrop.new()
|
||||
backdrop.name = "PopupBackdrop"
|
||||
backdrop.color = Color(0, 0, 0, 0.55)
|
||||
backdrop.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
backdrop._popups = popups
|
||||
parent.add_child(backdrop)
|
||||
backdrop.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
for popup: BasePopup in popups:
|
||||
parent.move_child(popup, -1)
|
||||
popup.visibility_changed.connect(backdrop._update)
|
||||
backdrop._update()
|
||||
return backdrop
|
||||
|
||||
|
||||
func _update() -> void:
|
||||
visible = _popups.any(func(popup: BasePopup) -> bool: return popup.visible)
|
||||
@@ -0,0 +1 @@
|
||||
uid://swjhekc8beob
|
||||
@@ -1,25 +1,24 @@
|
||||
extends PopupWindow
|
||||
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 = $QRPanel/QRCodeRect
|
||||
|
||||
@onready
|
||||
var url_label: Label = $QRPanel/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
|
||||
print("QR code generated for: ", url)
|
||||
GameLog.debug("QR code generated for: ", url)
|
||||
|
||||
|
||||
func open() -> void:
|
||||
super()
|
||||
File renamed without changes.
@@ -1,64 +1,65 @@
|
||||
extends PopupWindow
|
||||
|
||||
@onready
|
||||
var search_list: VBoxContainer = $ScrollContainer/VBoxContainer
|
||||
|
||||
@onready
|
||||
var search_panel: Panel = %SearchPanel
|
||||
|
||||
@onready
|
||||
var clear_button: Button = $ClearButton
|
||||
|
||||
@onready
|
||||
var search_bar: TextEdit = $Searchbar
|
||||
class_name SearchPopup
|
||||
extends BasePopup
|
||||
## 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:
|
||||
print(search_bar.text)
|
||||
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():
|
||||
@@ -69,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.
|
||||
@@ -80,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
|
||||
@@ -106,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:
|
||||
@@ -145,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():
|
||||
@@ -152,9 +160,10 @@ func check_if_soundtrack_exists(soundtrack: String) -> bool:
|
||||
soundtrack_exists = true
|
||||
return soundtrack_exists
|
||||
|
||||
|
||||
func get_list_of_soundtracks() -> void:
|
||||
print("get_list_of_soundtracks")
|
||||
var handle_soundtracks: Callable = func handle_soundtracks(array) -> void:
|
||||
GameLog.debug("get_list_of_soundtracks")
|
||||
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():
|
||||
@@ -164,9 +173,10 @@ func get_list_of_soundtracks() -> void:
|
||||
for soundtrack: String in soundtracks:
|
||||
add_soundtrack_reuse(soundtrack)
|
||||
else:
|
||||
print("Unexpected data")
|
||||
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 = []
|
||||
@@ -179,6 +189,7 @@ func set_soundtracks(list: Array) -> void:
|
||||
"cleaned": clean_term(soundtrack)
|
||||
})
|
||||
|
||||
|
||||
func clear() -> void:
|
||||
search_bar.text = ""
|
||||
show_all_soundtracks()
|
||||
File renamed without changes.
@@ -1,81 +1,40 @@
|
||||
extends PopupWindow
|
||||
|
||||
@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
|
||||
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.
|
||||
|
||||
## 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)
|
||||
@@ -93,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
|
||||
@@ -110,109 +69,140 @@ func _ready() -> void:
|
||||
select_server_button.select(Settings.selected_server)
|
||||
select_server_button.item_selected.connect(select_server)
|
||||
visibility_changed.connect(_on_visibility_changed)
|
||||
# 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 window, e.g. winning turns statistics off
|
||||
|
||||
# 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:
|
||||
print("select_server")
|
||||
GameLog.debug("select_server")
|
||||
Settings.default_path = select_server_button.get_item_text(new_server)
|
||||
Settings.selected_server = new_server
|
||||
print("Settings.default_path: " + Settings.default_path)
|
||||
print("Settings.selected_server: " + str(Settings.selected_server))
|
||||
print("new_server: " + str(new_server))
|
||||
print("select_server_button.get_item_text(new_server): " + select_server_button.get_item_text(new_server))
|
||||
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))
|
||||
server_changed.emit()
|
||||
|
||||
|
||||
func set_selected_server(new_server: int) -> void:
|
||||
print("set_selected_server")
|
||||
GameLog.debug("set_selected_server")
|
||||
select_server_button.select(new_server)
|
||||
Settings.default_path = select_server_button.get_item_text(new_server)
|
||||
Settings.selected_server = new_server
|
||||
print("Settings.default_path: " + Settings.default_path)
|
||||
print("Settings.selected_server: " + str(Settings.selected_server))
|
||||
print("new_server: " + str(new_server))
|
||||
print("select_server_button.get_item_text(new_server): " + select_server_button.get_item_text(new_server))
|
||||
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))
|
||||
server_changed.emit()
|
||||
File renamed without changes.
@@ -1,10 +1,6 @@
|
||||
extends PopupWindow
|
||||
|
||||
@onready
|
||||
var shortcuts_vbox: VBoxContainer = %shortcutsVBox
|
||||
|
||||
@onready
|
||||
var shortcuts_panel: Panel = %ShortcutsPanel
|
||||
class_name ShortcutsPopup
|
||||
extends BasePopup
|
||||
## Lists the keyboard shortcuts. Opened with Alt+K or the Shortcuts button.
|
||||
|
||||
var shortcuts_dic: Dictionary = {
|
||||
"Alt + S": "Search",
|
||||
@@ -25,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
|
||||
@@ -44,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)
|
||||
File renamed without changes.
@@ -1,78 +1,49 @@
|
||||
extends PopupWindow
|
||||
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)
|
||||
@@ -82,9 +53,10 @@ func show_section_if_has_data(data: Variant, vbox: VBoxContainer, separator: VSe
|
||||
separator.visible = false
|
||||
return false
|
||||
|
||||
|
||||
func start_sync() -> void:
|
||||
print("start_sync")
|
||||
self.visible = true
|
||||
GameLog.debug("start_sync")
|
||||
visible = true
|
||||
if has_synced:
|
||||
# Already synced, just show the results
|
||||
show_sync_result()
|
||||
@@ -97,11 +69,12 @@ func start_sync() -> void:
|
||||
if !Settings.currently_syncing:
|
||||
MusicServerClient.start_sync(sync_request_sent)
|
||||
|
||||
|
||||
func _on_sync_timeout() -> void:
|
||||
print("_on_sync_timeout!")
|
||||
GameLog.debug("_on_sync_timeout!")
|
||||
var get_progress: Callable = func get_progress(data_received: Dictionary) -> void:
|
||||
print("get_progress")
|
||||
print("data_received: ", data_received)
|
||||
GameLog.debug("get_progress")
|
||||
GameLog.debug("data_received: ", data_received)
|
||||
if data_received != null:
|
||||
if data_received.has("progress"):
|
||||
sync_progress_bar.value = int(data_received.progress)
|
||||
@@ -110,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:
|
||||
@@ -154,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,
|
||||
@@ -170,20 +151,23 @@ 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()
|
||||
print("sync finished")
|
||||
GameLog.debug("sync finished")
|
||||
show_sync_result()
|
||||
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()
|
||||
@@ -193,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)
|
||||
@@ -200,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 = ""
|
||||
@@ -215,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)
|
||||
@@ -223,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
|
||||
File renamed without changes.
@@ -1,16 +1,11 @@
|
||||
extends PopupWindow
|
||||
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:
|
||||
@@ -53,6 +48,3 @@ func show_winner(winner: PlayerObject) -> void:
|
||||
label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
players_list.add_child(label)
|
||||
placing += 1
|
||||
|
||||
|
||||
|
||||
File renamed without changes.
+6
-5
@@ -15,16 +15,17 @@ compatibility/default_parent_skeleton_in_mesh_instance_3d=true
|
||||
[application]
|
||||
|
||||
config/name="MusicPlayer"
|
||||
config/version="1.9.0"
|
||||
run/main_scene="res://MainWindow.tscn"
|
||||
config/version="2.0.0"
|
||||
run/main_scene="res://main/MainWindow.tscn"
|
||||
config/features=PackedStringArray("4.7", "GL Compatibility")
|
||||
config/icon="res://icon.svg"
|
||||
|
||||
[autoload]
|
||||
|
||||
Settings="*res://Settings.gd"
|
||||
Playlist="*res://playlist.gd"
|
||||
MusicServerClient="*res://music_server_client.gd"
|
||||
GameLog="*res://common/game_log.gd"
|
||||
Settings="*res://data/settings.gd"
|
||||
Playlist="*res://data/playlist.gd"
|
||||
MusicServerClient="*res://server/music_server_client.gd"
|
||||
|
||||
[debug]
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
[gd_scene format=3]
|
||||
|
||||
[ext_resource type="Script" uid="uid://elgn7gkxgagq" path="res://quiz/inspiration_list.gd" id="2_gxtxm"]
|
||||
|
||||
[node name="InspirationList" type="ScrollContainer" unique_id=498283761]
|
||||
layout_mode = 3
|
||||
horizontal_scroll_mode = 0
|
||||
vertical_scroll_mode = 3
|
||||
offset_right = 641.0
|
||||
offset_bottom = 384.0
|
||||
script = ExtResource("2_gxtxm")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=1215162949]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
@@ -0,0 +1,24 @@
|
||||
[gd_scene format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://quiz/music_list_panel.gd" id="part_script"]
|
||||
[ext_resource type="Script" uid="uid://gf1bk6xbe3j" path="res://quiz/scroll_to_end_container.gd" id="7_dj026"]
|
||||
[ext_resource type="Theme" uid="uid://rxexo3ur85as" path="res://themes/light_gray_theme.tres" id="7_wxbv6"]
|
||||
|
||||
[node name="MusicListPanel" type="PanelContainer" unique_id=2011292375]
|
||||
layout_mode = 3
|
||||
theme = ExtResource("7_wxbv6")
|
||||
offset_right = 1088.0
|
||||
offset_bottom = 512.0
|
||||
script = ExtResource("part_script")
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="." unique_id=707750459]
|
||||
layout_mode = 2
|
||||
follow_focus = true
|
||||
script = ExtResource("7_dj026")
|
||||
|
||||
[node name="MusicList" type="VBoxContainer" parent="ScrollContainer" unique_id=373398568]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 3
|
||||
theme_override_constants/separation = 10
|
||||
@@ -0,0 +1,29 @@
|
||||
[gd_scene format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://quiz/now_playing.gd" id="part_script"]
|
||||
|
||||
[sub_resource type="LabelSettings" id="LabelSettings_qspbu"]
|
||||
font_size = 50
|
||||
|
||||
[sub_resource type="LabelSettings" id="LabelSettings_3m52w"]
|
||||
font_size = 35
|
||||
|
||||
[node name="NowPlaying" type="VBoxContainer" unique_id=42468498]
|
||||
layout_mode = 3
|
||||
offset_right = 1088.0
|
||||
offset_bottom = 320.0
|
||||
script = ExtResource("part_script")
|
||||
|
||||
[node name="SoundtrackLabel" type="Label" parent="." unique_id=173147793]
|
||||
custom_minimum_size = Vector2(1, 1)
|
||||
layout_mode = 2
|
||||
label_settings = SubResource("LabelSettings_qspbu")
|
||||
horizontal_alignment = 1
|
||||
autowrap_mode = 2
|
||||
|
||||
[node name="SongLabel" type="Label" parent="." unique_id=1687490027]
|
||||
custom_minimum_size = Vector2(1, 1)
|
||||
layout_mode = 2
|
||||
label_settings = SubResource("LabelSettings_3m52w")
|
||||
horizontal_alignment = 1
|
||||
autowrap_mode = 2
|
||||
@@ -1,6 +1,6 @@
|
||||
[gd_scene load_steps=9 format=3 uid="uid://cslogy2csnd7a"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://dl6no2wr2c86y" path="res://Player.gd" id="1_if4kc"]
|
||||
[ext_resource type="Script" uid="uid://dl6no2wr2c86y" path="res://quiz/player_row.gd" id="1_if4kc"]
|
||||
[ext_resource type="Texture2D" uid="uid://r4as0nmtoa7p" path="res://noCharacter.png" id="2_hpj3s"]
|
||||
[ext_resource type="Texture2D" uid="uid://b2kj6m8qpsgb1" path="res://icons/person_remove_light.svg" id="3_j3uxe"]
|
||||
|
||||
@@ -19,7 +19,7 @@ bg_color = Color(0.180392, 0.180392, 0.180392, 1)
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_b2crt"]
|
||||
bg_color = Color(0.180392, 0.180392, 0.180392, 1)
|
||||
|
||||
[node name="Player" type="Control"]
|
||||
[node name="PlayerRow" type="Control"]
|
||||
custom_minimum_size = Vector2(2.08165e-12, 40)
|
||||
layout_mode = 3
|
||||
anchors_preset = 0
|
||||
@@ -42,6 +42,7 @@ layout_mode = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_821k2")
|
||||
|
||||
[node name="Name" type="Label" parent="HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(130, 40)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
@@ -57,6 +58,7 @@ layout_mode = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_w10e8")
|
||||
|
||||
[node name="Character" type="TextureButton" parent="HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(80, 40)
|
||||
layout_mode = 2
|
||||
focus_mode = 0
|
||||
@@ -71,6 +73,7 @@ layout_mode = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_78a8g")
|
||||
|
||||
[node name="AddPoint" type="Button" parent="HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(40, 40)
|
||||
layout_mode = 2
|
||||
focus_mode = 0
|
||||
@@ -83,6 +86,7 @@ layout_mode = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_i56hb")
|
||||
|
||||
[node name="RemovePoint" type="Button" parent="HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(40, 40)
|
||||
layout_mode = 2
|
||||
focus_mode = 0
|
||||
@@ -95,6 +99,7 @@ layout_mode = 2
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_b2crt")
|
||||
|
||||
[node name="RemovePlayer" type="TextureButton" parent="HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(40, 40)
|
||||
layout_mode = 2
|
||||
focus_mode = 0
|
||||
@@ -0,0 +1,94 @@
|
||||
[gd_scene format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://quiz/players_panel.gd" id="part_script"]
|
||||
[ext_resource type="Texture2D" uid="uid://bcfmpd7h512ef" path="res://icons/person_add_light.svg" id="5_31tjv"]
|
||||
[ext_resource type="Script" uid="uid://cgakje8gb37tf" path="res://quiz/player_name_field.gd" id="7_qsdfy"]
|
||||
|
||||
[sub_resource type="InputEventKey" id="InputEventKey_03bm3"]
|
||||
device = -1
|
||||
alt_pressed = true
|
||||
keycode = 65
|
||||
unicode = 63743
|
||||
|
||||
[sub_resource type="Shortcut" id="Shortcut_jafqj"]
|
||||
events = [SubResource("InputEventKey_03bm3")]
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_qk7sj"]
|
||||
bg_color = Color(0.180392, 0.180392, 0.180392, 1)
|
||||
|
||||
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_k1ygi"]
|
||||
bg_color = Color(0.180392, 0.180392, 0.180392, 1)
|
||||
|
||||
[node name="PlayersPanel" type="PanelContainer" unique_id=1708375945]
|
||||
layout_mode = 3
|
||||
offset_right = 641.0
|
||||
offset_bottom = 401.0
|
||||
script = ExtResource("part_script")
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=270084211]
|
||||
layout_mode = 2
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer" unique_id=513047023]
|
||||
custom_minimum_size = Vector2(2.08165e-12, 40)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer" unique_id=1331357386]
|
||||
custom_minimum_size = Vector2(2.08165e-12, 40)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 1
|
||||
text = "Players"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="AddPlayersButton" type="TextureButton" parent="VBoxContainer/HBoxContainer" unique_id=634410148]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(2.08165e-12, 40)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 8
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
shortcut = SubResource("Shortcut_jafqj")
|
||||
texture_normal = ExtResource("5_31tjv")
|
||||
|
||||
[node name="AddPlayerContainer" type="HBoxContainer" parent="VBoxContainer" unique_id=1371948739]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(2.08165e-12, 40)
|
||||
layout_mode = 2
|
||||
|
||||
[node name="Panel" type="Panel" parent="VBoxContainer/AddPlayerContainer" unique_id=1569854056]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_qk7sj")
|
||||
|
||||
[node name="PlayerNameField" type="TextEdit" parent="VBoxContainer/AddPlayerContainer" unique_id=332535327]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(2.08165e-12, 40)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
placeholder_text = "Player name"
|
||||
script = ExtResource("7_qsdfy")
|
||||
|
||||
[node name="AddPlayerButton" type="Button" parent="VBoxContainer/AddPlayerContainer" unique_id=245001313]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(2.08165e-12, 40)
|
||||
layout_mode = 2
|
||||
text = "Add"
|
||||
|
||||
[node name="Panel2" type="Panel" parent="VBoxContainer/AddPlayerContainer" unique_id=60215448]
|
||||
custom_minimum_size = Vector2(2.08165e-12, 40)
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
theme_override_styles/panel = SubResource("StyleBoxFlat_k1ygi")
|
||||
|
||||
[node name="ScrollContainer" type="ScrollContainer" parent="VBoxContainer" unique_id=1000000001]
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 15
|
||||
horizontal_scroll_mode = 0
|
||||
|
||||
[node name="PlayersListVBoxContainer" type="VBoxContainer" parent="VBoxContainer/ScrollContainer" unique_id=1000000002]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
size_flags_vertical = 15
|
||||
+135
@@ -0,0 +1,135 @@
|
||||
[gd_scene format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://quiz/quiz.gd" id="quiz_script"]
|
||||
[ext_resource type="PackedScene" uid="uid://ds15cgsf8vpvc" path="res://player/MusicPlayer.tscn" id="5_emn36"]
|
||||
[ext_resource type="PackedScene" uid="uid://cdy4kvemwaiom" path="res://popups/CharacterSelectPopup.tscn" id="6_dhrvg"]
|
||||
[ext_resource type="PackedScene" uid="uid://dldpeo5y3l5hq" path="res://popups/SettingsPopup.tscn" id="11_k62u5"]
|
||||
[ext_resource type="PackedScene" uid="uid://cxlsmxnrp0agh" path="res://popups/WinnerPopup.tscn" id="17_winner_window"]
|
||||
[ext_resource type="PackedScene" path="res://quiz/MusicListPanel.tscn" id="scene_MusicListPanel"]
|
||||
[ext_resource type="PackedScene" path="res://quiz/PlayersPanel.tscn" id="scene_PlayersPanel"]
|
||||
[ext_resource type="PackedScene" path="res://quiz/InspirationList.tscn" id="scene_InspirationList"]
|
||||
[ext_resource type="PackedScene" path="res://quiz/QuizToolbar.tscn" id="scene_QuizToolbar"]
|
||||
[ext_resource type="PackedScene" path="res://quiz/SongControls.tscn" id="scene_SongControls"]
|
||||
[ext_resource type="PackedScene" path="res://quiz/NowPlaying.tscn" id="scene_NowPlaying"]
|
||||
[ext_resource type="Script" path="res://quiz/song_loader.gd" id="song_loader_script"]
|
||||
|
||||
[node name="Quiz" type="Control" unique_id=1146705218]
|
||||
layout_mode = 3
|
||||
anchors_preset = 15
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
grow_horizontal = 2
|
||||
grow_vertical = 2
|
||||
mouse_filter = 2
|
||||
script = ExtResource("quiz_script")
|
||||
|
||||
[node name="Open" type="Button" parent="." unique_id=165052329]
|
||||
visible = false
|
||||
layout_mode = 0
|
||||
offset_left = 1296.0
|
||||
offset_top = 66.0
|
||||
offset_right = 1346.0
|
||||
offset_bottom = 97.0
|
||||
text = "Open"
|
||||
|
||||
[node name="FileDialog" type="FileDialog" parent="." unique_id=485771559]
|
||||
title = "Open a Directory"
|
||||
initial_position = 2
|
||||
size = Vector2i(616, 400)
|
||||
ok_button_text = "Select Current Folder"
|
||||
file_mode = 2
|
||||
access = 2
|
||||
show_hidden_files = true
|
||||
|
||||
[node name="MusicListPanel" parent="." unique_id=2011292375 instance=ExtResource("scene_MusicListPanel")]
|
||||
layout_mode = 0
|
||||
offset_left = 64.0
|
||||
offset_top = 384.0
|
||||
offset_right = 1152.0
|
||||
offset_bottom = 896.0
|
||||
|
||||
[node name="InspirationList" parent="." unique_id=498283761 instance=ExtResource("scene_InspirationList")]
|
||||
layout_mode = 0
|
||||
offset_left = 1216.0
|
||||
offset_top = 128.0
|
||||
offset_right = 1857.0
|
||||
offset_bottom = 512.0
|
||||
|
||||
[node name="NowPlaying" parent="." unique_id=42468498 instance=ExtResource("scene_NowPlaying")]
|
||||
layout_mode = 0
|
||||
offset_left = 64.0
|
||||
offset_top = 64.0
|
||||
offset_right = 1152.0
|
||||
offset_bottom = 384.0
|
||||
|
||||
[node name="MusicPlayer" parent="." unique_id=1990459832 instance=ExtResource("5_emn36")]
|
||||
layout_mode = 0
|
||||
offset_left = 64.0
|
||||
offset_top = 992.0
|
||||
offset_right = 1856.0
|
||||
offset_bottom = 1063.0
|
||||
|
||||
[node name="PlayersPanel" parent="." unique_id=1708375945 instance=ExtResource("scene_PlayersPanel")]
|
||||
layout_mode = 0
|
||||
offset_left = 1216.0
|
||||
offset_top = 576.0
|
||||
offset_right = 1857.0
|
||||
offset_bottom = 977.0
|
||||
|
||||
[node name="QuizToolbar" parent="." unique_id=1873301941 instance=ExtResource("scene_QuizToolbar")]
|
||||
layout_mode = 0
|
||||
offset_left = 1360.0
|
||||
offset_top = 64.0
|
||||
offset_right = 1855.0
|
||||
offset_bottom = 95.0
|
||||
|
||||
[node name="SongControls" parent="." unique_id=1640212773 instance=ExtResource("scene_SongControls")]
|
||||
layout_mode = 0
|
||||
offset_left = 65.0
|
||||
offset_top = 944.0
|
||||
offset_right = 368.0
|
||||
offset_bottom = 975.0
|
||||
|
||||
[node name="SettingsPopup" parent="." unique_id=1323246978 instance=ExtResource("11_k62u5")]
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
offset_left = 720.0
|
||||
offset_top = 153.0
|
||||
offset_right = 720.0
|
||||
offset_bottom = 153.0
|
||||
|
||||
[node name="CharacterSelectPopup" parent="." unique_id=1679856322 instance=ExtResource("6_dhrvg")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
offset_left = 405.0
|
||||
offset_top = 165.0
|
||||
offset_right = 1557.0
|
||||
offset_bottom = 805.0
|
||||
|
||||
[node name="WinnerPopup" parent="." unique_id=999999999 instance=ExtResource("17_winner_window")]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
layout_mode = 1
|
||||
offset_left = 720.0
|
||||
offset_top = 153.0
|
||||
offset_right = 720.0
|
||||
offset_bottom = 153.0
|
||||
|
||||
[node name="DebugLabel" type="Label" parent="." unique_id=1959221257]
|
||||
visible = false
|
||||
layout_mode = 0
|
||||
offset_left = 974.0
|
||||
offset_top = 904.0
|
||||
offset_right = 1014.0
|
||||
offset_bottom = 927.0
|
||||
scale = Vector2(3, 3)
|
||||
text = "DEBUG"
|
||||
horizontal_alignment = 1
|
||||
vertical_alignment = 1
|
||||
autowrap_mode = 2
|
||||
|
||||
[node name="SongLoader" type="Node" parent="." unique_id=1520867349]
|
||||
script = ExtResource("song_loader_script")
|
||||
|
||||
[connection signal="dir_selected" from="FileDialog" to="." method="_on_file_dialog_dir_selected"]
|
||||
@@ -0,0 +1,45 @@
|
||||
[gd_scene format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://quiz/quiz_toolbar.gd" id="part_script"]
|
||||
|
||||
[node name="QuizToolbar" type="Control" unique_id=1873301941]
|
||||
layout_mode = 3
|
||||
offset_right = 495.0
|
||||
offset_bottom = 31.0
|
||||
mouse_filter = 2
|
||||
script = ExtResource("part_script")
|
||||
|
||||
[node name="ResetPlaylistButton" type="Button" parent="." unique_id=1754155620]
|
||||
layout_mode = 0
|
||||
offset_right = 111.0
|
||||
offset_bottom = 31.0
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
text = "Reset playlist"
|
||||
|
||||
[node name="ResetPointsButton" type="Button" parent="." unique_id=942880776]
|
||||
layout_mode = 0
|
||||
offset_left = 128.0
|
||||
offset_right = 239.0
|
||||
offset_bottom = 31.0
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
text = "Reset points"
|
||||
|
||||
[node name="SettingsButton" type="Button" parent="." unique_id=1193400323]
|
||||
layout_mode = 0
|
||||
offset_left = 256.0
|
||||
offset_right = 367.0
|
||||
offset_bottom = 31.0
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
text = "Settings"
|
||||
|
||||
[node name="SoundTestButton" type="Button" parent="." unique_id=1332971879]
|
||||
layout_mode = 0
|
||||
offset_left = 384.0
|
||||
offset_right = 495.0
|
||||
offset_bottom = 31.0
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
text = "Sound test"
|
||||
@@ -0,0 +1,48 @@
|
||||
[gd_scene format=3]
|
||||
|
||||
[ext_resource type="Script" path="res://quiz/song_controls.gd" id="part_script"]
|
||||
|
||||
[sub_resource type="InputEventKey" id="InputEventKey_06rl4"]
|
||||
device = -1
|
||||
alt_pressed = true
|
||||
keycode = 86
|
||||
unicode = 8249
|
||||
|
||||
[sub_resource type="Shortcut" id="Shortcut_a7fvb"]
|
||||
events = [SubResource("InputEventKey_06rl4")]
|
||||
|
||||
[sub_resource type="InputEventKey" id="InputEventKey_7dnqw"]
|
||||
device = -1
|
||||
alt_pressed = true
|
||||
keycode = 67
|
||||
unicode = 231
|
||||
|
||||
[sub_resource type="Shortcut" id="Shortcut_d6fml"]
|
||||
events = [SubResource("InputEventKey_7dnqw")]
|
||||
|
||||
[node name="SongControls" type="Control" unique_id=1640212773]
|
||||
layout_mode = 3
|
||||
offset_right = 303.0
|
||||
offset_bottom = 31.0
|
||||
mouse_filter = 2
|
||||
script = ExtResource("part_script")
|
||||
|
||||
[node name="ShowAnswerButton" type="Button" parent="." unique_id=415388039]
|
||||
layout_mode = 0
|
||||
offset_right = 111.0
|
||||
offset_bottom = 31.0
|
||||
focus_mode = 0
|
||||
action_mode = 0
|
||||
shortcut = SubResource("Shortcut_a7fvb")
|
||||
text = "Show answer"
|
||||
|
||||
[node name="NextButton" type="Button" parent="." unique_id=1744703488]
|
||||
layout_mode = 0
|
||||
offset_left = 128.0
|
||||
offset_right = 303.0
|
||||
offset_bottom = 31.0
|
||||
focus_mode = 0
|
||||
disabled = true
|
||||
action_mode = 0
|
||||
shortcut = SubResource("Shortcut_d6fml")
|
||||
text = "Randomize new track"
|
||||
@@ -1,11 +1,11 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://1jbtmhbqolqe"]
|
||||
|
||||
[ext_resource type="Texture2D" uid="uid://c8r7ykphss4c2" path="res://characters/Link.png" id="1_ll28s"]
|
||||
[ext_resource type="Script" uid="uid://cjo13wixip8mq" path="res://song_list_object.gd" id="1_lspki"]
|
||||
[ext_resource type="Theme" uid="uid://8bllart6xn1f" path="res://default_theme.tres" id="1_x7fq8"]
|
||||
[ext_resource type="StyleBox" uid="uid://bsv6yb0yir04l" path="res://style_box_flat.tres" id="3_5h84f"]
|
||||
[ext_resource type="Script" uid="uid://cjo13wixip8mq" path="res://quiz/song_row.gd" id="1_lspki"]
|
||||
[ext_resource type="Theme" uid="uid://8bllart6xn1f" path="res://themes/default_theme.tres" id="1_x7fq8"]
|
||||
[ext_resource type="StyleBox" uid="uid://bsv6yb0yir04l" path="res://themes/style_box_flat.tres" id="3_5h84f"]
|
||||
|
||||
[node name="SongListObject" type="PanelContainer"]
|
||||
[node name="SongRow" type="PanelContainer"]
|
||||
custom_minimum_size = Vector2(1000, 0)
|
||||
offset_right = 1088.0
|
||||
offset_bottom = 59.0
|
||||
@@ -17,6 +17,7 @@ layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
|
||||
[node name="SongListLabel" type="Label" parent="HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
texture_filter = 2
|
||||
custom_minimum_size = Vector2(865, 28)
|
||||
layout_mode = 2
|
||||
@@ -34,6 +35,7 @@ size_flags_horizontal = 3
|
||||
size_flags_vertical = 0
|
||||
|
||||
[node name="SongListPlayers" type="HBoxContainer" parent="HBoxContainer"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 0
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
class_name InspirationList
|
||||
extends ScrollContainer
|
||||
## The list of every soundtrack, shown for inspiration. It scrolls slowly up and down
|
||||
## 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
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
scroll_direction = 1
|
||||
|
||||
|
||||
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: 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: float = scroll_bar.value
|
||||
|
||||
if scroll_direction == 1:
|
||||
# Scrolling down
|
||||
if current_pos + scroll_bar.page < scroll_bar.max_value:
|
||||
scroll_bar.value = minf(current_pos + scroll_amount,
|
||||
scroll_bar.max_value - scroll_bar.page)
|
||||
else:
|
||||
# Reached bottom, reverse direction
|
||||
scroll_direction = -1
|
||||
scroll_bar.value = scroll_bar.max_value - scroll_bar.page
|
||||
else:
|
||||
# Scrolling up
|
||||
if current_pos > 0:
|
||||
scroll_bar.value = maxf(current_pos + scroll_amount, 0)
|
||||
else:
|
||||
# Reached top, reverse direction
|
||||
scroll_direction = 1
|
||||
scroll_bar.value = 0
|
||||
|
||||
|
||||
## 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: Variant) -> void:
|
||||
if typeof(array) == TYPE_ARRAY:
|
||||
show_soundtracks(array)
|
||||
else:
|
||||
GameLog.warning("Unexpected data")
|
||||
MusicServerClient.fetch_all_soundtracks(populate_list)
|
||||
|
||||
|
||||
## Shows [param list] instead of the soundtracks shown now.
|
||||
func show_soundtracks(list: Array) -> void:
|
||||
soundtracks = []
|
||||
soundtracks.append_array(list)
|
||||
Settings.delete_children(_rows)
|
||||
for i: int in soundtracks.size():
|
||||
_rows.add_child(ListRow.create(soundtracks[i], i, 20))
|
||||
File renamed without changes.
@@ -0,0 +1,26 @@
|
||||
class_name MusicListPanel
|
||||
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://quiz/SongRow.tscn")
|
||||
|
||||
@onready var _rows: VBoxContainer = %MusicList
|
||||
|
||||
|
||||
## Shows the songs in the playlist again, so it shows their latest state.
|
||||
func refresh() -> void:
|
||||
Settings.delete_children(_rows)
|
||||
for s: SongObject in Playlist.get_playlist():
|
||||
var new_song_list_object: SongRow = _song_list_object.instantiate()
|
||||
new_song_list_object.song_object = s
|
||||
new_song_list_object.gui_input.connect(_on_row_input.bind(s.song_number))
|
||||
_rows.add_child(new_song_list_object)
|
||||
|
||||
|
||||
func _on_row_input(event: InputEvent, song_no: int) -> void:
|
||||
if event is InputEventMouseButton && event.pressed && event.button_index == MOUSE_BUTTON_LEFT:
|
||||
song_clicked.emit(song_no)
|
||||
@@ -0,0 +1 @@
|
||||
uid://b8e27uui01tt
|
||||
@@ -0,0 +1,10 @@
|
||||
class_name NowPlaying
|
||||
extends VBoxContainer
|
||||
## The soundtrack and song that are playing, in large text at the top of the quiz.
|
||||
|
||||
|
||||
## Shows [param soundtrack_title] and [param song_title]. The playlist hides them as
|
||||
## question marks until the answer is shown.
|
||||
func show_song(soundtrack_title: String, song_title: String) -> void:
|
||||
$SoundtrackLabel.text = soundtrack_title
|
||||
$SongLabel.text = song_title
|
||||
@@ -0,0 +1 @@
|
||||
uid://beoi75d4u568h
|
||||
@@ -1,18 +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()
|
||||
@@ -20,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)
|
||||
|
||||
File renamed without changes.
@@ -1,21 +1,6 @@
|
||||
class_name Player
|
||||
class_name PlayerRow
|
||||
extends Control
|
||||
|
||||
@onready
|
||||
var player_name_field := $HBoxContainer/Name
|
||||
|
||||
@onready
|
||||
var add_button := $HBoxContainer/AddPoint
|
||||
|
||||
@onready
|
||||
var minus_button := $HBoxContainer/RemovePoint
|
||||
|
||||
@onready
|
||||
var character := $HBoxContainer/Character
|
||||
|
||||
@onready
|
||||
var remove_player := $HBoxContainer/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:
|
||||
print("Back in player list with new")
|
||||
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)
|
||||
File renamed without changes.
@@ -0,0 +1,84 @@
|
||||
class_name PlayersPanel
|
||||
extends PanelContainer
|
||||
## The players and their points, with a row for adding new players.
|
||||
##
|
||||
## The players themselves live in [code]Settings.player_array[/code]; this panel shows
|
||||
## them and adds and removes them there.
|
||||
|
||||
## 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].
|
||||
signal player_removed(player_object: PlayerObject)
|
||||
|
||||
## Emitted when the character button in [param row] is pressed.
|
||||
signal change_character_requested(row: PlayerRow, player_name: String)
|
||||
|
||||
var _player_row: PackedScene = preload("res://quiz/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: 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.enter_key_pressed.connect(add_player)
|
||||
|
||||
|
||||
## Shows or hides the row for adding players, and the buttons for removing them.
|
||||
func add_players() -> void:
|
||||
GameLog.debug("add_players")
|
||||
Settings.edit_players = !Settings.edit_players
|
||||
add_player_container.visible = !add_player_container.visible
|
||||
new_player_name_field.grab_focus()
|
||||
|
||||
|
||||
## Adds a player with the name in the name field, unless it is empty or there are
|
||||
## 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:
|
||||
var new_player_object: PlayerObject = PlayerObject.new(new_player_name_field.text)
|
||||
new_player_name_field.text = ""
|
||||
Settings.player_array.append(new_player_object)
|
||||
player_added.emit(new_player_object)
|
||||
reload()
|
||||
|
||||
|
||||
## Shows the players in [code]Settings.player_array[/code] again, e.g. after their
|
||||
## points were reset.
|
||||
func reload() -> void:
|
||||
GameLog.debug("load_players")
|
||||
Settings.delete_children(players_list_container)
|
||||
var counter: int = 0
|
||||
for player_object: PlayerObject in Settings.player_array:
|
||||
GameLog.debug(player_object.player_name)
|
||||
|
||||
var new_player: PlayerRow = _player_row.instantiate()
|
||||
new_player.player_object = player_object
|
||||
new_player.list_index = counter
|
||||
|
||||
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
|
||||
|
||||
|
||||
func _on_change_character_clicked(row: PlayerRow, player_name: String) -> void:
|
||||
change_character_requested.emit(row, player_name)
|
||||
|
||||
|
||||
func _on_player_removed(player_object: PlayerObject) -> void:
|
||||
GameLog.debug("_on_player_removed ", player_object.player_name)
|
||||
Settings.player_array.erase(player_object)
|
||||
reload()
|
||||
player_removed.emit(player_object)
|
||||
@@ -0,0 +1 @@
|
||||
uid://ctayn0soyatfk
|
||||
+298
@@ -0,0 +1,298 @@
|
||||
class_name Quiz
|
||||
extends Control
|
||||
## The quiz: the song that is playing, the playlist, the players and their points, and
|
||||
## the popups opened from here (settings, character select, winner and log).
|
||||
##
|
||||
## The main window shows the quiz below its top bar. Pictures and buttons keep the
|
||||
## positions they had in the main window, so the quiz fills the whole window and lets
|
||||
## clicks through where it has nothing.
|
||||
|
||||
## Emitted when another MusicServer is picked in the settings, after the quiz has
|
||||
## reloaded from it.
|
||||
|
||||
signal server_changed
|
||||
|
||||
## Emitted with a line for the game log, e.g. when a player gets a point.
|
||||
signal event_logged(text: String)
|
||||
|
||||
var songs: Array = []
|
||||
var current_player_row: PlayerRow
|
||||
|
||||
## True while statistics are off because a player won, until points are reset.
|
||||
var stats_paused_by_win: bool = false
|
||||
|
||||
## Dims the window behind an open popup and keeps clicks from reaching the buttons under it.
|
||||
var popup_backdrop: PopupBackdrop
|
||||
|
||||
@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())
|
||||
if OS.has_feature("debug"):
|
||||
GameLog.debug("is_debug")
|
||||
debug_label.visible = true
|
||||
Settings.is_debug = true
|
||||
settings_popup.set_winning_score(5)
|
||||
settings_popup.set_selected_server(4)
|
||||
song_controls.next_pressed.connect(play_next_song)
|
||||
song_controls.show_answer_pressed.connect(show_answer_pressed)
|
||||
toolbar.sound_test_pressed.connect(music_player.get_sound_test_song)
|
||||
toolbar.settings_pressed.connect(show_settings)
|
||||
toolbar.reset_playlist_pressed.connect(reset_playlist)
|
||||
toolbar.reset_points_pressed.connect(reset_points)
|
||||
song_loader.song_list_loaded.connect(update_song_list)
|
||||
song_loader.song_added.connect(_on_song_added)
|
||||
|
||||
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.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)
|
||||
|
||||
inspiration_list.load_from_server()
|
||||
song_loader.load_song_list()
|
||||
character_select_popup.load_characters()
|
||||
|
||||
|
||||
func _input(event: InputEvent) -> void:
|
||||
if event is InputEventKey and event.pressed:
|
||||
if event.alt_pressed && event.keycode == KEY_UP:
|
||||
GameLog.debug("Alt + UP pressed")
|
||||
music_player.change_volume(0.05)
|
||||
if event.alt_pressed && event.keycode == KEY_DOWN:
|
||||
GameLog.debug("Alt + DOWN pressed")
|
||||
music_player.change_volume(-0.05)
|
||||
if event.alt_pressed && event.ctrl_pressed && event.keycode == KEY_LEFT:
|
||||
GameLog.debug("Ctrl + Alt + LEFT pressed")
|
||||
music_player.restart()
|
||||
elif event.alt_pressed && event.keycode == KEY_LEFT:
|
||||
GameLog.debug("Alt + LEFT pressed")
|
||||
music_player.seek(-5)
|
||||
if event.alt_pressed && event.keycode == KEY_RIGHT:
|
||||
GameLog.debug("Alt + RIGHT pressed")
|
||||
music_player.seek(5)
|
||||
|
||||
|
||||
## Disables the buttons that use the song list while the server syncs it.
|
||||
func lock_for_sync() -> void:
|
||||
toolbar.set_locked(true)
|
||||
song_controls.set_locked(true)
|
||||
|
||||
|
||||
## Reloads from the server after a sync and starts a new game.
|
||||
func on_sync_finished() -> void:
|
||||
refresh_from_server()
|
||||
toolbar.set_locked(false)
|
||||
song_controls.set_locked(false)
|
||||
reset_points()
|
||||
|
||||
|
||||
## Reloads the soundtracks, playlist and characters from the server.
|
||||
func refresh_from_server() -> void:
|
||||
GameLog.debug("refresh_from_server")
|
||||
inspiration_list.load_from_server()
|
||||
reset_playlist()
|
||||
character_select_popup.load_characters()
|
||||
|
||||
|
||||
func _on_server_changed() -> void:
|
||||
GameLog.debug("_on_server_changed")
|
||||
refresh_from_server()
|
||||
reset_points()
|
||||
server_changed.emit()
|
||||
|
||||
|
||||
func show_answer_pressed() -> void:
|
||||
GameLog.debug("show_answer_pressed")
|
||||
Playlist.song_is_answered()
|
||||
update_song_list()
|
||||
|
||||
|
||||
func play_next_song() -> void:
|
||||
GameLog.debug("play_next_song ", Playlist.latest_played_song)
|
||||
if Playlist.first_song_played:
|
||||
Playlist.set_latest_played_song()
|
||||
Playlist.first_song_played = true
|
||||
music_player.play_song_object(Playlist.latest_played_song)
|
||||
if !(Playlist.get_preloaded_songs() > 0):
|
||||
song_controls.set_next_enabled(false)
|
||||
song_loader.fetch_song_if_idle()
|
||||
|
||||
|
||||
func _popups() -> Array[BasePopup]:
|
||||
return [settings_popup, character_select_popup, winner_popup]
|
||||
|
||||
|
||||
# The cache size can be changed in the settings. Waiting until the settings are closed
|
||||
# avoids starting downloads while the user is still clicking the number up and down.
|
||||
func _on_settings_popup_visibility_changed() -> void:
|
||||
if !settings_popup.visible:
|
||||
song_loader.fill_cache()
|
||||
|
||||
|
||||
func _on_song_added() -> void:
|
||||
song_controls.set_next_enabled(true)
|
||||
if Settings.is_debug:
|
||||
update_song_list()
|
||||
|
||||
|
||||
func update_song_list() -> void:
|
||||
GameLog.debug("update_song_list")
|
||||
if Playlist.currently_playing_song >= 0:
|
||||
now_playing.show_song(Playlist.get_current_soundtrack_title(),
|
||||
Playlist.get_current_song_title())
|
||||
music_list_panel.refresh()
|
||||
|
||||
|
||||
func song_object_clicked(song_no: int) -> void:
|
||||
GameLog.debug("Clicked Song Object: ", Playlist.song_object_array[song_no].to_string())
|
||||
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:
|
||||
var sound: AudioStream = AudioStreamMP3.new()
|
||||
sound.data = body
|
||||
Playlist.add_song_to_song_object(song_no, sound)
|
||||
music_player.play_song_object(song_no)
|
||||
MusicServerClient.fetch_song(song_no, song_downloaded)
|
||||
else:
|
||||
GameLog.debug("song already downloaded")
|
||||
music_player.play_song_object(song_no)
|
||||
|
||||
|
||||
func reset_playlist() -> void:
|
||||
GameLog.debug("reset_playlist")
|
||||
var playlist_reset_done: Callable = func playlist_reset_done() -> void:
|
||||
Playlist.reset_playlist()
|
||||
song_loader.load_song_list()
|
||||
|
||||
MusicServerClient.reset_playlist(playlist_reset_done)
|
||||
|
||||
|
||||
func reset_points() -> void:
|
||||
GameLog.debug("reset_points")
|
||||
for player_objects: PlayerObject in Settings.player_array:
|
||||
player_objects.reset_points()
|
||||
players_panel.reload()
|
||||
# A new game starts, so turn statistics back on if winning turned them off
|
||||
if stats_paused_by_win:
|
||||
Settings.add_to_stats = true
|
||||
stats_paused_by_win = false
|
||||
|
||||
|
||||
func show_settings() -> void:
|
||||
GameLog.debug("show_settings")
|
||||
settings_popup.open()
|
||||
|
||||
|
||||
# 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.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:
|
||||
GameLog.debug("_on_point_given")
|
||||
event_logged.emit(player_object.player_name + " got a point")
|
||||
if Playlist.currently_playing_song >= 0:
|
||||
Playlist.add_point(player_object.id)
|
||||
update_song_list()
|
||||
|
||||
|
||||
func _on_make_point_given_sound() -> void:
|
||||
music_player.play_sound_effect(preload("res://sounds/itemopen.wav"))
|
||||
|
||||
|
||||
func _on_point_taken(player_object: PlayerObject) -> void:
|
||||
GameLog.debug("_on_point_taken")
|
||||
event_logged.emit(player_object.player_name + " lost a point")
|
||||
music_player.play_sound_effect(preload("res://sounds/itemequip.wav"))
|
||||
if Playlist.currently_playing_song >= 0:
|
||||
Playlist.remove_point(player_object.id)
|
||||
update_song_list()
|
||||
|
||||
|
||||
func _on_player_won(player_object: PlayerObject) -> void:
|
||||
GameLog.debug("_on_player_won")
|
||||
winner_popup.show_winner(player_object)
|
||||
music_player.play_song(preload("res://sounds/winning.mp3"))
|
||||
# Songs played after the game is won don't count towards the statistics
|
||||
if Settings.add_to_stats:
|
||||
Settings.add_to_stats = false
|
||||
stats_paused_by_win = true
|
||||
|
||||
|
||||
func _on_player_removed(player_object: PlayerObject) -> void:
|
||||
Playlist.remove_player_from_list(player_object.id)
|
||||
update_song_list()
|
||||
|
||||
|
||||
func _on_change_character_requested(new_player: PlayerRow, player_name: String) -> void:
|
||||
GameLog.debug("_on_player_change_character_clicked")
|
||||
current_player_row = new_player
|
||||
GameLog.debug("Change character for: " + player_name)
|
||||
character_select_popup.show_grid(player_name)
|
||||
|
||||
|
||||
func _on_new_character_selected(character_texture: ImageTexture) -> void:
|
||||
GameLog.debug("_on_new_character_selected")
|
||||
current_player_row.new_character_selected(character_texture)
|
||||
|
||||
|
||||
###Local
|
||||
|
||||
var local_path: String = '/Users/sebastian/ResilioSync/Sorterat_test/Metal Gear Solid 4 - Guns of the Patriots/2-16 Metal Gear Saga.mp3'
|
||||
|
||||
|
||||
func open() -> void:
|
||||
file_dialog.popup()
|
||||
|
||||
|
||||
func _on_file_dialog_dir_selected(path: String) -> void:
|
||||
GameLog.debug(path)
|
||||
dir_contents(path)
|
||||
|
||||
|
||||
func dir_contents(path: String) -> void:
|
||||
var dir: DirAccess = DirAccess.open(path)
|
||||
if dir:
|
||||
dir.list_dir_begin()
|
||||
var file_name: String = dir.get_next()
|
||||
songs.clear()
|
||||
var soundtracks: Array = []
|
||||
while file_name != "":
|
||||
if dir.current_is_dir():
|
||||
#GameLog.debug("Found directory: " + file_name)
|
||||
soundtracks.append(file_name)
|
||||
else:
|
||||
#GameLog.debug("Found file: " + file_name)
|
||||
if file_name.ends_with(".mp3"):
|
||||
songs.append(path + "/" + file_name)
|
||||
file_name = dir.get_next()
|
||||
inspiration_list.show_soundtracks(soundtracks)
|
||||
else:
|
||||
GameLog.warning("An error occurred when trying to access the path.")
|
||||
@@ -0,0 +1 @@
|
||||
uid://b0od47vseenx4
|
||||
@@ -0,0 +1,25 @@
|
||||
class_name QuizToolbar
|
||||
extends Control
|
||||
## The row of quiz buttons under the top bar: reset playlist, reset points, settings
|
||||
## and sound test.
|
||||
|
||||
signal reset_playlist_pressed
|
||||
signal reset_points_pressed
|
||||
signal settings_pressed
|
||||
signal sound_test_pressed
|
||||
|
||||
@onready var reset_playlist_button: Button = $ResetPlaylistButton
|
||||
@onready var sound_test_button: Button = $SoundTestButton
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
reset_playlist_button.pressed.connect(reset_playlist_pressed.emit)
|
||||
$ResetPointsButton.pressed.connect(reset_points_pressed.emit)
|
||||
$SettingsButton.pressed.connect(settings_pressed.emit)
|
||||
sound_test_button.pressed.connect(sound_test_pressed.emit)
|
||||
|
||||
|
||||
## Disables the buttons that use the song list, e.g. while the server syncs it.
|
||||
func set_locked(locked: bool) -> void:
|
||||
reset_playlist_button.disabled = locked
|
||||
sound_test_button.disabled = locked
|
||||
@@ -0,0 +1 @@
|
||||
uid://dmsy36s1ht7x5
|
||||
@@ -0,0 +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: float = 0.0
|
||||
|
||||
|
||||
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)
|
||||
File renamed without changes.
@@ -0,0 +1,27 @@
|
||||
class_name SongControls
|
||||
extends Control
|
||||
## The buttons for the song that is playing: show the answer, and go to the next song.
|
||||
|
||||
signal show_answer_pressed
|
||||
signal next_pressed
|
||||
|
||||
@onready var show_answer_button: Button = $ShowAnswerButton
|
||||
@onready var next_button: Button = $NextButton
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
show_answer_button.pressed.connect(show_answer_pressed.emit)
|
||||
next_button.pressed.connect(next_pressed.emit)
|
||||
|
||||
|
||||
## Enables Next while there is a downloaded song to play next.
|
||||
func set_next_enabled(enabled: bool) -> void:
|
||||
next_button.disabled = !enabled
|
||||
|
||||
|
||||
## Disables both buttons, e.g. while the server syncs the song list. Unlocking enables
|
||||
## Show answer again; Next comes back when the next song has been downloaded.
|
||||
func set_locked(locked: bool) -> void:
|
||||
show_answer_button.disabled = locked
|
||||
if locked:
|
||||
next_button.disabled = true
|
||||
@@ -0,0 +1 @@
|
||||
uid://d6nevh66yaw1
|
||||
@@ -0,0 +1,101 @@
|
||||
class_name SongLoader
|
||||
extends Node
|
||||
## Downloads songs from the server into the Playlist ahead of time, so the next song
|
||||
## can start at once.
|
||||
##
|
||||
## How many songs it keeps ready is the cache size in the settings
|
||||
## ([code]Playlist.number_of_tracks_to_preload[/code]).
|
||||
|
||||
## 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.
|
||||
signal song_added
|
||||
|
||||
## Seconds to wait before retrying a download step that failed.
|
||||
var retry_seconds: float = 5.0
|
||||
|
||||
|
||||
## Loads the songs already in the server playlist, then starts preloading new ones.
|
||||
## Preloading waits for the list, because loading the list clears the playlist.
|
||||
func load_song_list() -> void:
|
||||
GameLog.debug("load_song_list")
|
||||
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:
|
||||
var new_song_object: SongObject = SongObject.new()
|
||||
new_song_object.song_number = d.SongNo
|
||||
new_song_object.soundtrack_title = d.Soundtrack
|
||||
new_song_object.song_title = d.Song
|
||||
new_song_object.is_answered = true
|
||||
new_song_object.has_played = true
|
||||
Playlist.add_to_playlist(new_song_object)
|
||||
elif data != null:
|
||||
GameLog.warning("Unexpected data")
|
||||
Playlist.update_last_played_song()
|
||||
song_list_loaded.emit()
|
||||
fetch_song_if_idle()
|
||||
MusicServerClient.fetch_song_list(song_list_fetched, _retry_later.bind(load_song_list))
|
||||
|
||||
|
||||
## Starts downloading one more song, which keeps downloading until the cache is full.
|
||||
func fetch_song() -> void:
|
||||
GameLog.debug("fetch_song")
|
||||
Playlist.is_song_downloading = true
|
||||
_download_random_song(SongObject.new())
|
||||
|
||||
|
||||
## Starts downloading a song unless one is already downloading.
|
||||
func fetch_song_if_idle() -> void:
|
||||
if !Playlist.is_song_downloading:
|
||||
fetch_song()
|
||||
|
||||
|
||||
## Starts downloading if nothing is downloading and the cache is not full, e.g. after
|
||||
## the cache size was raised.
|
||||
func fill_cache() -> void:
|
||||
if !Playlist.is_song_downloading && Playlist.should_preload_more_songs():
|
||||
fetch_song()
|
||||
|
||||
|
||||
# Fetching a song takes three requests. If one fails, only that step is retried, so
|
||||
# the server and the game keep the same songs in their playlists.
|
||||
func _download_random_song(new_song_object: SongObject) -> void:
|
||||
var song_downloaded: Callable = func song_downloaded(body: PackedByteArray) -> void:
|
||||
GameLog.debug("song_downloaded")
|
||||
var sound: AudioStream = AudioStreamMP3.new()
|
||||
sound.data = body
|
||||
new_song_object.song = sound
|
||||
_add_song_to_queue(new_song_object)
|
||||
MusicServerClient.fetch_random_song(Settings.use_low_played_mode, song_downloaded,
|
||||
_retry_later.bind(_download_random_song.bind(new_song_object)))
|
||||
|
||||
|
||||
func _add_song_to_queue(new_song_object: SongObject) -> void:
|
||||
MusicServerClient.add_to_queue(_fetch_song_info.bind(new_song_object),
|
||||
_retry_later.bind(_add_song_to_queue.bind(new_song_object)))
|
||||
|
||||
|
||||
func _fetch_song_info(new_song_object: SongObject) -> void:
|
||||
var song_info_fetched: Callable = func song_info_fetched(data_received: Dictionary) -> void:
|
||||
GameLog.debug("song_info_fetched")
|
||||
new_song_object.song_number = data_received.SongNo
|
||||
new_song_object.soundtrack_title = data_received.Soundtrack
|
||||
new_song_object.song_title = data_received.Song
|
||||
GameLog.debug("new_song_object: ", new_song_object.to_string())
|
||||
Playlist.add_to_playlist(new_song_object)
|
||||
if Playlist.should_preload_more_songs():
|
||||
fetch_song()
|
||||
else:
|
||||
Playlist.is_song_downloading = false
|
||||
song_added.emit()
|
||||
MusicServerClient.fetch_song_info(song_info_fetched,
|
||||
_retry_later.bind(_fetch_song_info.bind(new_song_object)))
|
||||
|
||||
|
||||
func _retry_later(step: Callable) -> void:
|
||||
GameLog.warning("Song download failed, retrying in %s seconds" % retry_seconds)
|
||||
get_tree().create_timer(retry_seconds).timeout.connect(step)
|
||||
@@ -0,0 +1 @@
|
||||
uid://dwqirg7x7mmj6
|
||||
@@ -1,33 +1,19 @@
|
||||
class_name SongListObject
|
||||
class_name SongRow
|
||||
extends PanelContainer
|
||||
|
||||
@onready
|
||||
var song_list_label: Label = $HBoxContainer/SongListLabel
|
||||
|
||||
@onready
|
||||
var song_list_players: HBoxContainer = $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:
|
||||
Loaded 100 of 158 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user