// ---------- 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() { const body = document.getElementById('personnagePanelBody'); return body ? Number.parseInt(body.dataset.elementId, 10) : null; } function _savePersonnageData(data) { const 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((r) => r.json()) .then((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((tile) => { tile.classList.toggle('selected', tile.getAttribute('onclick') === `swapPersonnageCharacter('${forgeCharacter}')`); }); _savePersonnageData({ source: 'forge', forge_character: forgeCharacter }); }