Enrichit les declencheurs/actions de scene (clic/survol/affichage, surbrillance/video/son/visibilite/indication/attendre) et fiabilise la pose d'un fond/decor importe
- Ajoute clic/survol/affichage-ecran comme declencheurs, et surbrillance, video, son, visibilite, indication, attendre comme actions, utilisables aussi bien par l'editeur manuel (menu lateral Objets/Ecran) que par Ruby (IA), avec blocs deplacables/supprimables dans une chaine. - Corrige plusieurs variantes du bug "impossible de poser un objet hors du champ de la camera" (troncature du chainage d'actions a 4 maillons, fond importe pose a 128x128 au lieu de sa taille reelle, decalage du fond au vrai glisser-depose, redimensionnement manuel jamais propage au monde). - Ajoute un vrai glisser-depose depuis la galerie vers la scene, la gestion complete de "Mes assets" (sous-sections Fonds/Decors/Sons/ Videos, suppression, reclassement fond<->decor sans re-upload). - Ajoute l'upload de son (limite 3 min) et de video (MP4 uniquement, limite 5 min), avec validation de la duree reelle du fichier, et une replique audio optionnelle dans une bulle de dialogue. - Fixe la taille de pose d'un objet/decor importe a 200x200 avec une boite de collision de 150x150. - Filtre le selecteur de fichier des actions son/video par type reel. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
b07b231a61
commit
559331f9cf
+189
-10
@@ -28,8 +28,12 @@ import auth
|
||||
import db
|
||||
import screens
|
||||
from db.dialogue_lines import QUESTION_REWARD_TYPES
|
||||
from screens.rendering.collision_rules import ACTION_TYPES, LEAF_ACTION_TYPES, CONDITION_OPERATOR_KEYS
|
||||
from screens.rendering.collision_rules import ACTION_TYPES, LEAF_ACTION_TYPES, CONDITION_OPERATOR_KEYS, TRIGGER_TYPES
|
||||
from screens.rendering.screen_triggers import TRIGGER_TYPES_SCREEN
|
||||
from screens.labels.data_operations import DATA_OPERATION_LABELS
|
||||
from screens.labels.element_visibility import ELEMENT_VISIBILITY_LABELS
|
||||
from screens.labels.surbrillance_values import SURBRILLANCE_LABELS
|
||||
from screens.labels.video_modes import VIDEO_MODE_LABELS
|
||||
from .scenario_client import generate_image_url
|
||||
|
||||
_CONDITION_OPERATORS = sorted(CONDITION_OPERATOR_KEYS)
|
||||
@@ -76,7 +80,30 @@ _LEAF_ACTION_SCHEMA = {
|
||||
"mark_completed": {"type": "boolean"},
|
||||
"target_variable": {"type": "string", "description": "Pour type=variable : nom d'une variable globale existante."},
|
||||
"data_operation": {"type": "string", "enum": _DATA_OPERATIONS},
|
||||
"data_value": {"type": "string", "description": "Absent pour definir_bool_vrai/definir_bool_faux/basculer_bool."},
|
||||
"data_value": {"type": "string", "description": "Pour type=variable : absent pour definir_bool_vrai/definir_bool_faux/basculer_bool. Pour type=attendre : nombre de SECONDES à attendre avant \"then\" (ex. \"2\", \"1.5\")."},
|
||||
"object_id": {
|
||||
"type": "integer",
|
||||
"description": (
|
||||
"Pour type=surbrillance/visibilite/indication : id de l'objet CIBLÉ par cette "
|
||||
"action — n'importe quel objet de l'écran, pas forcément celui qui porte le "
|
||||
"déclencheur (ex. le joueur touche un interrupteur -> indication près d'une porte)."
|
||||
),
|
||||
},
|
||||
"valeur": {
|
||||
"type": "string",
|
||||
"description": (
|
||||
f"Pour type=surbrillance : une valeur parmi {sorted(SURBRILLANCE_LABELS)!r}. "
|
||||
f"Pour type=visibilite : une valeur parmi {sorted(ELEMENT_VISIBILITY_LABELS)!r}. "
|
||||
"Pour type=condition (voir _ACTION_SCHEMA) : la valeur littérale à comparer."
|
||||
),
|
||||
},
|
||||
"asset_url": {
|
||||
"type": "string",
|
||||
"description": "Pour type=son/video : URL d'un fichier déjà présent dans \"Mes assets\".",
|
||||
},
|
||||
"mode": {"type": "string", "enum": sorted(VIDEO_MODE_LABELS), "description": "Pour type=video."},
|
||||
"texte": {"type": "string", "description": "Pour type=indication : le texte affiché dans la bulle."},
|
||||
"duree_ms": {"type": "integer", "description": "Pour type=indication : durée d'affichage en ms (optionnel)."},
|
||||
"then": {"description": "Feuille suivante (même forme), récursif."},
|
||||
},
|
||||
"required": ["type"],
|
||||
@@ -93,11 +120,16 @@ _ACTION_SCHEMA = {
|
||||
"target_variable": {"type": "string"},
|
||||
"data_operation": {"type": "string", "enum": _DATA_OPERATIONS},
|
||||
"data_value": {"type": "string"},
|
||||
"then": {"description": "Feuille suivante (dialogue/variable), récursif."},
|
||||
"object_id": _LEAF_ACTION_SCHEMA["properties"]["object_id"],
|
||||
"valeur": _LEAF_ACTION_SCHEMA["properties"]["valeur"],
|
||||
"asset_url": _LEAF_ACTION_SCHEMA["properties"]["asset_url"],
|
||||
"mode": _LEAF_ACTION_SCHEMA["properties"]["mode"],
|
||||
"texte": _LEAF_ACTION_SCHEMA["properties"]["texte"],
|
||||
"duree_ms": _LEAF_ACTION_SCHEMA["properties"]["duree_ms"],
|
||||
"then": {"description": "Feuille suivante (dialogue/variable/surbrillance/visibilite/son/video/indication), récursif."},
|
||||
"sub_action": {"description": "Pour type=interagir : une action (pas \"interagir\" à nouveau)."},
|
||||
"variable": {"type": "string", "description": "Pour type=condition."},
|
||||
"operateur": {"type": "string", "enum": _CONDITION_OPERATORS},
|
||||
"valeur": {"type": "string"},
|
||||
"si_vrai": {"description": "Pour type=condition : null ou une feuille."},
|
||||
"si_faux": {"description": "Pour type=condition : null ou une feuille."},
|
||||
},
|
||||
@@ -196,8 +228,13 @@ TOOLS = [
|
||||
"name": "set_collision_rules",
|
||||
"description": (
|
||||
"Remplace TOUTES les règles \"déclencheur -> action\" d'un objet. "
|
||||
"Sanitizé côté serveur (screens.sanitize_collision_rules) : toute "
|
||||
"valeur invalide est silencieusement retirée plutôt que rejetée."
|
||||
"Déclencheurs disponibles : \"collision\" (contact avec le personnage "
|
||||
"\"joueur\" — voir set_object_role), \"clic\" (l'objet est cliqué/touché, "
|
||||
"aucun joueur requis), \"survol\" (le pointeur survole l'objet, aucun "
|
||||
"joueur requis). Sanitizé côté serveur (screens.sanitize_collision_rules) : "
|
||||
"toute valeur invalide est silencieusement retirée plutôt que rejetée — "
|
||||
"compare le nombre de `rules` renvoyées dans le résultat à ce que tu as "
|
||||
"envoyé pour détecter un retrait silencieux."
|
||||
),
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
@@ -208,7 +245,7 @@ TOOLS = [
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"trigger": {"type": "string"},
|
||||
"trigger": {"type": "string", "enum": list(TRIGGER_TYPES)},
|
||||
"action": _ACTION_SCHEMA,
|
||||
},
|
||||
"required": ["trigger", "action"],
|
||||
@@ -218,6 +255,33 @@ TOOLS = [
|
||||
"required": ["object_id", "rules"],
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "set_screen_triggers",
|
||||
"description": (
|
||||
"Remplace TOUTES les règles \"déclencheur d'écran -> action\" de l'écran "
|
||||
"en cours — un déclencheur d'ÉCRAN se déclenche SANS avoir besoin d'un "
|
||||
"objet (narration/cinématique dès l'affichage). Seul déclencheur : "
|
||||
"\"affichage\" (à l'affichage de l'écran). Même sanitisation que "
|
||||
"set_collision_rules."
|
||||
),
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"rules": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"trigger": {"type": "string", "enum": list(TRIGGER_TYPES_SCREEN)},
|
||||
"action": _ACTION_SCHEMA,
|
||||
},
|
||||
"required": ["trigger", "action"],
|
||||
},
|
||||
},
|
||||
},
|
||||
"required": ["rules"],
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "add_generated_image",
|
||||
"description": (
|
||||
@@ -238,9 +302,12 @@ TOOLS = [
|
||||
{
|
||||
"name": "append_action_to_trigger",
|
||||
"description": (
|
||||
"Ajoute une action à la SUITE d'une chaîne déjà posée sur un "
|
||||
"déclencheur existant, sans reconstruire toute la règle — "
|
||||
"after_id désigne la dernière feuille de la chaîne."
|
||||
"Insère une action JUSTE APRÈS un bloc précis d'une chaîne déjà "
|
||||
"posée sur un déclencheur existant, sans reconstruire toute la "
|
||||
"règle — after_id peut désigner N'IMPORTE QUEL bloc de la "
|
||||
"chaîne, pas forcément le dernier : une suite déjà présente "
|
||||
"après ce bloc passe derrière la nouvelle action plutôt que "
|
||||
"d'être remplacée."
|
||||
),
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
@@ -252,6 +319,81 @@ TOOLS = [
|
||||
"required": ["object_id", "after_id", "action"],
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "append_action_to_screen_trigger",
|
||||
"description": (
|
||||
"Équivalent d'append_action_to_trigger pour un déclencheur D'ÉCRAN "
|
||||
"(voir set_screen_triggers), sur l'écran en cours — after_id peut "
|
||||
"désigner N'IMPORTE QUEL bloc de la chaîne, pas forcément le "
|
||||
"dernier."
|
||||
),
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"after_id": {"type": "string"},
|
||||
"action": _LEAF_ACTION_SCHEMA,
|
||||
},
|
||||
"required": ["after_id", "action"],
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "remove_trigger_action",
|
||||
"description": (
|
||||
"Retire UNE action précise (leaf_id) d'un déclencheur d'OBJET, où "
|
||||
"qu'elle soit dans l'arbre (feuille directe, maillon d'une chaîne "
|
||||
"\"then\", sous-action d'un \"interagir\", ou branche d'une "
|
||||
"\"condition\"). Si c'était la SEULE action de la règle, la règle "
|
||||
"entière disparaît ; si elle avait une suite \"then\", celle-ci "
|
||||
"prend sa place."
|
||||
),
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"object_id": {"type": "integer"},
|
||||
"leaf_id": {"type": "string"},
|
||||
},
|
||||
"required": ["object_id", "leaf_id"],
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "remove_screen_trigger_action",
|
||||
"description": "Équivalent de remove_trigger_action pour un déclencheur D'ÉCRAN.",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {"leaf_id": {"type": "string"}},
|
||||
"required": ["leaf_id"],
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "move_trigger_action",
|
||||
"description": (
|
||||
"Déplace une action précise (leaf_id) d'un cran vers le haut "
|
||||
"(\"up\") ou le bas (\"down\") DANS SA PROPRE CHAÎNE \"then\", pour "
|
||||
"un déclencheur d'OBJET — jamais au-delà de sa branche de "
|
||||
"condition/sous-action interagir."
|
||||
),
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"object_id": {"type": "integer"},
|
||||
"leaf_id": {"type": "string"},
|
||||
"direction": {"type": "string", "enum": ["up", "down"]},
|
||||
},
|
||||
"required": ["object_id", "leaf_id", "direction"],
|
||||
},
|
||||
},
|
||||
{
|
||||
"name": "move_screen_trigger_action",
|
||||
"description": "Équivalent de move_trigger_action pour un déclencheur D'ÉCRAN.",
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"leaf_id": {"type": "string"},
|
||||
"direction": {"type": "string", "enum": ["up", "down"]},
|
||||
},
|
||||
"required": ["leaf_id", "direction"],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
@@ -376,6 +518,37 @@ def _dispatch_append_action_to_trigger(slug, screen_id, user_id, object_id, afte
|
||||
return {"ok": ok}
|
||||
|
||||
|
||||
def _dispatch_set_screen_triggers(slug, screen_id, user_id, rules):
|
||||
sanitized = screens.sanitize_screen_triggers(rules)
|
||||
screens.set_screen_triggers(slug, screen_id, sanitized)
|
||||
return {"ok": True, "rules": sanitized}
|
||||
|
||||
|
||||
def _dispatch_append_action_to_screen_trigger(slug, screen_id, user_id, after_id, action):
|
||||
ok = screens.append_action_to_screen_trigger(slug, screen_id, after_id, action)
|
||||
return {"ok": ok}
|
||||
|
||||
|
||||
def _dispatch_remove_trigger_action(slug, screen_id, user_id, object_id, leaf_id):
|
||||
ok = screens.remove_action_from_trigger(slug, object_id, leaf_id)
|
||||
return {"ok": ok}
|
||||
|
||||
|
||||
def _dispatch_remove_screen_trigger_action(slug, screen_id, user_id, leaf_id):
|
||||
ok = screens.remove_action_from_screen_trigger(slug, screen_id, leaf_id)
|
||||
return {"ok": ok}
|
||||
|
||||
|
||||
def _dispatch_move_trigger_action(slug, screen_id, user_id, object_id, leaf_id, direction):
|
||||
ok = screens.move_action_in_trigger(slug, object_id, leaf_id, direction)
|
||||
return {"ok": ok}
|
||||
|
||||
|
||||
def _dispatch_move_screen_trigger_action(slug, screen_id, user_id, leaf_id, direction):
|
||||
ok = screens.move_action_in_screen_trigger(slug, screen_id, leaf_id, direction)
|
||||
return {"ok": ok}
|
||||
|
||||
|
||||
def _dispatch_add_generated_image(slug, screen_id, user_id, kind, prompt):
|
||||
"""Scenario -> "Mes assets" (auth.save_user_asset, source="ia") ->
|
||||
posée sur l'écran via LE MÊME chemin que Phase 1
|
||||
@@ -401,6 +574,12 @@ _DISPATCH = {
|
||||
"create_global_variable": _dispatch_create_global_variable,
|
||||
"set_collision_rules": _dispatch_set_collision_rules,
|
||||
"append_action_to_trigger": _dispatch_append_action_to_trigger,
|
||||
"set_screen_triggers": _dispatch_set_screen_triggers,
|
||||
"append_action_to_screen_trigger": _dispatch_append_action_to_screen_trigger,
|
||||
"remove_trigger_action": _dispatch_remove_trigger_action,
|
||||
"remove_screen_trigger_action": _dispatch_remove_screen_trigger_action,
|
||||
"move_trigger_action": _dispatch_move_trigger_action,
|
||||
"move_screen_trigger_action": _dispatch_move_screen_trigger_action,
|
||||
"add_generated_image": _dispatch_add_generated_image,
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user