Files
Forge-Engine/screens/scenes/add_scene_object.py
T
williamandClaude Sonnet 5 2347a70c45
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
Quêtes/collision : personnages nommés, dialogues qui parlent, boîte de dialogue en jeu
- "ℹ️ 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>
2026-09-03 11:20:58 +02:00

71 lines
3.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import json
import db
from .ensure_scene_schema import ensure_scene_schema
_DEFAULT_PERSONNAGE_DATA = {"source": "forge", "forge_character": "male-adventurer"}
_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):
"""Pose un nouvel objet sur une scène (voir ensure_scene_schema.py).
kind="personnage" : attributes._personnage_data, MÊME structure/mêmes
fonctions resolve_personnage_* que le widget "personnage" de l'éditeur
document (screens/rendering/personnage_data.py) — un objet de scène
"personnage" partage donc directement la bibliothèque Forge et le
système d'animation déjà écrits. kind="decor" : une simple image fixe
(attributes.src), pas d'animation. kind="fond" : même image fixe que
"decor" (render_scene_object.py ne les distingue pas au rendu), mais
posée en dessous de tout le reste (z_index très négatif, voir
static/style.css .sceneBackground) et à la taille RÉELLE de l'image
choisie (voir screens/labels/background_library.py) par défaut — une
image plus grande que la scène devient alors le "monde" que la caméra
suit en défilant (voir static/js/play/personnage-controller.js)."""
ensure_scene_schema(slug)
kind = kind if kind in _KINDS else "personnage"
attributes = {}
width = height = None
if kind == "personnage":
data = dict(_DEFAULT_PERSONNAGE_DATA)
if forge_character:
from ..labels.sprite_library import PUBLIC_SPRITE_LIBRARY
from ..labels.animal_sprite_library import ADMIN_SPRITE_LIBRARY
if forge_character in PUBLIC_SPRITE_LIBRARY or forge_character in ADMIN_SPRITE_LIBRARY:
data["forge_character"] = forge_character
attributes["_personnage_data"] = json.dumps(data)
elif kind == "fond" and background_slug:
from ..labels.background_library import BACKGROUND_LIBRARY
background = BACKGROUND_LIBRARY.get(background_slug)
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
else:
max_z = conn.execute(
"SELECT MAX(z_index) AS m FROM _scene_objects WHERE scene_id = ?", (scene_id,)
).fetchone()["m"]
z_index = (max_z or 0) + 1
cols = ["scene_id", "kind", "attributes", "z_index"]
values = [scene_id, kind, json.dumps(attributes), z_index]
if width is not None:
# x=0/y=0 (jamais le défaut 100/100, voir ensure_scene_schema.py) :
# un fond commence au coin haut-gauche de la scène/du monde, pas
# décalé arbitrairement comme un objet ordinaire.
cols += ["width", "height", "x", "y"]
values += [width, height, 0, 0]
placeholders = ", ".join(["?"] * len(values))
conn.execute(f"INSERT INTO _scene_objects ({', '.join(cols)}) VALUES ({placeholders})", values)
object_id = conn.execute("SELECT last_insert_rowid() AS id").fetchone()["id"]
conn.commit()
conn.close()
return object_id