182 lines
7.0 KiB
GDScript
182 lines
7.0 KiB
GDScript
extends VBoxContainer
|
|
|
|
const Style := preload("res://scripts/ui/ui_style.gd")
|
|
const FeatureFlags := preload("res://scripts/config/feature_flags.gd")
|
|
|
|
var main: Control
|
|
var selected_system := "All Systems"
|
|
var system_label: Label
|
|
var size_spin: SpinBox
|
|
var mode_option: OptionButton
|
|
var status_label: Label
|
|
var start_button: Button
|
|
var requested_count := 10
|
|
var targeted_setup := false
|
|
var goal_option: OptionButton
|
|
var target_label: Label
|
|
var pending_launch := false
|
|
var goal_row: HBoxContainer
|
|
|
|
func _ready() -> void:
|
|
set_anchors_preset(Control.PRESET_FULL_RECT)
|
|
add_theme_constant_override("separation", 16)
|
|
if not QuestionBankService.questions_changed.is_connected(_refresh_setup):
|
|
QuestionBankService.questions_changed.connect(_refresh_setup)
|
|
StudySessionService.session_started.connect(_on_session_started)
|
|
StudySessionService.session_failed.connect(_on_session_failed)
|
|
|
|
var title := Label.new()
|
|
title.text = "Block Setup"
|
|
Style.set_label(title, 34, Style.TEXT)
|
|
add_child(title)
|
|
|
|
system_label = Label.new()
|
|
Style.set_label(system_label, 22, Style.CYAN)
|
|
add_child(system_label)
|
|
|
|
goal_row = HBoxContainer.new()
|
|
goal_row.visible = FeatureFlags.progression_enabled()
|
|
var goal_label := Label.new()
|
|
goal_label.text = "Finish line"
|
|
goal_row.add_child(goal_label)
|
|
goal_option = OptionButton.new()
|
|
goal_option.add_item("Questions")
|
|
goal_option.set_item_metadata(0, "question_count")
|
|
goal_option.add_item("Study duration")
|
|
goal_option.set_item_metadata(1, "duration")
|
|
goal_option.item_selected.connect(_on_goal_changed)
|
|
goal_row.add_child(goal_option)
|
|
add_child(goal_row)
|
|
|
|
var size_row := HBoxContainer.new()
|
|
target_label = Label.new()
|
|
target_label.text = "Question count"
|
|
size_row.add_child(target_label)
|
|
size_spin = SpinBox.new()
|
|
size_spin.min_value = 1
|
|
size_spin.max_value = 40
|
|
size_spin.value = 6
|
|
size_spin.custom_minimum_size = Vector2(110, 44)
|
|
size_row.add_child(size_spin)
|
|
add_child(size_row)
|
|
|
|
var mode_row := HBoxContainer.new()
|
|
var mode_label := Label.new()
|
|
mode_label.text = "Mode"
|
|
mode_row.add_child(mode_label)
|
|
mode_option = OptionButton.new()
|
|
mode_option.add_item("Tutor")
|
|
mode_option.add_item("Timed")
|
|
mode_option.custom_minimum_size = Vector2(180, 44)
|
|
mode_row.add_child(mode_option)
|
|
add_child(mode_row)
|
|
|
|
var note := Label.new()
|
|
note.text = "Tutor mode shows explanations after each answer. Timed mode scores the block at the end."
|
|
note.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
Style.set_label(note, 16, Style.MUTED)
|
|
add_child(note)
|
|
|
|
status_label = Label.new()
|
|
status_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
|
Style.set_label(status_label, 16, Style.RED)
|
|
add_child(status_label)
|
|
|
|
start_button = Button.new()
|
|
start_button.text = "Launch Block"
|
|
start_button.custom_minimum_size = Vector2(220, 54)
|
|
Style.style_primary_button(start_button)
|
|
start_button.pressed.connect(_start_block)
|
|
add_child(start_button)
|
|
|
|
|
|
func on_show(payload: Variant = null) -> void:
|
|
if typeof(payload) == TYPE_DICTIONARY and FeatureFlags.progression_enabled():
|
|
targeted_setup = bool(payload.get("mastery_target", false))
|
|
selected_system = str(payload.get("step3_system", "All Systems"))
|
|
requested_count = int(payload.get("question_count", 10))
|
|
goal_option.set_meta("mastery_target", targeted_setup)
|
|
var requested_goal := str(payload.get("goal_type", "question_count"))
|
|
goal_option.select(1 if requested_goal == "duration" else 0)
|
|
_on_goal_changed(goal_option.selected)
|
|
size_spin.value = requested_count
|
|
QuestionBankService.load_targeted_questions(selected_system, requested_count,
|
|
int(payload.get("difficulty_min", 0)), int(payload.get("difficulty_max", 0)))
|
|
else:
|
|
goal_option.set_meta("mastery_target", false)
|
|
targeted_setup = false
|
|
selected_system = str(payload.get("step3_system", "All Systems")) if typeof(payload) == TYPE_DICTIONARY else (str(payload) if payload != null else "All Systems")
|
|
_refresh_setup()
|
|
|
|
|
|
func _refresh_setup() -> void:
|
|
if system_label == null:
|
|
return
|
|
system_label.text = "Zone: %s" % selected_system
|
|
var available := QuestionBankService.get_questions_for_system(selected_system).size()
|
|
if str(goal_option.get_item_metadata(goal_option.selected)) != "duration":
|
|
size_spin.max_value = max(5, available)
|
|
if not QuestionBankService.is_loading and available > 0 and str(goal_option.get_item_metadata(goal_option.selected)) != "duration":
|
|
size_spin.value = min(requested_count, available) if targeted_setup else min(size_spin.value, available)
|
|
targeted_setup = false
|
|
start_button.disabled = available == 0 or QuestionBankService.is_loading
|
|
if QuestionBankService.is_loading:
|
|
status_label.text = "Question API unavailable. Retrying in %d seconds..." % QuestionBankService.next_retry_seconds if QuestionBankService.load_state == "retrying" else "Loading question bank..."
|
|
elif available == 0:
|
|
status_label.text = QuestionBankService.last_error if QuestionBankService.last_error != "" else "No questions are available for this zone yet."
|
|
else:
|
|
status_label.text = "%d questions available.%s" % [available,
|
|
" The requested difficulty range was broadened." if QuestionBankService.targeted_fallback_used else ""]
|
|
|
|
|
|
func _start_block() -> void:
|
|
if not QuestionBankService.has_questions():
|
|
status_label.text = "Questions have not loaded from the API yet."
|
|
return
|
|
if not FeatureFlags.progression_enabled():
|
|
BlockSessionService.start_block(selected_system, int(size_spin.value), mode_option.get_item_text(mode_option.selected))
|
|
main.show_screen("player")
|
|
return
|
|
pending_launch = true
|
|
start_button.disabled = true
|
|
status_label.text = "Preparing your study goal..."
|
|
var mastery_target := bool(goal_option.get_meta("mastery_target", false))
|
|
var goal_type := "weak_system" if mastery_target else str(goal_option.get_item_metadata(goal_option.selected))
|
|
var target := 10 if mastery_target else int(size_spin.value)
|
|
StudySessionService.start(goal_type, target, mode_option.get_item_text(mode_option.selected),
|
|
selected_system if selected_system != "All Systems" else "")
|
|
|
|
|
|
func _on_goal_changed(_index: int) -> void:
|
|
if str(goal_option.get_item_metadata(goal_option.selected)) == "duration":
|
|
target_label.text = "Minutes"
|
|
size_spin.min_value = 10
|
|
size_spin.max_value = 60
|
|
size_spin.step = 5
|
|
size_spin.value = 20
|
|
else:
|
|
target_label.text = "Question count"
|
|
size_spin.min_value = 5
|
|
size_spin.step = 1
|
|
_refresh_setup()
|
|
|
|
|
|
func _on_session_started() -> void:
|
|
if not pending_launch or not visible:
|
|
return
|
|
pending_launch = false
|
|
var goal_type := str(StudySessionService.progress.get("goalType", "question_count"))
|
|
var block_size := 40 if goal_type == "duration" else int(StudySessionService.progress.get("targetValue", size_spin.value))
|
|
BlockSessionService.start_block(selected_system, block_size, mode_option.get_item_text(mode_option.selected))
|
|
if goal_type == "duration":
|
|
BlockSessionService.seconds_remaining = float(int(StudySessionService.progress.get("secondsRemaining", 0)))
|
|
main.show_screen("player")
|
|
|
|
|
|
func _on_session_failed(message: String) -> void:
|
|
if not pending_launch or not visible:
|
|
return
|
|
pending_launch = false
|
|
start_button.disabled = false
|
|
status_label.text = message
|