Supprime le type d'écran "document" et recentre le produit sur le jeu 2D
Le document de cadrage produit cible des formateurs non techniques créant des serious games/quiz gamifiés — l'éditeur générique "document" (blocs de logique en nœuds, timeline d'animation, définitions d'objets/relations, templates réutilisables) est une complexité hors cible que l'effort d'ingénierie récent avait déjà abandonnée au profit du jeu_2d. - Onboarding : ne garde que le parcours "RPG" (jeu_2d), retire Quiz/Embranchement/Créer mon jeu de A à Z (tous document-only) - Suppression en bloc des modules exclusifs au document : routes/elements, routes/element_types, routes/objects, routes/legacy_actions, screens/elements, screens/element_types, screens/widgets, screens/legacy_actions, le rendu render_element_html.py et son cluster, templates/screen_edit.html, templates/game_dashboard.html, flow-editor.js/tabs-and-blocks.js/animation-timeline.js - Dashboard toujours simplifié (un seul mode possible désormais) - Tests document-only supprimés, tests de logique partagée (flow, événements personnalisés, animations) retargetés sur des écrans jeu_2d - Aucune régression jeu_2d : 299 tests passent Carte d'onboarding retravaillée : argumentaire RH non technique (liste à coche, badge "Compatible LMS"), taille et interaction de retournement ajustées.
This commit is contained in:
+60
-53
@@ -5,22 +5,6 @@
|
||||
{% block extra_head %}
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='vendor/fonts/fonts.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='vendor/animate.min.css') }}">
|
||||
<style>
|
||||
/* render_scene_object.py positionne son <img> en absolu (left/top/width/
|
||||
height en px) pour s'afficher correctement en enfant DIRECT de
|
||||
.playScreen.playScene (voir templates/play.html) — dans le canevas de
|
||||
CET éditeur, cette même balise est nichée dans .canvasElementInner,
|
||||
lui-même déjà positionné/dimensionné par .canvasElement (voir plus
|
||||
bas) : sans ce correctif, l'image se positionnait EN PLUS depuis
|
||||
#canvas (son ancêtre positionné le plus proche), en double du
|
||||
décalage déjà appliqué par .canvasElement. */
|
||||
.canvasElementInner > img{ position:static !important; left:auto !important; top:auto !important; width:100% !important; height:100% !important; z-index:auto !important; }
|
||||
/* Même correctif que ci-dessus, pour le widget "💬 Boîte de dialogue"
|
||||
(screens/scenes/render_scene_object.py, un <div>, pas un <img>). */
|
||||
.canvasElementInner > .dialogueBoxWidget, .canvasElementInner > .quizBoxWidget, .canvasElementInner > .scoreWidget{
|
||||
position:static !important; left:auto !important; top:auto !important; width:100% !important; height:100% !important; z-index:auto !important;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<!-- Fondations multi-éditeurs — éditeur de scène 2D (jeux "jeu_2d", voir
|
||||
@@ -29,15 +13,27 @@
|
||||
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 encore construit. -->
|
||||
<p class="hint" style="background:var(--panel); border:1px solid var(--border); border-radius:8px; padding:10px 14px; margin:10px 0; display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap;">
|
||||
<span>🎮 Jeu 2D — scène à taille fixe.</span>
|
||||
<!-- "Objets"/"Propriétés" (voir panelLeftFloat/propsPanelFloat plus bas) :
|
||||
posés ICI, dans la barre d'outils de la scène, plutôt qu'en position
|
||||
fixe flottant au-dessus de la page (ancien "🧩 Objets"/"⚙️ Propriétés"
|
||||
tout en haut de l'écran) — ils y chevauchaient "Mes écrans"/"Jouer"/
|
||||
"Exporter" (voir base.html) une fois les panneaux fermés. Remplace la
|
||||
simple mention "Jeu 2D — scène à taille fixe." -->
|
||||
<!-- 3 zones INDÉPENDANTES (grid, jamais un flex space-between) : "Objets"/
|
||||
"Propriétés" ne sont visibles que panneau fermé (display:none sinon,
|
||||
voir .floatPanelReopenBtn) — avec un flex classique, la zone caméra
|
||||
se retrouve seule dans la rangée la plupart du temps et se déplace
|
||||
selon ce qui l'entoure. Un grid à 3 colonnes fixe sa position
|
||||
(toujours centrée) sans dépendre des deux autres zones. -->
|
||||
<p class="hint" style="background:var(--panel); border:1px solid var(--border); border-radius:8px; padding:10px 14px; margin:10px 0; display:grid; grid-template-columns:1fr auto 1fr; align-items:center; gap:12px;">
|
||||
<span><button type="button" id="panelLeftReopenBtn" class="button is-small floatPanelReopenBtn" onclick="openLeftPanel()" title="Rouvrir le panneau des objets">Objets</button></span>
|
||||
<!-- Taille de la CAMÉRA (scene_width/scene_height) — TOUJOURS
|
||||
modifiable ici, directement en pixels (demande explicite : "la
|
||||
caméra a la même taille [que le rectangle de prévisualisation],
|
||||
c'est tout"), pas seulement suggérée automatiquement quand un
|
||||
fond dépasse (voir toujours "🎥 Champ de la caméra" plus bas si
|
||||
c'est le cas, purement informatif dans ce cas). -->
|
||||
<span style="display:flex; align-items:center; gap:6px;">
|
||||
<span style="display:flex; align-items:center; gap:6px; justify-self:center;">
|
||||
<label class="hint" style="margin:0;">Caméra (px)
|
||||
<input type="number" id="scene-camera-w" class="input is-small" style="width:80px;" min="1" value="{{ screen.scene_width }}">
|
||||
×
|
||||
@@ -45,12 +41,12 @@
|
||||
</label>
|
||||
<button type="button" class="button is-small" onclick="applyCameraSize()">Appliquer</button>
|
||||
</span>
|
||||
<span style="justify-self:end;"><button type="button" id="propsPanelReopenBtn" class="button is-small floatPanelReopenBtn" onclick="openPropsPanel()" title="Rouvrir le panneau de propriétés">Propriétés</button></span>
|
||||
</p>
|
||||
|
||||
<div class="builder3" id="builder3">
|
||||
{% set obj = objects|selectattr("id", "equalto", selected_id)|first if selected_id else none %}
|
||||
|
||||
<button type="button" id="panelLeftReopenBtn" class="button is-small floatPanelReopenBtn" onclick="openLeftPanel()" title="Rouvrir le panneau des objets">🧩 Objets</button>
|
||||
<div class="floatPanel floatPanel--left" id="panelLeftFloat">
|
||||
<div class="floatPanelHeader" data-drag-panel="panelLeftFloat" data-drag-key="left">
|
||||
<span class="floatPanelTitle">⋮⋮ Objets de cette scène</span>
|
||||
@@ -61,7 +57,7 @@
|
||||
<div class="control is-expanded"><input class="input is-small" type="text" name="name" value="{{ screen.name }}" required></div>
|
||||
<div class="control"><button type="submit" class="button is-small">Renommer</button></div>
|
||||
</form>
|
||||
<a class="button is-small is-primary is-fullwidth mb-2" href="{{ url_for('game_play', slug=game.slug) }}" target="_blank">▶️ Jouer depuis le début</a>
|
||||
<a class="button is-small is-primary is-fullwidth mb-2" href="{{ url_for('game_play', slug=game.slug) }}" target="_blank">Jouer depuis le début</a>
|
||||
<hr style="border-color:var(--border); margin:10px 0;">
|
||||
|
||||
<!-- "Objets de cette scène" (arborescence des objets déjà posés)
|
||||
@@ -74,7 +70,7 @@
|
||||
personnage-preview.js, réutilisé tel quel : ne dépend que de
|
||||
data-personnage, pas de la table d'origine). -->
|
||||
<details class="propGroup" open>
|
||||
<summary>🎭 Personnages</summary>
|
||||
<summary>Personnages</summary>
|
||||
<div class="propGroupBody">
|
||||
<div class="iconGallery personnageGallery">
|
||||
{% for slug_c, character in sprite_library_families %}
|
||||
@@ -93,7 +89,7 @@
|
||||
grande que la scène, la caméra la suit en défilant derrière le
|
||||
personnage (voir static/js/play/personnage-controller.js). -->
|
||||
<details class="propGroup">
|
||||
<summary>🏞️ Images de fond</summary>
|
||||
<summary>Images de fond</summary>
|
||||
<div class="propGroupBody">
|
||||
<div class="iconGallery personnageGallery">
|
||||
{% for slug_b, background in background_library.items() %}
|
||||
@@ -114,12 +110,12 @@
|
||||
l'auteur ne fait que la poser où il veut et la styliser (panneau
|
||||
de propriétés de droite, une fois sélectionnée). -->
|
||||
<details class="propGroup">
|
||||
<summary>🖥️ Interface</summary>
|
||||
<summary>Interface</summary>
|
||||
<div class="propGroupBody">
|
||||
<button type="button" class="button is-small is-fullwidth mb-2" onclick="addSceneObject('dialogue_box')">💬 Boîte de dialogue</button>
|
||||
<button type="button" class="button is-small is-fullwidth mb-2" onclick="addSceneObject('quiz_box')">❓ Boîte à quiz</button>
|
||||
<button type="button" class="button is-small is-fullwidth" onclick="addSceneObject('score_widget')">🏆 Score</button>
|
||||
<p class="help">Boîte de dialogue/quiz : gérées automatiquement par le moteur (voir l'onglet "🗺️ Quêtes") — position et style réglables une fois posées. Score : affiche en continu les points gagnés.</p>
|
||||
<button type="button" class="button is-small is-fullwidth mb-2" onclick="addSceneObject('dialogue_box')">Boîte de dialogue</button>
|
||||
<button type="button" class="button is-small is-fullwidth mb-2" onclick="addSceneObject('quiz_box')">Boîte à quiz</button>
|
||||
<button type="button" class="button is-small is-fullwidth" onclick="addSceneObject('score_widget')">Score</button>
|
||||
<p class="help">Boîte de dialogue/quiz : gérées automatiquement par le moteur (voir l'onglet "Quêtes") — position et style réglables une fois posées. Score : affiche en continu les points gagnés.</p>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
@@ -127,10 +123,18 @@
|
||||
|
||||
<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="collision-rules" onclick="switchBuilderTab('collision-rules')">🧩 Collision</button>
|
||||
<button type="button" class="builderTabBtn" data-tab="quests" onclick="switchBuilderTab('quests')">🗺️ Quêtes</button>
|
||||
<button type="button" class="builderTabBtn" data-tab="events" onclick="switchBuilderTab('events')">📣 Événements</button>
|
||||
<button type="button" class="builderTabBtn active" data-tab="screen" onclick="switchBuilderTab('screen')">Scène</button>
|
||||
<button type="button" class="builderTabBtn" data-tab="collision-rules" onclick="switchBuilderTab('collision-rules')">Collision</button>
|
||||
<button type="button" class="builderTabBtn" data-tab="quests" onclick="switchBuilderTab('quests')">Quêtes</button>
|
||||
{% if game.onboarding_type != 'rpg' %}
|
||||
<!-- Événements personnalisés (voir db.list_custom_events) : aucun
|
||||
moyen de les DÉCLENCHER depuis un jeu RPG pour l'instant —
|
||||
l'assistant "+ Action" de l'onglet Collision exclut
|
||||
délibérément le type "evenement" (voir COLLISION_WIZARD_
|
||||
ACTION_TYPES, static/js/scenes/collision-rules-editor.js) —
|
||||
demande explicite : cet onglet n'a donc aucune utilité ici. -->
|
||||
<button type="button" class="builderTabBtn" data-tab="events" onclick="switchBuilderTab('events')">Événements</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="builderTabPanel active" data-tab-panel="screen">
|
||||
@@ -187,7 +191,7 @@
|
||||
règle sans recharger la page. -->
|
||||
<div class="builderTabPanel" id="collisionRulesTabPanel" data-tab-panel="collision-rules">
|
||||
<div class="dashPanelHeader">
|
||||
<h2>🧩 Collision</h2>
|
||||
<h2>Collision</h2>
|
||||
</div>
|
||||
<p class="hint">Pour chaque objet (jamais le joueur lui-même), choisis ce qui doit se passer à son contact — la carte se construit au fil de tes choix, comme des pièces qui s'emboîtent.</p>
|
||||
<div id="collisionRulesCards" class="collisionRulesCards"></div>
|
||||
@@ -219,13 +223,14 @@
|
||||
convention que #collisionRulesCards ci-dessus. -->
|
||||
<div class="builderTabPanel" id="questsTabPanel" data-tab-panel="quests">
|
||||
<div class="dashPanelHeader">
|
||||
<h2>🗺️ Quêtes</h2>
|
||||
<h2>Quêtes</h2>
|
||||
<button type="button" class="button is-small primary" onclick="createQuestFromListPage()">+ Créer une quête</button>
|
||||
</div>
|
||||
<p class="hint">Titre, objectif, récompense, statut, résultat — clique sur une ligne pour écrire son enchaînement de dialogue.</p>
|
||||
<div id="questsListWrap"></div>
|
||||
</div>
|
||||
|
||||
{% if game.onboarding_type != 'rpg' %}
|
||||
<div class="builderTabPanel" id="eventsTabPanel" data-tab-panel="events">
|
||||
<div class="dashPanelHeader">
|
||||
<h2>Événements personnalisés</h2>
|
||||
@@ -256,7 +261,7 @@
|
||||
{% set usages = custom_event_usages.get(ev.id, []) %}
|
||||
{% if usages %}
|
||||
{% for u in usages %}
|
||||
<a href="{{ url_for('screen_edit', slug=game.slug, screen_id=u.screen_id) }}" class="linkLikeButton" title="{{ 'Écouté ici' if u.role == 'ecoute' else 'Déclenché ici' }}">{{ '🔔' if u.role == 'ecoute' else '📣' }} {{ u.screen_name }}{{ ' (modèle)' if u.is_template else '' }}</a>{{ ', ' if not loop.last else '' }}
|
||||
<a href="{{ url_for('screen_edit', slug=game.slug, screen_id=u.screen_id) }}" class="linkLikeButton" title="{{ 'Écouté ici' if u.role == 'ecoute' else 'Déclenché ici' }}">{{ u.screen_name }}{{ ' (modèle)' if u.is_template else '' }}</a>{{ ', ' if not loop.last else '' }}
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<span class="hint">Inutilisé</span>
|
||||
@@ -279,10 +284,12 @@
|
||||
<p class="hint">Aucun événement pour l'instant — crée le premier ci-dessus.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- Panneau droit : propriétés de l'objet sélectionné. -->
|
||||
<button type="button" id="propsPanelReopenBtn" class="button is-small floatPanelReopenBtn" onclick="openPropsPanel()" title="Rouvrir le panneau de propriétés">⚙️ Propriétés</button>
|
||||
<!-- Panneau droit : propriétés de l'objet sélectionné. Son bouton de
|
||||
réouverture est maintenant dans la barre d'outils tout en haut
|
||||
(voir #propsPanelReopenBtn plus haut), pas ici. -->
|
||||
<div class="floatPanel floatPanel--right" id="propsPanelFloat">
|
||||
<div class="floatPanelHeader" data-drag-panel="propsPanelFloat" data-drag-key="props">
|
||||
<span class="floatPanelTitle">⋮⋮ Propriétés</span>
|
||||
@@ -295,10 +302,8 @@
|
||||
{% set _WIDGET_ICONS = {'dialogue_box': '💬', 'quiz_box': '❓', 'score_widget': '🏆'} %}
|
||||
{% set _WIDGET_LABELS = {'dialogue_box': 'Boîte de dialogue', 'quiz_box': 'Boîte à quiz', 'score_widget': 'Score'} %}
|
||||
<div class="fieldRow" style="align-items:center; justify-content:space-between;">
|
||||
<h2 class="subtitle is-6" style="margin:0;">{{ '🏃' if obj.kind == 'personnage' else _WIDGET_ICONS.get(obj.kind, '🖼️') }} {{ obj.name or ('Personnage' if obj.kind == 'personnage' else _WIDGET_LABELS.get(obj.kind, 'Décor')) }}</h2>
|
||||
<form method="post" action="{{ url_for('scene_object_delete', slug=game.slug, object_id=obj.id) }}" onsubmit="return confirm('Supprimer cet objet ?');">
|
||||
<button type="submit" class="button is-small is-danger">🗑️</button>
|
||||
</form>
|
||||
<h2 class="subtitle is-6" style="margin:0;">{{ obj.name or ('Personnage' if obj.kind == 'personnage' else _WIDGET_LABELS.get(obj.kind, 'Décor')) }}</h2>
|
||||
<button type="button" class="button is-small is-danger" onclick="if (confirm('Supprimer cet objet ?')) deleteSceneObject({{ obj.id }});">🗑️</button>
|
||||
</div>
|
||||
|
||||
{% if obj.kind != 'dialogue_box' %}
|
||||
@@ -310,7 +315,7 @@
|
||||
quest-editor.js) : un dialogue peut mettre en scène n'importe
|
||||
quel objet, pas seulement un personnage. -->
|
||||
<details class="propGroup" open>
|
||||
<summary>ℹ️ Informations</summary>
|
||||
<summary>Informations</summary>
|
||||
<div class="propGroupBody">
|
||||
<div class="field">
|
||||
<label class="label is-small">Nom de l'objet</label>
|
||||
@@ -355,7 +360,7 @@
|
||||
texte, couleur de chaque zone (header/body/footer, "header_bg"
|
||||
sert de fond unique pour "🏆 Score", qui n'a pas de body/footer). -->
|
||||
<details class="propGroup" open>
|
||||
<summary>🎨 Style</summary>
|
||||
<summary>Style</summary>
|
||||
<div class="propGroupBody" data-element-id="{{ obj.id }}">
|
||||
<div class="field">
|
||||
<label class="label is-small">Police</label>
|
||||
@@ -419,7 +424,7 @@
|
||||
static/js/play/personnage-controller.js), ce panneau n'aurait
|
||||
donc aucun effet. -->
|
||||
<details class="propGroup">
|
||||
<summary>🧱 Collision</summary>
|
||||
<summary>Collision</summary>
|
||||
<div class="propGroupBody" id="collisionPanelBody" data-element-id="{{ obj.id }}">
|
||||
<div class="field">
|
||||
<div class="control">
|
||||
@@ -458,13 +463,13 @@
|
||||
|
||||
{% if obj.kind == 'personnage' %}
|
||||
<details class="propGroup" open>
|
||||
<summary>🎭 Personnage</summary>
|
||||
<summary>Personnage</summary>
|
||||
<div class="propGroupBody" id="personnagePanelBody" data-element-id="{{ obj.id }}">
|
||||
<!-- Rôle (voir screens/rendering/personnage_role.py) : seul un
|
||||
personnage "Joueur" est déplacé/animé automatiquement au
|
||||
clavier et suivi par la caméra — "Ennemi"/"PNJ" restent
|
||||
immobiles tant qu'aucune logique de flow ne les pilote. -->
|
||||
<div class="field"><label class="label is-small">🏷️ Rôle</label>
|
||||
<div class="field"><label class="label is-small">Rôle</label>
|
||||
<div class="control">
|
||||
<div class="select is-small">
|
||||
<select id="personnage-role" onchange="saveSceneObjectRole(this.value)">
|
||||
@@ -504,8 +509,8 @@
|
||||
le moindre nœud de flow à poser — ce panneau ne fait que
|
||||
permettre de les remapper/bloquer un axe. -->
|
||||
<details class="propGroup">
|
||||
<summary>🕹️ Commandes</summary>
|
||||
<div class="propGroupBody" id="personnageCommandsBody" data-element-id="{{ obj.id }}">
|
||||
<summary>Commandes</summary>
|
||||
<div class="propGroupBody" id="personnageCommandsBody" data-element-id="{{ obj.id }}" data-commands-extra='{{ (personnage_commands.extra if personnage_commands else [])|tojson }}'>
|
||||
<p class="help">Ce personnage se déplace et s'anime automatiquement avec ces touches, sans rien à poser dans la logique. Clique un champ puis appuie sur la touche voulue.</p>
|
||||
<div class="field is-grouped">
|
||||
<div class="control"><label class="label is-small">Haut</label>
|
||||
@@ -539,10 +544,13 @@
|
||||
<!-- Touches supplémentaires (sauter, attaquer...) : au-delà des 4
|
||||
touches de déplacement et de "Interagir" ci-dessus, chacune
|
||||
liée à UNE pose au choix du personnage (voir
|
||||
ELEMENT_ANIMATIONS_MAP/PERSONNAGE_COMMANDS_EXTRA, déjà
|
||||
connues de scene-editor.js) — construites en JS, jamais en
|
||||
Jinja : le nombre de lignes change dynamiquement (+ Ajouter
|
||||
/ ×). -->
|
||||
ELEMENT_ANIMATIONS_MAP, déjà connue de scene-editor.js, et
|
||||
data-commands-extra ci-dessus, LU sur #personnageCommandsBody
|
||||
plutôt que via une variable globale — voir
|
||||
initPersonnageCommandsPanel(), rappelée après un changement
|
||||
de sélection sans rechargement) — construites en JS, jamais
|
||||
en Jinja : le nombre de lignes change dynamiquement
|
||||
(+ Ajouter / ×). -->
|
||||
<div class="field"><label class="label is-small">Animations supplémentaires (sauter, attaquer...)</label>
|
||||
<div id="personnageCommandsExtra"></div>
|
||||
<button type="button" class="button is-small" onclick="addPersonnageCommandExtraRow()">+ Ajouter une touche</button>
|
||||
@@ -588,7 +596,6 @@
|
||||
var ANIMATE_CSS_LABELS = {{ animate_css_labels_json|safe }};
|
||||
var SPRITE_LIBRARY = {{ sprite_library_json|safe }};
|
||||
var ELEMENT_ANIMATIONS_MAP = {{ element_animations_map_json|safe }};
|
||||
var PERSONNAGE_COMMANDS_EXTRA = {{ (personnage_commands.extra if personnage_commands else [])|tojson }};
|
||||
var COLLISION_RULE_OBJECTS = {{ collision_rule_objects_json|safe }};
|
||||
var COLLISION_ACTION_TYPES = {{ collision_action_types|tojson }};
|
||||
var COLLISION_TRIGGER_TYPES = {{ collision_trigger_types|tojson }};
|
||||
|
||||
Reference in New Issue
Block a user