Files
step3/scripts/ui/block_setup.gd

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