// ---------- Panneau de propriétés "🎭 Personnage" (Phase 8) ---------- // Panneau sur-mesure (custom_panel, voir registry.py) : PAS de soumission // générique via #propsForm — chaque changement s'enregistre en AJAX vers // POST /game//elements//personnage (element_set_personnage_data.py), // qui renvoie les animations résolues pour relancer immédiatement l'aperçu // animé (voir personnage-preview.js) sans recharger la page. L'import de // sprites personnalisés est retiré pour le moment — seuls les personnages // Forge (SPRITE_LIBRARY) sont proposés. function _personnageElementId() { var body = document.getElementById('personnagePanelBody'); return body ? parseInt(body.dataset.elementId, 10) : null; } function _savePersonnageData(data) { var elementId = _personnageElementId(); if (!elementId) return Promise.resolve(); return fetch('/game/' + GAME_SLUG + '/elements/' + elementId + '/personnage', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data), }) .then(function (r) { return r.json(); }) .then(function (json) { if (json.animations) { // Reste en phase avec le sélecteur d'animation du nœud de flow/clip // de Timeline (voir flow-editor.js/animation-timeline.js), sans // recharger la page. ELEMENT_ANIMATIONS_MAP[elementId] = json.animations; restartPersonnageIdlePreview(elementId, json.animations); } return json; }); } function swapPersonnageCharacter(forgeCharacter) { document.querySelectorAll('#personnageGallerySwap .iconGalleryTile').forEach(function (tile) { tile.classList.toggle('selected', tile.getAttribute('onclick') === "swapPersonnageCharacter('" + forgeCharacter + "')"); }); _savePersonnageData({ source: 'forge', forge_character: forgeCharacter }); }