Remplace le système de quêtes par des déclencheurs, ajoute l'action variable et le chaînage
Supprime le concept de "quête" au profit d'un onglet unique "Déclencheurs"
portant toute la logique (dialogue, condition, marquage terminé) directement
sur l'objet de scène. Ajoute une nouvelle action "Modifier une variable"
(réutilisant le vocabulaire du graphe de flow) utilisable après une
collision, une interaction ou une branche de condition, ainsi qu'un
chaînage d'actions ("then") permettant d'enchaîner plusieurs actions à la
suite et d'étendre un déclencheur déjà posé sans le recréer.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
c504ace167
commit
d664ed5637
+1
-1
@@ -145,7 +145,7 @@
|
||||
// bonnes réponses au quiz requis pour un statut de fin "reussi" plutôt
|
||||
// que "echoue" (SCORM lesson_status ET xAPI, voir dialogue-box-controller.js).
|
||||
window.FORGE_SUCCESS_THRESHOLD_PERCENT = {{ success_threshold_percent|tojson }};
|
||||
// Score max possible du quiz (voir db/quests/reward_budget.py) : permet
|
||||
// Score max possible du quiz (voir db/dialogue_lines.py::sum_question_rewards) : permet
|
||||
// au SCORM 1.2 de fournir cmi.core.score.min/max en plus du score brut
|
||||
// (voir static/js/play/offline/scorm-api.js), pour qu'un LMS calcule un
|
||||
// vrai pourcentage au lieu d'afficher le score brut à tort étiqueté "%".
|
||||
|
||||
+92
-67
@@ -115,7 +115,7 @@
|
||||
<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>
|
||||
<p class="help">Boîte de dialogue/quiz : gérées automatiquement par le moteur (voir l'onglet "Déclencheurs") — position et style réglables une fois posées. Score : affiche en continu les points gagnés.</p>
|
||||
</div>
|
||||
</details>
|
||||
</div>
|
||||
@@ -124,15 +124,16 @@
|
||||
<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="triggers" onclick="switchBuilderTab('triggers')">Déclencheurs</button>
|
||||
<button type="button" class="builderTabBtn" data-tab="variables" onclick="switchBuilderTab('variables')">Variables</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. -->
|
||||
moyen de les DÉCLENCHER depuis un jeu RPG pour l'instant — le
|
||||
seul point d'entrée qui le permettait (l'action "evenement" de
|
||||
l'ancien éditeur de collision) a été retiré (demande explicite,
|
||||
ne servait à rien). game.onboarding_type est de toute façon
|
||||
TOUJOURS "rpg" désormais (voir la suppression des types Quiz/
|
||||
Embranchement) — cet onglet ne s'affiche donc plus jamais. -->
|
||||
<button type="button" class="builderTabBtn" data-tab="events" onclick="switchBuilderTab('events')">Événements</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
@@ -179,55 +180,82 @@
|
||||
<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>
|
||||
|
||||
<!-- Éditeur de collision (voir screens/rendering/collision_rules.py) :
|
||||
une carte par objet ÉLIGIBLE (jamais un "fond", jamais le
|
||||
personnage "joueur" — ce sont toujours les AUTRES objets qui
|
||||
réagissent à SA présence), listant ses règles "déclencheur ->
|
||||
action" déjà posées + un bouton "+ Action" qui ouvre l'assistant
|
||||
(modale plus bas). Rendu ENTIÈREMENT en JS (voir
|
||||
static/js/scenes/collision-rules-editor.js) à partir de
|
||||
COLLISION_RULE_OBJECTS/CUSTOM_EVENTS_MAP — jamais en Jinja, pour
|
||||
pouvoir se redessiner à chaud après chaque ajout/suppression de
|
||||
règle sans recharger la page. -->
|
||||
<div class="builderTabPanel" id="collisionRulesTabPanel" data-tab-panel="collision-rules">
|
||||
<div class="dashPanelHeader">
|
||||
<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>
|
||||
<!-- Onglet "Déclencheurs" (voir static/js/triggers/trigger-editor.js) :
|
||||
un graphe de logique lu par déclencheur — TOUS les déclencheurs du
|
||||
jeu, plus aucune notion de quête pour les regrouper (un créateur
|
||||
peut faire un simple quiz sans jamais poser de personnage). Chaque
|
||||
déclencheur (objet -> collision -> action) porte SON PROPRE
|
||||
dialogue, édité en ligne (voir openTriggerWizard(), static/js/
|
||||
scenes/collision-rules-editor.js). -->
|
||||
<div class="builderTabPanel" id="triggersTabPanel" data-tab-panel="triggers">
|
||||
<div id="triggersTabPanelBody"></div>
|
||||
</div>
|
||||
|
||||
<!-- Assistant "+ Action" — un seul écran affiché à la fois
|
||||
(#collisionRuleModalBody, reconstruit à chaque étape), jamais un
|
||||
vrai formulaire à plusieurs pages : chaque clic choisit une
|
||||
option et avance directement à la suivante. -->
|
||||
<div class="modal" id="collisionRuleModal">
|
||||
<div class="modal-background" onclick="closeCollisionRuleModal()"></div>
|
||||
<div class="modal-content">
|
||||
<div class="box">
|
||||
<div class="fieldRow" style="align-items:center; justify-content:space-between;">
|
||||
<h2 class="title is-5" style="margin:0;">Nouvelle règle</h2>
|
||||
<button type="button" class="button is-small" onclick="closeCollisionRuleModal()">✕</button>
|
||||
</div>
|
||||
<div id="collisionRuleModalBody"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Éditeur de quêtes (voir db/quests/) : une LISTE (comme l'onglet
|
||||
Événements juste après), pas une grille de cartes — chaque ligne
|
||||
résume titre/objectif/récompense/statut/résultat, cliquer dessus
|
||||
ouvre la modale d'arbre de dialogue (static/js/quests/
|
||||
quest-editor.js, partagée avec l'assistant "+ Action" de l'onglet
|
||||
Collision). Rendu ENTIÈREMENT en JS depuis QUESTS_JSON, même
|
||||
convention que #collisionRulesCards ci-dessus. -->
|
||||
<div class="builderTabPanel" id="questsTabPanel" data-tab-panel="quests">
|
||||
<!-- Variables globales (voir db/global_vars/) : accessibles à TOUT le
|
||||
jeu par leur NOM (immuable après création — condition de
|
||||
visibilité, action "Modifier une variable", voir
|
||||
static/js/play/conditions.js::readVariableValue), pas seulement à
|
||||
un écran. Même patron que l'onglet Événements ci-dessous (table +
|
||||
formulaires classiques, POST/redirect — pas de rendu JS). -->
|
||||
<div class="builderTabPanel" id="variablesTabPanel" data-tab-panel="variables">
|
||||
<div class="dashPanelHeader">
|
||||
<h2>Quêtes</h2>
|
||||
<button type="button" class="button is-small primary" onclick="createQuestFromListPage()">+ Créer une quête</button>
|
||||
<h2>Variables</h2>
|
||||
<button type="button" class="button is-small primary" onclick="toggleDashCreate('variables')">+ Créer une variable</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>
|
||||
<p class="hint">Accessibles depuis n'importe quelle scène (conditions, actions "Modifier une variable"). Le nom ne peut plus changer une fois créé.</p>
|
||||
<form method="post" action="{{ url_for('create_global_var', slug=game.slug) }}" class="dashCreateBar" id="dashCreate-variables" style="display:none;">
|
||||
<input type="hidden" name="screen_id" value="{{ screen.id }}">
|
||||
<input type="text" name="name" placeholder="NOM (ex. score_total)" required autocomplete="off">
|
||||
<select name="type">
|
||||
{% for key, t in global_variable_types.items() %}
|
||||
<option value="{{ key }}">{{ t.label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<input type="text" name="default_value" placeholder="Valeur par défaut">
|
||||
<label class="checkbox" style="display:flex; align-items:center; gap:4px; white-space:nowrap;" title="Chaque joueur a sa propre valeur (décoché = valeur unique partagée par tous)">
|
||||
<input type="checkbox" name="per_player" value="1" checked> Par joueur
|
||||
</label>
|
||||
<button type="submit" class="primary">Créer</button>
|
||||
</form>
|
||||
|
||||
{% if global_variables %}
|
||||
{% for v in global_variables %}
|
||||
<form method="post" action="{{ url_for('global_var_edit', slug=game.slug, var_id=v.id) }}" id="varEditForm{{ v.id }}">
|
||||
<input type="hidden" name="screen_id" value="{{ screen.id }}">
|
||||
</form>
|
||||
{% endfor %}
|
||||
<div class="fieldsTableWrap">
|
||||
<table class="table is-fullwidth is-hoverable fieldsTable">
|
||||
<thead><tr><th>Nom</th><th>Type</th><th>Valeur</th><th>Par joueur</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
{% for v in global_variables %}
|
||||
<tr>
|
||||
<td><code>{{ v.name }}</code></td>
|
||||
<td>
|
||||
<select form="varEditForm{{ v.id }}" name="type" aria-label="Type de « {{ v.name }} »">
|
||||
{% for key, t in global_variable_types.items() %}
|
||||
<option value="{{ key }}" {{ 'selected' if v.type == key else '' }}>{{ t.label }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</td>
|
||||
<td><input type="text" form="varEditForm{{ v.id }}" name="value" value="{{ v.value or '' }}" placeholder="Valeur" aria-label="Valeur de « {{ v.name }} »"></td>
|
||||
<td><span class="hint">{{ 'Oui' if v.per_player else 'Non (partagée)' }}</span></td>
|
||||
<td class="fieldsTableActions">
|
||||
<button type="submit" form="varEditForm{{ v.id }}" class="button is-small" title="Enregistrer">💾</button>
|
||||
<form method="post" action="{{ url_for('global_var_delete', slug=game.slug, var_id=v.id) }}"
|
||||
onsubmit="return confirm('Supprimer la variable « {{ v.name }} » ? Toute condition/action qui la référence encore cessera de fonctionner.');">
|
||||
<input type="hidden" name="screen_id" value="{{ screen.id }}">
|
||||
<button type="submit" class="button is-small is-danger" title="Supprimer">🗑️</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="hint">Aucune variable pour l'instant — crée la première ci-dessus.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% if game.onboarding_type != 'rpg' %}
|
||||
@@ -311,9 +339,9 @@
|
||||
screens/scenes/set_scene_object_name.py) — n'importe quel kind
|
||||
(personnage, décor, fond), pas seulement un personnage — c'est
|
||||
CE nom qui est ensuite proposé comme "qui parle" dans l'éditeur
|
||||
de dialogue de quête (voir "🗺️ Quêtes", static/js/quests/
|
||||
quest-editor.js) : un dialogue peut mettre en scène n'importe
|
||||
quel objet, pas seulement un personnage. -->
|
||||
de dialogue d'un déclencheur (voir "Déclencheurs", static/js/
|
||||
triggers/trigger-editor.js) : un dialogue peut mettre en scène
|
||||
n'importe quel objet, pas seulement un personnage. -->
|
||||
<details class="propGroup" open>
|
||||
<summary>Informations</summary>
|
||||
<div class="propGroupBody">
|
||||
@@ -602,18 +630,15 @@
|
||||
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 COLLISION_RULE_OBJECTS = {{ collision_rule_objects_json|safe }};
|
||||
var COLLISION_ACTION_TYPES = {{ collision_action_types|tojson }};
|
||||
var COLLISION_TRIGGER_TYPES = {{ collision_trigger_types|tojson }};
|
||||
var COLLISION_DEFAULT_PERIMETRE_PX = {{ collision_default_perimetre_px }};
|
||||
// Éditeur de quêtes (voir static/js/quests/quest-editor.js) — chargé
|
||||
// avant collision-rules-editor.js : l'action "quete" de l'assistant
|
||||
// "+ Action" ouvre directement cet éditeur (renderQuestPickerStep).
|
||||
var QUESTS_JSON = {{ quests_json|safe }};
|
||||
// "qui parle" dans l'éditeur de dialogue (voir "ℹ️ Informations" +
|
||||
// static/js/quests/quest-editor.js::questSpeakerOptions) — n'importe
|
||||
// quel objet de scène nommé, pas seulement un personnage.
|
||||
// static/js/triggers/trigger-editor.js::triggerSpeakerOptions) —
|
||||
// n'importe quel objet de scène nommé, pas seulement un personnage.
|
||||
var SCENE_OBJECT_NAMES_JSON = {{ scene_object_names_json|safe }};
|
||||
// Assistant "condition" d'un déclencheur (voir static/js/scenes/
|
||||
// collision-rules-editor.js::renderCollisionWizardConditionStep) — les
|
||||
// noms des variables globales du jeu (voir l'onglet "Variables"
|
||||
// ci-dessus), pour peupler son sélecteur de variable.
|
||||
var GLOBAL_VARIABLE_NAMES = {{ global_variables | map(attribute='name') | list | tojson }};
|
||||
</script>
|
||||
<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>
|
||||
@@ -621,6 +646,6 @@
|
||||
<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>
|
||||
<script src="{{ url_for('static', filename='js/quests/quest-editor.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/triggers/trigger-editor.js') }}"></script>
|
||||
<script src="{{ url_for('static', filename='js/scenes/collision-rules-editor.js') }}"></script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user