Dev #5

Merged
w-vandal merged 16 commits from dev into main 2026-08-30 09:29:41 +00:00
Showing only changes of commit a7a315cce7 - Show all commits
+47 -31
View File
@@ -265,28 +265,39 @@
}
// Trouve, pour un élément et un type de déclencheur ("clic"/"soumission"),
// le nœud Déclencheur qui lui est rattaché — c'est le point d'entrée du
// graphe à exécuter. On cherche dans TOUS les écrans (pas seulement
// l'écran courant) : bindClicks() attache les gestionnaires une seule
// fois, sur tous les éléments de tous les écrans (déjà tous présents dans
// le DOM, seuls les écrans non actifs sont masqués) — chaque identifiant
// d'élément est unique dans tout le jeu, donc pas d'ambiguïté possible.
// Cette recherche globale couvre aussi les écrans-MODÈLES (un élément de
// jeu réutilisable, ex. "mail content", posé sur une scène) : un
// déclencheur "Au clic"/"Au survol" posé dans l'éditeur du modèle, sur un
// de SES PROPRES enfants, est donc retrouvé même quand ce modèle est
// utilisé ailleurs — d'où le besoin de renvoyer aussi l'écran d'origine du
// nœud (screenIdKey), pas seulement le nœud : runFlowFrom() doit exécuter
// le graphe dans CET écran-là (celui du modèle), jamais dans celui
// réellement affiché, sans quoi le nœud trouvé n'existerait pas dans le
// graphe utilisé et l'action ne se déclencherait jamais.
function findTriggerNode(elementId, event) {
// TOUS les nœuds Déclencheur qui lui sont rattachés — ce sont les points
// d'entrée des graphes à exécuter. Renvoie un TABLEAU (jamais un seul
// nœud) : rien n'empêche de poser plusieurs déclencheurs indépendants
// "Au clic" sur le MÊME élément (ex. l'un ouvre une ligne de Répéteur,
// l'autre éteint une surbrillance) — un seul clic doit alors exécuter
// CHACUN de ces graphes, pas seulement le premier trouvé (régression
// vécue : un second déclencheur posé sur un élément qui en avait déjà un
// restait silencieusement inerte, aucune erreur, juste jamais exécuté).
//
// On cherche dans TOUS les écrans (pas seulement l'écran courant) :
// bindClicks() attache les gestionnaires une seule fois, sur tous les
// éléments de tous les écrans (déjà tous présents dans le DOM, seuls les
// écrans non actifs sont masqués) — chaque identifiant d'élément est
// unique dans tout le jeu, donc pas d'ambiguïté possible. Cette recherche
// globale couvre aussi les écrans-MODÈLES (un élément de jeu réutilisable,
// ex. "mail content", posé sur une scène) : un déclencheur "Au clic"/"Au
// survol" posé dans l'éditeur du modèle, sur un de SES PROPRES enfants,
// est donc retrouvé même quand ce modèle est utilisé ailleurs — d'où le
// besoin de renvoyer aussi l'écran d'origine de CHAQUE nœud (screenIdKey),
// pas seulement le nœud : runFlowFrom() doit exécuter le graphe dans CET
// écran-là (celui du modèle), jamais dans celui réellement affiché, sans
// quoi le nœud trouvé n'existerait pas dans le graphe utilisé et l'action
// ne se déclencherait jamais.
function findTriggerNodess(elementId, event) {
const found = [];
for (const screenIdKey in gameData.flows) {
const flow = gameData.flows[screenIdKey];
const found = flow.nodes.find(n => n.node_type === 'trigger' && n.trigger_element_id === elementId && (n.trigger_event || 'clic') === event);
if (found) return { node: found, screenId: screenIdKey };
gameData.flows[screenIdKey].nodes.forEach(function(n) {
if (n.node_type === 'trigger' && n.trigger_element_id === elementId && (n.trigger_event || 'clic') === event) {
found.push({ node: n, screenId: screenIdKey });
}
});
}
return null;
return found;
}
// Ré-attache les gestionnaires de clic sur chaque élément — appelé au
@@ -313,8 +324,8 @@
function bindClicks() {
document.querySelectorAll('[data-element-id]').forEach(elDiv => {
const elementId = parseInt(elDiv.dataset.elementId, 10);
const trigger = findTriggerNode(elementId, 'clic');
if (trigger) {
const triggers = findTriggerNodess(elementId, 'clic');
if (triggers.length) {
// Garde-fou anti-doublon : refreshRuntimeData() rappelle bindClicks()
// après chaque changement de donnée, mais un élément qui n'est ni un
// Répéteur ni une Jauge garde le MÊME nœud DOM d'un rafraîchissement
@@ -337,7 +348,9 @@
const rowEl = ev.target.closest('.repeaterItem');
window.lastClickedRowId = rowEl ? parseInt(rowEl.dataset.rowId, 10) : null;
window.lastClickedDefinitionId = elDiv.dataset.definitionId ? parseInt(elDiv.dataset.definitionId, 10) : null;
runFlowFrom(trigger.node.id, trigger.screenId);
// Exécute CHAQUE déclencheur "Au clic" posé sur cet élément, pas
// seulement le premier (voir le commentaire de findTriggerNodess).
triggers.forEach(trigger => runFlowFrom(trigger.node.id, trigger.screenId));
});
}
});
@@ -354,12 +367,15 @@
document.querySelectorAll('[data-element-id]').forEach(elDiv => {
if (elDiv.dataset.hoverTriggerBound) return;
const elementId = parseInt(elDiv.dataset.elementId, 10);
const enterTrigger = findTriggerNode(elementId, 'survol');
const leaveTrigger = findTriggerNode(elementId, 'fin_survol');
if (!enterTrigger && !leaveTrigger) return;
const enterTriggers = findTriggerNodess(elementId, 'survol');
const leaveTriggers = findTriggerNodess(elementId, 'fin_survol');
if (!enterTriggers.length && !leaveTriggers.length) return;
elDiv.dataset.hoverTriggerBound = '1';
if (enterTrigger) elDiv.addEventListener('mouseenter', () => runFlowFrom(enterTrigger.node.id, enterTrigger.screenId));
if (leaveTrigger) elDiv.addEventListener('mouseleave', () => runFlowFrom(leaveTrigger.node.id, leaveTrigger.screenId));
// Exécute CHAQUE déclencheur trouvé, pas seulement le premier — voir
// le commentaire de findTriggerNodess (plusieurs déclencheurs
// indépendants sur le même élément/événement doivent tous s'exécuter).
if (enterTriggers.length) elDiv.addEventListener('mouseenter', () => enterTriggers.forEach(t => runFlowFrom(t.node.id, t.screenId)));
if (leaveTriggers.length) elDiv.addEventListener('mouseleave', () => leaveTriggers.forEach(t => runFlowFrom(t.node.id, t.screenId)));
});
}
@@ -982,10 +998,10 @@
// continue sur son (ou ses) fil de sortie ; chaque nœud Condition évalue
// une donnée du jeu et suit le(s) fil(s) "Vrai" ou "Faux" en conséquence.
// flowScreenId : l'écran PROPRIÉTAIRE du graphe à exécuter — par défaut
// l'écran affiché, mais peut être un écran-MODÈLE (voir findTriggerNode)
// l'écran affiché, mais peut être un écran-MODÈLE (voir findTriggerNodes)
// quand le nœud de départ vient d'un déclencheur posé dans l'éditeur d'un
// élément de jeu réutilisable, sur un de ses propres enfants. Sans ce
// paramètre, un tel déclencheur était bien retrouvé (findTriggerNode
// paramètre, un tel déclencheur était bien retrouvé (findTriggerNodes
// cherche dans tous les écrans) mais son graphe n'existait jamais dans
// l'écran réellement affiché : rien ne se déclenchait, silencieusement.
function runFlowFrom(nodeId, flowScreenId) {
@@ -1018,7 +1034,7 @@
// Déclenche un événement personnalisé (voir screens/custom_events/,
// action "declencher_evenement") : une pure NOTIFICATION, sans aucun
// paramètre — scan GLOBAL de gameData.flows, même principe que
// findTriggerNode()/runScreenShowTriggers() ci-dessus, puisqu'un
// findTriggerNodes()/runScreenShowTriggers() ci-dessus, puisqu'un
// écouteur (nœud Déclencheur trigger_event="evenement") peut vivre sur
// N'IMPORTE QUELLE autre scène ou modèle, pas seulement l'écran affiché.
// C'est à CHAQUE écouteur de décider quoi faire ensuite (via sa propre