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>
132 lines
7.7 KiB
Python
132 lines
7.7 KiB
Python
import json
|
||
|
||
from flask import render_template, request, abort, g
|
||
|
||
import db
|
||
import screens
|
||
|
||
|
||
def render_scene_edit(slug, screen_id, game):
|
||
"""Vue de l'éditeur de scène 2D — appelée par routes/screens/
|
||
screen_edit.py (le dispatcher, selon screen["kind"]), PAS une route
|
||
Flask elle-même : même URL/endpoint "screen_edit" pour les deux types
|
||
d'écran. Réutilise TEL QUEL tout ce qui est déjà générique côté logique
|
||
de flow/animation/variables/événements (voir screens/screens_repo/
|
||
ensure_schema.py — le type d'écran est une propriété PAR ÉCRAN) —
|
||
seuls les objets de scène et la palette de nœuds proposée diffèrent de
|
||
l'éditeur document."""
|
||
screen = screens.get_screen(slug, screen_id)
|
||
if not screen:
|
||
abort(404)
|
||
objects = screens.list_scene_objects(slug, screen_id)
|
||
for o in objects:
|
||
o["rendered_html"] = screens.render_scene_object(o)
|
||
|
||
# "Monde" (voir screens.resolve_scene_world_size, réutilisé aussi par
|
||
# la miniature de "Mes écrans", routes/games/game_dashboard.py) : un
|
||
# "fond" posé à sa taille RÉELLE (souvent plus grande que la scène,
|
||
# voir add_scene_object.py) rend visible/joignable, dans CET éditeur,
|
||
# tout ce qui dépasse la scène nominale plutôt que de le rogner (le
|
||
# glisser-déposer le permet déjà depuis le fix précédent — sans
|
||
# agrandir le canevas ici, cette partie restait invisible/injoignable
|
||
# à la souris). Le rectangle "🎥 Champ de la caméra" (voir
|
||
# templates/scene_edit.html) marque ensuite où s'arrête la scène
|
||
# nominale À L'INTÉRIEUR de ce monde plus grand.
|
||
world_width, world_height = screens.resolve_scene_world_size(screen, objects)
|
||
|
||
selected_id = request.args.get("selected", type=int)
|
||
personnage_data = None
|
||
personnage_commands = None
|
||
personnage_role = None
|
||
collision_settings = None
|
||
dialogue_box_style = None
|
||
selected_obj = next((o for o in objects if o["id"] == selected_id), None) if selected_id else None
|
||
_UI_WIDGET_KINDS = ("dialogue_box", "quiz_box", "score_widget")
|
||
if selected_obj:
|
||
if selected_obj["kind"] not in _UI_WIDGET_KINDS:
|
||
collision_settings = screens.resolve_collision_settings(selected_obj)
|
||
if selected_obj["kind"] == "personnage":
|
||
personnage_data = screens.resolve_personnage_data(selected_obj)
|
||
personnage_commands = screens.resolve_personnage_commands(selected_obj)
|
||
personnage_role = screens.resolve_personnage_role(selected_obj)
|
||
elif selected_obj["kind"] in _UI_WIDGET_KINDS:
|
||
dialogue_box_style = screens.resolve_dialogue_box_style(selected_obj)
|
||
|
||
_DEFAULT_OBJECT_LABELS = {"personnage": "Personnage", "fond": "Fond"}
|
||
object_labels = {
|
||
o["id"]: (o.get("name") or _DEFAULT_OBJECT_LABELS.get(o["kind"], "Décor"))
|
||
for o in objects
|
||
}
|
||
element_animations_map = {
|
||
o["id"]: screens.resolve_personnage_animations(o) for o in objects if o["kind"] == "personnage"
|
||
}
|
||
|
||
flow_blocks = screens.list_flow_blocks(slug, screen_id)
|
||
flow_nodes = screens.list_flow_nodes(slug, screen_id)
|
||
flow_edges = screens.list_flow_edges(slug, screen_id)
|
||
animation_clips = screens.list_animation_clips(slug, screen_id)
|
||
global_variables = db.list_global_variables(slug)
|
||
custom_events = db.list_custom_events(slug)
|
||
custom_event_usages = screens.list_custom_event_usages(slug)
|
||
custom_events_map = {e["id"]: {"name": e["name"]} for e in custom_events}
|
||
all_screens = screens.list_screens(slug)
|
||
screens_map = {s["id"]: s["name"] for s in all_screens}
|
||
blocks_view = screens.blocks_view(flow_blocks, flow_nodes, object_labels)
|
||
|
||
# Sprites animaux CraftPix (screens.ADMIN_SPRITE_LIBRARY) : réservés au
|
||
# rôle admin (licence CraftPix, voir core/sprite_gate.py et le plan
|
||
# "Bibliothèque de sprites animaux CraftPix").
|
||
sprite_library = screens.SPRITE_LIBRARY if g.current_user["role"] == "admin" else screens.PUBLIC_SPRITE_LIBRARY
|
||
sprite_library_families = screens.sprite_gallery_families(sprite_library)
|
||
# Images de fond CraftPix (screens.BACKGROUND_LIBRARY) : même règle
|
||
# d'accès que les sprites animaux — réservées au rôle admin.
|
||
background_library = screens.BACKGROUND_LIBRARY if g.current_user["role"] == "admin" else {}
|
||
|
||
return render_template(
|
||
"scene_edit.html",
|
||
game=game, screen=screen, all_screens=all_screens,
|
||
world_width=world_width, world_height=world_height,
|
||
objects=objects, selected_id=selected_id, personnage_data=personnage_data,
|
||
personnage_commands=personnage_commands or screens.DEFAULT_PERSONNAGE_COMMANDS,
|
||
personnage_role=personnage_role or screens.DEFAULT_PERSONNAGE_ROLE,
|
||
personnage_role_choices=screens.PERSONNAGE_ROLES, personnage_role_labels=screens.PERSONNAGE_ROLE_LABELS,
|
||
collision_settings=collision_settings or screens.DEFAULT_COLLISION_SETTINGS,
|
||
dialogue_box_style=dialogue_box_style or screens.DEFAULT_DIALOGUE_BOX_STYLE,
|
||
dialogue_box_font_weight_choices=screens.FONT_WEIGHT_CHOICES,
|
||
sprite_library=sprite_library, sprite_library_families=sprite_library_families,
|
||
sprite_library_json=json.dumps(sprite_library),
|
||
background_library=background_library,
|
||
element_animations_map_json=json.dumps(element_animations_map),
|
||
element_orientation_values=screens.ELEMENT_ORIENTATION_VALUES,
|
||
element_orientation_labels_json=json.dumps(screens.ELEMENT_ORIENTATION_LABELS),
|
||
element_visibility_values=screens.ELEMENT_VISIBILITY_VALUES,
|
||
element_visibility_labels_json=json.dumps(screens.ELEMENT_VISIBILITY_LABELS),
|
||
trigger_events=screens.TRIGGER_EVENTS_2D, action_type_labels=screens.ACTION_TYPE_LABELS_2D,
|
||
object_action_properties=screens.OBJECT_ACTION_PROPERTIES,
|
||
element_action_property_labels_json=json.dumps(dict(screens.OBJECT_ACTION_PROPERTIES)),
|
||
condition_operators=screens.CONDITION_OPERATORS,
|
||
data_operations=screens.DATA_OPERATIONS, data_operation_labels=screens.DATA_OPERATION_LABELS,
|
||
score_status_choices=db.SCORE_STATUS_CHOICES,
|
||
score_status_labels_json=json.dumps(db.SCORE_STATUS_LABELS),
|
||
data_operation_labels_json=json.dumps(screens.DATA_OPERATION_LABELS),
|
||
condition_operator_labels_json=json.dumps(screens.CONDITION_OPERATOR_LABELS),
|
||
action_type_labels_json=json.dumps(screens.ACTION_TYPE_LABELS_2D),
|
||
elements_labels_json=json.dumps(object_labels), screens_map_json=json.dumps(screens_map),
|
||
flow_nodes=flow_nodes, flow_edges=flow_edges,
|
||
flow_nodes_json=json.dumps(flow_nodes), flow_edges_json=json.dumps(flow_edges),
|
||
flow_blocks_json=json.dumps(flow_blocks), blocks_view=blocks_view,
|
||
animation_clips=animation_clips, animation_clips_json=json.dumps(animation_clips),
|
||
animate_css_catalog=screens.ANIMATE_CSS_CATALOG, animate_css_labels_json=json.dumps(screens.ANIMATE_CSS_LABELS),
|
||
global_variables=global_variables, global_variable_types=db.GLOBAL_VARIABLE_TYPES,
|
||
custom_events=custom_events, custom_event_usages=custom_event_usages,
|
||
custom_events_map_json=json.dumps(custom_events_map),
|
||
definitions=db.list_definitions(slug),
|
||
definitions_json=json.dumps({str(d["id"]): screens.data_definition_options(slug, d["id"]) for d in db.list_definitions(slug)}),
|
||
definitions_names_json=json.dumps({d["id"]: d["name"] for d in db.list_definitions(slug)}),
|
||
# "qui parle" dans un dialogue de déclencheur (voir "ℹ️
|
||
# Informations", static/js/triggers/trigger-editor.js) : les noms
|
||
# donnés à N'IMPORTE QUEL objet de scène (personnage, décor,
|
||
# fond), toutes scènes du jeu confondues.
|
||
scene_object_names_json=json.dumps(screens.list_named_scene_object_names(slug)),
|
||
)
|