Quêtes/collision : personnages nommés, dialogues qui parlent, boîte de dialogue en jeu
Build and deploy / test-python (push) Successful in 11m32s
Build and deploy / test-js (push) Successful in 1m12s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped

- "ℹ️ Informations" (propriétés d'un personnage) : nom éditable, réutilisé
  comme "qui parle" dans l'éditeur de dialogue de quête (menu déroulant,
  plus "Joueur" toujours disponible) — remplace l'ancien choix binaire
  joueur/pnj. db.sanitize_quest_dialogues accepte maintenant un nom libre.

- Nouveau menu "🖥️ Interface" (palette d'objets) avec le premier widget :
  "💬 Boîte de dialogue" (kind="dialogue_box", screens/rendering/
  dialogue_box_style.py) — position/taille comme tout objet de scène,
  panneau "🎨 Style" dédié (police, taille, épaisseur, couleur du texte,
  couleur header/body/footer). Rendu en <div> à 3 zones, jamais soumis à
  la collision ni à l'éditeur de collision (exclu partout : obstacles,
  liste des règles, payload). Rendu côté jeu dans .sceneUI, une couche
  FIXE au viewport (jamais .sceneWorld, qui défile avec la caméra).

- static/js/play/dialogue-box-controller.js : fait le lien moteur entre
  l'action "quete" de l'éditeur de collision et ce widget — affiche la
  réplique en cours du dialogue correspondant au STATUT ACTUEL de la
  quête (gameData.quests, désormais exposé game-wide par
  full_game_payload.py), avance au clic sur "Suivant" (header = qui
  parle, body = texte, footer = bouton), se masque à la dernière réplique.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
william
2026-09-03 11:20:58 +02:00
co-authored by Claude Sonnet 5
parent 390ee831ca
commit 2347a70c45
27 changed files with 774 additions and 41 deletions
+7 -1
View File
@@ -5,7 +5,11 @@ import db
from .ensure_scene_schema import ensure_scene_schema
_DEFAULT_PERSONNAGE_DATA = {"source": "forge", "forge_character": "male-adventurer"}
_KINDS = {"personnage", "decor", "fond"}
_KINDS = {"personnage", "decor", "fond", "dialogue_box"}
# Widget d'interface (voir "🖥️ Interface", screens/rendering/dialogue_box_style.py) :
# assez grand pour lire une réplique sans redimensionner, jamais confondu
# visuellement avec un personnage/décor (128×128 par défaut).
_DIALOGUE_BOX_DEFAULT_SIZE = (320, 150)
def add_scene_object(slug, scene_id, kind="personnage", forge_character=None, background_slug=None):
@@ -40,6 +44,8 @@ def add_scene_object(slug, scene_id, kind="personnage", forge_character=None, ba
if background:
attributes["src"] = background["url"]
width, height = background["width"], background["height"]
elif kind == "dialogue_box":
width, height = _DIALOGUE_BOX_DEFAULT_SIZE
conn = db.connect(slug)
if kind == "fond":
z_index = -1000
+26
View File
@@ -1,6 +1,7 @@
import html as html_lib
from ..rendering.personnage_data import resolve_personnage_idle_frame
from ..rendering.dialogue_box_style import resolve_dialogue_box_style
def render_scene_object(obj):
@@ -39,6 +40,31 @@ def render_scene_object(obj):
# remplirait quand même son cadre (width/height:100%, voir la règle
# `.canvasElementInner > img` dans scene_edit.html) mais DÉFORMÉE,
# rendant le personnage minuscule/écrasé dans sa boîte de sélection.
if obj["kind"] == "dialogue_box":
# Widget d'interface (voir "🖥️ Interface" + screens/rendering/
# dialogue_box_style.py) : un <div> à 3 zones, pas un <img> — voir
# static/js/play/dialogue-box-controller.js pour comment header/
# body/footer sont remplis EN JEU (data-dialogue-role sert de
# sélecteur), masqué par défaut au jeu (voir static/style.css,
# `.playScreen .dialogueBoxWidget`) jusqu'à ce qu'une règle de
# collision "quete" ait un dialogue à afficher. Toujours VISIBLE
# dans l'éditeur (avec un texte d'exemple) pour pouvoir le
# positionner/styliser.
style = resolve_dialogue_box_style(obj)
box_style = (
f"position:absolute; left:{obj['x']}px; top:{obj['y']}px; "
f"width:{obj['width']}px; height:{obj['height']}px; z-index:{obj['z_index']}; "
f"font-family:{html_lib.escape(style['font_family'])}; font-size:{style['font_size']}px; "
f"font-weight:{style['font_weight']}; color:{html_lib.escape(style['text_color'])};"
)
return (
f'<div class="dialogueBoxWidget" data-object-id="{obj["id"]}" data-element-id="{obj["id"]}" style="{box_style}">'
f'<div class="dialogueBoxHeader" data-dialogue-role="header" style="background:{html_lib.escape(style["header_bg"])}">Nom du personnage</div>'
f'<div class="dialogueBoxBody" data-dialogue-role="body" style="background:{html_lib.escape(style["body_bg"])}">Le texte du dialogue s\'affiche ici.</div>'
f'<div class="dialogueBoxFooter" style="background:{html_lib.escape(style["footer_bg"])}">'
f'<button type="button" class="dialogueBoxNextBtn" data-dialogue-role="next-btn" onclick="forgeDialogueBoxAdvance({obj["id"]})">Suivant →</button>'
f'</div></div>'
)
if obj["kind"] == "personnage":
src = resolve_personnage_idle_frame(obj)
src_attr = f' src="{html_lib.escape(src)}"' if src else ""
@@ -0,0 +1,22 @@
import json
import db
from ..rendering.dialogue_box_style import sanitize_dialogue_box_style
from .ensure_scene_schema import ensure_scene_schema
def set_scene_object_dialogue_box_style(slug, object_id, raw_style):
ensure_scene_schema(slug)
conn = db.connect(slug)
row = conn.execute("SELECT attributes FROM _scene_objects WHERE id = ?", (object_id,)).fetchone()
if not row:
conn.close()
return None
attributes = json.loads(row["attributes"] or "{}")
style = sanitize_dialogue_box_style(raw_style)
attributes["_dialogue_box_style"] = json.dumps(style)
conn.execute("UPDATE _scene_objects SET attributes = ? WHERE id = ?", (json.dumps(attributes), object_id))
conn.commit()
conn.close()
return style
+18
View File
@@ -0,0 +1,18 @@
import db
from .ensure_scene_schema import ensure_scene_schema
def set_scene_object_name(slug, object_id, name):
"""Enregistre le nom d'un objet de scène (colonne `name`, voir
ensure_scene_schema.py — déjà utilisée comme LIBELLÉ d'affichage
partout ailleurs, mais jamais éditable avant le panneau "ℹ️
Informations", templates/scene_edit.html). Pour un personnage, ce nom
est aussi celui proposé comme "qui parle" dans l'éditeur de dialogue
de quête (voir screens/rendering/personnage_names.py, static/js/
quests/quest-editor.js)."""
ensure_scene_schema(slug)
conn = db.connect(slug)
conn.execute("UPDATE _scene_objects SET name = ? WHERE id = ?", ((name or "").strip() or None, object_id))
conn.commit()
conn.close()