Add full-length Step 3 Day 1 simulation

This commit is contained in:
hz
2026-07-14 16:54:11 -05:00
parent 8aaf7d61e9
commit 206a5c0124
19 changed files with 1520 additions and 32 deletions
+98 -12
View File
@@ -45,6 +45,7 @@ var pending_correct_advance := false
var question_text_scale := 1.0
var presented_choices: Array = []
var awaiting_next_question := false
var simulation_submit_pending := false
var progress_label: Label
var timer_label: Label
@@ -131,6 +132,10 @@ func _ready() -> void:
QuestionBankService.questions_changed.connect(_on_random_question_changed)
if not QuestionBankService.questions_load_failed.is_connected(_on_random_question_failed):
QuestionBankService.questions_load_failed.connect(_on_random_question_failed)
if not ExamSimulationService.state_changed.is_connected(_on_simulation_state_changed):
ExamSimulationService.state_changed.connect(_on_simulation_state_changed)
if not ExamSimulationService.request_failed.is_connected(_on_simulation_request_failed):
ExamSimulationService.request_failed.connect(_on_simulation_request_failed)
var top := HBoxContainer.new()
top.add_theme_constant_override("separation", 12)
@@ -212,6 +217,8 @@ func _ready() -> void:
flag_button.toggled.connect(func(value: bool):
flagged = value
_refresh_toolbar_state()
if BlockSessionService.session_kind == "simulation":
_save_simulation_current_response()
)
question_toolbar.add_child(flag_button)
@@ -369,7 +376,7 @@ func _ready() -> void:
actions.add_child(skip_button)
next_button = Button.new()
next_button.text = "Submit"
next_button.text = "Submit Block" if BlockSessionService.session_kind == "simulation" and BlockSessionService.current_index + 1 == BlockSessionService.active_questions.size() else "Next" if BlockSessionService.session_kind == "simulation" else "Submit"
next_button.tooltip_text = ""
next_button.custom_minimum_size = Vector2(140, 48)
next_button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
@@ -420,14 +427,62 @@ func on_show(_payload: Variant = null) -> void:
func on_hide() -> void:
if BlockSessionService.session_kind == "simulation" and BlockSessionService.block_started:
_save_simulation_current_response()
awaiting_next_question = false
_hide_media_viewer()
_hide_report_modal(true)
_hide_labs_modal()
func _save_simulation_current_response() -> void:
if BlockSessionService.session_kind != "simulation":
return
var question := BlockSessionService.get_current_question()
if question.is_empty():
return
current_answer = BlockSessionService.answer_current(selected_choice, eliminated, flagged)
ExamSimulationService.save_response(
str(question.get("id", "")),
selected_choice,
eliminated,
flagged,
int(BlockSessionService.get_current_elapsed_seconds()))
func _on_simulation_state_changed() -> void:
if BlockSessionService.session_kind != "simulation":
return
var state := str(ExamSimulationService.simulation.get("state", ""))
if state == "between_blocks":
simulation_submit_pending = false
BlockSessionService.block_started = false
main.show_screen("simulation_break")
elif state == "completed":
simulation_submit_pending = false
BlockSessionService.block_started = false
main.show_screen("simulation_results", ExamSimulationService.simulation)
func _on_simulation_request_failed(message: String) -> void:
if BlockSessionService.session_kind != "simulation":
return
simulation_submit_pending = false
next_button.disabled = false
next_button.text = "Retry"
next_button.tooltip_text = message
func _process(delta: float) -> void:
_update_answer_feedback(delta)
if visible and BlockSessionService.session_kind == "simulation":
BlockSessionService.seconds_remaining = maxf(0.0, ExamSimulationService.get_block_deadline_unix() - Time.get_unix_time_from_system())
var simulation_seconds := int(ceil(BlockSessionService.seconds_remaining))
timer_label.text = "Block time: %02d:%02d" % [int(simulation_seconds / 60), simulation_seconds % 60]
if simulation_seconds <= 0 and not simulation_submit_pending:
simulation_submit_pending = true
ExamSimulationService.submit_block()
return
if visible and BlockSessionService.block_started and BlockSessionService.session_kind == "core":
_update_timer_bar()
if visible and BlockSessionService.block_started and BlockSessionService.mode == "Timed" and (report_overlay == null or not report_overlay.visible):
@@ -499,13 +554,14 @@ func _load_question() -> void:
next_button.disabled = true
skip_button.disabled = false
skip_button.visible = BlockSessionService.session_kind == "core"
report_button.visible = BlockSessionService.session_kind != "simulation"
var question := BlockSessionService.get_current_question()
if question.is_empty():
main.show_screen("selection", "step3")
return
progress_label.visible = BlockSessionService.session_kind != "core"
progress_label.text = "" if BlockSessionService.session_kind == "core" else "Question %s" % BlockSessionService.get_progress_text()
progress_label.text = "" if BlockSessionService.session_kind == "core" else BlockSessionService.get_progress_text()
timer_label.visible = BlockSessionService.session_kind != "core"
timer_label.text = "" if BlockSessionService.session_kind == "core" else "Tutor Mode" if BlockSessionService.mode == "Tutor" else "Timed"
timer_bar.visible = BlockSessionService.session_kind == "core"
@@ -538,8 +594,11 @@ func _load_question() -> void:
flagged = bool(saved_answer.get("flagged", false))
flag_button.button_pressed = flagged
_refresh_toolbar_state()
_show_answer_review()
_update_question_elo_label(true)
if BlockSessionService.session_kind == "simulation":
_apply_saved_choice_state()
else:
_show_answer_review()
_update_question_elo_label(true)
_refresh_nav_palette()
_refresh_toolbar_state()
@@ -556,9 +615,12 @@ func _select_choice(index: int) -> void:
return
var choice_id := str(choices[index].get("id", ""))
selected_choice = "" if selected_choice == choice_id else choice_id
if BlockSessionService.mode == "Timed":
if BlockSessionService.mode == "Timed" or BlockSessionService.mode == "Simulation":
current_answer = {}
next_button.text = "Submit"
if BlockSessionService.mode == "Simulation":
_save_simulation_current_response()
else:
next_button.text = "Submit"
_apply_saved_choice_state()
@@ -566,9 +628,12 @@ func _clear_selected_choice() -> void:
if selected_choice == "" or _is_correct_answer_locked():
return
selected_choice = ""
if BlockSessionService.mode == "Timed":
if BlockSessionService.mode == "Timed" or BlockSessionService.mode == "Simulation":
current_answer = {}
next_button.text = "Submit"
if BlockSessionService.mode == "Simulation":
_save_simulation_current_response()
else:
next_button.text = "Submit"
_apply_saved_choice_state()
@@ -635,7 +700,7 @@ func _choice_gui_input(event: InputEvent, index: int) -> void:
var mouse_event := event as InputEventMouseButton
if mouse_event == null or mouse_event.button_index != MOUSE_BUTTON_RIGHT or not mouse_event.pressed:
return
if not current_answer.is_empty():
if not current_answer.is_empty() and BlockSessionService.session_kind != "simulation":
return
var choices := _get_presented_choices()
@@ -653,10 +718,12 @@ func _choice_gui_input(event: InputEvent, index: int) -> void:
UISoundService.play_sound("eliminate")
_set_choice_button_text(index, choice)
_apply_saved_choice_state()
if BlockSessionService.session_kind == "simulation":
_save_simulation_current_response()
func _toggle_eliminate_selected() -> void:
if pending_correct_advance or followup_required or selected_choice == "" or not current_answer.is_empty() or _is_correct_answer_locked():
if pending_correct_advance or followup_required or selected_choice == "" or (not current_answer.is_empty() and BlockSessionService.session_kind != "simulation") or _is_correct_answer_locked():
return
var choices := _get_presented_choices()
for index in range(choices.size()):
@@ -668,9 +735,23 @@ func _toggle_eliminate_selected() -> void:
UISoundService.play_sound("eliminate")
_set_choice_button_text(index, choices[index])
_apply_saved_choice_state()
if BlockSessionService.session_kind == "simulation":
_save_simulation_current_response()
func _submit_or_next() -> void:
if BlockSessionService.session_kind == "simulation":
_save_simulation_current_response()
if BlockSessionService.current_index + 1 < BlockSessionService.active_questions.size():
BlockSessionService.move_next()
_load_question()
return
elif not simulation_submit_pending:
simulation_submit_pending = true
next_button.disabled = true
next_button.text = "Submitting..."
ExamSimulationService.submit_block()
return
if awaiting_next_question:
return
if pending_correct_advance:
@@ -1917,6 +1998,9 @@ func _is_correct_answer_locked() -> bool:
func _refresh_submit_state() -> void:
if next_button == null:
return
if BlockSessionService.session_kind == "simulation":
next_button.disabled = false
return
if pending_correct_advance or followup_required:
next_button.disabled = true
return
@@ -2489,12 +2573,14 @@ func _refresh_nav_palette() -> void:
Style.style_chip(button, Style.CYAN)
elif answer != null and bool(answer.get("flagged", false)):
Style.style_chip(button, Style.AMBER)
elif answer != null:
elif answer != null and str(answer.get("selected_choice_id", "")) != "":
Style.style_chip(button, Style.LIME)
else:
Style.style_chip(button, Color("596982"))
button.pressed.connect(func(target_index: int = index):
if BlockSessionService.mode == "Timed":
if BlockSessionService.mode == "Timed" or BlockSessionService.mode == "Simulation":
if BlockSessionService.mode == "Simulation":
_save_simulation_current_response()
BlockSessionService.move_to(target_index)
_load_question()
)