from flask import jsonify, request from werkzeug.wrappers import Response import screens from core.flask_app import app @app.route("/game//triggers/objects-catalog", methods=["GET"]) def triggers_objects_catalog(slug: str) -> Response: """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//triggers", methods=["GET"]) def triggers_get(slug: str) -> Response: """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//triggers", methods=["POST"]) def triggers_add(slug: str) -> Response | tuple[Response, int]: """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 {} raw_object_id = body.get("object_id") try: if raw_object_id is None: raise ValueError object_id = int(raw_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//triggers/", methods=["DELETE"]) def triggers_delete(slug: str, object_id: int) -> Response | tuple[Response, int]: """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//triggers//dialogue", methods=["POST"]) def trigger_dialogue_update(slug: str, object_id: int) -> Response | tuple[Response, int]: """É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//triggers//append-action", methods=["POST"]) def trigger_append_action(slug: str, object_id: int) -> Response | tuple[Response, int]: """ "+ Ajouter une action" (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 (toute autre feuille) de N'IMPORTE QUEL bloc de la chaîne, pas forcément le dernier — la nouvelle action s'INSÈRE juste après lui, une suite déjà présente passant derrière elle (demande explicite : "pouvoir ajouter n'importe quelle action à n'importe quel niveau du schéma"). 404 seulement si `after_id` est introuvable.""" if screens.get_scene_object(slug, object_id) 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 # Chemin d'écriture PARTAGÉ avec l'agent IA (voir ai/tools.py) — # ni cette route ni l'agent n'écrivent _collision_rules directement. if not screens.append_action_to_trigger(slug, object_id, after_id, body.get("action")): return jsonify({"error": "action précédente introuvable"}), 404 return jsonify({"ok": True}) @app.route("/game//triggers//action/", methods=["DELETE"]) def trigger_remove_action(slug: str, object_id: int, leaf_id: str) -> Response | tuple[Response, int]: """Retire UNE action précise (demande explicite : "chaque bloc étiquette + action doit être supprimable") — voir screens/rendering/ collision_rules.py::remove_action_from_rules, même chemin d'écriture partagé avec l'agent IA.""" if not screens.remove_action_from_trigger(slug, object_id, leaf_id): return jsonify({"error": "action introuvable"}), 404 return jsonify({"ok": True}) @app.route("/game//triggers//action//move", methods=["POST"]) def trigger_move_action(slug: str, object_id: int, leaf_id: str) -> Response | tuple[Response, int]: """Déplace une action précise d'un cran ("up"/"down", body {direction}) dans sa chaîne — demande explicite : "chaque bloc étiquette + action doit être déplaçable".""" body = request.get_json(silent=True) or {} direction = body.get("direction") if direction not in ("up", "down"): return jsonify({"error": "direction invalide"}), 400 if not screens.move_action_in_trigger(slug, object_id, leaf_id, direction): return jsonify({"error": "action introuvable"}), 404 return jsonify({"ok": True})