Add wrong answer follow-up prompts
This commit is contained in:
@@ -71,6 +71,10 @@ func _build_ui() -> void:
|
||||
user_label = Label.new()
|
||||
Style.set_label(user_label, 15, Style.CYAN)
|
||||
user_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||||
user_label.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
user_label.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND
|
||||
user_label.tooltip_text = "Open profile"
|
||||
user_label.gui_input.connect(_on_user_label_gui_input)
|
||||
header.add_child(user_label)
|
||||
|
||||
var screens_container := MarginContainer.new()
|
||||
@@ -246,3 +250,14 @@ func _open_profile_from_menu() -> void:
|
||||
if BlockSessionService.block_started:
|
||||
BlockSessionService.finish_block()
|
||||
show_screen("profile")
|
||||
|
||||
|
||||
func _on_user_label_gui_input(event: InputEvent) -> void:
|
||||
if not AuthState.is_logged_in:
|
||||
return
|
||||
var mouse_event := event as InputEventMouseButton
|
||||
if mouse_event == null or mouse_event.button_index != MOUSE_BUTTON_LEFT or not mouse_event.pressed:
|
||||
return
|
||||
_close_app_menu()
|
||||
if current_screen_name != "profile":
|
||||
show_screen("profile")
|
||||
|
||||
@@ -7,8 +7,13 @@ const CORRECT_ADVANCE_DELAY_SECONDS := 0.6
|
||||
var main: Control
|
||||
var choice_buttons: Array[Button] = []
|
||||
var choice_percent_labels: Array[Label] = []
|
||||
var choice_followup_panels: Array[PanelContainer] = []
|
||||
var choice_followup_contents: Array[VBoxContainer] = []
|
||||
var eliminated: Array = []
|
||||
var locked_wrong_choice_ids: Array = []
|
||||
var active_followup_choice_id := ""
|
||||
var followup_required := false
|
||||
var followup_answered := false
|
||||
var flagged := false
|
||||
var selected_choice := ""
|
||||
var current_answer := {}
|
||||
@@ -159,6 +164,23 @@ func _ready() -> void:
|
||||
button.add_child(percent_label)
|
||||
choice_percent_labels.append(percent_label)
|
||||
|
||||
var followup_panel := PanelContainer.new()
|
||||
followup_panel.visible = false
|
||||
followup_panel.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
followup_panel.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||
followup_panel.offset_left = 16
|
||||
followup_panel.offset_top = 42
|
||||
followup_panel.offset_right = -16
|
||||
followup_panel.offset_bottom = -10
|
||||
followup_panel.add_theme_stylebox_override("panel", Style.panel_box(Color("26151a"), Style.RED))
|
||||
button.add_child(followup_panel)
|
||||
choice_followup_panels.append(followup_panel)
|
||||
|
||||
var followup_content := VBoxContainer.new()
|
||||
followup_content.add_theme_constant_override("separation", 6)
|
||||
followup_panel.add_child(followup_content)
|
||||
choice_followup_contents.append(followup_content)
|
||||
|
||||
question_content.add_child(button)
|
||||
|
||||
explanation_label = RichTextLabel.new()
|
||||
@@ -242,6 +264,7 @@ func _load_question() -> void:
|
||||
_reset_wrong_feedback()
|
||||
eliminated.clear()
|
||||
locked_wrong_choice_ids.clear()
|
||||
_clear_followups()
|
||||
flagged = false
|
||||
selected_choice = ""
|
||||
current_answer = {}
|
||||
@@ -300,7 +323,7 @@ func _load_question() -> void:
|
||||
|
||||
|
||||
func _select_choice(index: int) -> void:
|
||||
if pending_correct_advance:
|
||||
if pending_correct_advance or followup_required:
|
||||
return
|
||||
var question := BlockSessionService.get_current_question()
|
||||
var choices: Array = question.get("choices", [])
|
||||
@@ -335,7 +358,7 @@ func _skip_current_question() -> void:
|
||||
|
||||
|
||||
func _choice_gui_input(event: InputEvent, index: int) -> void:
|
||||
if pending_correct_advance:
|
||||
if pending_correct_advance or followup_required:
|
||||
return
|
||||
var mouse_event := event as InputEventMouseButton
|
||||
if mouse_event == null or mouse_event.button_index != MOUSE_BUTTON_RIGHT or not mouse_event.pressed:
|
||||
@@ -361,7 +384,7 @@ func _choice_gui_input(event: InputEvent, index: int) -> void:
|
||||
|
||||
|
||||
func _toggle_eliminate_selected() -> void:
|
||||
if pending_correct_advance or selected_choice == "" or not current_answer.is_empty():
|
||||
if pending_correct_advance or followup_required or selected_choice == "" or not current_answer.is_empty():
|
||||
return
|
||||
var question := BlockSessionService.get_current_question()
|
||||
var choices: Array = question.get("choices", [])
|
||||
@@ -395,9 +418,13 @@ func _submit_or_next() -> void:
|
||||
_trigger_correct_feedback()
|
||||
return
|
||||
_lock_wrong_choice(attempted_choice)
|
||||
_show_wrong_answer_followup(attempted_choice)
|
||||
selected_choice = ""
|
||||
current_answer = {}
|
||||
next_button.text = "Submit"
|
||||
next_button.disabled = true
|
||||
if skip_button != null:
|
||||
skip_button.disabled = true
|
||||
_trigger_wrong_feedback()
|
||||
_apply_saved_choice_state()
|
||||
return
|
||||
@@ -666,10 +693,17 @@ func _apply_saved_choice_state() -> void:
|
||||
if pending_correct_advance:
|
||||
_apply_correct_answer_preview()
|
||||
|
||||
if followup_required:
|
||||
for index in range(choices.size()):
|
||||
var id := str(choices[index].get("id", ""))
|
||||
choice_buttons[index].disabled = id != active_followup_choice_id
|
||||
|
||||
|
||||
func _set_choice_button_text(index: int, choice: Dictionary) -> void:
|
||||
var choice_id := str(choice.get("id", ""))
|
||||
var choice_text := str(choice.get("text", ""))
|
||||
var has_visible_followup := index < choice_followup_panels.size() and choice_followup_panels[index].visible
|
||||
choice_buttons[index].custom_minimum_size = Vector2(0, 190) if has_visible_followup or choice_id == active_followup_choice_id else Vector2(0, 64)
|
||||
_set_choice_percent_label(index, "")
|
||||
if locked_wrong_choice_ids.has(choice_id):
|
||||
var percentage := _get_choice_selection_percentage(choice_id)
|
||||
@@ -729,6 +763,127 @@ func _on_choice_percentages_updated(question_id: String, percentages: Dictionary
|
||||
_apply_saved_choice_state()
|
||||
|
||||
|
||||
func _show_wrong_answer_followup(choice_id: String) -> void:
|
||||
active_followup_choice_id = choice_id
|
||||
followup_required = true
|
||||
followup_answered = false
|
||||
var index := _choice_index_for_id(choice_id)
|
||||
if index < 0:
|
||||
_unlock_after_followup()
|
||||
return
|
||||
_build_followup_panel(index, choice_id)
|
||||
|
||||
|
||||
func _build_followup_panel(index: int, choice_id: String) -> void:
|
||||
_clear_followup_content(index)
|
||||
var panel := choice_followup_panels[index]
|
||||
var content := choice_followup_contents[index]
|
||||
var followup := _get_followup_for_choice(choice_id)
|
||||
|
||||
var prompt := Label.new()
|
||||
prompt.text = str(followup.get("prompt", "What clue in the stem should make this answer less likely?"))
|
||||
prompt.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
Style.set_label(prompt, 14, Style.TEXT)
|
||||
content.add_child(prompt)
|
||||
|
||||
var choices: Array = followup.get("choices", [])
|
||||
if choices.is_empty():
|
||||
choices = [{"id": "A", "text": "Review the stem clue"}]
|
||||
followup["correct_choice_id"] = "A"
|
||||
followup["teaching_point"] = "Review the stem for the clue that makes this answer less appropriate before trying again."
|
||||
for option in choices:
|
||||
if typeof(option) != TYPE_DICTIONARY:
|
||||
continue
|
||||
var option_button := Button.new()
|
||||
option_button.text = "%s. %s" % [str(option.get("id", "")), str(option.get("text", ""))]
|
||||
option_button.alignment = HORIZONTAL_ALIGNMENT_LEFT
|
||||
option_button.custom_minimum_size = Vector2(0, 32)
|
||||
Style.style_chip(option_button, Style.CYAN)
|
||||
option_button.pressed.connect(func(option_id := str(option.get("id", "")), selected_button := option_button, followup_data := followup):
|
||||
_answer_followup(option_id, selected_button, followup_data)
|
||||
)
|
||||
content.add_child(option_button)
|
||||
|
||||
panel.visible = true
|
||||
|
||||
|
||||
func _answer_followup(option_id: String, selected_button: Button, followup: Dictionary) -> void:
|
||||
if not followup_required or followup_answered:
|
||||
return
|
||||
followup_answered = true
|
||||
var correct_id := str(followup.get("correct_choice_id", ""))
|
||||
var is_correct := option_id == correct_id
|
||||
Style.style_chip(selected_button, Style.LIME if is_correct else Style.RED)
|
||||
|
||||
var index := _choice_index_for_id(active_followup_choice_id)
|
||||
if index >= 0:
|
||||
for child in choice_followup_contents[index].get_children():
|
||||
if child is Button:
|
||||
child.disabled = true
|
||||
|
||||
var teaching := Label.new()
|
||||
teaching.text = str(followup.get("teaching_point", "Review the stem clue before choosing again."))
|
||||
teaching.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
Style.set_label(teaching, 14, Style.AMBER)
|
||||
choice_followup_contents[index].add_child(teaching)
|
||||
|
||||
_unlock_after_followup()
|
||||
|
||||
|
||||
func _unlock_after_followup() -> void:
|
||||
followup_required = false
|
||||
next_button.disabled = false
|
||||
if skip_button != null:
|
||||
skip_button.disabled = false
|
||||
_apply_saved_choice_state()
|
||||
|
||||
|
||||
func _clear_followups() -> void:
|
||||
active_followup_choice_id = ""
|
||||
followup_required = false
|
||||
followup_answered = false
|
||||
for index in range(choice_followup_panels.size()):
|
||||
_clear_followup_content(index)
|
||||
choice_followup_panels[index].visible = false
|
||||
|
||||
|
||||
func _clear_followup_content(index: int) -> void:
|
||||
if index < 0 or index >= choice_followup_contents.size():
|
||||
return
|
||||
for child in choice_followup_contents[index].get_children():
|
||||
choice_followup_contents[index].remove_child(child)
|
||||
child.queue_free()
|
||||
|
||||
|
||||
func _get_followup_for_choice(choice_id: String) -> Dictionary:
|
||||
var question := BlockSessionService.get_current_question()
|
||||
var followups: Dictionary = question.get("wrong_answer_followups", {})
|
||||
if followups.has(choice_id) and typeof(followups[choice_id]) == TYPE_DICTIONARY:
|
||||
var authored: Dictionary = followups[choice_id]
|
||||
return {
|
||||
"prompt": str(authored.get("prompt", "What clue in the stem should make this answer less likely?")),
|
||||
"choices": authored.get("choices", []),
|
||||
"correct_choice_id": str(authored.get("correctChoiceId", authored.get("correct_choice_id", ""))),
|
||||
"teaching_point": str(authored.get("teachingPoint", authored.get("teaching_point", "Review the stem clue before choosing again.")))
|
||||
}
|
||||
|
||||
return {
|
||||
"prompt": "What clue in the stem should make this answer less likely?",
|
||||
"choices": [{"id": "A", "text": "Review the stem clue"}],
|
||||
"correct_choice_id": "A",
|
||||
"teaching_point": "Review the stem for the clue that makes this answer less appropriate before trying again."
|
||||
}
|
||||
|
||||
|
||||
func _choice_index_for_id(choice_id: String) -> int:
|
||||
var question := BlockSessionService.get_current_question()
|
||||
var choices: Array = question.get("choices", [])
|
||||
for index in range(choices.size()):
|
||||
if str(choices[index].get("id", "")) == choice_id:
|
||||
return index
|
||||
return -1
|
||||
|
||||
|
||||
func _on_question_rating_updated(question_id: String, question_elo: int, _rating: Dictionary) -> void:
|
||||
if str(BlockSessionService.get_current_question().get("id", "")) != question_id:
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user