Remplace le système de quêtes par des déclencheurs, ajoute l'action variable et le chaînage
Build and deploy / test-python (push) Successful in 8m45s
Build and deploy / test-js (push) Successful in 49s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped

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:
william
2026-09-07 22:05:06 +02:00
co-authored by Claude Sonnet 5
parent c504ace167
commit d664ed5637
62 changed files with 3129 additions and 2266 deletions
+1
View File
@@ -0,0 +1 @@
from . import triggers
+112
View File
@@ -0,0 +1,112 @@
from flask import jsonify, request
import screens
from core.flask_app import app
@app.route("/game/<slug>/triggers/objects-catalog", methods=["GET"])
def triggers_objects_catalog(slug):
"""Catalogue des objets de scène éligibles à un déclencheur, pour la
grille "choisis un objet" de la modale d'ajout (voir
screens.list_all_collision_objects)."""
return jsonify(screens.list_all_collision_objects(slug))
@app.route("/game/<slug>/triggers", methods=["GET"])
def triggers_get(slug):
"""TOUS les déclencheurs du jeu (voir screens.list_all_triggers) — plus
de recherche inversée par quête, la logique est portée directement par
chaque déclencheur."""
return jsonify(screens.list_all_triggers(slug))
@app.route("/game/<slug>/triggers", methods=["POST"])
def triggers_add(slug):
"""Ajoute un déclencheur sur l'objet choisi — body {object_id, rule},
`rule` construite par l'assistant "déclencheur -> action" (voir
openTriggerWizard, static/js/scenes/collision-rules-editor.js) :
n'importe quel type de déclencheur/action qu'il propose passe donc
déjà tel quel, sans changement à faire ici si de nouveaux types
apparaissent un jour. Même contrat "liste ENTIÈRE" que l'éditeur de
collision (routes/scenes/scene_object_collision_rules.py) : on
recharge les règles existantes de l'objet, on y ajoute la nouvelle, on
sauvegarde la liste complète."""
body = request.get_json(silent=True) or {}
try:
object_id = int(body.get("object_id"))
except (TypeError, ValueError):
return jsonify({"error": "object_id invalide"}), 400
obj = screens.get_scene_object(slug, object_id)
if obj is None:
return jsonify({"error": "objet introuvable"}), 404
new_rule = body.get("rule")
if not isinstance(new_rule, dict):
return jsonify({"error": "rule invalide"}), 400
rules = screens.resolve_collision_rules(obj)
rules.append(new_rule)
sanitized = screens.sanitize_collision_rules(rules)
screens.set_scene_object_collision_rules(slug, object_id, sanitized)
return jsonify({"ok": True})
@app.route("/game/<slug>/triggers/<int:object_id>", methods=["DELETE"])
def triggers_delete(slug, object_id):
"""Retire TOUTES les règles de collision de CET objet — sans quête
pour distinguer "cette règle-ci" d'une autre, un objet ne porte de
toute façon en pratique qu'un seul déclencheur à la fois (posé par
l'assistant, jamais cumulé)."""
obj = screens.get_scene_object(slug, object_id)
if obj is None:
return jsonify({"error": "objet introuvable"}), 404
screens.set_scene_object_collision_rules(slug, object_id, [])
return jsonify({"ok": True})
@app.route("/game/<slug>/triggers/<int:object_id>/dialogue", methods=["POST"])
def trigger_dialogue_update(slug, object_id):
"""Édition EN LIGNE des répliques d'UN dialogue précis (voir
screens/rendering/collision_rules.py::"dialogue".dialogue) — body
{dialogue_id, lines}. 404 si `dialogue_id` ne correspond à aucun
dialogue de CET objet (règle supprimée entre-temps, référence
orpheline)."""
obj = screens.get_scene_object(slug, object_id)
if obj is None:
return jsonify({"error": "objet introuvable"}), 404
body = request.get_json(silent=True) or {}
dialogue_id = body.get("dialogue_id")
if not isinstance(dialogue_id, str) or not dialogue_id.strip():
return jsonify({"error": "dialogue_id invalide"}), 400
rules = screens.resolve_collision_rules(obj)
if not screens.set_dialogue_lines_in_rules(rules, dialogue_id, body.get("lines")):
return jsonify({"error": "dialogue introuvable"}), 404
sanitized = screens.sanitize_collision_rules(rules)
screens.set_scene_object_collision_rules(slug, object_id, sanitized)
return jsonify({"ok": True})
@app.route("/game/<slug>/triggers/<int:object_id>/append-action", methods=["POST"])
def trigger_append_action(slug, object_id):
""""+ Ajouter une action" en bout de chaîne (voir screens/rendering/
collision_rules.py::set_then_action_in_rules, static/js/triggers/
trigger-editor.js) — body {after_id, action} : `after_id` est le
dialogue.id (feuille "dialogue") ou id (feuille "variable") de la
DERNIÈRE feuille d'une chaîne. 404 si `after_id` introuvable ou si
cette feuille a déjà un "then" (jamais écrasé par cette route — un
nouvel appel doit d'abord recharger la chaîne à jour)."""
obj = screens.get_scene_object(slug, object_id)
if obj is None:
return jsonify({"error": "objet introuvable"}), 404
body = request.get_json(silent=True) or {}
after_id = body.get("after_id")
if not isinstance(after_id, str) or not after_id.strip():
return jsonify({"error": "after_id invalide"}), 400
rules = screens.resolve_collision_rules(obj)
if not screens.set_then_action_in_rules(rules, after_id, body.get("action")):
return jsonify({"error": "action précédente introuvable ou déjà chaînée"}), 404
sanitized = screens.sanitize_collision_rules(rules)
screens.set_scene_object_collision_rules(slug, object_id, sanitized)
return jsonify({"ok": True})