Enrichit les declencheurs/actions de scene (clic/survol/affichage, surbrillance/video/son/visibilite/indication/attendre) et fiabilise la pose d'un fond/decor importe
- 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:
co-authored by
Claude Sonnet 5
parent
b07b231a61
commit
559331f9cf
@@ -16,7 +16,7 @@
|
||||
// automatiquement sélectionné/ouvert dans le panneau de propriétés (voir
|
||||
// onSceneObjectMouseDown, qui recharge encore la page pour ça) — juste
|
||||
// posé sur le canevas, cliquable ensuite comme n'importe quel autre.
|
||||
function addSceneObject(kind, forgeCharacter, backgroundSlug, assetId) {
|
||||
function addSceneObject(kind, forgeCharacter, backgroundSlug, assetId, x, y) {
|
||||
var body = new FormData();
|
||||
body.append('kind', kind);
|
||||
if (forgeCharacter) body.append('forge_character', forgeCharacter);
|
||||
@@ -24,6 +24,10 @@ function addSceneObject(kind, forgeCharacter, backgroundSlug, assetId) {
|
||||
// "Mes assets" (voir auth/user_assets_dir.py) : une image importée par
|
||||
// le créateur ou générée par l'IA, distincte de BACKGROUND_LIBRARY.
|
||||
if (assetId) body.append('asset_id', assetId);
|
||||
// Vrai glisser-déposer (voir onCanvasDrop ci-dessous) : position EXACTE
|
||||
// du dépôt — absent pour un simple clic sur la galerie, qui garde le
|
||||
// défaut habituel (100,100, voir screens/scenes/add_scene_object.py).
|
||||
if (x != null && y != null) { body.append('x', x); body.append('y', y); }
|
||||
fetch("/game/" + GAME_SLUG + "/screens/" + SCREEN_ID + "/scene-objects/add", {
|
||||
method: 'POST', headers: { 'X-Requested-With': 'fetch' }, body: body,
|
||||
})
|
||||
@@ -65,18 +69,113 @@ function addSceneObject(kind, forgeCharacter, backgroundSlug, assetId) {
|
||||
.catch(function () { window.location.reload(); });
|
||||
}
|
||||
|
||||
// ---------- Vrai glisser-déposer depuis la galerie (demande explicite :
|
||||
// "n'importe où sur la scène", pas seulement un clic qui pose à une
|
||||
// position par défaut) ----------
|
||||
// dataTransfer ne porte que des CHAÎNES : les 4 arguments d'addSceneObject
|
||||
// sont sérialisés en JSON plutôt qu'éclatés en plusieurs champs (certains
|
||||
// sont `null` — JSON les distingue proprement d'une chaîne vide, un
|
||||
// simple attribut HTML ne le pourrait pas). `draggable="true"` sur un
|
||||
// <button> coexiste nativement avec son `onclick` (un clic sans
|
||||
// mouvement déclenche toujours "click", un glisser réel "dragstart" —
|
||||
// jamais les deux à la fois) : le clic simple (pose à la position par
|
||||
// défaut) continue donc de fonctionner exactement comme avant.
|
||||
function onGalleryTileDragStart(ev, kind, forgeCharacter, backgroundSlug, assetId) {
|
||||
ev.dataTransfer.effectAllowed = 'copy';
|
||||
ev.dataTransfer.setData('application/json', JSON.stringify({
|
||||
kind: kind, forgeCharacter: forgeCharacter || null,
|
||||
backgroundSlug: backgroundSlug || null, assetId: assetId || null,
|
||||
}));
|
||||
}
|
||||
|
||||
// Un navigateur refuse "drop" par défaut sur TOUT élément — preventDefault()
|
||||
// sur "dragover" est ce qui l'autorise explicitement ici (voir MDN, aucun
|
||||
// contournement plus simple).
|
||||
function onCanvasDragOver(ev) {
|
||||
ev.preventDefault();
|
||||
ev.dataTransfer.dropEffect = 'copy';
|
||||
}
|
||||
|
||||
function onCanvasDrop(ev) {
|
||||
ev.preventDefault();
|
||||
var raw = ev.dataTransfer.getData('application/json');
|
||||
if (!raw) return;
|
||||
var data;
|
||||
try { data = JSON.parse(raw); } catch (e) { return; }
|
||||
var canvas = document.getElementById('canvas');
|
||||
var rect = canvas.getBoundingClientRect();
|
||||
// Pas de zoom/échelle CSS sur #canvas (voir templates/scene_edit.html,
|
||||
// juste width/height en px fixes) : une simple soustraction suffit,
|
||||
// aucune division par un facteur d'échelle à appliquer. getBoundingClientRect()
|
||||
// intègre déjà nativement le défilement de .canvasFrame.
|
||||
var x = ev.clientX - rect.left;
|
||||
var y = ev.clientY - rect.top;
|
||||
addSceneObject(data.kind, data.forgeCharacter, data.backgroundSlug, data.assetId, x, y);
|
||||
}
|
||||
|
||||
// "Mes assets" (voir routes/assets/upload_user_asset.py, panneau "Mes
|
||||
// assets" de scene_edit.html) : un simple rechargement après l'upload —
|
||||
// la galerie est rendue côté serveur (user_assets), pas la peine de la
|
||||
// reconstruire en JS pour un flux qui n'arrive pas souvent.
|
||||
function uploadUserAsset(inputEl) {
|
||||
// `sceneKind` ("fond"/"decor", images uniquement — demande explicite :
|
||||
// "un bouton d'import séparé pour les images de fond et les objets") :
|
||||
// décide comment un CLIC sur la tuile résultante pose l'objet (voir
|
||||
// screens/scenes/add_scene_object.py) — absent pour un son/une vidéo,
|
||||
// qui n'ont rien à "poser".
|
||||
function uploadUserAsset(inputEl, sceneKind) {
|
||||
var file = inputEl.files && inputEl.files[0];
|
||||
if (!file) return;
|
||||
var body = new FormData();
|
||||
body.append('file', file);
|
||||
if (sceneKind) body.append('scene_kind', sceneKind);
|
||||
fetch('/assets/upload', { method: 'POST', body: body })
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function () { window.location.reload(); })
|
||||
.then(function (r) {
|
||||
// Bug corrigé : un rechargement inconditionnel masquait toute
|
||||
// erreur serveur (ex. son de plus de 3 minutes, voir
|
||||
// auth/validate_audio_duration.py) — le fichier semblait juste
|
||||
// "disparaître" sans explication, jamais ajouté à la galerie.
|
||||
return r.json().then(function (json) { return { ok: r.ok, json: json }; });
|
||||
})
|
||||
.then(function (result) {
|
||||
if (!result.ok) { alert(result.json.error || "Impossible d'importer ce fichier."); return; }
|
||||
window.location.reload();
|
||||
})
|
||||
.catch(function () { window.location.reload(); });
|
||||
}
|
||||
|
||||
// Suppression d'un fichier de "Mes assets" (demande explicite : "voir,
|
||||
// utiliser ou supprimer des son, image et vidéo") — voir
|
||||
// routes/assets/delete_user_asset.py. Un simple rechargement après coup,
|
||||
// comme uploadUserAsset() juste au-dessus : ce panneau ne se recompose
|
||||
// pas assez souvent pour justifier une suppression ciblée du DOM.
|
||||
function deleteUserAsset(assetId, name) {
|
||||
if (!confirm('Supprimer "' + name + '" ? Cette action est définitive.')) return;
|
||||
fetch('/assets/' + assetId, { method: 'DELETE' })
|
||||
.then(function (r) { return r.json().then(function (json) { return { ok: r.ok, json: json }; }); })
|
||||
.then(function (result) {
|
||||
if (!result.ok) { alert(result.json.error || "Impossible de supprimer ce fichier."); return; }
|
||||
window.location.reload();
|
||||
})
|
||||
.catch(function () { window.location.reload(); });
|
||||
}
|
||||
|
||||
// "→ Fond"/"→ Décor" (demande diagnostiquée depuis un "décalage" : une
|
||||
// image importée par le mauvais bouton, ou avant la séparation fond/décor,
|
||||
// restait bloquée dans la mauvaise section, impossible à poser comme fond
|
||||
// sans la ré-importer en double) — voir auth/update_user_asset_scene_kind.py.
|
||||
// Ne touche jamais un objet DÉJÀ posé sur une scène : juste la classification
|
||||
// de l'ASSET, pour que la prochaine pose utilise la bonne section/le bon
|
||||
// rendu (object-fit:fill pour un fond, :contain pour un décor).
|
||||
function reclassifyUserAsset(assetId, sceneKind) {
|
||||
fetch('/assets/' + assetId + '/scene-kind', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ scene_kind: sceneKind }),
|
||||
})
|
||||
.then(function (r) { return r.json().then(function (json) { return { ok: r.ok, json: json }; }); })
|
||||
.then(function (result) {
|
||||
if (!result.ok) { alert(result.json.error || "Reclassement impossible."); return; }
|
||||
window.location.reload();
|
||||
})
|
||||
.catch(function () { window.location.reload(); });
|
||||
}
|
||||
|
||||
@@ -137,6 +236,24 @@ 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 }),
|
||||
}).then(function (r) { return r.json(); }).then(function (data) {
|
||||
// Bug corrigé (même famille que iaChatRefreshAfterTurn(), voir
|
||||
// static/js/ia/ia-chat.js) : un "fond" redimensionné/déplacé à la
|
||||
// souris au-delà du champ de la caméra doit élargir le "monde" tout
|
||||
// de suite — sans ça, window.SCENE_WIDTH/SCENE_HEIGHT (posées une
|
||||
// seule fois à l'ouverture de la page) restaient à leur ancienne
|
||||
// valeur et le glisser-déposer continuait de tout borner à l'ancien
|
||||
// cadre, rendant injoignable à la souris tout ce qui dépassait
|
||||
// désormais la scène nominale.
|
||||
if (data && typeof data.world_width === 'number' && typeof data.world_height === 'number') {
|
||||
window.SCENE_WIDTH = data.world_width;
|
||||
window.SCENE_HEIGHT = data.world_height;
|
||||
var canvas = document.getElementById('canvas');
|
||||
if (canvas) {
|
||||
canvas.style.width = data.world_width + 'px';
|
||||
canvas.style.height = data.world_height + 'px';
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user