Export Web/SCORM : port complet du runtime jouable côté navigateur
Phase 1.2 de la feuille de route produit — un paquet SCORM tourne seul dans le LMS du client, sans serveur Forge Engine disponible. Ajoute static/js/play/offline/ (miroir JS de screens/rendering/ et data_actions/, sous window.FORGE_OFFLINE) pour que variables, score, données d'objet, répéteurs et conditions de visibilité fonctionnent entièrement en mémoire côté navigateur ; branche actions.js et bindings.js dessus au lieu d'un fetch() serveur. Ajoute publish/build_scorm_package.py (paquet statique + imsmanifest.xml SCORM 1.2 + wrapper API SCORM) et le bouton "Exporter (Web/SCORM)". Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
9776fbc057
commit
f3b72d73c9
@@ -6,6 +6,7 @@ from ..flow.list_flow_nodes import list_flow_nodes
|
||||
from ..flow.list_flow_edges import list_flow_edges
|
||||
from ..animations.list_animation_clips import list_animation_clips
|
||||
from ..element_types.list_element_types import list_element_types
|
||||
from ..element_types.load_template_tree import _load_template_tree
|
||||
from ..rendering.personnage_data import resolve_personnage_animations
|
||||
from ..scenes.list_scene_objects import list_scene_objects
|
||||
from ..scenes.render_scene_object import render_scene_object
|
||||
@@ -100,7 +101,16 @@ def full_game_payload(slug, player_id=db.PLAYER_SHARED):
|
||||
{**{fname: r.get(col) for fname, col in cols.items()}, "id": r["id"]}
|
||||
for r in rows
|
||||
]
|
||||
fields_meta[str(d["id"])] = [{"name": f["name"], "type": f["type"]} for f in full["fields"]]
|
||||
# min_value/max_value (2.2 — bornage automatique, voir
|
||||
# apply_data_action.py) : nécessaires ici pour que le port
|
||||
# client-side de l'export Web/SCORM (static/js/play/offline/,
|
||||
# voir le plan "port complet du runtime jouable côté navigateur")
|
||||
# puisse reproduire le même bornage sans aller-retour serveur —
|
||||
# inoffensif pour le runtime en ligne, qui ignore ces deux clés.
|
||||
fields_meta[str(d["id"])] = [
|
||||
{"name": f["name"], "type": f["type"], "min_value": f.get("min_value"), "max_value": f.get("max_value")}
|
||||
for f in full["fields"]
|
||||
]
|
||||
|
||||
# element_type_id -> id de son écran-modèle : nécessaire côté client (voir
|
||||
# collectAnimationClips() dans play.html) pour savoir, quand un élément de
|
||||
@@ -111,6 +121,21 @@ def full_game_payload(slug, player_id=db.PLAYER_SHARED):
|
||||
# modèle est utilisé ailleurs.
|
||||
element_types = {str(t["id"]): t["template_screen_id"] for t in list_element_types(slug)}
|
||||
|
||||
# Arbre complet (éléments + descendants) de l'écran-modèle de chaque
|
||||
# élément de jeu réutilisable — voir screens/element_types/
|
||||
# load_template_tree.py, appelée aujourd'hui à CHAQUE rendu (aperçu
|
||||
# créateur ET jeu publié en ligne). Nécessaire ici pour que le port
|
||||
# client-side de l'export Web/SCORM (static/js/play/offline/,
|
||||
# rendu d'un exemplaire d'élément de jeu OU d'un modèle de ligne de
|
||||
# Répéteur) puisse le rejouer sans base de données — inoffensif pour
|
||||
# le runtime en ligne, qui l'ignore.
|
||||
element_type_templates = {}
|
||||
for type_id_str, template_screen_id in element_types.items():
|
||||
if template_screen_id:
|
||||
tree = _load_template_tree(slug, int(type_id_str))
|
||||
if tree:
|
||||
element_type_templates[type_id_str] = tree
|
||||
|
||||
# Instantané des variables globales, pour qu'un nœud Condition puisse en
|
||||
# tester une côté CLIENT (evaluateConditionClause, templates/play.html) —
|
||||
# {{$var}} (Répéteur/Condition de visibilité), lui, reste résolu côté
|
||||
@@ -122,8 +147,16 @@ def full_game_payload(slug, player_id=db.PLAYER_SHARED):
|
||||
for v in db.list_global_variables_for_player(slug, player_id)
|
||||
}
|
||||
|
||||
# id de définition -> nom de l'objet (voir filter_repeater_rows.py côté
|
||||
# serveur, static/js/play/offline/filter-repeater-rows.js côté port
|
||||
# hors ligne) : nécessaire pour résoudre une référence "{{Objet.champ}}"
|
||||
# (Répéteur/Donnée liée/Condition de visibilité) — data/fields_meta
|
||||
# ci-dessus sont déjà indexés par id, jamais par nom.
|
||||
definition_names = {str(d["id"]): d["name"] for d in definitions}
|
||||
|
||||
return {
|
||||
"screens": payload_screens, "flows": flows, "animations": animations,
|
||||
"element_types": element_types, "data": data, "fields_meta": fields_meta,
|
||||
"variables": variables, "personnage_animations": personnage_animations,
|
||||
"definition_names": definition_names, "element_type_templates": element_type_templates,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user