- "ℹ️ 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>
73 lines
3.1 KiB
JavaScript
73 lines
3.1 KiB
JavaScript
// ---------- Widget "💬 Boîte de dialogue" — moteur d'exécution ----------
|
|
// Fait le lien entre l'éditeur de collision (action "quete", voir
|
|
// static/js/play/collision-rules-controller.js) et le widget d'interface
|
|
// posé par l'auteur (voir "🖥️ Interface", templates/scene_edit.html,
|
|
// screens/rendering/dialogue_box_style.py) : à l'exécution d'une action
|
|
// "quete", affiche la réplique en cours du dialogue correspondant au
|
|
// STATUT ACTUEL de la quête (gameData.quests, voir full_game_payload.py),
|
|
// avance d'une réplique à chaque clic sur "Suivant", se masque à la fin.
|
|
// Silencieux (aucun effet) si la quête est introuvable, n'a aucune
|
|
// réplique pour son statut, ou si l'auteur n'a posé aucun widget sur
|
|
// l'écran — même esprit que le reste du moteur (jamais planter).
|
|
|
|
var forgeDialogueBoxState = {};
|
|
|
|
function forgeQuestById(questId) {
|
|
return (gameData.quests || []).find(function (q) { return q.id === questId; });
|
|
}
|
|
|
|
function forgeDialogueBoxWidgetOnCurrentScreen() {
|
|
return document.querySelector('.playScreen[data-screen-id="' + window.currentScreenId + '"] .dialogueBoxWidget');
|
|
}
|
|
|
|
function forgeDialogueBoxWidgetById(objectId) {
|
|
return document.querySelector('.dialogueBoxWidget[data-object-id="' + objectId + '"]');
|
|
}
|
|
|
|
function forgeRenderDialogueBoxLine(widget, line) {
|
|
var header = widget.querySelector('[data-dialogue-role="header"]');
|
|
var body = widget.querySelector('[data-dialogue-role="body"]');
|
|
if (header) header.textContent = line.speaker;
|
|
if (body) body.textContent = line.text;
|
|
}
|
|
|
|
// Appelée par l'action "quete" (collision-rules-controller.js) — trouve
|
|
// le widget posé sur l'écran ACTUELLEMENT affiché (un seul à la fois,
|
|
// comme le reste du moteur 2D).
|
|
function forgeShowDialogueBox(questId) {
|
|
var quest = forgeQuestById(questId);
|
|
if (!quest) return;
|
|
var lines = (quest.dialogues && quest.dialogues[quest.status]) || [];
|
|
if (!lines.length) return;
|
|
var widget = forgeDialogueBoxWidgetOnCurrentScreen();
|
|
if (!widget) return;
|
|
var objectId = widget.getAttribute('data-object-id');
|
|
forgeDialogueBoxState[objectId] = { lines: lines, index: 0 };
|
|
forgeRenderDialogueBoxLine(widget, lines[0]);
|
|
widget.classList.add('is-active');
|
|
}
|
|
|
|
// Appelée par le bouton "Suivant →" rendu dans le widget lui-même (voir
|
|
// render_scene_object.py, onclick="forgeDialogueBoxAdvance(<id>)").
|
|
function forgeDialogueBoxAdvance(objectId) {
|
|
var state = forgeDialogueBoxState[objectId];
|
|
var widget = forgeDialogueBoxWidgetById(objectId);
|
|
if (!state || !widget) return;
|
|
state.index += 1;
|
|
if (state.index >= state.lines.length) {
|
|
widget.classList.remove('is-active');
|
|
delete forgeDialogueBoxState[objectId];
|
|
return;
|
|
}
|
|
forgeRenderDialogueBoxLine(widget, state.lines[state.index]);
|
|
}
|
|
|
|
// static/js/play/__tests__/ (node:test) — même garde que les autres
|
|
// contrôleurs de ce dossier.
|
|
if (typeof module !== 'undefined' && module.exports) {
|
|
module.exports = {
|
|
forgeQuestById, forgeShowDialogueBox, forgeDialogueBoxAdvance,
|
|
forgeDialogueBoxWidgetOnCurrentScreen, forgeDialogueBoxWidgetById, forgeRenderDialogueBoxLine,
|
|
};
|
|
}
|