@@ -19,11 +19,11 @@
{% endblock %}
{% block content %}
<!-- Fondations multi - éditeurs — éditeur de scène 2D (jeux "jeu_2d", voir
db/games/get_game_type.py). Réutilise TEL QUEL le moteur de logique de
f low partagé (mêmes JS : flow - editor.js/tabs - and - blocks.js/
animation - timeline.js) — seule la palette de nœuds et le panneau
gauche/canevas diffèrent de templates/screen_edit.html. Voir le plan
"Fondations d'une platef orm e multi - éditeurs" . -->
db/games/get_game_type.py). Le graphe de logique générique (blocs de
logique/Timeline d'animation, voir templates/screen_edit.html, gardé
tel quel pour l'éditeur document) a été retiré de CET éditeur à la
demande de l'utilisateur, en prévision d'un éditeur dédié aux scènes
2D (collision/périmètre - > action), pas enc ore construit . -->
< p class = "hint" style = "background:var(--panel); border:1px solid var(--border); border-radius:8px; padding:10px 14px; margin:10px 0;" >
🎮 Jeu 2D — scène à taille fixe ({{ screen.scene_width }}× {{ screen.scene_height }} px).
< / p >
@@ -93,8 +93,6 @@
< div class = "builderCanvasArea" >
< div class = "builderTabs" id = "builderTabs" >
< button type = "button" class = "builderTabBtn active" data-tab = "screen" onclick = "switchBuilderTab('screen')" > 🖥️ Scène< / button >
< button type = "button" class = "builderTabBtn" data-tab = "blocks" onclick = "switchBuilderTab('blocks')" > 🧩 Blocs de logique< / button >
< button type = "button" class = "builderTabBtn" data-tab = "anim" onclick = "switchBuilderTab('anim')" > 🎬 Timeline d'animation< / button >
< button type = "button" class = "builderTabBtn" data-tab = "events" onclick = "switchBuilderTab('events')" > 📣 Événements< / button >
< / div >
@@ -107,11 +105,12 @@
onmousedown = "onSceneObjectMouseDown(event, {{ o.id }})" >
< div class = "canvasElementInner" > {{ o.rendered_html|safe }}< / div >
< div class = "resizeHandle" onmousedown = "onSceneObjectResizeMouseDown(event, {{ o.id }})" > < / div >
{% if o.id == selected_id %}
{% if o.id == selected_id and o.kind != 'fond' %}
<!-- Boîte de collision (voir screens/rendering/collision_settings.py) :
aperçu visuel + réglable directement au glisser — seulement pour
l'objet SÉLECTIONNÉ (celui dont le panneau "🧱 Collision" est ouvert),
voir initCollisionBoxOverlay() dans scene - editor.js. -->
voir initCollisionBoxOverlay() dans scene - editor.js. JAMAIS pour un
"fond" (voir la même garde sur le panneau "🧱 Collision" plus bas). -->
< div class = "collisionBoxOverlay {{ 'is-circle' if collision_settings.shape == 'cercle' else '' }} {{ 'is-disabled' if not collision_settings.enabled else '' }}"
id = "collisionBoxOverlay"
style = "left:{{ (o.width - collision_settings.width) / 2 + collision_settings.offset_x }}px; top:{{ (o.height - collision_settings.height) / 2 + collision_settings.offset_y }}px; width:{{ collision_settings.width }}px; height:{{ collision_settings.height }}px;"
@@ -126,445 +125,6 @@
< p class = "hint" style = "text-align:center;" > Glisse pour déplacer, tire le coin pour redimensionner, clique pour sélectionner. Positions en pixels (scène à taille fixe).< / p >
< / div >
< div class = "builderTabPanel" id = "blocksTabPanel" data-tab-panel = "blocks" >
< div class = "dashPanelHeader" >
< h2 > Blocs de logique< / h2 >
< button type = "button" class = "button is-small primary" onclick = "toggleDashCreate('blocks')" > + Créer un bloc< / button >
< / div >
< form method = "post" action = "{{ url_for('flow_block_new', slug=game.slug, screen_id=screen.id) }}" class = "dashCreateBar" id = "dashCreate-blocks" style = "display:none;" >
< input type = "text" name = "name" placeholder = "Nom du bloc (ex. Ouvrir la porte)" required autocomplete = "off" >
< input type = "text" name = "description" placeholder = "Description (optionnel)" >
< button type = "submit" class = "primary" > Créer< / button >
< / form >
{% if blocks_view %}
{% for b in blocks_view %}
< form method = "post" action = "{{ url_for('flow_block_edit', slug=game.slug, screen_id=screen.id, block_id=b.id) }}" id = "blockEditForm{{ b.id }}" > < / form >
{% endfor %}
< div class = "fieldsTableWrap" >
< table class = "table is-fullwidth is-hoverable fieldsTable" >
< thead > < tr > < th > Nom< / th > < th > Description< / th > < th > Objets concernés< / th > < th > Nœuds< / th > < th > < / th > < / tr > < / thead >
< tbody >
{% for b in blocks_view %}
< tr >
< td > < strong > {{ b.name }}< / strong > < / td >
< td > {{ b.description|truncatesentences(3) }}< / td >
< td > {% if b.element_labels %}{{ b.element_labels|join(', ') }}{% else %}< span class = "hint" > —< / span > {% endif %}< / td >
< td > {{ b.node_count }}< / td >
< td class = "fieldsTableActions" >
< button type = "button" class = "button is-small is-primary" onclick = "openLogicBlockPanel({{ b.id }})" title = "Ouvrir le graphe de ce bloc" > 🧩 Ouvrir< / button >
< button type = "button" class = "button is-small" onclick = "toggleBlockEditRow({{ b.id }})" title = "Modifier le nom/la description" > ✏️< / button >
< form method = "post" action = "{{ url_for('flow_block_delete', slug=game.slug, screen_id=screen.id, block_id=b.id) }}"
onsubmit = "return confirm('Supprimer le bloc « {{ b.name }} » ? Cela supprime aussi ses {{ b.node_count }} nœud(s) et leurs connexions.');" style = "display:inline;" >
< button type = "submit" class = "button is-small is-danger" title = "Supprimer" > 🗑️< / button >
< / form >
< / td >
< / tr >
< tr class = "hidden" id = "blockEditRow{{ b.id }}" >
< td colspan = "5" >
< div class = "dashCreateBar" style = "display:flex;" >
< input type = "text" form = "blockEditForm{{ b.id }}" name = "name" value = "{{ b.name }}" required >
< input type = "text" form = "blockEditForm{{ b.id }}" name = "description" value = "{{ b.description or '' }}" placeholder = "Description" >
< button type = "submit" form = "blockEditForm{{ b.id }}" class = "primary" > Enregistrer< / button >
< / div >
< / td >
< / tr >
{% endfor %}
< / tbody >
< / table >
< / div >
{% else %}
< p class = "hint" > Aucun bloc pour l'instant — crée le premier ci-dessus, puis ouvre-le pour poser des déclencheurs/conditions/actions.< / p >
{% endif %}
< / div >
< div class = "floatPanel floatPanel--center hidden" id = "logicBlockPanel" >
< div class = "floatPanelHeader" id = "logicBlockPanelHeader" >
< span class = "floatPanelTitle" > ⋮⋮ < span id = "logicBlockPanelTitle" > Bloc< / span > < / span >
< button type = "button" class = "button is-small floatPanelFullscreenBtn" title = "Plein écran" > ⛶< / button >
< button type = "button" class = "button is-small" onclick = "closeLogicBlockPanel()" > ✕< / button >
< / div >
< div class = "floatPanelBody logicBlockPanelBody" >
< div class = "flowToolbar" >
< button type = "button" class = "smallBtn button is-small" onclick = "openNodeForm('trigger')" > ➕ Déclencheur< / button >
< button type = "button" class = "smallBtn button is-small" onclick = "openNodeForm('condition')" > ➕ Condition< / button >
< button type = "button" class = "smallBtn button is-small" onclick = "openNodeForm('action')" > ➕ Action< / button >
< span class = "hint" > Clique un point de sortie d'un nœud, puis le point d'entrée d'un autre nœud, pour les relier. Reclique une connexion pour la supprimer.< / span >
< / div >
< div id = "nodeForm" class = "nodeForm" style = "display:none;" >
< div class = "fieldRow" style = "align-items:center; justify-content:space-between;" >
< strong id = "nodeFormTitle" > Nouveau nœud< / strong >
< button type = "button" class = "smallBtn button is-small" onclick = "closeNodeForm()" > ✕< / button >
< / div >
< div id = "nodeFormTrigger" style = "display:none;" >
< label > Événement< select id = "nf-trigger-event" onchange = "toggleFlowTriggerFields()" >
{% for key, label in trigger_events %}
< option value = "{{ key }}" > {{ label }}< / option >
{% endfor %}
< / select > < / label >
< label id = "nf-trigger-element-row" style = "display:none;" > Objet< select id = "nf-trigger-element" >
{% for o in objects %}
< option value = "{{ o.id }}" > {{ o.name or ('Personnage' if o.kind == 'personnage' else 'Décor') }} (obj-{{ o.id }})< / option >
{% endfor %}
< / select > < / label >
< p class = "hint" id = "nf-trigger-repeater-hint" style = "display:none;" > < / p >
< p class = "hint" id = "nf-trigger-affichage-hint" style = "display:none;" > < / p >
< div id = "nf-trigger-event-row" style = "display:none;" >
< label > Événement personnalisé< select id = "nf-trigger-custom-event" >
{% for ev in custom_events %}
< option value = "{{ ev.id }}" > {{ ev.name }}< / option >
{% endfor %}
< / select > < / label >
{% if not custom_events %}
< p class = "hint" > Aucun événement pour l'instant — < a href = "javascript:void(0)" onclick = "switchBuilderTab('events'); return false;" > crées-en un dans l'onglet Événements< / a > .< / p >
{% endif %}
< / div >
< div id = "nf-trigger-key-row" style = "display:none;" >
< label > Touche à surveiller
< input type = "text" id = "nf-trigger-key-display" readonly placeholder = "Clique ici puis appuie sur une touche" onkeydown = "captureFlowTriggerKey(event)" >
< / label >
< input type = "hidden" id = "nf-trigger-key" >
< / div >
< div id = "nf-trigger-interval-row" style = "display:none;" >
< label > Toutes les... millisecondes
< input type = "number" id = "nf-trigger-interval" min = "50" step = "50" value = "1000" placeholder = "ex. 1000" >
< / label >
< / div >
< / div >
< div id = "nodeFormCondition" style = "display:none;" >
< label > Source< select id = "nf-cond-source" onchange = "onFlowConditionSourceChange()" >
< option value = "objet" > Champ d'un objet de données< / option >
< option value = "variable" > Variable globale< / option >
< option value = "collision" > Collision entre deux objets de scène< / option >
< / select > < / label >
< div id = "nf-cond-objet-fields" { % if not definitions % } style = "display:none;" { % endif % } >
< label > Objet de données< select id = "nf-cond-definition" onchange = "onFlowDefinitionChange('cond')" >
{% for d in definitions %}
< option value = "{{ d.id }}" > {{ d.name }}< / option >
{% endfor %}
< / select > < / label >
< label > Ligne concernée< select id = "nf-cond-row" > < / select > < / label >
< label > Champ< select id = "nf-cond-field" onchange = "onFlowFieldChange()" > < / select > < / label >
< / div >
{% if not definitions %}
< p class = "hint" id = "nf-cond-no-objet-hint" > Aucun objet de données défini dans ce jeu pour l'instant — < a href = "{{ url_for('game_dashboard', slug=game.slug, tab='objects') }}" > crées-en un< / a > .< / p >
{% endif %}
< div id = "nf-cond-variable-fields" style = "display:none;" >
< label > Variable< select id = "nf-cond-variable" >
< option value = "" > (sélectionner une variable)< / option >
{% for v in global_variables %}
< option value = "{{ v.name }}" > {{ v.name }}< / option >
{% endfor %}
< / select > < / label >
< label > Chemin (optionnel)< input type = "text" id = "nf-cond-variable-chemin" placeholder = "ex. .nom ou [0].valeur" > < / label >
{% if not global_variables %}
< p class = "hint" > Aucune variable globale pour l'instant — < a href = "{{ url_for('game_dashboard', slug=game.slug, tab='variables') }}" target = "_blank" > crées-en une ↗< / a > < / p >
{% endif %}
< / div >
< div id = "nf-cond-collision-fields" style = "display:none;" >
< label > Premier objet< select id = "nf-cond-element-a" >
{% for o in objects %}
< option value = "{{ o.id }}" > {{ o.name or ('Personnage' if o.kind == 'personnage' else 'Décor') }} (obj-{{ o.id }})< / option >
{% endfor %}
< / select > < / label >
< label > Deuxième objet< select id = "nf-cond-element-b" >
{% for o in objects %}
< option value = "{{ o.id }}" > {{ o.name or ('Personnage' if o.kind == 'personnage' else 'Décor') }} (obj-{{ o.id }})< / option >
{% endfor %}
< / select > < / label >
< p class = "hint" > Vrai si les deux objets se touchent (leurs rectangles se chevauchent à l'écran) — relie le port "Faux" du nœud pour "ne se touchent pas".< / p >
< / div >
< div id = "nf-cond-operator-value-row" >
< label > Condition< select id = "nf-cond-operator" >
{% for key, label in condition_operators %}
< option value = "{{ key }}" > {{ label }}< / option >
{% endfor %}
< / select > < / label >
< label > Valeur de comparaison< input type = "text" id = "nf-cond-value" placeholder = "ex. 1, vrai, 10..." > < / label >
< / div >
< div id = "nf-cond-extra-wrap" >
< label id = "nf-cond-combinator-row" style = "display:none;" > Combiner les conditions avec
< select id = "nf-cond-combinator" >
< option value = "et" > ET (toutes les conditions doivent être vraies)< / option >
< option value = "ou" > OU (au moins une condition doit être vraie)< / option >
< / select >
< / label >
< div id = "nf-cond-extra-clauses" > < / div >
< button type = "button" class = "smallBtn button is-small" onclick = "addFlowConditionClause()" > + Ajouter une condition< / button >
< / div >
< / div >
< div id = "nodeFormAction" style = "display:none;" >
< label > Type d'action< select id = "nf-action-type" onchange = "toggleFlowActionFields()" >
{% for key, label in action_type_labels.items() %}
< option value = "{{ key }}" > {{ label }}< / option >
{% endfor %}
< / select > < / label >
< select id = "nf-action-target-screen" style = "display:none;" >
{% for s in all_screens %}
< option value = "{{ s.id }}" > {{ s.name }}< / option >
{% endfor %}
< / select >
<!-- Équivalent scène de "Modifier un élément" — palette réduite
(visibilité/position en pixels/orientation), voir
OBJECT_ACTION_PROPERTIES (screens/scenes/flow_palette.py). -->
< div id = "nf-action-object-fields" style = "display:none;" >
< select id = "nf-action-target-object" >
{% for o in objects %}
< option value = "{{ o.id }}" > {{ o.name or ('Personnage' if o.kind == 'personnage' else 'Décor') }} (obj-{{ o.id }})< / option >
{% endfor %}
< / select >
< select id = "nf-action-object-property" onchange = "toggleFlowObjectPropertyValue()" style = "margin-top:6px;" >
{% for key, label in object_action_properties %}
< option value = "{{ key }}" > {{ label }}< / option >
{% endfor %}
< / select >
< select id = "nf-action-object-value-visibility" style = "margin-top:6px;" >
{% for key, label in element_visibility_values %}
< option value = "{{ key }}" > {{ label }}< / option >
{% endfor %}
< / select >
< select id = "nf-action-object-value-orientation" style = "display:none; margin-top:6px;" >
{% for key, label in element_orientation_values %}
< option value = "{{ key }}" > {{ label }}< / option >
{% endfor %}
< / select >
< input type = "number" id = "nf-action-object-value-number" style = "display:none; margin-top:6px;" placeholder = "Nouvelle valeur en pixels" >
< / div >
< div id = "nf-action-wait-fields" style = "display:none;" >
< label > Délai en secondes avant de continuer
< input type = "number" id = "nf-action-wait-seconds" min = "0" step = "0.5" value = "3" placeholder = "ex. 3" >
< / label >
< / div >
< div id = "nf-action-data-fields" style = "display:none;" >
{% if not definitions %}
< p class = "hint" > Aucun objet de données défini dans ce jeu pour l'instant — < a href = "{{ url_for('game_dashboard', slug=game.slug, tab='objects') }}" > crées-en un< / a > .< / p >
{% else %}
< select id = "nf-action-data-definition" onchange = "onFlowDefinitionChange('action')" >
{% for d in definitions %}
< option value = "{{ d.id }}" > {{ d.name }}< / option >
{% endfor %}
< / select >
< select id = "nf-action-data-row" style = "margin-top:6px;" > < / select >
< select id = "nf-action-data-field" style = "margin-top:6px;" > < / select >
< select id = "nf-action-data-operation" style = "margin-top:6px;" onchange = "updateDataValuePlaceholder('nf-action-data-operation', 'nf-action-data-value')" >
{% for key, label in data_operations %}
< option value = "{{ key }}" > {{ label }}< / option >
{% endfor %}
< / select >
< input type = "text" id = "nf-action-data-value" style = "margin-top:6px;" placeholder = "Valeur / montant" >
{% endif %}
< / div >
< div id = "nf-action-sound-fields" style = "display:none;" >
< label > URL du son
< input type = "text" id = "nf-action-sound-url" placeholder = "URL du fichier audio" >
< / label >
< div style = "margin-top:6px;" >
< label for = "nf-action-sound-upload" style = "font-size:12px;" > Envoyer un fichier :< / label >
< input type = "file" class = "nodeFormUploadInput" id = "nf-action-sound-upload" data-target = "nf-action-sound-url" accept = "audio/*" style = "font-size:12px;" >
< span class = "uploadStatus" style = "font-size:12px; color:var(--text-dim);" > < / span >
< / div >
< / div >
< div id = "nf-action-sprite-fields" style = "display:none;" >
< label > Personnage à animer
< select id = "nf-action-sprite-target-element" onchange = "onSpriteTargetElementChange()" >
< option value = "" > — Choisir —< / option >
{% for o in objects if o.kind == 'personnage' %}
< option value = "{{ o.id }}" > {{ o.name or 'Personnage' }} (obj-{{ o.id }})< / option >
{% endfor %}
< / select >
< / label >
{% if not objects|selectattr('kind', 'equalto', 'personnage')|list %}
< p class = "hint" > Aucun personnage sur cette scène — ajoute-en un depuis la galerie "🎭 Personnages" du panneau de gauche.< / p >
{% endif %}
< div id = "nf-action-sprite-animation-grid" class = "iconGallery" style = "margin-top:6px;" > < / div >
< input type = "hidden" id = "nf-action-sprite-animation-name" value = "" >
< div class = "fieldRow" style = "margin-top:6px;" >
< label style = "flex:1;" > Images/seconde
< input type = "number" id = "nf-action-sprite-fps" min = "1" step = "1" value = "8" >
< / label >
< label style = "flex:1; display:flex; align-items:center; gap:6px;" >
< input type = "checkbox" id = "nf-action-sprite-loop" checked style = "width:auto;" > En boucle
< / label >
< / div >
< / div >
< div id = "nf-action-variable-fields" style = "display:none;" >
{% if not global_variables %}
< p class = "hint" > Aucune variable globale pour l'instant — crées-en une depuis le tableau de bord.< / p >
{% else %}
< select id = "nf-action-variable-name" >
{% for v in global_variables %}
< option value = "{{ v.name }}" > {{ v.name }}< / option >
{% endfor %}
< / select >
< select id = "nf-action-variable-operation" style = "margin-top:6px;" onchange = "updateDataValuePlaceholder('nf-action-variable-operation', 'nf-action-variable-value')" >
{% for key, label in data_operations %}
< option value = "{{ key }}" > {{ label }}< / option >
{% endfor %}
< / select >
< input type = "text" id = "nf-action-variable-value" style = "margin-top:6px;" placeholder = "Valeur / montant" >
{% endif %}
< / div >
<!-- Score/progression (voir db/scoring/) : concept de premier
ordre distinct des variables globales ci - dessus — un seul
score par joueur, pas de nom à choisir. Réutilise le même
vocabulaire d'opérations (DATA_OPERATIONS). -->
< div id = "nf-action-score-fields" style = "display:none;" >
< select id = "nf-action-score-operation" onchange = "updateDataValuePlaceholder('nf-action-score-operation', 'nf-action-score-value')" >
{% for key, label in data_operations %}
< option value = "{{ key }}" > {{ label }}< / option >
{% endfor %}
< / select >
< input type = "text" id = "nf-action-score-value" style = "margin-top:6px;" placeholder = "Valeur / montant" >
< / div >
< div id = "nf-action-status-fields" style = "display:none;" >
< select id = "nf-action-status-value" >
{% for key, label in score_status_choices %}
< option value = "{{ key }}" > {{ label }}< / option >
{% endfor %}
< / select >
< / div >
< div id = "nf-action-event-fields" style = "display:none;" >
{% if not custom_events %}
< p class = "hint" > Aucun événement pour l'instant — < a href = "javascript:void(0)" onclick = "switchBuilderTab('events'); return false;" > crées-en un dans l'onglet Événements< / a > .< / p >
{% else %}
< select id = "nf-action-custom-event" >
{% for ev in custom_events %}
< option value = "{{ ev.id }}" > {{ ev.name }}< / option >
{% endfor %}
< / select >
{% endif %}
< / div >
< / div >
< div class = "actionsRow" > < button type = "button" class = "smallBtn button is-small is-primary" onclick = "submitNodeForm()" > + Ajouter ce nœud< / button > < / div >
< / div >
< div class = "flowCanvasWrap" >
< svg id = "flowEdges" class = "flowEdgesSvg" > < / svg >
< div id = "flowCanvas" class = "flowCanvas" > < / div >
< / div >
< / div >
< div class = "floatPanelResizeHandle" id = "logicBlockPanelResizeHandle" > < / div >
< / div >
< div class = "builderTabPanel" id = "animTabPanel" data-tab-panel = "anim" >
< div class = "flowToolbar" >
< label style = "display:flex; align-items:center; gap:6px;" >
Piste
< select id = "anim-new-track-element" >
{% for o in objects %}
< option value = "{{ o.id }}" > {{ o.name or ('Personnage' if o.kind == 'personnage' else 'Décor') }}< / option >
{% endfor %}
< / select >
< / label >
< button type = "button" class = "smallBtn button is-small" onclick = "addAnimTrack()" > ➕ Ajouter un clip< / button >
< / div >
< div id = "animClipForm" class = "nodeForm" style = "display:none;" >
< div class = "fieldRow" style = "align-items:center; justify-content:space-between;" >
< strong id = "animClipFormTitle" > Clip d'animation< / strong >
< button type = "button" class = "smallBtn button is-small" onclick = "closeAnimClipForm()" > ✕< / button >
< / div >
< label > Type
< select id = "anim-clip-kind" onchange = "toggleAnimClipKind()" >
< option value = "animate_css" > Animation Animate.css< / option >
< option value = "custom" > Animation personnalisée — images-clés< / option >
< option value = "sprite" > Animation de sprites< / option >
< / select >
< / label >
< div id = "anim-clip-animate-css-fields" >
< label > Animation
< select id = "anim-clip-animate-name" >
{% for category, items in animate_css_catalog %}
< optgroup label = "{{ category }}" >
{% for key, label in items %}
< option value = "{{ key }}" > {{ label }}< / option >
{% endfor %}
< / optgroup >
{% endfor %}
< / select >
< / label >
< / div >
< div id = "anim-clip-custom-fields" style = "display:none;" >
< div id = "anim-clip-keyframes" > < / div >
< button type = "button" class = "smallBtn button is-small" onclick = "addAnimKeyframeRow()" > + Ajouter une image-clé< / button >
< / div >
< div id = "anim-clip-sprite-fields" style = "display:none;" >
< p class = "hint" id = "anim-clip-sprite-no-animations" style = "display:none;" > Cet objet n'est pas un personnage, ou n'a encore aucune animation.< / p >
< div id = "anim-clip-sprite-animation-grid" class = "iconGallery" > < / div >
< input type = "hidden" id = "anim-clip-sprite-animation-name" value = "" >
< label style = "margin-top:6px; display:block;" > Images/seconde
< input type = "number" id = "anim-clip-sprite-fps" min = "1" step = "1" value = "8" >
< / label >
< / div >
< div class = "fieldRow" >
< label style = "flex:1;" > Départ en secondes
< input type = "number" id = "anim-clip-start" min = "0" step = "0.1" value = "0" >
< / label >
< label style = "flex:1;" > Durée en secondes
< input type = "number" id = "anim-clip-duration" min = "0.1" step = "0.1" value = "1" >
< / label >
< / div >
< div class = "fieldRow" >
< label style = "flex:1;" > Délai en secondes
< input type = "number" id = "anim-clip-delay" min = "0" step = "0.1" value = "0" >
< / label >
< label style = "flex:1;" > Vitesse
< select id = "anim-clip-easing" >
< option value = "ease" > Progressive< / option >
< option value = "linear" > Constante< / option >
< option value = "ease-in" > Accélère< / option >
< option value = "ease-out" > Ralentit< / option >
< option value = "ease-in-out" > Accélère puis ralentit< / option >
< / select >
< / label >
< / div >
< label > Répétitions
< select id = "anim-clip-iteration" >
< option value = "1" > Une fois< / option >
< option value = "2" > 2 fois< / option >
< option value = "3" > 3 fois< / option >
< option value = "infinite" > En boucle< / option >
< / select >
< / label >
< div class = "actionsRow" >
< button type = "button" class = "smallBtn button is-small is-primary" onclick = "saveAnimClipForm()" > Enregistrer< / button >
< button type = "button" class = "smallBtn button is-small" onclick = "previewAnimClip()" > ▶ Aperçu< / button >
< button type = "button" class = "smallBtn button is-small is-danger" onclick = "deleteAnimClipForm()" > 🗑️ Supprimer< / button >
< / div >
< / div >
< div class = "animTimelineWrap" >
< div id = "animRuler" class = "animRuler" > < / div >
< div id = "animTracks" class = "animTracks" > < / div >
< / div >
< / div >
< div class = "builderTabPanel" id = "eventsTabPanel" data-tab-panel = "events" >
< div class = "dashPanelHeader" >
< h2 > Événements personnalisés< / h2 >
@@ -615,7 +175,7 @@
< / table >
< / div >
{% else %}
< p class = "hint" > Aucun événement pour l'instant — crée le premier ci-dessus, puis pose un nœud "Sur un événement personnalisé" (Déclencheur) ou "Déclencher un événement" (Action) dans la Logique de la scène .< / p >
< p class = "hint" > Aucun événement pour l'instant — crée le premier ci-dessus.< / p >
{% endif %}
< / div >
< / div >
@@ -666,12 +226,17 @@
< / div >
< / details >
{% if obj.kind != 'fond' %}
<!-- Boîte de collision (voir screens/rendering/collision_settings.py,
static/js/play/conditions.js::elementsOverlap) : posée AUTOMATIQUEMENT
sur tout objet (= sa boîte visuelle par défaut, tant que rien
n'est changé ici) — désactivable, taille/forme/décalage réglables
pour un sprite très paddé dont la silhouette réelle est plus
petite que son canevas. -->
petite que son canevas. JAMAIS pour un "fond" (image de décor,
voir add_scene_object.py) : ni obstacle ni cible de collision
possible (forgeSolidObstacles l'exclut déjà côté jeu, voir
static/js/play/personnage - controller.js), ce panneau n'aurait
donc aucun effet. -->
< details class = "propGroup" >
< summary > 🧱 Collision< / summary >
< div class = "propGroupBody" id = "collisionPanelBody" data-element-id = "{{ obj.id }}" >
@@ -708,6 +273,7 @@
< p class = "help" > Taille/décalage réglés par rapport au centre de l'objet — laisse la taille par défaut (celle de l'objet) si son image n'a pas de marge inutile.< / p >
< / div >
< / details >
{% endif %}
{% if obj.kind == 'personnage' %}
< details class = "propGroup" open >
@@ -841,9 +407,7 @@
< script src = "{{ url_for('static', filename='js/screen_edit/tree-panels.js') }}" > < / script >
< script src = "{{ url_for('static', filename='js/screen_edit/panel-init.js') }}" > < / script >
< script src = "{{ url_for('static', filename='js/screen_edit/personnage-panel.js') }}" > < / script >
< script src = "{{ url_for('static', filename='js/screen_edit/flow-editor .js') }}" > < / script >
< script src = "{{ url_for('static', filename='js/screen_edit/tabs-and-blocks.js') }}" > < / script >
< script src = "{{ url_for('static', filename='js/screen_edit/animation-timeline.js') }}" > < / script >
< script src = "{{ url_for('static', filename='js/screen_edit/builder-tabs .js') }}" > < / script >
< script src = "{{ url_for('static', filename='js/screen_edit/personnage-preview.js') }}" > < / script >
< script src = "{{ url_for('static', filename='js/scenes/scene-editor.js') }}" > < / script >
{% endblock %}