Quiz jouable : budget de récompense, boîte à quiz, widget score
- Récompense d'une quête = budget MAXIMUM pour ses questions : la somme des points des "❓ Question" ne peut plus dépasser recompense_score (routes/quests/quest_dialogues.py à l'enregistrement des dialogues, quest_update.py si on abaisse la récompense sous ce qui est déjà réparti) — message d'erreur explicite (400) dans les deux sens, affiché via alert() côté éditeur (quest-editor.js), jamais enregistré silencieusement dans un état incohérent. - Nombre de choix par question plafonné à 4 (au lieu de 8). - Deux nouveaux widgets "🖥️ Interface" (mêmes fondations que "💬 Boîte de dialogue" — screens/rendering/dialogue_box_style.py, même panneau "🎨 Style") : - "❓ Boîte à quiz" : affiche la question et ses choix, sans pied (une question se résout au clic sur un choix, pas de "Suivant"). - "🏆 Score" : affiche en continu les points gagnés, TOUJOURS visible une fois posé (contrairement aux boîtes de dialogue/quiz, masquées par défaut). - Moteur d'exécution (static/js/play/dialogue-box-controller.js, refonte) : une conversation de quête alterne maintenant répliques (boîte de dialogue) et questions (boîte à quiz) selon le type de chaque ligne. Bonne réponse -> crédite le score (compteur runtime dédié, jamais lié au score du moteur document) et avance ; mauvaise réponse -> rien, la question reste affichée pour réessayer ; aucune boîte à quiz posée -> la question est ignorée plutôt que de bloquer la conversation. Le dialogue "en_cours" épuisé (donc, s'il contenait des questions, toutes répondues) fait passer la quête "terminee" (en mémoire seulement, comme le reste de ce système — voir accepter/ refuser une offre de quête) : le joueur peut alors quitter la scène normalement, ces widgets n'étant jamais des obstacles. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
e972acf9e6
commit
b5546df044
@@ -152,3 +152,66 @@ def test_list_named_scene_object_names_is_alphabetical_deduped_and_includes_any_
|
||||
screens.set_scene_object_name(slug, dialogue_box_id, "Boîte nommée par erreur")
|
||||
|
||||
assert screens.list_named_scene_object_names(slug) == ["Anna", "Un coffre", "Zed le forgeron"]
|
||||
|
||||
|
||||
# ---------- Widgets "❓ Boîte à quiz" / "🏆 Score" ----------
|
||||
|
||||
def test_add_quiz_box_has_sensible_default_size(tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
assert obj["kind"] == "quiz_box"
|
||||
assert obj["width"] == 340
|
||||
assert obj["height"] == 240
|
||||
|
||||
|
||||
def test_add_score_widget_has_sensible_default_size(tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="score_widget")
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
assert obj["kind"] == "score_widget"
|
||||
assert obj["width"] == 140
|
||||
assert obj["height"] == 50
|
||||
|
||||
|
||||
def test_render_quiz_box_has_header_and_choices(tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
assert 'data-quiz-role="header"' in html
|
||||
assert 'data-quiz-role="choices"' in html
|
||||
assert f'data-object-id="{obj_id}"' in html
|
||||
assert "quizBoxFooter" not in html # pas de pied, une question se résout au clic
|
||||
|
||||
|
||||
def test_render_score_widget_shows_zero_by_default(tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="score_widget")
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
assert 'data-score-role="value"' in html
|
||||
assert f'data-object-id="{obj_id}"' in html
|
||||
|
||||
|
||||
def test_quiz_box_and_score_widget_share_the_same_style_system_as_dialogue_box(tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
quiz_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
style = screens.set_scene_object_dialogue_box_style(slug, quiz_id, {"header_bg": "#abcdef"})
|
||||
assert style["header_bg"] == "#abcdef"
|
||||
obj = screens.get_scene_object(slug, quiz_id)
|
||||
assert screens.resolve_dialogue_box_style(obj)["header_bg"] == "#abcdef"
|
||||
|
||||
|
||||
def test_quiz_box_and_score_widget_excluded_from_collision_rules_payload(tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.add_scene_object(slug, screen_id, kind="score_widget")
|
||||
payload = screens.full_game_payload(slug)
|
||||
scene = next(s for s in payload["screens"] if s["id"] == screen_id)
|
||||
quiz = next(o for o in scene["elements"] if o["kind"] == "quiz_box")
|
||||
score = next(o for o in scene["elements"] if o["kind"] == "score_widget")
|
||||
for widget in (quiz, score):
|
||||
assert "collision_rules" not in widget
|
||||
assert "collision" not in widget
|
||||
assert widget["dialogue_box_style"] == screens.DEFAULT_DIALOGUE_BOX_STYLE
|
||||
|
||||
Reference in New Issue
Block a user