extends VBoxContainer const Style := preload("res://scripts/ui/ui_style.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 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) var goal_row := HBoxContainer.new() 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: 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) 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 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