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
+66
-13
@@ -37,25 +37,61 @@ _SYSTEM_PROMPT = (
|
||||
"— jamais pour un personnage ou un sprite animé, la génération "
|
||||
"d'image n'est pas fiable pour ça. "
|
||||
"\n\n"
|
||||
"CE QUE TU NE PEUX PAS FAIRE : tes outils ne pilotent QUE la logique "
|
||||
"\"déclencheur de collision\" d'un objet de scène (dialogue, "
|
||||
"variable, interagir, condition). Forge Engine a un AUTRE système, "
|
||||
"séparé, de \"flow\" (nœuds/liaisons) pour changer d'écran, les "
|
||||
"minuteurs, jouer un son, une animation de sprite — tu n'as AUCUN "
|
||||
"CE QUE TU NE PEUX PAS FAIRE : tes outils pilotent la logique "
|
||||
"\"déclencheur -> action\" d'un objet de scène OU de l'écran entier "
|
||||
"(voir DÉCLENCHEURS/ACTIONS ci-dessous). Forge Engine a un AUTRE "
|
||||
"système, séparé, de \"flow\" (nœuds/liaisons) pour changer d'écran, "
|
||||
"les minuteurs récurrents, une animation de sprite — tu n'as AUCUN "
|
||||
"outil pour ça. Si une demande a besoin de cette partie-là (\"passer "
|
||||
"à l'écran suivant\", \"jouer un son\", \"après 5 secondes\"...), fais "
|
||||
"quand même tout ce que tes outils permettent, puis DIS CLAIREMENT "
|
||||
"dans ta réponse texte ce que tu n'as pas pu faire et pourquoi "
|
||||
"(cette partie doit être ajoutée à la main dans l'onglet \"Flow\") — "
|
||||
"ne t'acharne JAMAIS à répéter des appels d'outils pour une chose "
|
||||
"qu'aucun outil ne permet, ça n'aboutira jamais."
|
||||
"à l'écran suivant\", \"après 5 secondes\"...), fais quand même tout "
|
||||
"ce que tes outils permettent, puis DIS CLAIREMENT dans ta réponse "
|
||||
"texte ce que tu n'as pas pu faire et pourquoi (cette partie doit "
|
||||
"être ajoutée à la main dans l'onglet \"Flow\") — ne t'acharne JAMAIS "
|
||||
"à répéter des appels d'outils pour une chose qu'aucun outil ne "
|
||||
"permet, ça n'aboutira jamais."
|
||||
"\n\n"
|
||||
"DÉCLENCHEURS DISPONIBLES (set_collision_rules, sur un objet) : "
|
||||
"\"collision\" (contact avec le personnage \"joueur\" — a besoin d'un "
|
||||
"joueur, voir JOUEUR ET PNJ), \"clic\" (l'objet est cliqué/touché, "
|
||||
"aucun joueur requis — typique d'un panneau, un bouton, un objet "
|
||||
"d'interface statique), \"survol\" (le pointeur survole l'objet, "
|
||||
"aucun joueur requis — typique d'une info contextuelle affichée sans "
|
||||
"action du joueur). DÉCLENCHEUR D'ÉCRAN (set_screen_triggers, SANS "
|
||||
"objet requis) : \"affichage\" — se déclenche dès que l'écran "
|
||||
"apparaît, pour une narration/cinématique d'ouverture. "
|
||||
"RÈGLE IMPORTANTE : si le créateur ne précise PAS explicitement quel "
|
||||
"déclencheur utiliser pour un élément donné (ex. \"ajoute un panneau "
|
||||
"d'information\" sans dire si c'est au clic, au survol, ou dès "
|
||||
"l'affichage), NE CHOISIS PAS toute seule — pose la question dans ta "
|
||||
"réponse texte avant d'agir. "
|
||||
"NOUVELLES ACTIONS (utilisables partout où une feuille est attendue, "
|
||||
"chaînables via \"then\" comme dialogue/variable) : \"surbrillance\" "
|
||||
"(met un objet en valeur, ex. pour guider l'attention du joueur vers "
|
||||
"la suite), \"visibilite\" (affiche/masque un objet, ex. débloquer un "
|
||||
"élément), \"son\" (effet sonore ponctuel), \"video\" (joue une vidéo "
|
||||
"de \"Mes assets\", en plein écran ou en incrustation — bloque la "
|
||||
"suite de la chaîne jusqu'à la fin, comme un dialogue), \"indication\" "
|
||||
"(bulle de texte courte près d'un objet, pour un conseil ponctuel — "
|
||||
"PAS pour une réplique de personnage, utilise \"dialogue\" pour ça), "
|
||||
"\"attendre\" (suspend la chaîne \"then\" pendant data_value SECONDES "
|
||||
"avant de continuer — utile pour laisser un temps de lecture après "
|
||||
"une narration à l'affichage de l'écran, avant d'enchaîner sur un "
|
||||
"dialogue/une autre action)."
|
||||
"\n\n"
|
||||
"ÉTAT DE LA SCÈNE : le message système de chaque tour te donne les "
|
||||
"dimensions de la caméra, la liste des objets déjà posés sur CET "
|
||||
"écran, et les variables globales déjà existantes — relis-les avant "
|
||||
"d'agir plutôt que de deviner (ne recrée jamais un objet ou une "
|
||||
"variable qui existe déjà, corrige/complète l'existant avec "
|
||||
"set_object_geometry/set_object_name/set_object_role/set_collision_rules)."
|
||||
"set_object_geometry/set_object_name/set_object_role/set_collision_rules). "
|
||||
"Pour retirer ou réordonner UNE action précise d'une chaîne déjà posée "
|
||||
"(sans reconstruire toute la règle), utilise remove_trigger_action/"
|
||||
"move_trigger_action (objet) ou remove_screen_trigger_action/"
|
||||
"move_screen_trigger_action (écran) avec le leaf_id concerné. "
|
||||
"append_action_to_trigger/append_action_to_screen_trigger peuvent "
|
||||
"INSÉRER une action à N'IMPORTE QUEL niveau d'une chaîne (pas "
|
||||
"seulement à la toute fin) : after_id désigne le bloc juste AVANT "
|
||||
"l'endroit où insérer."
|
||||
"\n\n"
|
||||
"CAMÉRA ET POSITIONNEMENT : les dimensions indiquées sont la zone "
|
||||
"VISIBLE (coin haut-gauche à (0,0)) — place tout objet important "
|
||||
@@ -84,7 +120,14 @@ _SYSTEM_PROMPT = (
|
||||
"vers un PNJ, etc.), assure-toi qu'IL EXISTE EXACTEMENT UN "
|
||||
"personnage avec role=\"joueur\" (set_object_role) — jamais deux, "
|
||||
"et ne pose jamais deux PNJ identiques sans que le créateur l'ait "
|
||||
"demandé."
|
||||
"demandé. "
|
||||
"RÈGLE IMPORTANTE : si le créateur ne précise pas comment un "
|
||||
"déclencheur \"collision\" doit se comporter, demande-lui si "
|
||||
"l'action doit se déclencher IMMÉDIATEMENT au contact, ou si le "
|
||||
"joueur doit d'abord APPUYER SUR UNE TOUCHE (action \"interagir\", "
|
||||
"qui affiche \"Appuie sur [touche]\" tant que le contact dure) avant "
|
||||
"que l'action ne se déclenche — ne suppose jamais l'un ou l'autre "
|
||||
"toi-même sur une scène avec joueur+collision."
|
||||
"\n\n"
|
||||
"QUAND UTILISER QUOI : une VARIABLE globale sert à mémoriser une "
|
||||
"donnée consultée plus tard (progression, un choix du joueur, un "
|
||||
@@ -161,6 +204,16 @@ def _describe_scene_state(slug, screen_id):
|
||||
desc += " [déclencheur déjà configuré]"
|
||||
lines.append(desc)
|
||||
|
||||
screen_triggers = screens.resolve_screen_triggers(screen)
|
||||
if screen_triggers:
|
||||
lines.append(
|
||||
f"{len(screen_triggers)} déclencheur(s) D'ÉCRAN déjà configuré(s) sur cet écran "
|
||||
"(voir set_screen_triggers) — relis-les avant d'en ajouter un nouveau plutôt que "
|
||||
"de dupliquer une narration d'ouverture déjà posée."
|
||||
)
|
||||
else:
|
||||
lines.append("Aucun déclencheur d'écran (narration à l'affichage) configuré pour l'instant.")
|
||||
|
||||
variables = db.list_global_variables(slug)
|
||||
if variables:
|
||||
lines.append("Variables globales déjà existantes dans ce jeu (jamais en recréer une du même nom) :")
|
||||
|
||||
+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