Ajoute un script de démo pour tester dialogues/surbrillance/message différé
Les trois mécanismes de guidage du joueur discutés (boîte de dialogue en
popup, réaction à un clic, mise en évidence d'un élément à cliquer)
existent DÉJÀ dans le moteur (voir README, section "Survol, séquences
temporisées, surbrillance, overlay, verrouillage") : widget "Superposition
/ boîte de dialogue", action "Modifier un élément → Surbrillance", et
déclencheur "affichage" combiné à l'action "Attendre" pour un message qui
arrive tout seul après quelques secondes. Rien à coder côté moteur.
scripts/build_demo_dialogues.py construit, via les VRAIES routes Flask
(mêmes routes qu'utilise le navigateur), un jeu de démo persistant
("Demo Dialogues Surbrillance") avec deux écrans :
- "Tutoriel" : dialogue de bienvenue à l'ouverture de l'écran -> son
bouton OK ferme le dialogue et met un autre bouton en surbrillance ->
cliquer ce bouton l'éteint, le désactive (verrouillage anti-reclic) et
ouvre un dialogue "Bravo" en réaction, fermable à son tour.
- "Message différé" : un dialogue "mentor" apparaît tout seul 3 secondes
après l'affichage de l'écran (affichage -> attendre -> visibilité),
sans aucune action du joueur.
Vérifié via /runtime-payload (nombre de nœuds/arêtes attendu sur les deux
écrans) et /play (overlay + surbrillance + attente bien exposés côté
rendu). 128 tests toujours au vert (script indépendant, aucun changement
moteur).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
169b720620
commit
28d8cd8cd0
@@ -0,0 +1,177 @@
|
||||
"""Crée un jeu de démo "Démo — Dialogues & Surbrillance" qui exerce, bout
|
||||
en bout, les trois mécanismes de guidage du joueur déjà présents dans le
|
||||
moteur (voir README, section "Survol, séquences temporisées, surbrillance,
|
||||
overlay, verrouillage") :
|
||||
|
||||
- Écran 1 "Tutoriel" : une boîte de dialogue (widget "superposition")
|
||||
s'ouvre TOUTE SEULE à l'affichage de l'écran (déclencheur "affichage"),
|
||||
accueille le joueur et lui indique quoi faire. Son bouton "OK" la
|
||||
referme ET met un autre bouton en surbrillance (liseré clignotant) pour
|
||||
guider le clic suivant. Cliquer ce bouton met fin à la surbrillance, le
|
||||
désactive (grisé, ne peut plus être recliqué) et ouvre une DEUXIÈME
|
||||
boîte de dialogue "Bravo" en réaction au clic — fermable à son tour.
|
||||
- Écran 2 "Message différé" : une boîte de dialogue "arrive toute seule"
|
||||
3 secondes après l'affichage de l'écran (déclencheur "affichage" ->
|
||||
action "attendre" -> action "Modifier un élément → Visibilité"), sans
|
||||
aucune action du joueur — utile pour un message de mentor/notification.
|
||||
|
||||
Ce script utilise le VRAI client de test Flask contre les VRAIES routes de
|
||||
l'appli (mêmes routes que celles appelées par le navigateur), et crée un
|
||||
jeu PERSISTANT sous projects/ (contrairement aux tests pytest, il n'est
|
||||
PAS supprimé à la fin) — pensé pour être ouvert et joué dans le navigateur
|
||||
après coup.
|
||||
|
||||
Usage : python3 scripts/build_demo_dialogues.py
|
||||
"""
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from core.flask_app import app
|
||||
from core import jinja_filters # noqa: F401 - enregistre les filtres Jinja
|
||||
import routes # noqa: F401 - enregistre toutes les routes
|
||||
|
||||
|
||||
def _redirect_id(resp, pattern):
|
||||
return int(re.search(pattern, resp.headers["Location"]).group(1))
|
||||
|
||||
|
||||
def _create_screen(client, slug, name):
|
||||
resp = client.post(f"/game/{slug}/screens/new", data={"name": name}, follow_redirects=False)
|
||||
return _redirect_id(resp, r"/screens/(\d+)/edit")
|
||||
|
||||
|
||||
def _add_element(client, slug, screen_id, widget):
|
||||
resp = client.post(f"/game/{slug}/screens/{screen_id}/elements/add", data={"widget": widget}, follow_redirects=False)
|
||||
return _redirect_id(resp, r"selected=(\d+)")
|
||||
|
||||
|
||||
def _add_child(client, slug, parent_id, widget):
|
||||
resp = client.post(f"/game/{slug}/elements/{parent_id}/children/add", data={"widget": widget}, follow_redirects=False)
|
||||
return _redirect_id(resp, r"selected=(\d+)")
|
||||
|
||||
|
||||
def _save(client, slug, element_id, **ctrl):
|
||||
data = {f"ctrl_{k}": v for k, v in ctrl.items()}
|
||||
client.post(f"/game/{slug}/elements/{element_id}/save", data=data)
|
||||
|
||||
|
||||
def _add_node(client, slug, screen_id, **fields):
|
||||
resp = client.post(
|
||||
f"/game/{slug}/screens/{screen_id}/flow/nodes/add",
|
||||
data=json.dumps({**fields}),
|
||||
content_type="application/json",
|
||||
)
|
||||
assert resp.status_code == 200, resp.get_data(as_text=True)
|
||||
return resp.get_json()["id"]
|
||||
|
||||
|
||||
def _add_edge(client, slug, screen_id, from_id, to_id, from_port="out"):
|
||||
resp = client.post(
|
||||
f"/game/{slug}/screens/{screen_id}/flow/edges/add",
|
||||
data=json.dumps({"from_node_id": from_id, "from_port": from_port, "to_node_id": to_id}),
|
||||
content_type="application/json",
|
||||
)
|
||||
assert resp.status_code == 200, resp.get_data(as_text=True)
|
||||
|
||||
|
||||
def _modifier_element(client, slug, screen_id, target_id, prop, value):
|
||||
return _add_node(
|
||||
client, slug, screen_id,
|
||||
node_type="action", action_type="modifier_element",
|
||||
target_element_id=target_id, element_property=prop, element_value=value,
|
||||
)
|
||||
|
||||
|
||||
def build():
|
||||
app.config["TESTING"] = True
|
||||
with app.test_client() as client:
|
||||
resp = client.post("/games/new", data={"name": "Demo Dialogues Surbrillance"}, follow_redirects=False)
|
||||
assert resp.status_code == 302, resp.get_data(as_text=True)
|
||||
slug = resp.headers["Location"].rstrip("/").split("/")[-1]
|
||||
print(f"Jeu créé : slug={slug}")
|
||||
|
||||
# ---------------- Écran 1 : Tutoriel ----------------
|
||||
screen1 = _create_screen(client, slug, "Tutoriel")
|
||||
|
||||
overlay_bienvenue = _add_element(client, slug, screen1, "superposition")
|
||||
_save(client, slug, overlay_bienvenue, couleur_boite="#20263a", arrondi="16")
|
||||
t1 = _add_child(client, slug, overlay_bienvenue, "titre")
|
||||
_save(client, slug, t1, content="Bienvenue !")
|
||||
t2 = _add_child(client, slug, overlay_bienvenue, "texte")
|
||||
_save(client, slug, t2, content="Ce petit tutoriel te montre comment fonctionnent les dialogues et la surbrillance. Clique sur OK pour commencer.")
|
||||
btn_ok = _add_child(client, slug, overlay_bienvenue, "bouton")
|
||||
_save(client, slug, btn_ok, content="OK, c'est parti")
|
||||
|
||||
btn_target = _add_element(client, slug, screen1, "bouton")
|
||||
_save(client, slug, btn_target, content="Clique-moi !")
|
||||
|
||||
overlay_bravo = _add_element(client, slug, screen1, "superposition")
|
||||
_save(client, slug, overlay_bravo, couleur_boite="#1f3a24", arrondi="16")
|
||||
t3 = _add_child(client, slug, overlay_bravo, "titre")
|
||||
_save(client, slug, t3, content="Bravo !")
|
||||
t4 = _add_child(client, slug, overlay_bravo, "texte")
|
||||
_save(client, slug, t4, content="Tu as cliqué le bon bouton. Le tutoriel est terminé.")
|
||||
btn_close_bravo = _add_child(client, slug, overlay_bravo, "bouton")
|
||||
_save(client, slug, btn_close_bravo, content="Fermer")
|
||||
|
||||
# Déclencheur "affichage" (écran entier) -> ouvre le dialogue de bienvenue.
|
||||
trig_affichage = _add_node(client, slug, screen1, node_type="trigger", trigger_event="affichage")
|
||||
act_open_bienvenue = _modifier_element(client, slug, screen1, overlay_bienvenue, "visibilite", "visible")
|
||||
_add_edge(client, slug, screen1, trig_affichage, act_open_bienvenue)
|
||||
|
||||
# Clic sur "OK" -> ferme le dialogue de bienvenue ET met le bouton cible en surbrillance.
|
||||
trig_ok = _add_node(client, slug, screen1, node_type="trigger", trigger_element_id=btn_ok, trigger_event="clic")
|
||||
act_close_bienvenue = _modifier_element(client, slug, screen1, overlay_bienvenue, "visibilite", "masque")
|
||||
act_highlight_on = _modifier_element(client, slug, screen1, btn_target, "surbrillance", "on")
|
||||
_add_edge(client, slug, screen1, trig_ok, act_close_bienvenue)
|
||||
_add_edge(client, slug, screen1, trig_ok, act_highlight_on)
|
||||
|
||||
# Clic sur le bouton cible -> arrête la surbrillance, le désactive, ouvre "Bravo".
|
||||
trig_target = _add_node(client, slug, screen1, node_type="trigger", trigger_element_id=btn_target, trigger_event="clic")
|
||||
act_highlight_off = _modifier_element(client, slug, screen1, btn_target, "surbrillance", "off")
|
||||
act_lock_target = _modifier_element(client, slug, screen1, btn_target, "desactive", "on")
|
||||
act_open_bravo = _modifier_element(client, slug, screen1, overlay_bravo, "visibilite", "visible")
|
||||
_add_edge(client, slug, screen1, trig_target, act_highlight_off)
|
||||
_add_edge(client, slug, screen1, trig_target, act_lock_target)
|
||||
_add_edge(client, slug, screen1, trig_target, act_open_bravo)
|
||||
|
||||
# Clic sur "Fermer" (dans "Bravo") -> referme "Bravo".
|
||||
trig_close_bravo = _add_node(client, slug, screen1, node_type="trigger", trigger_element_id=btn_close_bravo, trigger_event="clic")
|
||||
act_close_bravo = _modifier_element(client, slug, screen1, overlay_bravo, "visibilite", "masque")
|
||||
_add_edge(client, slug, screen1, trig_close_bravo, act_close_bravo)
|
||||
|
||||
# ---------------- Écran 2 : Message différé ----------------
|
||||
screen2 = _create_screen(client, slug, "Message différé")
|
||||
|
||||
overlay_mentor = _add_element(client, slug, screen2, "superposition")
|
||||
_save(client, slug, overlay_mentor, couleur_boite="#2a2440", arrondi="16")
|
||||
t5 = _add_child(client, slug, overlay_mentor, "titre")
|
||||
_save(client, slug, t5, content="Message du mentor")
|
||||
t6 = _add_child(client, slug, overlay_mentor, "texte")
|
||||
_save(client, slug, t6, content="Ce message est apparu tout seul, 3 secondes après l'arrivée sur cet écran — sans aucun clic.")
|
||||
btn_close_mentor = _add_child(client, slug, overlay_mentor, "bouton")
|
||||
_save(client, slug, btn_close_mentor, content="Fermer")
|
||||
|
||||
info2 = _add_element(client, slug, screen2, "texte")
|
||||
_save(client, slug, info2, content="Attends 3 secondes sans rien faire : un message va apparaître tout seul.")
|
||||
|
||||
trig_affichage2 = _add_node(client, slug, screen2, node_type="trigger", trigger_event="affichage")
|
||||
act_wait = _add_node(client, slug, screen2, node_type="action", action_type="attendre", data_value="3")
|
||||
act_open_mentor = _modifier_element(client, slug, screen2, overlay_mentor, "visibilite", "visible")
|
||||
_add_edge(client, slug, screen2, trig_affichage2, act_wait)
|
||||
_add_edge(client, slug, screen2, act_wait, act_open_mentor)
|
||||
|
||||
trig_close_mentor = _add_node(client, slug, screen2, node_type="trigger", trigger_element_id=btn_close_mentor, trigger_event="clic")
|
||||
act_close_mentor = _modifier_element(client, slug, screen2, overlay_mentor, "visibilite", "masque")
|
||||
_add_edge(client, slug, screen2, trig_close_mentor, act_close_mentor)
|
||||
|
||||
print(f"Terminé. Ouvre http://127.0.0.1:5050/game/{slug}/play pour tester.")
|
||||
print(f"Éditeur : http://127.0.0.1:5050/game/{slug}/screens/{screen1}/edit")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
build()
|
||||
Reference in New Issue
Block a user