Pas un bug moteur cette fois : le moteur ne fige JAMAIS de couleur de texte par défaut à la création d'un élément (voir default_style_for_widget.py) — un titre/texte fraîchement posé retombe donc sur le texte SOMBRE par défaut de Bulma, pensé pour un fond clair. Mon script de démo ne posait jamais explicitement de couleur de texte sur les titres/paragraphes à l'intérieur des boîtes de dialogue (fond sombre volontaire) : le texte y était donc quasi invisible, ce qui donnait l'impression trompeuse que "la boîte de dialogue est assombrie" — alors que seul son texte, invisible par défaut sur fond sombre, l'était (la boîte elle-même a bien sa couleur opaque demandée, #20263a/#1f3a24/ #2a2440, sans aucun voile supplémentaire dessus). Ajout de la couleur de texte manquante (#f5f6fa) sur chaque titre/texte posé dans une boîte de dialogue. Valeur volontairement différente de la suggestion par défaut du panneau (#e8eaf0) : save_element_controls.py ignore délibérément un réglage renvoyé identique à sa valeur par défaut tant qu'il n'a jamais été personnalisé (même logique anti-figeage que default_style_for_widget.py) — envoyer #e8eaf0 tel quel n'aurait donc eu AUCUN effet, silencieusement (piège rencontré et documenté dans le script). Jeu de démo régénéré (supprimé puis reconstruit) avec le correctif. 129 tests toujours au vert (script indépendant, aucun changement moteur). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
195 lines
10 KiB
Python
195 lines
10 KiB
Python
"""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.
|
|
|
|
Couleur du texte posée explicitement (#f5f6fa) sur chaque titre/texte à
|
|
l'intérieur d'une boîte de dialogue : le moteur ne fige JAMAIS de couleur
|
|
par défaut à la création d'un élément (voir
|
|
default_style_for_widget.py — "la figer dès la création ferait apparaître
|
|
une couleur que personne n'a demandée"), un titre/texte fraîchement posé
|
|
retombe donc sur le texte sombre par défaut de Bulma, pensé pour un fond
|
|
clair — illisible sur le fond sombre d'une boîte de dialogue tant que
|
|
personne n'a choisi une couleur claire à la main dans le panneau de
|
|
propriétés. Sans ce réglage explicite ici, la démo donnait l'impression
|
|
trompeuse que "la boîte de dialogue est assombrie" (elle ne l'est pas :
|
|
seul son texte, invisible par défaut sur fond sombre, l'était). Valeur
|
|
volontairement DIFFÉRENTE de la couleur suggérée par défaut dans le
|
|
panneau (#e8eaf0) : save_element_controls.py ignore délibérément un
|
|
réglage renvoyé identique à sa valeur par défaut tant qu'il n'a jamais été
|
|
personnalisé (même logique anti-figeage), donc envoyer #e8eaf0 telle
|
|
quelle ici n'aurait eu AUCUN effet, silencieusement.
|
|
|
|
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 !", color="#f5f6fa")
|
|
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.", color="#f5f6fa")
|
|
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 !", color="#f5f6fa")
|
|
t4 = _add_child(client, slug, overlay_bravo, "texte")
|
|
_save(client, slug, t4, content="Tu as cliqué le bon bouton. Le tutoriel est terminé.", color="#f5f6fa")
|
|
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", color="#f5f6fa")
|
|
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.", color="#f5f6fa")
|
|
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()
|