Phase A (2/2) : éditeur de scène 2D — route, template, runtime jouable
Build and deploy / test-python (push) Successful in 1m56s
Build and deploy / test-js (push) Successful in 8s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped

Deuxième et dernier commit de la Phase A du plan "Fondations d'une
plateforme multi-éditeurs" (le premier, efa7d1d, posait le schéma et le
CRUD des objets de scène). Ce commit branche l'éditeur et le mode
jouable sur ces fondations, en réutilisant TEL QUEL tout ce qui est déjà
générique côté moteur de logique.

Éditeur (routes/scenes/, templates/scene_edit.html,
static/js/scenes/scene-editor.js) :
- routes/screens/screen_edit.py délègue à render_scene_edit() dès que
  game_type == "jeu_2d" — même endpoint Flask "screen_edit" pour les
  deux éditeurs, aucune route dupliquée.
- Nouveau template scene_edit.html : canevas à taille FIXE en pixels
  (scene_width/scene_height) avec glisser-déposer/redimensionnement en
  pixels (scene-editor.js, mirror pixel de tree-panels.js), galerie
  personnages/décors. Les onglets "Blocs de logique"/"Timeline
  d'animation"/"Événements" réutilisent tels quels flow-editor.js,
  tabs-and-blocks.js et animation-timeline.js.
- flow-editor.js : nouveau flag FLOW_TARGETS_OBJECTS (false par défaut,
  true dans scene_edit.html) qui fait écrire submitNodeForm() vers
  trigger_object_id/target_object_id au lieu de trigger_element_id/
  target_element_id, plus une branche "Modifier un objet de scène"
  (position px, visibilité, orientation) et des gardes null partout
  (le DOM de scene_edit.html n'a pas tous les champs de l'éditeur
  document).
- blocks_view.py étend l'ensemble d'ids d'éléments concernés par un
  bloc pour inclure aussi trigger_object_id/target_object_id.

Runtime jouable (templates/play.html, static/js/play/actions.js,
screens/payload/full_game_payload.py) :
- full_game_payload() construit les écrans d'un jeu jeu_2d à partir de
  _scene_objects (render_scene_object) au lieu de _screen_elements, et
  récupère les animations de personnage sur les objets de scène.
- play.html : #playFrame occupe tout le viewport pour une scène jeu_2d
  (pas de ratio fluide) ; la scène se centre via .playScreen.playScene,
  qui réutilise tel quel showScreen() (déjà indexé par id d'écran, pas
  par forme DOM) — aucun fichier "scenes.js" séparé n'a été nécessaire.
- actions.js : jouer_animation_sprite retombe sur target_object_id si
  target_element_id est absent ; nouvelle action modifier_objet_scene
  avec applyObjectProperty() (positions en px, contrairement aux % de
  applyElementProperty()).

Un bug de gabarit a été découvert et corrigé pendant la vérification :
le commentaire CSS de play.html contenait littéralement "{% for %}"
comme texte français, que Jinja interprétait comme une vraie balise et
faisait planter le rendu — reformulé. render_scene_object() n'était en
outre jamais appelé par la vue de l'éditeur (les objets de scène
s'affichaient sans image) — scene_edit_view.py attache maintenant
rendered_html à chaque objet avant de les passer au template.

Nouveaux tests (tests/test_scene_edit_view.py, 10 cas) : dispatch
document vs jeu_2d, rendu d'un personnage sélectionné, CRUD géométrie/
suppression d'objet, nœuds de flow ciblant un objet de scène (action et
condition de collision), payload et page /play pour un jeu jeu_2d,
présence des nouveaux helpers JS. Suite complète : 309 tests passent
(299 existants + 10 nouveaux, aucune régression). node --check et
node --test (13 tests JS) passent également.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
william
2026-08-31 10:05:40 +02:00
co-authored by Claude Sonnet 5
parent efa7d1d9b0
commit f0070faced
17 changed files with 1300 additions and 28 deletions
+36 -3
View File
@@ -124,6 +124,29 @@ function applyElementProperty(targetEl, property, value) {
targetEl.style[spec.css] = value + spec.suffix;
}
// Fondations multi-éditeurs — éditeur de scène 2D : équivalent
// d'applyElementProperty() ci-dessus pour un objet de scène (action
// "Modifier un objet de scène"), mais en PIXELS (pas de %, voir
// scene_width/scene_height) — visibilite/orientation restent identiques
// à l'éditeur document (déléguées à applyElementProperty), seule la
// position change de suffixe.
function applyObjectProperty(targetEl, property, value) {
if (property === 'pos_x_relatif' || property === 'pos_y_relatif') {
const css = property === 'pos_x_relatif' ? 'left' : 'top';
const delta = parseFloat(value);
if (isNaN(delta)) return;
const current = parseFloat(targetEl.style[css]) || 0;
targetEl.style[css] = (current + delta) + 'px';
return;
}
if (property === 'pos_x' || property === 'pos_y') {
if (value === undefined || value === null) return;
targetEl.style[property === 'pos_x' ? 'left' : 'top'] = value + 'px';
return;
}
applyElementProperty(targetEl, property, value);
}
function runActionNode(node) {
if (node.action_type === 'ecran_suivant') {
const idx = screenIndexById(window.currentScreenId);
@@ -163,6 +186,15 @@ function runActionNode(node) {
const targetEl = document.querySelector('[data-element-id="' + node.target_element_id + '"]');
if (targetEl) applyElementProperty(targetEl, node.element_property, node.element_value);
return Promise.resolve();
} else if (node.action_type === 'modifier_objet_scene' && (node.target_element_id || node.target_object_id)) {
// Fondations multi-éditeurs — éditeur de scène 2D : équivalent de
// "modifier_element" ci-dessus, mais en pixels (voir
// applyObjectProperty() plus bas) plutôt qu'en %. data-element-id
// est posé sur CHAQUE objet de scène en plus de data-object-id (voir
// render_scene_object.py), donc le même sélecteur suffit.
const targetEl = document.querySelector('[data-element-id="' + (node.target_element_id || node.target_object_id) + '"]');
if (targetEl) applyObjectProperty(targetEl, node.element_property, node.element_value);
return Promise.resolve();
} else if (node.action_type === 'declencher_evenement' && node.target_custom_event_id) {
// Notification pure (voir screens/custom_events/) : ne fait que
// signaler l'événement, jamais choisir un élément — c'est à
@@ -240,12 +272,13 @@ function runActionNode(node) {
// Fire-and-forget : ne bloque jamais la suite du graphe.
if (node.data_value) new Audio(node.data_value).play().catch(function() {});
return Promise.resolve();
} else if (node.action_type === 'jouer_animation_sprite' && node.target_element_id) {
const targetEl = document.querySelector('[data-element-id="' + node.target_element_id + '"]');
} else if (node.action_type === 'jouer_animation_sprite' && (node.target_element_id || node.target_object_id)) {
const spriteTargetId = node.target_element_id || node.target_object_id;
const targetEl = document.querySelector('[data-element-id="' + spriteTargetId + '"]');
if (targetEl) {
let spriteData = {};
try { spriteData = JSON.parse(node.data_value || '{}'); } catch (e) { /* data_value invalide : rien à jouer */ }
runSpriteAnimation(targetEl, resolveSpriteFrames(node.target_element_id, spriteData));
runSpriteAnimation(targetEl, resolveSpriteFrames(spriteTargetId, spriteData));
}
return Promise.resolve();
}
+145
View File
@@ -0,0 +1,145 @@
// ---------- Éditeur de scène 2D (fondations multi-éditeurs) ----------
// JS propre à templates/scene_edit.html : ajout/déplacement/redimension-
// nement d'objets de scène EN PIXELS (pas de %, voir ensure_scene_schema.py),
// mirror des fonctions équivalentes de static/js/screen_edit/tree-panels.js
// pour l'éditeur document (onElementMouseDown/onResizeMouseDown/
// saveGeometry), mais sur une scène à taille FIXE plutôt qu'un canevas
// fluide — la logique de flow elle-même (flow-editor.js, tabs-and-blocks.js,
// animation-timeline.js, personnage-panel.js, personnage-preview.js) est
// réutilisée TELLE QUELLE, chargée telle quelle par scene_edit.html.
function addSceneObject(kind, forgeCharacter) {
var form = document.createElement('form');
form.method = 'post';
form.action = "/game/" + GAME_SLUG + "/screens/" + SCREEN_ID + "/scene-objects/add";
var html = '<input type="hidden" name="kind" value="' + kind + '">';
if (forgeCharacter) html += '<input type="hidden" name="forge_character" value="' + forgeCharacter + '">';
form.innerHTML = html;
document.body.appendChild(form);
form.requestSubmit();
}
function saveSceneObjectGeometry(id, x, y, width, height) {
return fetch("/game/" + GAME_SLUG + "/scene-objects/" + id + "/geometry", {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ x: x, y: y, width: width, height: height }),
});
}
function updateScenePosFields(x, y, w, h) {
var xEl = document.getElementById('scene-pos-x'), yEl = document.getElementById('scene-pos-y');
var wEl = document.getElementById('scene-pos-w'), hEl = document.getElementById('scene-pos-h');
if (xEl) xEl.value = Math.round(x);
if (yEl) yEl.value = Math.round(y);
if (wEl) wEl.value = Math.round(w);
if (hEl) hEl.value = Math.round(h);
}
// Glisser-déposer en pixels, clampé à la taille FIXE de la scène
// (SCENE_WIDTH/SCENE_HEIGHT, voir templates/scene_edit.html) — mirror
// d'onElementMouseDown() (tree-panels.js), mais sans conversion en % :
// une scène a des coordonnées pixel absolues, pas fluides.
function onSceneObjectMouseDown(e, id) {
if (e.target.classList.contains('resizeHandle')) return;
e.preventDefault();
var el = document.getElementById('el-' + id);
var startX = e.clientX, startY = e.clientY;
var startLeft = parseFloat(el.style.left) || 0, startTop = parseFloat(el.style.top) || 0;
var width = parseFloat(el.style.width) || 0, height = parseFloat(el.style.height) || 0;
var moved = false;
function onMove(ev) {
var dx = ev.clientX - startX, dy = ev.clientY - startY;
if (Math.abs(dx) > 3 || Math.abs(dy) > 3) moved = true;
var newLeft = Math.max(0, Math.min(SCENE_WIDTH - width, startLeft + dx));
var newTop = Math.max(0, Math.min(SCENE_HEIGHT - height, startTop + dy));
el.style.left = newLeft + 'px';
el.style.top = newTop + 'px';
}
function onUp() {
document.removeEventListener('mousemove', onMove);
document.removeEventListener('mouseup', onUp);
if (moved) {
var newLeft = parseFloat(el.style.left), newTop = parseFloat(el.style.top);
updateScenePosFields(newLeft, newTop, width, height);
saveSceneObjectGeometry(id, newLeft, newTop, width, height);
} else {
window.location.href = SCREEN_EDIT_URL + '?selected=' + id;
}
}
document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onUp);
}
function onSceneObjectResizeMouseDown(e, id) {
e.stopPropagation();
e.preventDefault();
var el = document.getElementById('el-' + id);
var startX = e.clientX, startY = e.clientY;
var startWidth = parseFloat(el.style.width) || 0, startHeight = parseFloat(el.style.height) || 0;
var left = parseFloat(el.style.left) || 0, top = parseFloat(el.style.top) || 0;
function onMove(ev) {
var dx = ev.clientX - startX, dy = ev.clientY - startY;
var newWidth = Math.max(8, Math.min(SCENE_WIDTH - left, startWidth + dx));
var newHeight = Math.max(8, Math.min(SCENE_HEIGHT - top, startHeight + dy));
el.style.width = newWidth + 'px';
el.style.height = newHeight + 'px';
}
function onUp() {
document.removeEventListener('mousemove', onMove);
document.removeEventListener('mouseup', onUp);
var newWidth = parseFloat(el.style.width), newHeight = parseFloat(el.style.height);
updateScenePosFields(left, top, newWidth, newHeight);
saveSceneObjectGeometry(id, left, top, newWidth, newHeight);
}
document.addEventListener('mousemove', onMove);
document.addEventListener('mouseup', onUp);
}
// Champs numériques Position & taille du panneau de propriétés — mirror
// du comportement équivalent pour l'éditeur document (voir l'IIFE dans
// initBuilderPanel(), panel-init.js), mais ids/route distincts (pixels).
(function () {
var xEl = document.getElementById('scene-pos-x'), yEl = document.getElementById('scene-pos-y');
var wEl = document.getElementById('scene-pos-w'), hEl = document.getElementById('scene-pos-h');
if (!xEl || !wEl || !CURRENT_SELECTED_ID) return;
function apply() {
var canvasEl = document.getElementById('el-' + CURRENT_SELECTED_ID);
var x = parseFloat(xEl.value) || 0, y = parseFloat(yEl.value) || 0;
var w = parseFloat(wEl.value) || 1, h = parseFloat(hEl.value) || 1;
if (canvasEl) {
canvasEl.style.left = x + 'px'; canvasEl.style.top = y + 'px';
canvasEl.style.width = w + 'px'; canvasEl.style.height = h + 'px';
}
saveSceneObjectGeometry(CURRENT_SELECTED_ID, x, y, w, h);
}
[xEl, yEl, wEl, hEl].forEach(function (el) { el.addEventListener('change', apply); });
})();
// Personnage d'un objet de scène : mirror de swapPersonnageCharacter()
// (personnage-panel.js), mais POST vers /scene-objects/.../personnage
// (voir routes/scenes/scene_object_personnage_data.py) plutôt que
// /elements/.../personnage — même réponse (animations résolues),
// relance l'aperçu animé via restartPersonnageIdlePreview() (personnage-
// preview.js, déjà générique : ne dépend que de data-element-id/
// data-personnage, présents sur un objet de scène comme sur un élément).
function swapSceneObjectCharacter(forgeCharacter) {
var body = document.getElementById('personnagePanelBody');
if (!body) return;
var objectId = parseInt(body.dataset.elementId, 10);
document.querySelectorAll('#personnageGallerySwap .iconGalleryTile').forEach(function (tile) {
tile.classList.toggle('selected', tile.getAttribute('onclick') === "swapSceneObjectCharacter('" + forgeCharacter + "')");
});
fetch('/game/' + GAME_SLUG + '/scene-objects/' + objectId + '/personnage', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ source: 'forge', forge_character: forgeCharacter }),
})
.then(function (r) { return r.json(); })
.then(function (json) {
if (json.animations) {
ELEMENT_ANIMATIONS_MAP[objectId] = json.animations;
restartPersonnageIdlePreview(objectId, json.animations);
}
});
}
+82 -13
View File
@@ -6,6 +6,17 @@
// Jinja) — voir static/js/play/ pour le même patron déjà en place côté
// moteur de jeu.
var FLOW_SCREEN_ID = SCREEN_ID;
// Fondations multi-éditeurs — ce même fichier (moteur de logique de flow
// partagé, voir le plan "Fondations d'une plateforme multi-éditeurs")
// sert aussi bien à l'éditeur document (cible trigger_element_id/
// target_element_id, vers _screen_elements) qu'à l'éditeur de scène 2D
// (cible trigger_object_id/target_object_id, vers _scene_objects) : posé
// à `true` par templates/scene_edit.html, sinon absent (donc falsy) sur
// templates/screen_edit.html — décide quelle paire de colonnes remplir à
// l'enregistrement d'un nœud (voir submitNodeForm()).
var FLOW_TARGETS_OBJECTS = typeof FLOW_TARGETS_OBJECTS !== 'undefined' ? FLOW_TARGETS_OBJECTS : false;
var FLOW_TRIGGER_ID_FIELD = FLOW_TARGETS_OBJECTS ? 'trigger_object_id' : 'trigger_element_id';
var FLOW_TARGET_ID_FIELD = FLOW_TARGETS_OBJECTS ? 'target_object_id' : 'target_element_id';
// Blocs de logique (voir screens/flow/blocks/) : ALL_FLOW porte TOUS les
// nœuds/arêtes de cet écran, tous blocs confondus (chargement inchangé) ;
// un seul bloc est ouvert à la fois (modale unique, à la Unity) — pas
@@ -44,7 +55,7 @@ function nodeLabel(node) {
return '⏱️ Toutes les ' + (node.trigger_interval_ms || '?') + ' ms';
}
return '🖱️ ' + (TRIGGER_LABELS_MAP[node.trigger_event] || 'Au clic') + ' sur ' +
(ELEMENTS_LABELS[node.trigger_element_id] || '?');
(ELEMENTS_LABELS[node.trigger_element_id || node.trigger_object_id] || '?');
}
if (node.node_type === 'condition') {
var clauseSubject = function(c, field, variable) {
@@ -83,6 +94,18 @@ function nodeLabel(node) {
base += ' → ' + (SCREENS_MAP[node.target_screen_id] || '?') + ' (ligne cliquée)';
} else if (node.action_type === 'activer_onglet') {
base += ' → ' + (ELEMENTS_LABELS[node.target_element_id] || '?') + ' (masque ses frères)';
} else if (node.action_type === 'modifier_objet_scene') {
// Fondations multi-éditeurs — équivalent scène de "modifier_element"
// ci-dessous, palette réduite (visibilité/position en pixels/
// orientation seulement).
base += ' → ' + (ELEMENTS_LABELS[node.target_element_id || node.target_object_id] || '?') + ' : ' + (ELEMENT_PROPERTY_LABELS_MAP[node.element_property] || node.element_property);
if (node.element_property === 'visibilite') {
base += ' = ' + (ELEMENT_VISIBILITY_LABELS_MAP[node.element_value] || node.element_value);
} else if (node.element_property === 'orientation') {
base += ' = ' + (ELEMENT_ORIENTATION_LABELS_MAP[node.element_value] || node.element_value);
} else {
base += ' = ' + node.element_value;
}
} else if (node.action_type === 'modifier_element') {
base += ' → ' + (ELEMENTS_LABELS[node.target_element_id] || '?') + ' : ' + (ELEMENT_PROPERTY_LABELS_MAP[node.element_property] || node.element_property);
if (node.element_property === 'visibilite') {
@@ -118,7 +141,7 @@ function nodeLabel(node) {
} else if (node.action_type === 'jouer_son') {
base += ' « ' + (node.data_value || '?') + ' »';
} else if (node.action_type === 'jouer_animation_sprite') {
base += ' → ' + (ELEMENTS_LABELS[node.target_element_id] || '?');
base += ' → ' + (ELEMENTS_LABELS[node.target_element_id || node.target_object_id] || '?');
try {
var spriteData = JSON.parse(node.data_value || '{}');
base += ' « ' + (spriteData.animation || '?') + ' » (' + (spriteData.fps || '?') + ' i/s' + (spriteData.loop ? ', boucle' : '') + ')';
@@ -502,6 +525,7 @@ function toggleFlowActionFields() {
var typeSel = document.getElementById('nf-action-type');
var screenSel = document.getElementById('nf-action-target-screen');
var elementFields = document.getElementById('nf-action-element-fields');
var objectFields = document.getElementById('nf-action-object-fields');
var dataFields = document.getElementById('nf-action-data-fields');
var variableFields = document.getElementById('nf-action-variable-fields');
var waitFields = document.getElementById('nf-action-wait-fields');
@@ -510,24 +534,53 @@ function toggleFlowActionFields() {
var soundFields = document.getElementById('nf-action-sound-fields');
var spriteFields = document.getElementById('nf-action-sprite-fields');
var isOnglet = typeSel.value === 'activer_onglet';
soundFields.style.display = typeSel.value === 'jouer_son' ? 'block' : 'none';
spriteFields.style.display = typeSel.value === 'jouer_animation_sprite' ? 'block' : 'none';
// Champs propres à l'éditeur document ci-dessous (nf-action-element-*/
// -onglet-*) : absents du DOM de l'éditeur de scène (templates/
// scene_edit.html), d'où les gardes "if (x)" partout — même
// #nodeForm/flow-editor.js réutilisé pour les deux éditeurs (voir
// FLOW_TARGETS_OBJECTS en tête de ce fichier).
if (soundFields) soundFields.style.display = typeSel.value === 'jouer_son' ? 'block' : 'none';
if (spriteFields) spriteFields.style.display = typeSel.value === 'jouer_animation_sprite' ? 'block' : 'none';
if (typeSel.value === 'jouer_animation_sprite') onSpriteTargetElementChange();
screenSel.style.display = (typeSel.value === 'aller_a' || typeSel.value === 'ouvrir_ligne') ? 'block' : 'none';
elementFields.style.display = (typeSel.value === 'modifier_element' || isOnglet) ? 'block' : 'none';
dataFields.style.display = typeSel.value === 'modifier_donnee' ? 'block' : 'none';
variableFields.style.display = typeSel.value === 'modifier_variable' ? 'block' : 'none';
waitFields.style.display = typeSel.value === 'attendre' ? 'block' : 'none';
if (screenSel) screenSel.style.display = (typeSel.value === 'aller_a' || typeSel.value === 'ouvrir_ligne') ? 'block' : 'none';
if (elementFields) elementFields.style.display = (typeSel.value === 'modifier_element' || isOnglet) ? 'block' : 'none';
if (objectFields) objectFields.style.display = typeSel.value === 'modifier_objet_scene' ? 'block' : 'none';
if (dataFields) dataFields.style.display = typeSel.value === 'modifier_donnee' ? 'block' : 'none';
if (variableFields) variableFields.style.display = typeSel.value === 'modifier_variable' ? 'block' : 'none';
if (waitFields) waitFields.style.display = typeSel.value === 'attendre' ? 'block' : 'none';
if (eventFields) eventFields.style.display = typeSel.value === 'declencher_evenement' ? 'block' : 'none';
if (addRowFields) addRowFields.style.display = typeSel.value === 'ajouter_ligne' ? 'block' : 'none';
document.getElementById('nf-action-onglet-hint').style.display = isOnglet ? 'block' : 'none';
document.getElementById('nf-action-element-property-wrap').style.display = isOnglet ? 'none' : 'block';
var ongletHint = document.getElementById('nf-action-onglet-hint');
if (ongletHint) ongletHint.style.display = isOnglet ? 'block' : 'none';
var elementPropertyWrap = document.getElementById('nf-action-element-property-wrap');
if (elementPropertyWrap) elementPropertyWrap.style.display = isOnglet ? 'none' : 'block';
if (typeSel.value === 'modifier_element') toggleFlowElementPropertyValue();
if (typeSel.value === 'modifier_objet_scene') toggleFlowObjectPropertyValue();
if (typeSel.value === 'modifier_donnee') onFlowDefinitionChange('action');
updateDataValuePlaceholder('nf-action-data-operation', 'nf-action-data-value');
updateDataValuePlaceholder('nf-action-variable-operation', 'nf-action-variable-value');
}
// Fondations multi-éditeurs — équivalent de toggleFlowElementPropertyValue()
// pour l'action "Modifier un objet de scène" (palette réduite : pas de
// contenu/couleur/largeur/hauteur, juste visibilité/position en pixels/
// orientation — voir screens/scenes/flow_palette.py OBJECT_ACTION_PROPERTIES).
function toggleFlowObjectPropertyValue() {
var propSel = document.getElementById('nf-action-object-property');
if (!propSel) return;
var visSel = document.getElementById('nf-action-object-value-visibility');
var orientSel = document.getElementById('nf-action-object-value-orientation');
var numInp = document.getElementById('nf-action-object-value-number');
[visSel, orientSel, numInp].forEach(function(el) { if (el) el.style.display = 'none'; });
if (propSel.value === 'visibilite' && visSel) {
visSel.style.display = 'block';
} else if (propSel.value === 'orientation' && orientSel) {
orientSel.style.display = 'block';
} else if (numInp) {
numInp.style.display = 'block';
}
}
// "alea" (Phase 2 — hasard) attend "min,max" dans data_value (voir
// screens/data_actions/compute_operation.py) plutôt qu'un seul nombre
// comme les autres opérations — indice visuel dans le champ plutôt que
@@ -635,7 +688,7 @@ function submitNodeForm() {
} else if (payload.trigger_event === 'minuteur') {
payload.trigger_interval_ms = parseInt(document.getElementById('nf-trigger-interval').value, 10);
} else if (payload.trigger_event !== 'affichage') {
payload.trigger_element_id = parseInt(document.getElementById('nf-trigger-element').value, 10);
payload[FLOW_TRIGGER_ID_FIELD] = parseInt(document.getElementById('nf-trigger-element').value, 10);
}
} else if (currentNodeFormType === 'condition') {
var condSource = document.getElementById('nf-cond-source').value;
@@ -717,6 +770,22 @@ function submitNodeForm() {
payload.element_value = v1;
}
}
} else if (actionType === 'modifier_objet_scene') {
// Fondations multi-éditeurs — équivalent scène de "modifier_element"
// ci-dessus, palette réduite (voir OBJECT_ACTION_PROPERTIES) : pas
// de va-et-vient/couleur, juste visibilité/position en pixels/
// orientation. FLOW_TARGET_ID_FIELD vaut "target_object_id" ici
// (voir tête de fichier).
payload[FLOW_TARGET_ID_FIELD] = parseInt(document.getElementById('nf-action-target-object').value, 10);
var objProp = document.getElementById('nf-action-object-property').value;
payload.element_property = objProp;
if (objProp === 'visibilite') {
payload.element_value = document.getElementById('nf-action-object-value-visibility').value;
} else if (objProp === 'orientation') {
payload.element_value = document.getElementById('nf-action-object-value-orientation').value;
} else {
payload.element_value = document.getElementById('nf-action-object-value-number').value;
}
} else if (actionType === 'modifier_donnee') {
payload.target_definition_id = parseInt(document.getElementById('nf-action-data-definition').value, 10);
payload.target_row_id = parseInt(document.getElementById('nf-action-data-row').value, 10) || null;
@@ -754,7 +823,7 @@ function submitNodeForm() {
// logique, qui continuaient à jouer les frames de l'ancien
// personnage indéfiniment (bug signalé par l'utilisateur).
var elementId = document.getElementById('nf-action-sprite-target-element').value;
payload.target_element_id = parseInt(elementId, 10);
payload[FLOW_TARGET_ID_FIELD] = parseInt(elementId, 10);
payload.data_value = JSON.stringify({
animation: document.getElementById('nf-action-sprite-animation-name').value,
fps: parseInt(document.getElementById('nf-action-sprite-fps').value, 10) || 8,