Phase 5 : position/déplacement d'élément + condition de collision
Build and deploy / test-python (push) Successful in 1m29s
Build and deploy / test-js (push) Successful in 7s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped

Ajoute le positionnement absolu (pos_x/pos_y, réutilise left/top en %
déjà en place) et relatif (pos_x_relatif/pos_y_relatif, ajoute un delta
à la position actuelle plutôt que de l'écraser) comme nouvelles
propriétés de l'action "Modifier un élément".

Ajoute une nouvelle source de condition "collision" (aux côtés de
"objet"/"variable") : deux éléments (cond_element_a/cond_element_b,
ALTER TABLE sans contrainte FK, même patron que block_id/
trigger_custom_event_id) dont on compare les rectangles à l'écran via
getBoundingClientRect() côté client (elementsOverlap(), dans
conditions.js). Pas d'opérateur/valeur à choisir : le chevauchement EST
directement le booléen vrai/faux du nœud — le créateur relie le port
"Faux" pour "ne se touchent pas", exactement comme pour n'importe quelle
autre condition (design plus simple que réinterpréter égal/différent,
qui n'a pas de sens pour superieur/inferieur).

delete_element.py et flow_nodes_referencing_element.py nettoient
désormais aussi les nœuds de collision référençant un élément supprimé
(ou l'un de ses descendants), pour rester cohérents avec le nettoyage
déjà en place pour trigger_element_id/target_element_id.

Combiné à la Phase 3 (minuteur récurrent) et au déplacement au clavier,
ça couvre des jeux type casse-briques/Pong/ramasse-objets sans
construire un vrai moteur physique (pas de vélocité/accélération/
gravité continues, cadrage volontairement limité).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
william
2026-08-30 17:34:51 +02:00
co-authored by Claude Sonnet 5
parent 92fbfbc9dd
commit 9e263435e6
10 changed files with 271 additions and 41 deletions
+29 -2
View File
@@ -2,8 +2,12 @@
// Extrait de templates/play.html (voir plan de modularisation) : partie
// PURE du moteur (aucun accès DOM) — lecture de champs/variables et
// comparaison, c'est la logique la plus amenée à grossir (nouvelles
// opérations, condition de collision...), donc la plus utile à tester
// (voir static/js/play/__tests__/conditions.test.js).
// opérations...), donc la plus utile à tester (voir
// static/js/play/__tests__/conditions.test.js). SEULE EXCEPTION :
// elementsOverlap() (Phase 5, condition de collision) touche forcément le
// DOM (getBoundingClientRect) — isolée dans sa propre fonction, non
// testée par node:test (voir son commentaire), le reste du fichier reste
// pur et testable.
// Lit la valeur actuelle d'un champ d'objet dans l'instantané de données
// du jeu (gameData.data), pour l'évaluation d'une condition.
@@ -97,10 +101,33 @@ function resolveVariablePath(rawValue, path) {
// comportement historique), soit une VARIABLE GLOBALE (source
// "variable" — voir ensure_flow_schema.py pour cond_source/cond_variable/
// cond_variable_chemin).
// Phase 5 — condition de collision : chevauchement des rectangles de
// deux éléments (getBoundingClientRect, donc leur position RÉELLE à
// l'écran — reflète les déplacements posés via "Modifier un élément →
// Position", voir static/js/play/actions.js). Aucun élément trouvé (id
// invalide, élément masqué avec display:none — getBoundingClientRect
// renvoie alors une boîte de largeur/hauteur nulles) => pas de
// chevauchement plutôt que de planter.
function elementsOverlap(elementIdA, elementIdB) {
const a = document.querySelector('[data-element-id="' + elementIdA + '"]');
const b = document.querySelector('[data-element-id="' + elementIdB + '"]');
if (!a || !b) return false;
const r1 = a.getBoundingClientRect(), r2 = b.getBoundingClientRect();
return r1.left < r2.right && r1.right > r2.left && r1.top < r2.bottom && r1.bottom > r2.top;
}
function evaluateConditionClause(clause) {
const source = clause.source ?? clause.cond_source ?? 'objet';
const operator = clause.operator ?? clause.cond_operator;
const expected = clause.value ?? clause.cond_value;
if (source === 'collision') {
// Pas d'opérateur/valeur à comparer (contrairement à "objet"/
// "variable") : le chevauchement EST directement le booléen vrai/faux
// du nœud — pour "ne se touchent pas", le créateur relie simplement
// le port "Faux" plutôt que "Vrai" dans le graphe, comme pour
// n'importe quelle autre condition.
return elementsOverlap(clause.element_a ?? clause.cond_element_a, clause.element_b ?? clause.cond_element_b);
}
if (source === 'variable') {
const varName = clause.variable ?? clause.cond_variable;
const path = clause.variable_chemin ?? clause.cond_variable_chemin;