Enrichit les declencheurs/actions de scene (clic/survol/affichage, surbrillance/video/son/visibilite/indication/attendre) et fiabilise la pose d'un fond/decor importe
Build and deploy / test-python (push) Successful in 10m59s
Build and deploy / test-js (push) Successful in 1m27s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped

- Ajoute clic/survol/affichage-ecran comme declencheurs, et surbrillance,
  video, son, visibilite, indication, attendre comme actions, utilisables
  aussi bien par l'editeur manuel (menu lateral Objets/Ecran) que par
  Ruby (IA), avec blocs deplacables/supprimables dans une chaine.
- Corrige plusieurs variantes du bug "impossible de poser un objet hors
  du champ de la camera" (troncature du chainage d'actions a 4 maillons,
  fond importe pose a 128x128 au lieu de sa taille reelle, decalage du
  fond au vrai glisser-depose, redimensionnement manuel jamais propage
  au monde).
- Ajoute un vrai glisser-depose depuis la galerie vers la scene, la
  gestion complete de "Mes assets" (sous-sections Fonds/Decors/Sons/
  Videos, suppression, reclassement fond<->decor sans re-upload).
- Ajoute l'upload de son (limite 3 min) et de video (MP4 uniquement,
  limite 5 min), avec validation de la duree reelle du fichier, et une
  replique audio optionnelle dans une bulle de dialogue.
- Fixe la taille de pose d'un objet/decor importe a 200x200 avec une
  boite de collision de 150x150.
- Filtre le selecteur de fichier des actions son/video par type reel.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
william
2026-09-12 00:28:46 +02:00
co-authored by Claude Sonnet 5
parent b07b231a61
commit 559331f9cf
67 changed files with 4739 additions and 306 deletions
+300 -72
View File
@@ -11,10 +11,23 @@
var COLLISION_ACTION_LABELS = {
dialogue: 'Déclencher ce dialogue', variable: 'Modifier une variable',
surbrillance: 'Mettre en surbrillance', visibilite: 'Rendre visible/invisible',
son: 'Jouer un son', video: 'Jouer une vidéo', indication: 'Afficher une indication',
attendre: 'Attendre quelques secondes',
interagir: 'Interagir avec une touche', condition: 'Si une condition est vraie',
};
var COLLISION_WIZARD_ACTION_TYPES = ['dialogue', 'variable', 'interagir', 'condition'];
var COLLISION_TRIGGER_LABELS = { collision: 'À la collision' };
// Ordre d'affichage — "interagir"/"condition" en dernier (comportements
// spéciaux), le reste (les FEUILLES, voir screens/rendering/
// collision_rules.py::LEAF_ACTION_TYPES) dans le même ordre que
// COLLISION_CHAIN_CHOICES ci-dessous, pour rester cohérent d'un écran à
// l'autre de l'assistant.
var COLLISION_WIZARD_ACTION_TYPES = ['dialogue', 'variable', 'surbrillance', 'visibilite', 'son', 'video', 'indication', 'attendre', 'interagir', 'condition'];
// FEUILLES chaînables via "then" — utilisées à la fois pour l'étape
// "Ensuite ?" (renderCollisionWizardChainStep) et les branches Vrai/Faux
// d'une condition (renderCollisionWizardBranchStep) : MÊME liste des deux
// côtés, voir screens/rendering/collision_rules.py::LEAF_ACTION_TYPES.
var COLLISION_CHAIN_CHOICES = ['dialogue', 'variable', 'surbrillance', 'visibilite', 'son', 'video', 'indication', 'attendre'];
var COLLISION_TRIGGER_LABELS = { collision: 'À la collision', clic: 'Au clic', survol: 'Au survol', affichage: 'À l\'affichage de l\'écran' };
// Opérations qui n'ont besoin d'AUCUNE valeur (voir screens/labels/
// data_operations.py::DATA_OPERATIONS) — le champ valeur de l'étape
// "Modifier une variable" (renderCollisionWizardVariableStep) se masque
@@ -60,7 +73,17 @@ var _collisionWizard = null;
// Appelée après avoir choisi l'objet dans la modale d'ajout de déclencheur
// (voir triggerSelectObject, trigger-editor.js).
function openTriggerWizard(objectId, bodyEl) {
_collisionWizard = { objectId: objectId, trigger: null, bodyEl: bodyEl };
_collisionWizard = { target: 'object', objectId: objectId, trigger: null, bodyEl: bodyEl };
renderCollisionWizardTriggerStep();
}
// Section "Déclencheurs de l'écran" (voir static/js/triggers/
// trigger-editor.js::openScreenAddTriggerModal) — même assistant, mais
// SANS objet propriétaire (voir screens/rendering/screen_triggers.py) :
// un seul déclencheur possible ("affichage"), donc l'étape 1 n'a qu'un
// choix, gardée pour rester cohérente avec le parcours objet.
function openScreenTriggerWizard(screenId, bodyEl) {
_collisionWizard = { target: 'screen', screenId: screenId, trigger: null, bodyEl: bodyEl };
renderCollisionWizardTriggerStep();
}
@@ -68,18 +91,20 @@ function collisionWizardBodyEl() {
return _collisionWizard.bodyEl;
}
// Étape 1 — déclencheur (un seul choix pour l'instant, "collision") : la
// carte à choix reste affichée telle quelle (plutôt que sautée
// automatiquement) pour rester extensible à de futurs types sans rien
// changer à la structure de l'assistant.
// Étape 1 — déclencheur : "collision"/"clic"/"survol" pour un objet,
// "affichage" (seul choix) pour un déclencheur d'écran — la carte à choix
// reste affichée telle quelle même à un seul choix (plutôt que sautée
// automatiquement) pour rester cohérente d'un parcours à l'autre.
function renderCollisionWizardTriggerStep() {
var keys = _collisionWizard.target === 'screen' ? ['affichage'] : ['collision', 'clic', 'survol'];
var buttons = keys.map(function (key, i) {
return '<button type="button" class="collisionWizardChoice" style="animation-delay:' + (i * 40) + 'ms" onclick="collisionWizardChooseTrigger(\'' + key + '\')">' +
'<span>' + COLLISION_TRIGGER_LABELS[key] + '</span>' +
'</button>';
}).join('');
collisionWizardBodyEl().innerHTML =
'<p class="hint mb-2">Cette règle se déclenche...</p>' +
'<div class="collisionWizardChoices">' +
'<button type="button" class="collisionWizardChoice" style="animation-delay:0ms" onclick="collisionWizardChooseTrigger(\'collision\')">' +
'<span>' + COLLISION_TRIGGER_LABELS.collision + '</span>' +
'</button>' +
'</div>';
'<div class="collisionWizardChoices">' + buttons + '</div>';
}
function collisionWizardChooseTrigger(trigger) {
@@ -90,9 +115,13 @@ function collisionWizardChooseTrigger(trigger) {
// Étape 2 (et, pour "interagir", étape 3 en second niveau — `nested`
// distingue les deux, "interagir" n'étant proposé qu'au premier niveau,
// voir screens/rendering/collision_rules.py::_MAX_ACTION_DEPTH) :
// quelle action exécuter.
// quelle action exécuter. "interagir" n'a de sens QUE pour "collision"
// (bulle "Appuie sur X" tant que le contact dure) — jamais pour "clic"/
// "survol"/"affichage", qui n'ont pas cette notion de contact prolongé.
function renderCollisionWizardActionStep(nested) {
var types = nested ? COLLISION_WIZARD_ACTION_TYPES.filter(function (t) { return t !== 'interagir'; }) : COLLISION_WIZARD_ACTION_TYPES;
var types = (nested || _collisionWizard.trigger !== 'collision')
? COLLISION_WIZARD_ACTION_TYPES.filter(function (t) { return t !== 'interagir'; })
: COLLISION_WIZARD_ACTION_TYPES;
var buttons = types.map(function (type, i) {
return '<button type="button" class="collisionWizardChoice" style="animation-delay:' + (i * 40) + 'ms" onclick="collisionWizardChooseAction(\'' + type + '\', ' + (nested ? 'true' : 'false') + ')">' +
'<span>' + COLLISION_ACTION_LABELS[type] + '</span>' +
@@ -121,46 +150,250 @@ function collisionWizardFinalizeAction(action, nested) {
collisionWizardSaveRule(action);
}
// Enregistre via la route /game/<slug>/triggers (routes/triggers/triggers.py)
// — même contrat "liste ENTIÈRE" que l'ancien éditeur de collision
// (recharge les règles existantes de l'objet, y ajoute la nouvelle,
// sauvegarde tout), géré côté serveur : le client ne poste que la
// nouvelle règle + l'id d'objet.
// Enregistre via /game/<slug>/triggers (objet, routes/triggers/triggers.py)
// ou /game/<slug>/screens/<id>/triggers (écran, routes/screens/
// screen_triggers.py) selon _collisionWizard.target — même contrat
// "liste ENTIÈRE" des deux côtés (recharge les règles existantes, y
// ajoute la nouvelle, sauvegarde tout), géré côté serveur.
function collisionWizardSaveRule(action) {
var rule = { trigger: _collisionWizard.trigger, action: action };
closeTriggerModal();
fetch('/game/' + GAME_SLUG + '/triggers', {
var isScreen = _collisionWizard.target === 'screen';
var url = isScreen
? '/game/' + GAME_SLUG + '/screens/' + _collisionWizard.screenId + '/triggers'
: '/game/' + GAME_SLUG + '/triggers';
var body = isScreen ? { rule: rule } : { object_id: _collisionWizard.objectId, rule: rule };
fetch(url, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ object_id: _collisionWizard.objectId, rule: rule }),
}).then(function () { triggerLoadAll(); });
body: JSON.stringify(body),
}).then(function () { isScreen ? screenTriggerLoadAll() : triggerLoadAll(); });
}
// "dialogue"/"variable" (des FEUILLES, voir screens/rendering/
// collision_rules.py) enchaînent sur "Ensuite ?" (voir
// renderCollisionWizardChainStep — Marquer terminé + chaîner une autre
// action, demande explicite : "pouvoir modifier un déclencheur pour lui
// ajouter des actions") ; "interagir" enchaîne sur une carte de plus
// (voir collisionWizardFinalizeAction) ; "condition" enchaîne sur son
// propre mini-formulaire (voir renderCollisionWizardConditionStep).
// Construit une FEUILLE du type demandé — passe par ses éventuelles
// étapes intermédiaires (objet ciblé, valeur, fichier "Mes assets"...)
// puis appelle `onBuilt(action)`. PARTAGÉ entre le premier choix
// d'action (collisionWizardChooseAction), le choix "Ensuite ?"
// (collisionWizardChainDone), les branches Vrai/Faux d'une condition
// (collisionWizardChooseBranch) et "+ Ajouter une action" sur un
// déclencheur déjà posé (trigger-editor.js::triggerAppendLeaf/
// screenTriggerAppendLeaf) — ces quatre points d'entrée ont besoin
// EXACTEMENT du même parcours pour chaque type de feuille.
function collisionWizardBuildLeafAction(type, onBuilt) {
if (type === 'dialogue') {
onBuilt({ type: 'dialogue', dialogue: { id: collisionGenerateActionId(), lines: [] } });
return;
}
if (type === 'variable') {
renderCollisionWizardVariableStep(onBuilt);
return;
}
if (type === 'surbrillance' || type === 'visibilite') {
renderCollisionWizardObjectTargetStep(function (objectId) {
renderCollisionWizardValeurStep(type, function (valeur) {
onBuilt({ type: type, id: collisionGenerateActionId(), object_id: objectId, valeur: valeur });
});
});
return;
}
if (type === 'son') {
renderCollisionWizardAssetStep(function (assetUrl) {
onBuilt({ type: 'son', id: collisionGenerateActionId(), asset_url: assetUrl });
}, 'audio');
return;
}
if (type === 'video') {
renderCollisionWizardAssetStep(function (assetUrl) {
renderCollisionWizardVideoModeStep(function (mode) {
onBuilt({ type: 'video', id: collisionGenerateActionId(), asset_url: assetUrl, mode: mode });
});
}, 'video');
return;
}
if (type === 'indication') {
renderCollisionWizardObjectTargetStep(function (objectId) {
_collisionWizard.pendingIndicationObjectId = objectId;
renderCollisionWizardIndicationStep(function (action) { onBuilt(action); });
});
return;
}
// 'attendre'
renderCollisionWizardAttendreStep(function (secondes) {
onBuilt({ type: 'attendre', id: collisionGenerateActionId(), data_value: secondes });
});
}
// ---------- Étape "attendre" (nombre de secondes) ----------
function renderCollisionWizardAttendreStep(onChoose) {
collisionWizardBodyEl().innerHTML =
'<p class="hint mb-2">Attendre combien de secondes avant de continuer ?</p>' +
'<input type="number" id="attendreWizSecondes" min="0" step="0.5" placeholder="2" style="width:100px; margin-bottom:10px;">' +
'<button type="button" class="button primary" onclick="collisionWizardAttendreNext()">Continuer</button>';
_collisionWizard.onAttendreChosen = onChoose;
}
function collisionWizardAttendreNext() {
var raw = document.getElementById('attendreWizSecondes').value;
var secondes = String(Math.max(0, parseFloat(raw) || 0));
var onChoose = _collisionWizard.onAttendreChosen;
_collisionWizard.onAttendreChosen = null;
onChoose(secondes);
}
// ---------- Étape "Sur quel objet ?" (surbrillance/visibilite/indication) ----------
// Réutilise le MÊME catalogue que le choix d'objet propriétaire du
// déclencheur (voir triggerRenderObjectStep, trigger-editor.js) : ces
// trois actions peuvent cibler N'IMPORTE QUEL objet éligible du jeu, pas
// forcément celui qui porte la règle (ex. le joueur touche un
// interrupteur -> une indication apparaît près d'une porte ailleurs).
function renderCollisionWizardObjectTargetStep(onChoose) {
collisionWizardBodyEl().innerHTML =
'<p class="hint mb-2">Sur quel objet ?</p>' +
'<div id="collisionWizardObjectTargetGrid" class="collisionWizardChoices">Chargement…</div>';
fetch('/game/' + GAME_SLUG + '/triggers/objects-catalog')
.then(function (r) { return r.json(); })
.then(function (objects) {
var grid = document.getElementById('collisionWizardObjectTargetGrid');
if (!grid) return;
if (!objects.length) { grid.innerHTML = '<p class="hint">Aucun objet éligible sur ce jeu.</p>'; return; }
grid.innerHTML = objects.map(function (o, i) {
return '<button type="button" class="collisionWizardChoice questTriggerObjectChoice" style="animation-delay:' + (i * 40) + 'ms" onclick="collisionWizardObjectTargetChosen(' + o.id + ')">' +
'<div class="questTriggerObjectThumb">' + collisionRuleThumbHtml(o) + '</div>' +
'<span>' + (o.name || '').replace(/</g, '&lt;') + '</span>' +
'</button>';
}).join('');
_collisionWizard.onObjectTargetChosen = onChoose;
});
}
function collisionWizardObjectTargetChosen(objectId) {
var onChoose = _collisionWizard.onObjectTargetChosen;
_collisionWizard.onObjectTargetChosen = null;
onChoose(objectId);
}
// ---------- Étape "valeur" (surbrillance/visibilite) ----------
// SURBRILLANCE_LABELS_MAP/ELEMENT_VISIBILITY_LABELS_MAP posés par
// templates/scene_edit.html (voir screens/labels/surbrillance_values.py
// et screens/labels/element_visibility.py — mêmes valeurs que le moteur).
function renderCollisionWizardValeurStep(actionType, onChoose) {
var labelsMap = actionType === 'surbrillance' ? SURBRILLANCE_LABELS_MAP : ELEMENT_VISIBILITY_LABELS_MAP;
var keys = Object.keys(labelsMap);
var buttons = keys.map(function (key, i) {
return '<button type="button" class="collisionWizardChoice" style="animation-delay:' + (i * 40) + 'ms" onclick="collisionWizardValeurChosen(\'' + key + '\')">' +
'<span>' + labelsMap[key] + '</span>' +
'</button>';
}).join('');
collisionWizardBodyEl().innerHTML =
'<p class="hint mb-2">' + (actionType === 'surbrillance' ? 'Surbrillance :' : 'Visibilité :') + '</p>' +
'<div class="collisionWizardChoices">' + buttons + '</div>';
_collisionWizard.onValeurChosen = onChoose;
}
function collisionWizardValeurChosen(valeur) {
var onChoose = _collisionWizard.onValeurChosen;
_collisionWizard.onValeurChosen = null;
onChoose(valeur);
}
// ---------- Étape "quel fichier ?" (son/vidéo) ----------
// USER_ASSETS_OPTIONS posé par templates/scene_edit.html ([{id, url,
// name, kind}], voir auth/user_asset_kind.py) — le créateur doit d'abord
// importer un fichier dans "Mes assets" (panneau de gauche) avant de
// pouvoir le choisir ici. `kind` ("audio"/"video", demande explicite : "la
// vidéo devra être en mp4 ou webm") filtre la liste au bon type — sans
// ça, l'action "vidéo" proposait AUSSI les images et les sons (et
// inversement pour "son"), un fichier du mauvais type ne pouvant de toute
// façon jamais être lu par <video>/<audio> côté jeu.
function renderCollisionWizardAssetStep(onChoose, kind) {
var assets = USER_ASSETS_OPTIONS.filter(function (a) { return a.kind === kind; });
if (!assets.length) {
var label = kind === 'video' ? 'vidéo (mp4/webm)' : 'son';
collisionWizardBodyEl().innerHTML =
'<p class="hint mb-2">Aucun fichier ' + label + ' dans "Mes assets" pour l\'instant — importe-en un depuis le panneau de gauche (section "Mes assets"), puis recommence.</p>';
return;
}
var options = assets.map(function (a) {
return '<option value="' + a.url + '">' + (a.name || '').replace(/</g, '&lt;') + '</option>';
}).join('');
collisionWizardBodyEl().innerHTML =
'<p class="hint mb-2">Quel fichier (voir "Mes assets") ?</p>' +
'<select id="collisionWizardAssetSelect" style="width:100%; margin-bottom:10px;">' + options + '</select>' +
'<button type="button" class="button primary" onclick="collisionWizardAssetNext()">Continuer</button>';
_collisionWizard.onAssetChosen = onChoose;
}
function collisionWizardAssetNext() {
var url = document.getElementById('collisionWizardAssetSelect').value;
var onChoose = _collisionWizard.onAssetChosen;
_collisionWizard.onAssetChosen = null;
onChoose(url);
}
// ---------- Étape "comment l'afficher ?" (vidéo) ----------
function renderCollisionWizardVideoModeStep(onChoose) {
var keys = Object.keys(VIDEO_MODE_LABELS_MAP);
var buttons = keys.map(function (key, i) {
return '<button type="button" class="collisionWizardChoice" style="animation-delay:' + (i * 40) + 'ms" onclick="collisionWizardVideoModeChosen(\'' + key + '\')">' +
'<span>' + VIDEO_MODE_LABELS_MAP[key] + '</span>' +
'</button>';
}).join('');
collisionWizardBodyEl().innerHTML =
'<p class="hint mb-2">Comment l\'afficher ?</p>' +
'<div class="collisionWizardChoices">' + buttons + '</div>';
_collisionWizard.onVideoModeChosen = onChoose;
}
function collisionWizardVideoModeChosen(mode) {
var onChoose = _collisionWizard.onVideoModeChosen;
_collisionWizard.onVideoModeChosen = null;
onChoose(mode);
}
// ---------- Étape "indication" (texte + durée) ----------
// L'objet ciblé a déjà été choisi juste avant (voir
// collisionWizardBuildLeafAction) — mémorisé sur le wizard le temps de
// cette étape plutôt que reporté en paramètre partout.
function renderCollisionWizardIndicationStep(onChoose) {
collisionWizardBodyEl().innerHTML =
'<p class="hint mb-2">Texte de la bulle :</p>' +
'<input type="text" id="indicationWizText" placeholder="Ex. Clique ici pour continuer" style="width:100%; margin-bottom:8px;">' +
'<label class="hint" style="display:block; margin-bottom:10px;">Durée d\'affichage en ms (optionnel, 3000 par défaut) : ' +
'<input type="number" id="indicationWizDuree" min="500" step="500" placeholder="3000" style="width:100px;"></label>' +
'<button type="button" class="button primary" onclick="collisionWizardIndicationNext()">Continuer</button>';
_collisionWizard.onIndicationChosen = onChoose;
}
function collisionWizardIndicationNext() {
var texte = document.getElementById('indicationWizText').value;
var dureeRaw = document.getElementById('indicationWizDuree').value;
var action = { type: 'indication', id: collisionGenerateActionId(), object_id: _collisionWizard.pendingIndicationObjectId, texte: texte };
if (dureeRaw) action.duree_ms = parseInt(dureeRaw, 10);
_collisionWizard.pendingIndicationObjectId = null;
var onChoose = _collisionWizard.onIndicationChosen;
_collisionWizard.onIndicationChosen = null;
onChoose(action);
}
// "dialogue"/"variable"/"surbrillance"/"visibilite"/"son"/"video"/
// "indication" (des FEUILLES, voir screens/rendering/collision_rules.py)
// enchaînent sur "Ensuite ?" (voir renderCollisionWizardChainStep —
// Marquer terminé + chaîner une autre action, demande explicite :
// "pouvoir modifier un déclencheur pour lui ajouter des actions") ;
// "interagir" enchaîne sur une carte de plus (voir
// collisionWizardFinalizeAction) ; "condition" enchaîne sur son propre
// mini-formulaire (voir renderCollisionWizardConditionStep).
function collisionWizardChooseAction(type, nested) {
if (type === 'interagir') {
collisionWizardFinalizeAction({ type: type }, nested);
return;
}
if (type === 'dialogue') {
var leaf = { type: 'dialogue', dialogue: { id: collisionGenerateActionId(), lines: [] } };
renderCollisionWizardChainStep(leaf, function (finalLeaf) { collisionWizardFinalizeAction(finalLeaf, nested); });
return;
}
if (type === 'variable') {
renderCollisionWizardVariableStep(function (action) {
renderCollisionWizardChainStep(action, function (finalLeaf) { collisionWizardFinalizeAction(finalLeaf, nested); });
});
return;
}
if (type === 'condition') {
renderCollisionWizardConditionStep(nested);
return;
}
collisionWizardBuildLeafAction(type, function (leaf) {
renderCollisionWizardChainStep(leaf, function (finalLeaf) { collisionWizardFinalizeAction(finalLeaf, nested); });
});
}
// ---------- Étape "Modifier une variable" ----------
@@ -216,47 +449,53 @@ function collisionWizardVariableNext() {
// chaîner une autre action à la suite (voir screens/rendering/
// collision_rules.py::"then", demande explicite "pouvoir modifier un
// déclencheur pour lui ajouter des actions") — jamais bloqué à une seule
// action par déclencheur. `onDone(leafAction)` reçoit la PREMIÈRE
// feuille de la chaîne (les suivantes sont attachées via `.then`).
function renderCollisionWizardChainStep(leafAction, onDone) {
// action par déclencheur. `onDone(headAction)` reçoit la PREMIÈRE feuille
// de la chaîne (les suivantes sont attachées via `.then`) — `headAction`
// (3e paramètre, optionnel) est la TÊTE de la chaîne, transmise SANS
// changement à travers chaque ré-appel récursif : `leafAction`, lui,
// change à chaque étape (c'est la feuille COURANTE qu'on est en train
// d'éditer/étendre). Bug corrigé : sans `headAction` distincte, "Rien de
// plus" renvoyait `_collisionWizard.chainLeaf` — qui pointe vers la
// DERNIÈRE feuille ajoutée après un premier chaînage, pas la première —
// une chaîne "attendre -> puis dialogue" ne sauvegardait donc QUE le
// dialogue, "attendre" disparaissait silencieusement.
function renderCollisionWizardChainStep(leafAction, onDone, headAction) {
headAction = headAction || leafAction;
var completionHtml = leafAction.type === 'dialogue'
? '<label style="display:flex; align-items:center; gap:6px; font-size:13px; margin-bottom:10px;">' +
'<input type="checkbox" id="condWizMarkCompleted"' + (leafAction.mark_completed ? ' checked' : '') + '> Marquer ce dialogue comme terminé une fois joué' +
'</label>'
: '';
var choiceButtons = COLLISION_CHAIN_CHOICES.map(function (choice, i) {
return '<button type="button" class="collisionWizardChoice" style="animation-delay:' + ((i + 1) * 40) + 'ms" onclick="collisionWizardChainDone(\'' + choice + '\')"><span>' + COLLISION_ACTION_LABELS[choice] + '</span></button>';
}).join('');
collisionWizardBodyEl().innerHTML =
completionHtml +
'<p class="hint mb-2">Ensuite ?</p>' +
'<div class="collisionWizardChoices">' +
'<button type="button" class="collisionWizardChoice" style="animation-delay:0ms" onclick="collisionWizardChainDone(\'none\')"><span>Rien de plus</span></button>' +
'<button type="button" class="collisionWizardChoice" style="animation-delay:40ms" onclick="collisionWizardChainDone(\'dialogue\')"><span>Déclencher un dialogue</span></button>' +
'<button type="button" class="collisionWizardChoice" style="animation-delay:80ms" onclick="collisionWizardChainDone(\'variable\')"><span>Modifier une variable</span></button>' +
choiceButtons +
'</div>';
_collisionWizard.chainLeaf = leafAction;
_collisionWizard.chainHead = headAction;
_collisionWizard.onChainDone = onDone;
}
function collisionWizardChainDone(choice) {
var leafAction = _collisionWizard.chainLeaf;
var headAction = _collisionWizard.chainHead;
var onDone = _collisionWizard.onChainDone;
if (leafAction.type === 'dialogue') {
if (document.getElementById('condWizMarkCompleted').checked) leafAction.mark_completed = true;
else delete leafAction.mark_completed;
}
if (choice === 'none') {
onDone(leafAction);
onDone(headAction);
return;
}
if (choice === 'dialogue') {
var nextLeaf = { type: 'dialogue', dialogue: { id: collisionGenerateActionId(), lines: [] } };
collisionWizardBuildLeafAction(choice, function (nextLeaf) {
leafAction.then = nextLeaf;
renderCollisionWizardChainStep(nextLeaf, onDone);
return;
}
// 'variable'
renderCollisionWizardVariableStep(function (action) {
leafAction.then = action;
renderCollisionWizardChainStep(action, onDone);
renderCollisionWizardChainStep(nextLeaf, onDone, headAction);
});
}
@@ -303,16 +542,14 @@ function collisionWizardConditionNext(nested) {
// indépendamment l'une de l'autre.
function renderCollisionWizardBranchStep(branchKey) {
var label = branchKey === 'si_vrai' ? '✓ Si VRAI...' : '✕ Si FAUX...';
var choiceButtons = COLLISION_CHAIN_CHOICES.map(function (choice, i) {
return '<button type="button" class="collisionWizardChoice" style="animation-delay:' + (i * 40) + 'ms" onclick="collisionWizardChooseBranch(\'' + branchKey + '\', \'' + choice + '\')"><span>' + COLLISION_ACTION_LABELS[choice] + '</span></button>';
}).join('');
collisionWizardBodyEl().innerHTML =
'<p class="hint mb-2">' + label + '</p>' +
'<div class="collisionWizardChoices">' +
'<button type="button" class="collisionWizardChoice" style="animation-delay:0ms" onclick="collisionWizardChooseBranch(\'' + branchKey + '\', \'dialogue\')">' +
'<span>Déclencher un dialogue</span>' +
'</button>' +
'<button type="button" class="collisionWizardChoice" style="animation-delay:40ms" onclick="collisionWizardChooseBranch(\'' + branchKey + '\', \'variable\')">' +
'<span>Modifier une variable</span>' +
'</button>' +
'<button type="button" class="collisionWizardChoice" style="animation-delay:80ms" onclick="collisionWizardChooseBranch(\'' + branchKey + '\', \'none\')">' +
choiceButtons +
'<button type="button" class="collisionWizardChoice" style="animation-delay:' + (COLLISION_CHAIN_CHOICES.length * 40) + 'ms" onclick="collisionWizardChooseBranch(\'' + branchKey + '\', \'none\')">' +
'<span>Ne rien faire</span>' +
'</button>' +
'</div>';
@@ -324,20 +561,11 @@ function collisionWizardChooseBranch(branchKey, choice) {
collisionWizardBranchDone(branchKey);
return;
}
if (choice === 'dialogue') {
var leaf = { type: 'dialogue', dialogue: { id: collisionGenerateActionId(), lines: [] } };
collisionWizardBuildLeafAction(choice, function (leaf) {
renderCollisionWizardChainStep(leaf, function (finalLeaf) {
_collisionWizard.pendingCondition[branchKey] = finalLeaf;
collisionWizardBranchDone(branchKey);
});
return;
}
// 'variable'
renderCollisionWizardVariableStep(function (action) {
renderCollisionWizardChainStep(action, function (finalLeaf) {
_collisionWizard.pendingCondition[branchKey] = finalLeaf;
collisionWizardBranchDone(branchKey);
});
});
}