From fc3589da805da172df152e2783b7b8c7ffccf4c7 Mon Sep 17 00:00:00 2001 From: Adrien Ufferte Date: Mon, 19 Jan 2026 10:21:34 +0100 Subject: [PATCH] Adult check for boss defeat --- kalulu_localization.csv | 1 + resources/user/student_progression.gd | 32 ++++++++ sources/gardens/gardens.gd | 1 - sources/menus/adult_block/adult_boss_block.gd | 56 +++++++++++++ .../menus/adult_block/adult_boss_block.gd.uid | 1 + .../menus/adult_block/adult_boss_block.tscn | 80 +++++++++++++++++++ sources/minigames/base/base_minigame.gd | 10 +++ sources/minigames/fish/fish_minigame.gd | 12 +++ sources/minigames/fish/fish_minigame.tscn | 8 +- 9 files changed, 199 insertions(+), 2 deletions(-) create mode 100644 sources/menus/adult_block/adult_boss_block.gd create mode 100644 sources/menus/adult_block/adult_boss_block.gd.uid create mode 100644 sources/menus/adult_block/adult_boss_block.tscn diff --git a/kalulu_localization.csv b/kalulu_localization.csv index be70b106..4db7ba93 100644 --- a/kalulu_localization.csv +++ b/kalulu_localization.csv @@ -17,6 +17,7 @@ DEVICE_NUMBER,Appareil {number},Dispositivo {number},Dispositivo {number} SETTINGS,Paramètres,Configuración,Parâmetros ADULT_CHECK_PROMPT,"Veuillez confirmer que vous êtes un adulte ! Appuyez sur les touches {1}, {2} et {3}","Confirme que es un adulto! Seleccione {1}, {2} y {3}","Por favor, confirme que você é um adulto! Pressione as teclas {1}, {2} e {3}" +ADULT_BOSS_BLOCK_PROMPT,"Un adulte doit intervenir. Refaites le boss avec l'enfant et demandez-lui de prononcer les mots à voix haute pour vérifier qu'il sait bien lire. Si l'enfant échoue à lire correctement les mots, il est recommandé de revenir en arrière dans les niveaux pour reprendre les bases. Pour débloquer l'application, appuyez sur les touches {1}, {2} et {3}","Se necesita la intervención de un adulto. Rehaga el jefe con el niño y pídale que pronuncie las palabras en voz alta para comprobar que sabe leer. Si el niño no logra leer correctamente las palabras, se recomienda volver atrás en los niveles para repasar las bases. Para desbloquear la aplicación, presione las teclas {1}, {2} y {3}","É necessária a intervenção de um adulto. Refaça o chefe com a criança e peça que ela pronuncie as palavras em voz alta para verificar se sabe ler. Se a criança não conseguir ler corretamente as palavras, recomenda-se voltar nos níveis para reforçar as bases. Para desbloquear o aplicativo, pressione as teclas {1}, {2} e {3}" STAR,Étoile,Estrella,Estrela BAR,Barre,Barra,Barra CIRCLE,Cercle,Círculo,Círculo diff --git a/resources/user/student_progression.gd b/resources/user/student_progression.gd index c3f4b08c..9613243b 100644 --- a/resources/user/student_progression.gd +++ b/resources/user/student_progression.gd @@ -19,6 +19,8 @@ static var cached_boss_gate_lessons_total: int = -1 @export var highest_boss_defeated: int = 0: set(value): highest_boss_defeated = _sanitize_highest_boss(value) +@export var boss_failure_streak: int = 0 +@export var boss_blocked: bool = false @export var last_modified: String @@ -279,11 +281,41 @@ func boss_completed(lesson_number: int) -> bool: if lesson_number <= highest_boss_defeated: return false highest_boss_defeated = lesson_number + if boss_failure_streak != 0: + boss_failure_streak = 0 last_modified = Time.get_datetime_string_from_system(true) progression_changed.emit() return true +func register_boss_failure() -> bool: + if boss_blocked: + return true + boss_failure_streak = max(0, boss_failure_streak) + 1 + if boss_failure_streak >= 2: + boss_blocked = true + last_modified = Time.get_datetime_string_from_system(true) + progression_changed.emit() + return boss_blocked + + +func reset_boss_failure_streak() -> void: + if boss_failure_streak == 0: + return + boss_failure_streak = 0 + last_modified = Time.get_datetime_string_from_system(true) + progression_changed.emit() + + +func clear_boss_block() -> void: + if not boss_blocked and boss_failure_streak == 0: + return + boss_blocked = false + boss_failure_streak = 0 + last_modified = Time.get_datetime_string_from_system(true) + progression_changed.emit() + + func add_level_time(lesson_number: int, game_number: int, time_spent: int) -> void: Log.trace("StudentProgression: Add time to level %d, minigame %d. Time added: %s" % [lesson_number, game_number, time_spent]) if game_number > 2: diff --git a/sources/gardens/gardens.gd b/sources/gardens/gardens.gd index 939ae797..680637ba 100644 --- a/sources/gardens/gardens.gd +++ b/sources/gardens/gardens.gd @@ -361,7 +361,6 @@ func _ready() -> void: Log.error("Gardens: Ready: No data for student progression") await OpeningCurtain.open() return - await get_tree().process_frame _lock() diff --git a/sources/menus/adult_block/adult_boss_block.gd b/sources/menus/adult_block/adult_boss_block.gd new file mode 100644 index 00000000..1eb54760 --- /dev/null +++ b/sources/menus/adult_block/adult_boss_block.gd @@ -0,0 +1,56 @@ +extends Control + +signal unlocked() + +const SYMBOLS_NAMES: Dictionary[String, String] = { + "1": "STAR", + "2": "BAR", + "3": "CIRCLE", + "4": "PLUS", + "5": "SQUARE", + "6": "TRIANGLE", +} + +var password: String = "" + +@onready var code_keyboard: CodeKeyboard = %CodeKeyboard +@onready var password_label: Label = %PasswordLabel + + +func _ready() -> void: + process_mode = Node.PROCESS_MODE_ALWAYS + hide() + _reset_password() + + +func _reset_password() -> void: + password = str(TeacherSettings.AVAILABLE_CODES.pick_random()) + var password_array: PackedStringArray = password.split("") + password_label.text = tr("ADULT_BOSS_BLOCK_PROMPT").format( + { + "1": tr(SYMBOLS_NAMES[password_array[0]]), + "2": tr(SYMBOLS_NAMES[password_array[1]]), + "3": tr(SYMBOLS_NAMES[password_array[2]]) + } + ) + + +func _on_code_keyboard_password_entered(entered_password: String) -> void: + if password != entered_password: + code_keyboard.reset_password() + _reset_password() + return + + if UserDataManager.student_progression: + UserDataManager.student_progression.clear_boss_block() + + get_tree().paused = false + hide() + unlocked.emit() + + +func show_block() -> void: + get_tree().paused = true + code_keyboard.reset_password() + _reset_password() + show() diff --git a/sources/menus/adult_block/adult_boss_block.gd.uid b/sources/menus/adult_block/adult_boss_block.gd.uid new file mode 100644 index 00000000..f498c161 --- /dev/null +++ b/sources/menus/adult_block/adult_boss_block.gd.uid @@ -0,0 +1 @@ +uid://dxs13x1x36ukn diff --git a/sources/menus/adult_block/adult_boss_block.tscn b/sources/menus/adult_block/adult_boss_block.tscn new file mode 100644 index 00000000..3f400b7c --- /dev/null +++ b/sources/menus/adult_block/adult_boss_block.tscn @@ -0,0 +1,80 @@ +[gd_scene load_steps=5 format=3 uid="uid://b7rx6esglyd6c"] + +[ext_resource type="PackedScene" uid="uid://djduptonofbh4" path="res://sources/menus/components/code_keyboard.tscn" id="1_6q05s"] +[ext_resource type="Script" uid="uid://dxs13x1x36ukn" path="res://sources/menus/adult_block/adult_boss_block.gd" id="1_jf7p7"] +[ext_resource type="PackedScene" uid="uid://wdjp1sv55q4f" path="res://sources/menus/components/night_sky/night_sky.tscn" id="2_cdfx1"] +[ext_resource type="PackedScene" uid="uid://dxc82xcl7jncd" path="res://sources/menus/main/plants/palm.tscn" id="3_0u8tq"] + +[node name="AdultBossBlock" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_jf7p7") + +[node name="Background" parent="." instance=ExtResource("2_cdfx1")] +layout_mode = 1 + +[node name="Palm" parent="Background" instance=ExtResource("3_0u8tq")] +position = Vector2(1280, 1792) +scale = Vector2(2.5, 2.5) + +[node name="PanelContainer" type="PanelContainer" parent="."] +layout_mode = 1 +anchors_preset = -1 +anchor_left = 0.156 +anchor_top = 0.06 +anchor_right = 0.84375 +anchor_bottom = 0.39 +grow_horizontal = 2 +grow_vertical = 2 +theme_type_variation = &"PanelKalulu" + +[node name="LabelContainer" type="MarginContainer" parent="PanelContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 30 +theme_override_constants/margin_top = 20 +theme_override_constants/margin_right = 30 +theme_override_constants/margin_bottom = 20 + +[node name="PasswordLabel" type="Label" parent="PanelContainer/LabelContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(1500, 600) +layout_mode = 2 +size_flags_vertical = 1 +theme_override_font_sizes/font_size = 60 +text = "ADULT_BOSS_BLOCK_PROMPT" +autowrap_mode = 2 + +[node name="CodeKeyboard" parent="." instance=ExtResource("1_6q05s")] +unique_name_in_owner = true +layout_mode = 0 +anchors_preset = 0 +anchor_right = 0.0 +anchor_bottom = 0.0 +offset_right = 2560.0 +offset_bottom = 1800.0 +grow_horizontal = 1 +grow_vertical = 0 + +[node name="PasswordVisualizer" parent="CodeKeyboard" index="0"] +offset_left = -376.0 +offset_top = -164.0 +offset_right = 376.0 +offset_bottom = 56.0 +key_size = 200 + +[node name="Icon1" parent="CodeKeyboard/PasswordVisualizer/Panel1" index="0"] +custom_minimum_size = Vector2(200, 200) + +[node name="Icon2" parent="CodeKeyboard/PasswordVisualizer/Panel2" index="0"] +custom_minimum_size = Vector2(200, 200) + +[node name="Icon3" parent="CodeKeyboard/PasswordVisualizer/Panel3" index="0"] +custom_minimum_size = Vector2(200, 200) + +[connection signal="password_entered" from="CodeKeyboard" to="." method="_on_code_keyboard_password_entered"] + +[editable path="CodeKeyboard"] diff --git a/sources/minigames/base/base_minigame.gd b/sources/minigames/base/base_minigame.gd index da57d251..fdb4cdb4 100644 --- a/sources/minigames/base/base_minigame.gd +++ b/sources/minigames/base/base_minigame.gd @@ -211,6 +211,8 @@ func _win() -> void: if gardens_data.has("boss_gate_lesson"): gardens_data.boss_completed = UserDataManager.student_progression.boss_completed(gardens_data.boss_gate_lesson as int) gardens_data.first_clear = gardens_data.boss_completed + if not is_final_boss: + UserDataManager.student_progression.reset_boss_failure_streak() else: gardens_data.first_clear = UserDataManager.student_progression.game_completed(lesson_nb, minigame_number) @@ -272,6 +274,14 @@ func _lose() -> void: minigame_ui.play_kalulu_speech(lose_kalulu_speech) await minigame_ui.kalulu_speech_ended + if gardens_data.has("boss_gate_lesson") and not is_final_boss and UserDataManager.student_progression: + var is_blocked: bool = UserDataManager.student_progression.register_boss_failure() + if is_blocked: + if has_method("show_adult_block"): + call("show_adult_block") + else: + Log.error("BaseMinigame: Adult block requested but no handler exists for %s" % Type.keys()[minigame_name]) + return _reset() diff --git a/sources/minigames/fish/fish_minigame.gd b/sources/minigames/fish/fish_minigame.gd index ecaa973c..ad4f7ca6 100644 --- a/sources/minigames/fish/fish_minigame.gd +++ b/sources/minigames/fish/fish_minigame.gd @@ -31,6 +31,7 @@ var tutorial_count: int = 0 @onready var progress_gauge: PercentMarginContainer = %ProgressionGaugePercentMarginContainer @onready var progress_gauge_goal: PercentMarginContainer = %ProgressionGaugeGoalPercentMarginContainer2 @onready var progress_gauge_internal: NinePatchRect = %ProgressionGaugeInternal +@onready var adult_block: Control = %AdultBossBlock func _fish_get_drag_data(_at_position: Vector2) -> Variant: @@ -42,6 +43,8 @@ func _fish_get_drag_data(_at_position: Vector2) -> Variant: func _ready() -> void: super() + if adult_block and adult_block.has_signal("unlocked"): + adult_block.unlocked.connect(_on_adult_block_unlocked) fish_start_zone.set_drag_forwarding(_fish_get_drag_data, Callable(), Callable()) (beacon1 as Control).set_drag_forwarding(Callable(), _beacon_can_drop_data, _beacon1_drop_data) (beacon2 as Control).set_drag_forwarding(Callable(), _beacon_can_drop_data, _beacon2_drop_data) @@ -201,3 +204,12 @@ func _ensure_words_to_present_count(target_count: int) -> void: words_to_present.append_array(base_pool) if words_to_present.size() > target_count: words_to_present.resize(target_count) + + +func show_adult_block() -> void: + if adult_block and adult_block.has_method("show_block"): + adult_block.call("show_block") + + +func _on_adult_block_unlocked() -> void: + await _reset() diff --git a/sources/minigames/fish/fish_minigame.tscn b/sources/minigames/fish/fish_minigame.tscn index 20410b01..7e8adecd 100644 --- a/sources/minigames/fish/fish_minigame.tscn +++ b/sources/minigames/fish/fish_minigame.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=70 format=3 uid="uid://df5derhil64ca"] +[gd_scene load_steps=71 format=3 uid="uid://df5derhil64ca"] [ext_resource type="PackedScene" uid="uid://8awe4usnucyv" path="res://sources/minigames/base/base_minigame.tscn" id="1_itrpi"] [ext_resource type="Script" uid="uid://dgodk76761a1n" path="res://sources/minigames/fish/fish_minigame.gd" id="2_68jip"] @@ -17,6 +17,7 @@ [ext_resource type="Texture2D" uid="uid://disgg14rdmp81" path="res://assets/minigames/fish/textplank.png" id="13_3p0ue"] [ext_resource type="Script" uid="uid://d5qkkg88431w" path="res://sources/utils/percent_margin_container.gd" id="14_b3ilp"] [ext_resource type="LabelSettings" uid="uid://bguqnhiblwick" path="res://resources/themes/minigames_label_settings.tres" id="17_47luc"] +[ext_resource type="PackedScene" uid="uid://b7rx6esglyd6c" path="res://sources/menus/adult_block/adult_boss_block.tscn" id="18_4v0wd"] [sub_resource type="Environment" id="Environment_5bcb7"] background_mode = 3 @@ -724,5 +725,10 @@ text = "a" label_settings = ExtResource("17_47luc") horizontal_alignment = 1 +[node name="AdultBossBlock" parent="." index="5" instance=ExtResource("18_4v0wd")] +unique_name_in_owner = true +visible = false +layout_mode = 1 + [connection signal="beacon_fish_dropped" from="." to="." method="_on_beacon_fish_dropped"] [connection signal="animation_finished" from="GameRoot/AspectRatioContainer/Fish/FishAnimatedSprite" to="." method="_on_fish_animated_sprite_animation_finished"]