Short songs (#30): a quiz settings switch and a Seconds per song row (30 by default, 10-120 in steps of 5), saved with the quiz settings. Only part of each quiz song plays, from a random point 10-60% into it, picked once per song so restart and repeat play the same part. The progress bar, time and seeking stay within the part, a hidden beginning counts inside it, and its end counts as the song ending (repeat, stop or go to the next song as set). Random songs (#51): the low-played switch in Settings is now a Normal / Low played / Classic choice, stored as Settings.random_mode and not saved. fetch_random_song(mode) picks /music/rand, /music/rand/low or /music/rand/classic. Closes #30 Closes #51 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
172 lines
7.5 KiB
GDScript
172 lines
7.5 KiB
GDScript
class_name QuizSettingsCard
|
|
extends BasePopup
|
|
## The quiz settings, in a small card that opens above the Quiz settings button by the
|
|
## music player: how songs are played and hidden, the winning score and the inspiration
|
|
## list. It does not dim the quiz, so it can be changed during a song.
|
|
##
|
|
## Every change is saved at once with [method Settings.save_settings].
|
|
##
|
|
## The card's bottom edge is where the card is placed, just above the player bar, and
|
|
## it grows upward to fit its rows.
|
|
##
|
|
## The application settings, such as the server, are in [SettingsPopup].
|
|
|
|
## The most seconds of a song's beginning that can be hidden.
|
|
const MAX_HIDE_SECONDS: float = 10.0
|
|
## The shortest and longest part of a song that Short songs plays, and the step between.
|
|
const MIN_SHORT_SONG_SECONDS: int = 10
|
|
const MAX_SHORT_SONG_SECONDS: int = 120
|
|
const SHORT_SONG_STEP: int = 5
|
|
## Space between the rows and the card's edges.
|
|
const PADDING: Vector2 = Vector2(16, 12)
|
|
|
|
@onready var card_panel: Panel = %CardPanel
|
|
@onready var rows: VBoxContainer = %VBox
|
|
@onready var auto_repeat_button: CheckButton = %AutoRepeatCheckButton
|
|
@onready var stop_after_current_button: CheckButton = %StopAfterCurrentCheckButton
|
|
@onready var hide_next_track_button: CheckButton = %HideNextTrackCheckButton
|
|
@onready var short_songs_button: CheckButton = %ShortSongsCheckButton
|
|
@onready var short_song_seconds_row: HBoxContainer = %ShortSongSecondsRow
|
|
@onready var short_song_seconds_label: Label = %ShortSongSecondsLabel
|
|
@onready var lower_short_song_seconds_button: Button = %LowerShortSongSecondsButton
|
|
@onready var increase_short_song_seconds_button: Button = %IncreaseShortSongSecondsButton
|
|
@onready var hide_beginning_button: CheckButton = %HideBeginningCheckButton
|
|
@onready var hide_for_seconds_row: HBoxContainer = %HideForSecondsRow
|
|
@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 hide_length_button: CheckButton = %HideLengthCheckButton
|
|
@onready var hide_ticks_button: CheckButton = %HideTicksCheckButton
|
|
@onready var winning_score_row: HBoxContainer = %WinningScoreRow
|
|
@onready var score_label: Label = %ScoreLabel
|
|
@onready var lower_winning_score_button: Button = %LowerButton
|
|
@onready var increase_winning_score_button: Button = %IncreaseButton
|
|
@onready var inspiration_speed_label: Label = %InspirationListSpeedLabel
|
|
@onready var lower_inspiration_speed_button: Button = %LowerInspirationSpeedButton
|
|
@onready var increase_inspiration_speed_button: Button = %IncreaseInspirationSpeedButton
|
|
|
|
|
|
func _ready() -> void:
|
|
auto_repeat_button.toggled.connect(_save_toggle.bind(&"auto_repeat_song"))
|
|
stop_after_current_button.toggled.connect(_save_toggle.bind(&"stop_after_current"))
|
|
hide_next_track_button.toggled.connect(_save_toggle.bind(&"hide_next_track"))
|
|
short_songs_button.toggled.connect(_on_short_songs_toggled)
|
|
lower_short_song_seconds_button.pressed.connect(
|
|
set_short_song_seconds.bind(-SHORT_SONG_STEP, true))
|
|
increase_short_song_seconds_button.pressed.connect(
|
|
set_short_song_seconds.bind(SHORT_SONG_STEP, true))
|
|
hide_beginning_button.toggled.connect(_on_hide_beginning_toggled)
|
|
hide_length_button.toggled.connect(_save_toggle.bind(&"hide_length"))
|
|
hide_ticks_button.toggled.connect(_save_toggle.bind(&"hide_ticks"))
|
|
lower_winning_score_button.pressed.connect(lower_winning_score)
|
|
increase_winning_score_button.pressed.connect(increase_winning_score)
|
|
lower_inspiration_speed_button.pressed.connect(lower_inspiration_speed)
|
|
increase_inspiration_speed_button.pressed.connect(increase_inspiration_speed)
|
|
lower_hide_for_seconds_button.pressed.connect(lower_hide_for_seconds)
|
|
increase_hide_for_seconds_button.pressed.connect(increase_hide_for_seconds)
|
|
visibility_changed.connect(_on_visibility_changed)
|
|
show_settings()
|
|
|
|
|
|
## Shows the current settings. This runs every time the card opens, in case a setting
|
|
## was changed somewhere else.
|
|
func show_settings() -> void:
|
|
auto_repeat_button.set_pressed_no_signal(Settings.auto_repeat_song)
|
|
stop_after_current_button.set_pressed_no_signal(Settings.stop_after_current)
|
|
hide_next_track_button.set_pressed_no_signal(Settings.hide_next_track)
|
|
short_songs_button.set_pressed_no_signal(Settings.short_songs)
|
|
short_song_seconds_row.visible = Settings.short_songs
|
|
short_song_seconds_label.text = str(Settings.short_song_seconds)
|
|
hide_beginning_button.set_pressed_no_signal(Settings.hide_beginning)
|
|
hide_length_button.set_pressed_no_signal(Settings.hide_length)
|
|
hide_ticks_button.set_pressed_no_signal(Settings.hide_ticks)
|
|
hide_for_seconds_row.visible = Settings.hide_beginning
|
|
hide_for_seconds_label.text = "%d" % Settings.hide_for_seconds
|
|
score_label.text = str(Settings.winning_score)
|
|
# A prepared quiz is won by having the most points when every song has played
|
|
winning_score_row.visible = !Settings.is_prepared_quiz()
|
|
inspiration_speed_label.text = str(Settings.inspiration_list_speed)
|
|
_fit_to_rows()
|
|
|
|
|
|
func lower_winning_score() -> void:
|
|
set_winning_score(Settings.winning_score - 1)
|
|
|
|
|
|
func increase_winning_score() -> void:
|
|
set_winning_score(Settings.winning_score + 1)
|
|
|
|
|
|
## Sets the winning score, never below 1.
|
|
func set_winning_score(new_score: int) -> void:
|
|
Settings.winning_score = maxi(new_score, 1)
|
|
score_label.text = str(Settings.winning_score)
|
|
Settings.save_settings()
|
|
|
|
|
|
func lower_inspiration_speed() -> void:
|
|
Settings.inspiration_list_speed = maxi(Settings.inspiration_list_speed - 1, 1)
|
|
inspiration_speed_label.text = str(Settings.inspiration_list_speed)
|
|
Settings.save_settings()
|
|
|
|
|
|
func increase_inspiration_speed() -> void:
|
|
Settings.inspiration_list_speed += 1
|
|
inspiration_speed_label.text = str(Settings.inspiration_list_speed)
|
|
Settings.save_settings()
|
|
|
|
|
|
func lower_hide_for_seconds() -> void:
|
|
Settings.hide_for_seconds = maxf(Settings.hide_for_seconds - 1.0, 0.0)
|
|
hide_for_seconds_label.text = "%d" % Settings.hide_for_seconds
|
|
Settings.save_settings()
|
|
|
|
|
|
func increase_hide_for_seconds() -> void:
|
|
Settings.hide_for_seconds = minf(Settings.hide_for_seconds + 1.0, MAX_HIDE_SECONDS)
|
|
hide_for_seconds_label.text = "%d" % Settings.hide_for_seconds
|
|
Settings.save_settings()
|
|
|
|
|
|
# Sets the on/off quiz setting named [param setting] and saves the quiz settings
|
|
func _save_toggle(on: bool, setting: StringName) -> void:
|
|
Settings.set(setting, on)
|
|
Settings.save_settings()
|
|
|
|
|
|
## Sets how many seconds of each song Short songs plays, between
|
|
## [constant MIN_SHORT_SONG_SECONDS] and [constant MAX_SHORT_SONG_SECONDS]. With
|
|
## [param relative] the seconds are added to the current length.
|
|
func set_short_song_seconds(seconds: int, relative: bool = false) -> void:
|
|
var length: int = Settings.short_song_seconds + seconds if relative else seconds
|
|
Settings.short_song_seconds = clampi(length, MIN_SHORT_SONG_SECONDS,
|
|
MAX_SHORT_SONG_SECONDS)
|
|
short_song_seconds_label.text = str(Settings.short_song_seconds)
|
|
Settings.save_settings()
|
|
|
|
|
|
func _on_short_songs_toggled(on: bool) -> void:
|
|
_save_toggle(on, &"short_songs")
|
|
short_song_seconds_row.visible = on
|
|
_fit_to_rows()
|
|
|
|
|
|
func _on_hide_beginning_toggled(on: bool) -> void:
|
|
_save_toggle(on, &"hide_beginning")
|
|
hide_for_seconds_row.visible = on
|
|
_fit_to_rows()
|
|
|
|
|
|
# Grows or shrinks the card upward, keeping its bottom edge in place
|
|
func _fit_to_rows() -> void:
|
|
var height: float = rows.get_combined_minimum_size().y + PADDING.y * 2
|
|
card_panel.position.y = -height
|
|
card_panel.size.y = height
|
|
rows.position = PADDING
|
|
rows.size = Vector2(card_panel.size.x - PADDING.x * 2, height - PADDING.y * 2)
|
|
|
|
|
|
func _on_visibility_changed() -> void:
|
|
if visible:
|
|
show_settings()
|