From 233cc26452012a1d9304d83280e0d31206024adb Mon Sep 17 00:00:00 2001 From: Denis Defolie Date: Sun, 11 Oct 2026 21:50:39 +0200 Subject: [PATCH] Fix overlapping mission markers on the world map MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Manual test: "La clairière" and "La cabane" overlapped on screen. Their fractional positions (0.8,0.65) and (0.92,0.78) were too close given the marker size at the project's default window size — exactly the risk flagged but unverified in the previous commit. Repositioned all 9 missions (not just the new ones) onto a regular 3x3 grid with 0.35 spacing, well clear of marker dimensions at the default resolution. Added a headless regression test (tests/data/test_mission_positions.gd) that fails if any two missions are closer than a 0.3 fractional gap on both axes — a data-level guard, not a substitute for visually checking actual on-screen layout. Co-Authored-By: Claude Sonnet 5 --- data/missions/mission_01_pre_des_mots.json | 2 +- .../missions/mission_02_pont_des_phrases.json | 2 +- data/missions/mission_03_clairiere_echo.json | 2 +- .../mission_04_ruche_qui_multiplie.json | 2 +- .../mission_05_verger_paniers_pleins.json | 2 +- .../missions/mission_06_mare_grenouilles.json | 2 +- .../mission_07_sentier_bons_compagnons.json | 2 +- .../mission_08_cabane_etiquettes.json | 2 +- .../mission_09_rocher_qui_repete.json | 2 +- docs/specs/decisions.md | 10 +++++++ docs/testing/manual-checklist.md | 2 +- tests/data/test_mission_positions.gd | 28 +++++++++++++++++++ tests/data/test_mission_positions.gd.uid | 1 + 13 files changed, 49 insertions(+), 10 deletions(-) create mode 100644 tests/data/test_mission_positions.gd create mode 100644 tests/data/test_mission_positions.gd.uid diff --git a/data/missions/mission_01_pre_des_mots.json b/data/missions/mission_01_pre_des_mots.json index eda4174..5a08353 100644 --- a/data/missions/mission_01_pre_des_mots.json +++ b/data/missions/mission_01_pre_des_mots.json @@ -4,6 +4,6 @@ "narrative_context": "Les lutins du pré n'arrivent plus à accorder leurs mots entre eux. Aide-les à retrouver les bonnes terminaisons pour que leurs phrases retrouvent leur sens !", "rule_id": "regle_accord_adjectif", "exercise_type": "multiple_choice", - "map_position": { "x": 0.2, "y": 0.7 }, + "map_position": { "x": 0.15, "y": 0.85 }, "review_status": "draft" } diff --git a/data/missions/mission_02_pont_des_phrases.json b/data/missions/mission_02_pont_des_phrases.json index 8f6b132..53f44f8 100644 --- a/data/missions/mission_02_pont_des_phrases.json +++ b/data/missions/mission_02_pont_des_phrases.json @@ -4,6 +4,6 @@ "narrative_context": "Un pont de pierres portant chacune un mot s'est abîmé. Repère la pierre fautive et corrige-la pour que les voyageurs puissent traverser en toute sécurité.", "rule_id": "regle_accord_adjectif", "exercise_type": "sentence_correction", - "map_position": { "x": 0.5, "y": 0.4 }, + "map_position": { "x": 0.5, "y": 0.5 }, "review_status": "draft" } diff --git a/data/missions/mission_03_clairiere_echo.json b/data/missions/mission_03_clairiere_echo.json index 19aa2fc..e4bcd11 100644 --- a/data/missions/mission_03_clairiere_echo.json +++ b/data/missions/mission_03_clairiere_echo.json @@ -4,6 +4,6 @@ "narrative_context": "L'écho de la clairière répète fidèlement les phrases qu'on lui confie, mais seulement si elles sont bien écrites. Dicte-lui la phrase sans faute pour l'entendre résonner.", "rule_id": "regle_accord_adjectif", "exercise_type": "dictation", - "map_position": { "x": 0.8, "y": 0.65 }, + "map_position": { "x": 0.85, "y": 0.85 }, "review_status": "draft" } diff --git a/data/missions/mission_04_ruche_qui_multiplie.json b/data/missions/mission_04_ruche_qui_multiplie.json index 97b1725..d903472 100644 --- a/data/missions/mission_04_ruche_qui_multiplie.json +++ b/data/missions/mission_04_ruche_qui_multiplie.json @@ -4,6 +4,6 @@ "narrative_context": "Dans la ruche, chaque mot veut savoir s'il doit rester seul ou se multiplier. Aide les abeilles a choisir la bonne forme avant que le miel ne deborde !", "rule_id": "regle_pluriel_nom", "exercise_type": "multiple_choice", - "map_position": { "x": 0.12, "y": 0.15 }, + "map_position": { "x": 0.15, "y": 0.15 }, "review_status": "draft" } diff --git a/data/missions/mission_05_verger_paniers_pleins.json b/data/missions/mission_05_verger_paniers_pleins.json index e594e91..aeb1120 100644 --- a/data/missions/mission_05_verger_paniers_pleins.json +++ b/data/missions/mission_05_verger_paniers_pleins.json @@ -4,6 +4,6 @@ "narrative_context": "Les paniers du verger portent des etiquettes, mais certaines se sont trompees de nombre. Trouve l'etiquette fautive et corrige-la avant la recolte.", "rule_id": "regle_pluriel_nom", "exercise_type": "sentence_correction", - "map_position": { "x": 0.38, "y": 0.12 }, + "map_position": { "x": 0.5, "y": 0.15 }, "review_status": "draft" } diff --git a/data/missions/mission_06_mare_grenouilles.json b/data/missions/mission_06_mare_grenouilles.json index 4e471ef..05e5243 100644 --- a/data/missions/mission_06_mare_grenouilles.json +++ b/data/missions/mission_06_mare_grenouilles.json @@ -4,6 +4,6 @@ "narrative_context": "Les grenouilles de la mare chantent une phrase, une seule fois. Ecoute-les bien et ecris exactement ce qu'elles chantent.", "rule_id": "regle_pluriel_nom", "exercise_type": "dictation", - "map_position": { "x": 0.65, "y": 0.15 }, + "map_position": { "x": 0.85, "y": 0.15 }, "review_status": "draft" } diff --git a/data/missions/mission_07_sentier_bons_compagnons.json b/data/missions/mission_07_sentier_bons_compagnons.json index b378b78..98c601c 100644 --- a/data/missions/mission_07_sentier_bons_compagnons.json +++ b/data/missions/mission_07_sentier_bons_compagnons.json @@ -4,6 +4,6 @@ "narrative_context": "Chaque mot du sentier cherche son compagnon : le bon petit mot qui l'accompagne toujours. Aide-les a se retrouver sans se tromper.", "rule_id": "regle_accord_determinant", "exercise_type": "multiple_choice", - "map_position": { "x": 0.9, "y": 0.3 }, + "map_position": { "x": 0.15, "y": 0.5 }, "review_status": "draft" } diff --git a/data/missions/mission_08_cabane_etiquettes.json b/data/missions/mission_08_cabane_etiquettes.json index 0675a5b..b115f88 100644 --- a/data/missions/mission_08_cabane_etiquettes.json +++ b/data/missions/mission_08_cabane_etiquettes.json @@ -4,6 +4,6 @@ "narrative_context": "Dans la cabane, les etiquettes des objets ont ete melangees par le vent. Repere celle qui ne va pas et remets le bon petit mot devant.", "rule_id": "regle_accord_determinant", "exercise_type": "sentence_correction", - "map_position": { "x": 0.92, "y": 0.78 }, + "map_position": { "x": 0.85, "y": 0.5 }, "review_status": "draft" } diff --git a/data/missions/mission_09_rocher_qui_repete.json b/data/missions/mission_09_rocher_qui_repete.json index 2e1f247..4da84c7 100644 --- a/data/missions/mission_09_rocher_qui_repete.json +++ b/data/missions/mission_09_rocher_qui_repete.json @@ -4,6 +4,6 @@ "narrative_context": "Le vieux rocher de la foret repete une phrase, une seule fois, avant de se rendormir. Ecoute bien et ecris-la sans faute.", "rule_id": "regle_accord_determinant", "exercise_type": "dictation", - "map_position": { "x": 0.45, "y": 0.88 }, + "map_position": { "x": 0.5, "y": 0.85 }, "review_status": "draft" } diff --git a/docs/specs/decisions.md b/docs/specs/decisions.md index bf935b7..34cb164 100644 --- a/docs/specs/decisions.md +++ b/docs/specs/decisions.md @@ -234,3 +234,13 @@ Ajouts à l'arborescence proposée : - Audio des 6 nouvelles dictées généré avec `make generate-dictation-audio` (voix Piper, point 20), crédité dans `assets/CREDITS.md`. **Non vérifié** : la disposition des 9 lieux sur la carte (positions fractionnelles choisies à l'écart les unes des autres, mais jamais vues à l'écran) — à vérifier à la main, voir `docs/testing/manual-checklist.md`. + +## 26. Bug signalé manuellement : deux marqueurs de mission se chevauchaient + +**Symptôme** : « La clairière » (`mission_03_clairiere_echo`) et « La cabane » (`mission_08_cabane_etiquettes`) se chevauchaient sur la carte — exactement le risque signalé sans être vérifié au point 25. + +**Cause** : leurs positions fractionnelles (0.8, 0.65) et (0.92, 0.78) étaient trop proches pour la taille des marqueurs (`MARKER_HALF_WIDTH`/`MARKER_HALF_HEIGHT` dans `world_map.gd`) à la résolution par défaut du projet (aucune taille de fenêtre fixée dans `project.godot`, donc le viewport par defaut de Godot, 1152x648). + +**Correction** : les 9 missions sont replacées sur une grille régulière 3x3 (x, y ∈ {0.15, 0.5, 0.85}), avec un écart de 0.35 entre positions adjacentes — largement supérieur à la largeur/hauteur des marqueurs rapportée à la fenêtre par défaut. Les trois missions historiques (`mission_01` à `_03`) sont déplacées elles aussi, pas seulement les nouvelles, pour que la grille soit cohérente partout plutôt que patchée au cas par cas. + +**Garde-fou** : nouveau test headless (`tests/data/test_mission_positions.gd`) qui échoue si deux missions ont un écart fractionnel inférieur à 0.3 sur les deux axes — ne remplace pas une vérification visuelle (l'écart réel en pixels dépend de la taille de fenêtre réelle, non fixée), mais attrape la classe d'erreur qui vient de se produire avant qu'elle ne soit signalée à la main. diff --git a/docs/testing/manual-checklist.md b/docs/testing/manual-checklist.md index 9c7ce2d..ddcedd5 100644 --- a/docs/testing/manual-checklist.md +++ b/docs/testing/manual-checklist.md @@ -7,7 +7,7 @@ Tout ce qui suit ne peut pas être vérifié sans écran (rendu, mise en page, a 1. `make run`. 2. Écran de sélection de profil vide → bouton « Nouveau profil » visible et cliquable. 3. Création de profil : le bouton de validation refuse un prénom vide avec un message visible (pas seulement une absence de réaction) ; CE1 est sélectionné par défaut ; le bouton CE2 bascule correctement l'état visuel des deux boutons (ils doivent se comporter comme des boutons radio, un seul actif à la fois). -4. Après création, arrivée sur la carte (« La forêt des accords ») avec **neuf** lieux visibles (point 25 de `decisions.md` : extension au-delà du prototype §15) et positionnés à des endroits différents, **jamais vérifié à l'écran avec ce nombre de lieux** — vérifier particulièrement qu'aucun marqueur ne se superpose à un autre, surtout sur petit écran. +4. Après création, arrivée sur la carte (« La forêt des accords ») avec **neuf** lieux visibles (point 25 de `decisions.md` : extension au-delà du prototype §15), disposés sur une grille 3x3 (point 26 : deux missions se chevauchaient avant ce repositionnement) — vérifier qu'aucun marqueur ne se superpose à un autre, y compris en redimensionnant la fenêtre vers une taille réduite. 5. Chaque lieu affiche un intitulé et un état (« À découvrir », etc.) lisible. 6. Clic sur un lieu → introduction de la mission : le texte narratif s'affiche en entier (pas tronqué), Plume (le hibou) affiche la même phrase dans sa bulle. 7. « Commencer la mission » → panneau de règle : énoncé et exemple lisibles, bouton « C'est parti ! » visible. diff --git a/tests/data/test_mission_positions.gd b/tests/data/test_mission_positions.gd new file mode 100644 index 0000000..b04e66b --- /dev/null +++ b/tests/data/test_mission_positions.gd @@ -0,0 +1,28 @@ +extends GutTest +## Garde-fou contre le chevauchement de marqueurs sur la carte (signale +## manuellement : "La clairiere" et "La cabane" se chevauchaient, voir +## docs/specs/decisions.md). scenes/world/world_map.gd place chaque marqueur +## a une position fractionnelle (map_position.x/y de data/missions/*.json) ; +## sans ecart suffisant entre deux missions, leurs boutons se recouvrent a +## l'ecran. Verifie l'ecart plutot que le rendu reel (non testable sans +## ecran), avec une marge volontairement un peu permissive par rapport a la +## grille 0.35 utilisee dans les donnees. + +const MIN_FRACTIONAL_GAP := 0.3 + + +func test_mission_markers_do_not_overlap() -> void: + var missions := ExerciseEngine.get_missions() + for i in missions.size(): + for j in range(i + 1, missions.size()): + var a: Dictionary = missions[i]["map_position"] + var b: Dictionary = missions[j]["map_position"] + var dx: float = abs(float(a["x"]) - float(b["x"])) + var dy: float = abs(float(a["y"]) - float(b["y"])) + assert_true( + dx >= MIN_FRACTIONAL_GAP or dy >= MIN_FRACTIONAL_GAP, + ( + "%s et %s sont trop proches (dx=%s, dy=%s)" + % [missions[i]["mission_id"], missions[j]["mission_id"], dx, dy] + ), + ) diff --git a/tests/data/test_mission_positions.gd.uid b/tests/data/test_mission_positions.gd.uid new file mode 100644 index 0000000..40a383a --- /dev/null +++ b/tests/data/test_mission_positions.gd.uid @@ -0,0 +1 @@ +uid://gyps56f0k7tv