b9d3c460874f5c27affed75bb31156e0ed87c884
48
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b9d3c46087 |
Bouton "đ Agrandir la camĂ©ra Ă la zone visible"
Fait de tout le "monde" affichĂ© par l'Ă©diteur (la taille rĂ©elle d'un "fond" plus grand que la scĂšne nominale, voir routes/scenes/ scene_edit_view.py::world_width/world_height) la scĂšne/camĂ©ra ELLE-MĂME â jusqu'ici scene_width/scene_height n'Ă©taient fixĂ©es qu'Ă la crĂ©ation de l'Ă©cran, jamais modifiables ensuite. Le bouton n'apparaĂźt que quand un fond dĂ©passe encore la scĂšne nominale (mĂȘme condition que le repĂšre "đ„ Champ de la camĂ©ra"), affiche la taille cible, recharge la page une fois appliquĂ©. La camĂ©ra ne recadre alors plus rien en jeu (monde == scĂšne == viewport, voir personnage-controller.js::forgeUpdateSceneCamera). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
6b4846a646 |
Fix vrai bug : un objet plus grand que la scÚne (fond/caméra) restait figé à (0,0)
ConfirmĂ© par l'utilisateur : liĂ© Ă l'introduction du fond/camĂ©ra (voir add_scene_object.py â un "fond" est posĂ© Ă sa taille RĂELLE, souvent bien plus grande que la scĂšne, exprĂšs, pour que la camĂ©ra le suive en dĂ©filant sur un monde plus grand que le viewport). Le glisser en position utilisait Math.max(0, Math.min(SCENE_WIDTH - width, ...)) â cette formule suppose SCENE_WIDTH - width POSITIF (objet plus petit que la scĂšne). Pour un objet plus GRAND (ex. un fond de 1920px sur une scĂšne de 960px), cette diffĂ©rence est NĂGATIVE, et Math.max(0, nĂ©gatif) ramenait TOUJOURS la position Ă 0 quel que soit le glisser â l'objet restait donc figĂ©, impossible Ă repositionner. Le redimensionnement plafonnait aussi la largeur/hauteur Ă "ce qui reste dans la scĂšne depuis son coin" (SCENE_WIDTH - left) â empĂȘchant justement de rendre un objet plus grand que la scĂšne par glisser (un fond ne pouvait ĂȘtre agrandi qu'en le recrĂ©ant via la galerie). Les deux bornes sont corrigĂ©es pour fonctionner quel que soit lequel (scĂšne ou objet) est le plus grand. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
7c9b2c9b25 |
Fix "impossible d'aller plus loin vers la droite" â dĂ©filement automatique du canevas
Cause confirmĂ©e (tous les objets bloquaient au mĂȘme endroit, pas spĂ©cifique Ă un widget) : une scĂšne Ă taille fixe (960Ă540 par ex.) peut ĂȘtre plus LARGE que la zone visible de .canvasFrame (overflow:auto) selon la largeur de fenĂȘtre â sa partie droite/basse dĂ©file alors HORS de vue, et le curseur atteignait le bord de cette zone VISIBLE bien avant celui de la scĂšne elle-mĂȘme : la souris ne pouvait tout simplement plus bouger physiquement plus loin, sans aucun rapport avec la limite rĂ©elle (960px) de la scĂšne. onSceneObjectMouseDown/onSceneObjectResizeMouseDown font maintenant dĂ©filer .canvasFrame automatiquement quand le curseur approche un bord pendant un glisser/redimensionnement â et continuent de dĂ©placer l'objet mĂȘme si la souris reste immobile prĂšs du bord (mirror du comportement standard d'un glisser-dĂ©poser dans une zone scrollable), grĂące Ă une position ACCUMULĂE plutĂŽt que recalculĂ©e depuis le point de dĂ©part fixe. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
046c38b4dc |
Annule la réservation d'espace des panneaux, corrige le vrai bug (drag invisible sous eux)
Retour en arriĂšre sur le commit prĂ©cĂ©dent : les panneaux flottants doivent RESTER en position:fixed par-dessus le canevas pour maximiser l'espace utile (remarque explicite) â leur rĂ©server une marge permanente allait Ă l'encontre de ce principe et ne rĂ©glait rien. Vrai bug identifiĂ© : un objet glissĂ© (dĂ©placĂ© ou redimensionnĂ©) sous l'un de ces panneaux (z-index:60) disparaissait littĂ©ralement Ă l'Ă©cran PENDANT le geste â toujours dĂ©placĂ©/enregistrĂ© correctement en dessous, juste invisible, donnant l'impression qu'on ne pouvait pas y dĂ©poser d'objet. ĂlevĂ© au-dessus des panneaux (z-index trĂšs haut) le temps du glisser/redimensionnement SEULEMENT, restaurĂ© ensuite. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
d705f58c4a |
Fix accĂšs au canevas masquĂ© + quiz : header quĂȘte, rĂ©ponse fausse n'avance jamais bloquĂ©e, animĂ©
- Bug corrigĂ© : les panneaux flottants "đ§© Objets"/"âïž PropriĂ©tĂ©s" sont en position:fixed (hors du flux flex de .builder3) â ils flottaient PAR-DESSUS le canevas sans jamais rĂ©duire sa largeur, rendant sa partie droite/gauche inaccessible au clic/glisser tant qu'un panneau restait ouvert. .builderCanvasArea rĂ©serve maintenant leur largeur (marge) dĂšs qu'un panneau est ouvert (:has()). - BoĂźte Ă quiz : header = "QuĂȘte : <titre>" (au lieu du texte de la question), corps = la question ET ses choix ensemble. - Mauvaise rĂ©ponse : ne bloque plus JAMAIS la progression (bug signalĂ© : "je suis obligĂ© de bien rĂ©pondre sinon j'avance pas") â surligne la bonne rĂ©ponse en vert (le choix cliquĂ© en rouge s'il Ă©tait faux) puis avance automatiquement aprĂšs un court dĂ©lai, sans jamais octroyer de points. Un second clic pendant la rĂ©vĂ©lation est ignorĂ©. - Animations : la boĂźte Ă quiz rejoue une entrĂ©e (pop-in) Ă CHAQUE nouvelle question, la bonne rĂ©ponse pulse en vert, une mauvaise rĂ©ponse "secoue" en rouge. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
b5546df044 |
Quiz jouable : budget de récompense, boßte à quiz, widget score
- RĂ©compense d'une quĂȘte = budget MAXIMUM pour ses questions : la somme des points des "â Question" ne peut plus dĂ©passer recompense_score (routes/quests/quest_dialogues.py Ă l'enregistrement des dialogues, quest_update.py si on abaisse la rĂ©compense sous ce qui est dĂ©jĂ rĂ©parti) â message d'erreur explicite (400) dans les deux sens, affichĂ© via alert() cĂŽtĂ© Ă©diteur (quest-editor.js), jamais enregistrĂ© silencieusement dans un Ă©tat incohĂ©rent. - Nombre de choix par question plafonnĂ© Ă 4 (au lieu de 8). - Deux nouveaux widgets "đ„ïž Interface" (mĂȘmes fondations que "đŹ BoĂźte de dialogue" â screens/rendering/dialogue_box_style.py, mĂȘme panneau "đš Style") : - "â BoĂźte Ă quiz" : affiche la question et ses choix, sans pied (une question se rĂ©sout au clic sur un choix, pas de "Suivant"). - "đ Score" : affiche en continu les points gagnĂ©s, TOUJOURS visible une fois posĂ© (contrairement aux boĂźtes de dialogue/quiz, masquĂ©es par dĂ©faut). - Moteur d'exĂ©cution (static/js/play/dialogue-box-controller.js, refonte) : une conversation de quĂȘte alterne maintenant rĂ©pliques (boĂźte de dialogue) et questions (boĂźte Ă quiz) selon le type de chaque ligne. Bonne rĂ©ponse -> crĂ©dite le score (compteur runtime dĂ©diĂ©, jamais liĂ© au score du moteur document) et avance ; mauvaise rĂ©ponse -> rien, la question reste affichĂ©e pour rĂ©essayer ; aucune boĂźte Ă quiz posĂ©e -> la question est ignorĂ©e plutĂŽt que de bloquer la conversation. Le dialogue "en_cours" Ă©puisĂ© (donc, s'il contenait des questions, toutes rĂ©pondues) fait passer la quĂȘte "terminee" (en mĂ©moire seulement, comme le reste de ce systĂšme â voir accepter/ refuser une offre de quĂȘte) : le joueur peut alors quitter la scĂšne normalement, ces widgets n'Ă©tant jamais des obstacles. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
e972acf9e6 |
QuĂȘtes : bulle "â Question" Ă choix multiples, Ă cĂŽtĂ© de "+ RĂ©plique"
Nouveau type de ligne dans un dialogue de quĂȘte (voir
db/quests/sanitize_quest_dialogues.py) : une bulle JAUNE, dans la mĂȘme
chaĂźne verticale que les rĂ©pliques (mĂȘme alignement, reliĂ©e par un
trait) â header : nombre de choix (2 Ă 8) + rĂ©compense (score, montant
en points) ; body : la question, ses choix et un bouton radio pour
désigner la bonne réponse. Aucune limite au nombre de questions par
colonne, comme pour une réplique.
Les lignes de dialogue portent maintenant explicitement
{"type": "dialogue", ...} (au lieu d'un objet sans type) pour
distinguer les deux formes â migration de forme, tests mis Ă jour.
PortĂ©e de ce commit : l'ĂDITEUR (crĂ©er/modifier une question). Le jeu
lui-mĂȘme (afficher la question au joueur, vĂ©rifier la rĂ©ponse,
attribuer la rĂ©compense) reste Ă construire â le widget "đŹ BoĂźte de
dialogue" ne sait aujourd'hui afficher qu'une réplique.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
e90d941527 |
QuĂȘte "nouvelle" : Ă©cran Accepter/Refuser Ă la fin du dialogue
Une fois le dialogue Ă©puisĂ©, si la quĂȘte est encore "nouvelle", la boĂźte de dialogue bascule sur un Ă©cran d'offre au lieu de se masquer : header "QuĂȘte : <titre>", body l'objectif, pied deux boutons. - Accepter -> la quĂȘte passe "en_cours" (en MĂMOIRE seulement, gameData.quests â jamais persistĂ© en base : le statut en base est celui de DĂPART pour toute nouvelle partie, pas un Ă©tat de partie en cours, voir PLAYER_SHARED/full_game_payload.py). - Refuser -> la boĂźte se referme SANS toucher au statut, qui reste "nouvelle" : la prochaine interaction rejoue exactement le mĂȘme dialogue depuis le dĂ©but â impossible d'avancer la quĂȘte sans l'accepter un jour. Pour tout autre statut (dĂ©jĂ "en_cours"/"terminee"), la boĂźte se masque normalement Ă la fin du dialogue, comme avant. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
aa17614c7b |
Fix bulle "Appuie sur X" trop haute au-dessus du personnage
La bulle s'ancrait sur le cadre BRUT du sprite (<img> style.left/top/ width/height) â beaucoup de sprites (animaux CraftPix notamment, voir screens/labels/animal_sprite_library.py) ont un trĂšs grand canevas padĂ© autour d'une silhouette bien plus petite, faisant flotter la bulle loin au-dessus du personnage visible. Elle s'ancre maintenant sur la BOĂTE DE COLLISION (objRect, dĂ©jĂ rĂ©solue via forgeElementBoxRect â largeur/hauteur/dĂ©calage rĂ©glables dans "đ§± Collision"), que l'auteur ajuste dĂ©jĂ pour Ă©pouser la silhouette rĂ©elle : ancre bien plus fidĂšle, sans configuration supplĂ©mentaire Ă faire. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
00f9cf33e9 |
Fix bulle "Appuie sur X" invisible + retire Attaque/ĂvĂ©nement du picker
Bug corrigĂ© : la bulle "Appuie sur X" Ă©tait ajoutĂ©e comme ENFANT de l'objet de scĂšne (un <img> pour "personnage"/"decor"/"fond") â un Ă©lĂ©ment REMPLACĂ dont les enfants DOM ajoutĂ©s en JS ne sont JAMAIS affichĂ©s par un navigateur, quel que soit son CSS. La bulle existait donc bien dans le DOM (aucune erreur) mais restait invisible Ă l'Ă©cran. Elle est maintenant ajoutĂ©e comme SĆUR de l'objet dans son parent (.sceneWorld/.sceneUI), positionnĂ©e en JS aux mĂȘmes coordonnĂ©es. Assistant "+ Action" de l'Ă©diteur de collision : "âïž Attaquer la cible" et "đŁ DĂ©clencher un Ă©vĂ©nement" retirĂ©s des choix proposĂ©s (pas besoin pour le moment) â le serveur continue d'accepter/d'exĂ©cuter une rĂšgle dĂ©jĂ enregistrĂ©e avec l'un des deux, rien ne casse pour l'existant. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
726775fe4e |
Fix "Ă la collision" â la marge de 3px restait insuffisante
forgeWouldCollide (personnage-controller.js) bloque le dĂ©placement par PAS ENTIER (cmd.vitesse px/tick, 4 par dĂ©faut mais rĂ©glable) : le joueur peut donc rester bloquĂ© jusqu'Ă PRESQUE un pas entier de distance de l'obstacle, pas seulement 1-3px â la marge de tolĂ©rance du correctif prĂ©cĂ©dent (3px) restait insuffisante mĂȘme pour la vitesse par dĂ©faut dans les cas les moins favorables. PortĂ©e Ă 24px, confortable pour toute vitesse raisonnablement configurĂ©e sans jamais dĂ©clencher "Ă la collision" alors que les objets sont encore visiblement sĂ©parĂ©s. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
3a67ba622b |
Fix "à la collision" qui ne se déclenchait jamais
Cause rĂ©elle : un objet portant une rĂšgle de collision est presque toujours AUSSI un obstacle solide (collision activĂ©e par dĂ©faut, voir forgeWouldCollide, personnage-controller.js) â le dĂ©placement du joueur est donc bloquĂ© PILE au contact, sans jamais laisser les deux boĂźtes se chevaucher rĂ©ellement. Le dĂ©clencheur "collision" testait un chevauchement STRICT (forgeShapesOverlap nu), qui n'Ă©tait donc jamais atteint : la rĂšgle ne se dĂ©clenchait jamais en jeu rĂ©el, malgrĂ© un contact visible Ă l'Ă©cran. Le dĂ©clencheur teste maintenant la boĂźte de l'objet Ă©largie d'une petite marge (3px) â assez pour dĂ©tecter un simple contact â SANS toucher au blocage physique du dĂ©placement (restĂ© strict, inchangĂ©). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
5e17e42dfa |
Test bout en bout : collision -> quĂȘte -> boĂźte de dialogue
VĂ©rifie explicitement le lien moteur demandĂ© : une rĂšgle de collision "quete" retrouve la quĂȘte, rĂ©cupĂšre les rĂ©pliques de son statut actuel, les affiche dans l'ordre dans le widget "đŹ BoĂźte de dialogue" posĂ© sur la scĂšne, avance au clic sur "Suivant", disparaĂźt Ă la derniĂšre rĂ©plique â dĂ©jĂ implĂ©mentĂ© (collision-rules-controller.js + dialogue-box-controller.js), ce test couvre la CHAĂNE COMPLĂTE plutĂŽt que chaque brique isolĂ©ment. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
077af2234d |
Fix "+ Réplique" qui remplaçait la bulle au lieu d'en ajouter une
Bug : questSaveDialogues() adoptait la rĂ©ponse du serveur comme nouvel Ă©tat local aprĂšs chaque enregistrement. Une bulle fraĂźchement ajoutĂ©e a un texte encore vide (rien tapĂ©) â sanitize_quest_dialogues.py la rejette lĂ©gitimement (jamais de rĂ©plique sans texte persistĂ©e) â donc la rĂ©ponse renvoyait un tableau amputĂ© de cette bulle, que le client adoptait aveuglĂ©ment, effaçant la bulle en cours d'Ă©criture. Le clic suivant sur "+ RĂ©plique" repartait donc du mĂȘme Ă©tat qu'avant, semblant "remplacer" la bulle plutĂŽt que d'en ajouter une seconde. Le client ne resynchronise plus jamais son Ă©tat depuis la rĂ©ponse d'enregistrement â il est dĂ©jĂ la seule source de vĂ©ritĂ© pendant l'Ă©dition. Colonnes de dialogue : occupent maintenant toute la hauteur disponible de la modale plein Ă©cran et dĂ©filent chacune indĂ©pendamment (titre et bouton "+ RĂ©plique" restent fixes), au lieu d'une hauteur minimale fixe. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
521792fe00 |
Dialogues de quĂȘte : bulles illimitĂ©es, n'importe quel objet, timeline reliĂ©e
- "qui parle" n'est plus rĂ©servĂ© au personnage : N'IMPORTE QUEL objet de scĂšne nommĂ© (personnage, dĂ©cor, fond â voir "âčïž Informations", screens/rendering/scene_object_names.py, ex-personnage_names.py) peut parler dans un dialogue. - ConfirmĂ©/documentĂ© : aucune limite au nombre de rĂ©pliques par colonne (bouton "+ RĂ©plique" reste toujours disponible). - Bulle = header (menu dĂ©roulant du "qui parle") + body (le texte), centrĂ©e dans sa colonne Ă 70% de largeur, alignĂ©e verticalement et reliĂ©e Ă la suivante par un trait â remplace l'ancien alignement gauche/droite façon chat. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
2347a70c45 |
QuĂȘtes/collision : personnages nommĂ©s, dialogues qui parlent, boĂźte de dialogue en jeu
- "âčïž Informations" (propriĂ©tĂ©s d'un personnage) : nom Ă©ditable, rĂ©utilisĂ© comme "qui parle" dans l'Ă©diteur de dialogue de quĂȘte (menu dĂ©roulant, plus "Joueur" toujours disponible) â remplace l'ancien choix binaire joueur/pnj. db.sanitize_quest_dialogues accepte maintenant un nom libre. - Nouveau menu "đ„ïž Interface" (palette d'objets) avec le premier widget : "đŹ BoĂźte de dialogue" (kind="dialogue_box", screens/rendering/ dialogue_box_style.py) â position/taille comme tout objet de scĂšne, panneau "đš Style" dĂ©diĂ© (police, taille, Ă©paisseur, couleur du texte, couleur header/body/footer). Rendu en <div> Ă 3 zones, jamais soumis Ă la collision ni Ă l'Ă©diteur de collision (exclu partout : obstacles, liste des rĂšgles, payload). Rendu cĂŽtĂ© jeu dans .sceneUI, une couche FIXE au viewport (jamais .sceneWorld, qui dĂ©file avec la camĂ©ra). - static/js/play/dialogue-box-controller.js : fait le lien moteur entre l'action "quete" de l'Ă©diteur de collision et ce widget â affiche la rĂ©plique en cours du dialogue correspondant au STATUT ACTUEL de la quĂȘte (gameData.quests, dĂ©sormais exposĂ© game-wide par full_game_payload.py), avance au clic sur "Suivant" (header = qui parle, body = texte, footer = bouton), se masque Ă la derniĂšre rĂ©plique. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
390ee831ca |
QuĂȘtes : dialogue dans une modale Ă part, en plein Ă©cran
Sépare la modale unique en deux étapes distinctes demandées par
l'utilisateur : #questFieldsModal ("je crĂ©e" â titre/objectif/
récompense/statut/résultat, taille normale) puis, sur un bouton dédié
"đŹ Ajouter les dialogues", #questDialogueModal ("j'ajoute les
dialogues" â les 3 colonnes de rĂ©pliques SEULES, occupant tout
l'Ă©cran). Un bouton "â" ramĂšne Ă la fiche ; "â Utiliser cette quĂȘte"
reste disponible dans les deux, pour l'intégration avec l'éditeur de
collision.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
78c1aba8a0 |
Ăditeur de quĂȘtes : onglet Ă cĂŽtĂ© de Collision, pas une page Ă part
Corrige la mauvaise interprĂ©tation de la demande initiale : la page dĂ©diĂ©e "đșïž QuĂȘtes" (nav du jeu) est retirĂ©e au profit d'un nouvel onglet "đșïž QuĂȘtes" DANS l'Ă©diteur de scĂšne 2D, juste Ă cĂŽtĂ© de "đ§© Collision" â mĂȘme liste tabulaire que l'onglet "đŁ ĂvĂ©nements" (titre/objectif/rĂ©compense/statut/rĂ©sultat par ligne, pas des cartes), un clic sur une ligne ouvrant la modale d'arbre de dialogue dĂ©jĂ construite. L'assistant "+ Action" de l'Ă©diteur de collision ouvre toujours cette mĂȘme modale pour "DĂ©clencher une quĂȘte". Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
51427b5297 |
Ăditeur de quĂȘtes : titre/objectif/rĂ©compense/statut/rĂ©sultat + dialogue Ă 3 colonnes
Nouvel espace "đșïž QuĂȘtes" (game-wide, comme les Ă©vĂ©nements personnalisĂ©s) : liste de cartes + "+ Nouvelle quĂȘte", chaque carte ouvrant une modale d'arbre de dialogue en bulles de théùtre ("speaker: texte"), une colonne par statut de quĂȘte (nouvelle/en cours/terminĂ©e) puisque le dialogue jouĂ© dĂ©pend de l'Ă©tat de la quĂȘte au moment oĂč le joueur parle au PNJ. Bulles alternĂ©es joueur/PNJ, couleur diffĂ©rente selon qui parle. L'action "DĂ©clencher une quĂȘte" de l'Ă©diteur de collision n'est plus un champ texte libre : elle ouvre directement CETTE MĂME modale (picker de quĂȘtes existantes + crĂ©ation Ă la volĂ©e), et finalise la rĂšgle avec l'id numĂ©rique de la quĂȘte choisie â collision_rules.py migrĂ© en consĂ©quence (quete_id devient un entier, comme evenement_id). Corrige aussi la miniature d'objet de la carte "đ§© Collision", 3Ă plus grande (120px) comme demandĂ©. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
b64541856f |
Ăditeur de collision : cartes "piĂšces qui s'emboĂźtent", animĂ©es
Refonte visuelle de l'onglet "đ§© Collision" â l'ancien rendu (pastilles plates + flĂšches) ne rendait pas la mĂ©taphore "piĂšces de puzzle qui s'emboĂźtent" demandĂ©e. Chaque maillon (dĂ©clencheur/action/sous-action) est maintenant une piĂšce chevronnĂ©e colorĂ©e par type, imbriquĂ©e dans la suivante via clip-path + marge nĂ©gative, avec une entrĂ©e animĂ©e en cascade. Cartes objet et choix de l'assistant modal redessinĂ©s en grille de cartes avec icĂŽne + libellĂ©, hover/entrĂ©e animĂ©s. Purement visuel â aucun changement de logique/donnĂ©es (dĂ©jĂ couvert par tests/test_collision_rules.py et static/js/play/__tests__/collision-rules-controller.test.js). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
ee270d0286 |
Ăditeur de collision (jeu 2D) : rĂšgles trigger -> action par objet
Nouvel onglet "đ§© Collision" dans l'Ă©diteur de scĂšne 2D : chaque objet (hors joueur et fond) peut porter des rĂšgles "Ă la collision" ou "dans un pĂ©rimĂštre (px)" dĂ©clenchant une action (quĂȘte, attaque, Ă©vĂ©nement, ou interagir - qui affiche "Appuie sur [touche]" puis exĂ©cute une sous-action Ă l'appui, un seul niveau d'imbrication). Backend (sanitisation, route de persistance, exposition dans full_game_payload) + assistant modal en cartes empilĂ©es cĂŽtĂ© client. Ajoute le moteur d'exĂ©cution runtime (collision-rules-controller.js) : dĂ©tecte l'entrĂ©e en collision/pĂ©rimĂštre avec le joueur Ă chaque tick, dĂ©clenche l'action une seule fois par entrĂ©e, pur JS client sans appel serveur (fonctionne Ă l'identique en ligne et en export SCORM). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
dfaf85fac1 |
Retire la collision sur les images de fond + les blocs de logique/timeline de l'éditeur 2D
Deux retours utilisateur sur l'Ă©diteur de scĂšne 2D : - Le panneau "đ§± Collision" (rĂ©glages + aperçu visuel sur le canevas) n'apparaĂźt plus pour un objet kind="fond" : jamais un obstacle ni une cible de collision possible (dĂ©jĂ exclu de forgeSolidObstacles cĂŽtĂ© jeu), ce panneau n'aurait donc aucun effet. - Retire les onglets "đ§© Blocs de logique" et "đŹ Timeline d'animation" de CET Ă©diteur (l'Ă©diteur document, templates/screen_edit.html, les garde tel quel) â en prĂ©vision d'un Ă©diteur dĂ©diĂ© aux scĂšnes 2D (collision/pĂ©rimĂštre -> action), pas encore construit. switchBuilderTab()/ toggleDashCreate() (gĂ©nĂ©riques, nĂ©cessaires Ă l'onglet "ĂvĂ©nements" restant) extraites dans un nouveau builder-tabs.js partagĂ© par les deux Ă©diteurs, pour ne plus avoir Ă charger flow-editor.js/tabs-and-blocks.js/ animation-timeline.js (propres aux blocs/timeline) dans l'Ă©diteur 2D. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
a231bcf2fa |
Aperçu de la boßte de collision dans l'éditeur + blocage physique au jeu
Deux retours utilisateur sur la boĂźte de collision (ajoutĂ©e prĂ©cĂ©demment) : - Visible et rĂ©glable directement dans l'Ă©diteur de scĂšne : pour l'objet sĂ©lectionnĂ©, un contour pointillĂ© (rectangle/cercle) se superpose sur le canevas â glisser son corps ajuste le dĂ©calage, glisser sa poignĂ©e (coin bas-droit) ajuste la taille, les deux se rĂ©percutent dans le panneau "đ§± Collision" et inversement (Ă©dition des champs -> aperçu Ă jour). static/js/scenes/scene-editor.js::onCollisionBoxMouseDown/ onCollisionBoxResizeMouseDown, mirror des poignĂ©es de position/taille dĂ©jĂ existantes pour l'objet lui-mĂȘme. - Bloque dĂ©sormais RĂELLEMENT le dĂ©placement au clavier : avant chaque pas, personnage-controller.js teste si la position candidate chevaucherait la boĂźte de collision d'un autre objet solide (jamais un "fond", jamais un objet Ă collision dĂ©sactivĂ©e) et annule ce pas â par AXE sĂ©parĂ©ment, pour permettre de glisser le long d'un mur en diagonale plutĂŽt qu'un blocage total au moindre contact. RĂ©utilise forgeShapesOverlap (conditions.js, factorisĂ© depuis elementsOverlap pour ne jamais dupliquer la rĂšgle "qu'est-ce qui se touche"). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
bc3feb5080 |
Panneau scÚne simplifié + boßte de collision + rÎle joueur/ennemi/pnj
Trois retours utilisateur sur l'Ă©diteur de scĂšne 2D : - Retire l'arborescence "Objets de cette scĂšne" (un objet reste sĂ©lectionnable en cliquant dessus sur le canevas) et le bouton "+ Ajouter un dĂ©cor" (le type "decor" reste supportĂ© cĂŽtĂ© serveur, juste plus accessible depuis ce menu). - Chaque objet de scĂšne a dĂ©sormais une boĂźte de collision AUTOMATIQUE (= sa propre boĂźte, comportement inchangĂ© pour une condition de collision dĂ©jĂ posĂ©e) rĂ©glable dans un nouveau panneau "đ§± Collision" : activĂ©e/dĂ©sactivĂ©e, forme (rectangle/cercle), taille et dĂ©calage â utile pour un sprite trĂšs paddĂ© (CraftPix) dont la silhouette rĂ©elle est bien plus petite que son canevas. elementsOverlap() (conditions.js) applique ces rĂ©glages en restant identique par dĂ©faut. - Nouveau champ "đ·ïž RĂŽle" (Joueur/Ennemi/PNJ) sur un personnage : SEUL un personnage "Joueur" est dĂ©sormais dĂ©placĂ©/animĂ© au clavier et suivi par la camĂ©ra (personnage-controller.js) â "Ennemi"/"PNJ" restent immobiles tant qu'aucune logique de flow ne les pilote (dĂ©placement ennemi automatique/apparition, dialogue de PNJ... hors scope de ce rĂ©glage, qui ne fait qu'identifier "quel objet est le joueur"). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
9199897784 |
Image de fond de scÚne + caméra qui suit le personnage, fps d'animation cohérent
Trois retours utilisateur : - fps d'idle/interagir/touches supplĂ©mentaires alignĂ©s sur celui de la marche (8 i/s partout, Ă©tait 4 pour idle â perçu comme "les autres animations sont lentes Ă cĂŽtĂ© de la marche"). - Nouveau menu "đïž Images de fond" dans "Objets de cette scĂšne" : pose un objet kind="fond" Ă la taille RĂELLE de l'image choisie (pack CraftPix intĂ©grĂ© en galerie, admin seulement â licence, voir core/sprite_gate.py), derriĂšre tout le reste, insensible au clic. - Si ce fond dĂ©passe la scĂšne, elle devient le "monde" : .playScreen. playScene est dĂ©sormais le viewport (overflow:hidden, taille fixe), .sceneWorld le monde Ă l'intĂ©rieur â la camĂ©ra centre le premier personnage trouvĂ©, bornĂ©e pour ne jamais montrer au-delĂ des bords (voir personnage-controller.js::forgeUpdateSceneCamera). Le personnage peut dĂ©sormais se dĂ©placer sur tout le monde, pas seulement le petit cadre visible (clampSceneObjectPosition, actions.js). Un jeu sans fond XXL garde un comportement strictement identique Ă avant (monde == scĂšne, transform vide). BibliothĂšque gĂ©nĂ©rĂ©e une fois par scripts/generate_background_manifest.py (assets/background/, non versionnĂ©, licence CraftPix) vers static/backgrounds/ (committĂ©), mĂȘme patron que generate_animal_sprite_manifest.py. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
5891c82c62 |
Commandes personnage : détection de touche au clavier + touches d'animation supplémentaires
Deux retours utilisateur sur le panneau "Commandes" (déplacement/
animation automatiques, voir précédent commit) :
- Les champs de touche (haut/bas/gauche/droite/interagir) capturent
maintenant la touche au clavier (clic puis appui â event.key, mĂȘme
valeur que heldKeys/triggers.js) au lieu d'ĂȘtre tapĂ©s Ă la main,
source d'erreurs ("Espace" vs " ", "flĂšche haut" vs "ArrowUp"...).
- Nouvelle section "Animations supplémentaires" : un nombre illimité de
touches, chacune liée à UNE pose au choix parmi celles réellement
disponibles pour ce personnage (sauter, attaquer, courir...) â pas
seulement les 4 touches de déplacement + interagir.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
74f7ca396f |
Déplacement/animation automatiques d'un personnage (scÚne 2D)
Simplification demandĂ©e pour l'Ă©diteur de jeu RPG : un personnage posĂ© sur une scĂšne 2D se dĂ©place et s'anime TOUT SEUL avec ZQSD (+ E pour interagir) dĂšs qu'il est posĂ© â plus besoin de poser le moindre nĆud de flow pour un mouvement de base. Le panneau "đčïž Commandes" du personnage permet de remapper les 4 touches de dĂ©placement et la touche d'interaction, et de bloquer un axe (horizontal/vertical seulement). EntiĂšrement client (static/js/play/personnage-controller.js), rĂ©utilise heldKeys (triggers.js) et applyObjectProperty/clampSceneObjectPosition/ runSpriteAnimation (actions.js) â aucune logique dupliquĂ©e, et ça fonctionne aussi bien en ligne que dans l'export Web/SCORM (aucune requĂȘte serveur). "walk"/"idle"/"interact" (poses dĂ©jĂ prĂ©sentes pour tout personnage Forge/CraftPix) sont utilisĂ©es telles quelles ; une pose absente dĂ©grade silencieusement (dĂ©placement sans animation) plutĂŽt que de planter. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
82d00b3800 |
Export Web/SCORM : inline les icĂŽnes en data: URI (CORS Firefox/file://)
Le chemin relatif ("icons/x.svg") était enfin correct, mais Firefox
bloque encore par CORS TOUT chemin de fichier pour mask-image sous
file:// â chaque ressource file:// y est une origine opaque distincte,
indĂ©pendamment du chemin (troisiĂšme rapport du mĂȘme utilisateur). Seule
une data: URI (aucune requĂȘte rĂ©seau sĂ©parĂ©e) contourne le problĂšme.
_build_icon_data_uris (build_scorm_package.py) encode chaque icĂŽne
RĂELLEMENT posĂ©e dans le jeu (Ă©lĂ©ment direct ou nichĂ© dans un Ă©lĂ©ment de
jeu réutilisable) en base64, ajouté au payload sous
gameData.icon_data_uris ; forgeRenderIcone (JS) le consulte pour toute
régénération aprÚs une action, avec repli sur l'ancien chemin relatif
si jamais une icÎne n'a pas été pré-encodée.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
b60fe63a54 |
Export Web/SCORM : corrige le chemin de l'icÎne doublé "static/static/"
Le premier correctif ("static/icons/x.svg") ne suffisait pas : cette URL
est posĂ©e en style inline (--icon-url) mais CONSOMMĂE par `mask-image:
var(--icon-url)` dans static/style.css (.icon-svg) â une URL relative
dans une propriété personnalisée CSS se résout par rapport à la feuille
de style oĂč le var() est UTILISĂ, pas oĂč elle est dĂ©finie (piĂšge CSS
connu). Résultat : "static/icons/x.svg" redevenait
"static/static/icons/x.svg" une fois résolu depuis static/style.css
(CORS bloquĂ© â deuxiĂšme rapport du mĂȘme utilisateur). Seul "icons/x.svg"
(sans le préfixe "static/") est correct une fois résolu depuis
static/style.css. Corrigé cÎté rendu initial
(_relativize_absolute_urls) et cÎté port JS (forgeRenderIcone, qui
régénÚre ce widget aprÚs une action).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
e08c53e042 |
Export Web/SCORM : corrige la résolution des champs "relation" hors ligne
Bug signalé : une Donnée liée/un filtre référençant un champ "relation"
affichait "{{champ}}" tel quel une fois exporté, alors qu'il fonctionnait
en ligne. Deux causes cumulées :
- full_game_payload.py lisait la colonne SQL "<champ>" au lieu de
"<champ>_id" (seule vraie colonne d'un champ relation, voir
_field_column dans filter_repeater_rows.py) en construisant
gameData.data â une valeur toujours None. Invisible en ligne (les
filtres y requĂȘtent la base fraĂźche, jamais cette snapshot), mais
fatal hors ligne (aucune base Ă requĂȘter).
- Une fois ce None corrigé, le port JS (forgeFieldColumn) relisait
cette mĂȘme valeur sous une clĂ© slugifiĂ©e+suffixĂ©e ("boss_id") alors
que gameData.data est déjà indexé par le nom D'AFFICHAGE du champ
("boss") â mismatch qui ne se voyait que sur un champ relation (le
seul cas oĂč slugify(nom)+suffixe diverge du nom original). Supprime
ce port erroné, lit directement row[fieldName] partout.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
7bc4c89eaa |
Export Web/SCORM : corrige les URLs absolues cassées en local (file://)
Bug signalĂ© : icĂŽne disparue et texte suivant du dialogue absent aprĂšs export. render_icone.py (et son miroir JS, forgeRenderIcone) bakaient une URL "/static/icons/..." en dur, correcte en ligne (racine du serveur Flask) mais bloquĂ©e par CORS une fois ouverte en local (file:///static/...). MĂȘme souci pour les fichiers envoyĂ©s par le crĂ©ateur (/game/<slug>/uploads/..., jamais dans static/, jamais copiĂ©s par le paquet). Ajoute _relativize_absolute_urls (réécriture globale post-rendu du HTML) + copie du dossier uploads/, et relativise le port JS de l'icĂŽne pour toute rĂ©gĂ©nĂ©ration aprĂšs action en mĂ©moire. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
f3b72d73c9 |
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> |
||
|
|
9776fbc057 |
Score/Progression (Phase 1.1 de la feuille de route produit)
Concept de premier ordre, distinct du systĂšme de variables globales â prĂ©alable identifiĂ© aux futurs exports SCORM/xAPI (note de cadrage .claude/Forge_Engine_Cadrage.pdf) : ces standards ont besoin d'un signal "score"/"terminĂ©" propre, pas d'une convention sur une variable choisie par le crĂ©ateur. - db/scoring/ (mĂȘme patron que db/global_vars/) : table _scoring, un score numĂ©rique + un statut (non_commence/en_cours/termine/reussi/ echoue) par joueur (voir db.PLAYER_SHARED pour l'aperçu crĂ©ateur). - Deux nouvelles actions de flux, dans les deux Ă©diteurs (document et scĂšne) : "Modifier le score" (rĂ©utilise le vocabulaire d'opĂ©rations dĂ©jĂ lĂ pour "Modifier une variable" â incrĂ©menter/dĂ©finir/etc., aucune nouvelle colonne de nĆud) et "DĂ©finir le statut de la partie". - Routes crĂ©ateur (routes/flow/flow_node_run_score.py, flow_node_run_status.py) + miroirs publics par joueur (routes/public_play/) â mĂȘme principe que flow_node_run_variable.py. - GET /game/<slug>/scoring/<player_id> : lecture interne, pas exposĂ©e au joueur, prĂ©parĂ©e pour ĂȘtre consommĂ©e par le futur export Web/SCORM. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
76a50fa87a |
Onboarding guidé systématique + tableau de bord simplifié
Comportement SYSTĂMATIQUE Ă chaque crĂ©ation de jeu (admin compris), pas une formalitĂ© rĂ©servĂ©e Ă l'inscription : /onboarding (routes/onboarding/) devient le point d'entrĂ©e unique â 4 cartes retournables (survol = explication au dos), dĂ©filement horizontal animĂ© vers le nom du jeu. Un admin y repasse Ă volontĂ© (pas de project_slug dĂ©diĂ©, jamais bloquĂ©/ redirigĂ© vers un projet prĂ©cĂ©dent) ; un compte "user" n'en a plus qu'un créé d'office (routes/auth/register_2fa.py), guidĂ© ici Ă la place. - db/games/game_type_catalog.py : catalogue des 4 types (Quiz/ Embranchement-escape game/RPG/CrĂ©er mon jeu de A Ă Z), _meta['onboarding_type'] dĂ©cide du "kind" du premier Ă©cran créé et si le tableau de bord complet reste accessible. - routes/games/game_dashboard.py, templates/game_dashboard_simple.html : un type restreint (quiz/embranchement/rpg) voit dĂ©sormais SON tableau de bord (mĂȘme route que "custom"), rendu en version simplifiĂ©e â juste ses Ă©crans en cartes avec un aperçu RĂEL du contenu (scĂšne mise Ă l'Ă©chelle par container query CSS, adaptĂ©e Ă la largeur rĂ©elle de la carte). "+ Ajouter un Ă©cran" n'y propose pas de choix de type : imposĂ© par le projet (routes/screens/screens_new.py), verrouillĂ© aussi cĂŽtĂ© serveur. - core/auth_guard.py : plus de blocage de game_dashboard par type â la restriction se fait au rendu, pas Ă l'accĂšs Ă la route. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
d5a59bdbd8 |
Fusion des deux moteurs : type d'écran par écran, plus par projet
"document" (Ă©crans %) et "jeu_2d" (scĂšne pixels) devient une propriĂ©tĂ© PAR ĂCRAN (_screens.kind, migration automatique idempotente dans ensure_schema.py, source = l'ancien game_type au niveau projet) plutĂŽt qu'un choix figĂ© pour tout le jeu â un mĂȘme projet peut dĂ©sormais mĂ©langer Ă©crans classiques et scĂšnes 2D librement. - routes/screens/screen_edit.py : dispatch vers l'Ă©diteur de scĂšne selon screen["kind"] (l'Ă©cran demandĂ©), plus game["game_type"]. - screens/payload/full_game_payload.py, templates/play.html, static/js/play/screens.js : le rendu jouable (payload, markup, bascule du mode plein-Ă©cran #playFrame) dĂ©cide Ă©cran par Ă©cran, y compris en cours de partie (changer d'Ă©cran ne recharge pas la page). - screens/screens_repo/create_screen.py : nouveau paramĂštre kind. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
727af55a5e |
Mouvement continu, limites de scÚne et priorité d'animation (jeu 2D)
Répond au manque signalé par l'utilisateur : le déclencheur "clavier"
existant (keydown) ne se déclenche qu'UNE FOIS par appui, insuffisant
pour "maintenir une touche fait avancer/sauter le personnage en continu".
- Deux nouveaux déclencheurs (screens/flow/constants.py,
screens/scenes/flow_palette.py) : "Tant qu'une touche est maintenue"
(se répÚte ~20 fois/seconde tant que la touche reste enfoncée,
runScreenHeldKeyTriggers() dans triggers.js â mĂȘme patron PAR ĂCRAN que
"minuteur", arrĂȘtĂ© au changement d'Ă©cran) et "Au relĂąchement d'une
touche" (un seul déclenchement, scan global comme "clavier"). Combinés
Ă l'action existante "Modifier un objet de scĂšne â DĂ©placer de... px
(relatif)", ça permet un vrai déplacement continu.
- preventDefault() sur toute touche que le jeu écoute réellement
(isGameKey(), triggers.js) : Espace/FlÚches font défiler la page par
défaut, et Espace réactive en plus le bouton actuellement focus (souvent
le bouton "Jouer" qui garde le focus aprĂšs l'ouverture de l'aperçu) â
ça pouvait donner l'impression qu'une touche du jeu ne faisait rien.
- Le personnage pouvait sortir du cadre de la scÚne en se déplaçant :
applyObjectProperty()/clampSceneObjectPosition() (static/js/play/actions.js)
bornent maintenant toute position (absolue ou relative) Ă
[0, scene_width/height â la taille de l'objet].
- Vitesse d'animation par défaut adaptée au nombre d'images : la valeur
fixe (8 i/s) venait d'un formulaire pensé pour les cycles Kenney (8
images) â un cycle CraftPix (walk=30 images) au mĂȘme 8 i/s prenait
~4 secondes, "trĂšs lent". Le choix d'une animation dans la galerie
calcule maintenant une vitesse par défaut proportionnelle à son nombre
d'images (flow-editor.js, animation-timeline.js).
- Priorité d'animation (bug : "je ne peux pas me déplacer et sauter") :
un dĂ©clencheur de dĂ©placement (touche maintenue) redemande "marche" Ă
chaque tick, écrasant aussitÎt une animation ponctuelle ("sauter")
démarrée entre-temps avant qu'elle ait pu s'afficher. runSpriteAnimation()
(actions.js) laisse maintenant une animation NON BOUCLĂE en cours
(mĂȘme Ă une seule frame, ex. une pose Kenney figĂ©e) aller jusqu'au bout
avant qu'une autre demande puisse l'interrompre.
Nouveaux tests : static/js/play/__tests__/{actions,triggers}.test.js
(idempotence + priorité d'animation, bornage aux limites de la scÚne,
isGameKey) ; tests/test_scene_edit_view.py (persistance d'un nĆud
"touche_maintenue").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
f0070faced |
Phase A (2/2) : Ă©diteur de scĂšne 2D â route, template, runtime jouable
DeuxiĂšme et dernier commit de la Phase A du plan "Fondations d'une
plateforme multi-éditeurs" (le premier,
|
||
|
|
70c2b8df05 |
Corrige : une animation déjà posée dans la logique ignorait le changement de personnage
Bug réel identifié grùce à la vidéo fournie + inspection directe de la
base de jeu de test : quand un nĆud de flow "Jouer une animation"
(ou un clip de Timeline "sprite") était configuré pour un personnage,
ses frames Ă©taient rĂ©solues et FIGĂES dans data_value/custom_keyframes
au moment de la configuration â changer ensuite le personnage Forge de
l'élément (galerie des propriétés) n'avait donc aucun effet sur les
animations déjà posées, qui continuaient à jouer indéfiniment les
frames de l'ANCIEN personnage.
Le nĆud/clip ne stocke dĂ©sormais que le NOM de l'animation
({"animation": "walk", "fps": 8, "loop": true}) â ses frames sont
rĂ©solues Ă l'EXĂCUTION, Ă partir du personnage ACTUELLEMENT assignĂ© Ă
l'élément cible :
- screens/payload/full_game_payload.py expose un nouveau
gameData.personnage_animations (Ă©lĂ©ment â animations), reconstruit Ă
chaque chargement de la page de jeu depuis _personnage_data â donc
toujours Ă jour, y compris aprĂšs un changement de personnage.
- static/js/play/actions.js (resolveSpriteFrames) et
static/js/play/screens.js (applyAnimationClip) résolvent le nom
d'animation en frames à ce moment précis, plutÎt que d'utiliser des
frames figĂ©es â repli sur l'ancien format {frames,...} pour les
nĆuds/clips dĂ©jĂ créés avant ce correctif.
- Ăditeur (flow-editor.js/animation-timeline.js) : simplifiĂ© en
consĂ©quence â plus besoin de deviner rĂ©troactivement quelle animation
correspond à une liste de frames stockées (l'ancien hack de
comparaison), le nom est maintenant stocké directement.
Nouveau test de régression (test_swapping_forge_character_updates_
already_configured_flow_action) qui reproduit exactement le scénario
filmé : configure l'action pour "male-adventurer", change le personnage
en "zombie", vérifie que gameData.personnage_animations reflÚte bien
zombie sans avoir Ă retoucher le nĆud de flow.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
5ead091c75 |
Personnages : toutes les animations, tous les personnages ; retrait de l'import de sprites personnalisés
Ă la demande de l'utilisateur (validation de la refonte visuelle du widget Personnage), deux ajustements : - BibliothĂšque de sprites Forge Ă©tendue aux 6 personnages du pack Kenney "Toon Characters" (aventurier/aventuriĂšre, personnage homme/femme, robot, zombie), avec TOUTES leurs poses (45 par personnage) plutĂŽt que 3 â regroupĂ©es en 31 animations nommĂ©es par personnage (poses numĂ©rotĂ©es type walk0..walk7 fusionnĂ©es en un seul cycle "walk", les autres restant des poses figĂ©es Ă une image). ~1,2 Mo au total, toujours un sous-ensemble curĂ© du pack source (assets/characters/, non versionnĂ©) â HD/Parts/Tilesheet/Vector toujours exclus. - Import de sprites personnalisĂ©s retirĂ© pour le moment : plus de tuile "â Sprite personnalisĂ©" dans la galerie d'ajout, plus de section d'import dans les propriĂ©tĂ©s d'un personnage â seuls les personnages Forge restent proposĂ©s. La rĂ©solution serveur de _personnage_data garde son support gĂ©nĂ©rique de la source "custom" (aucune migration requise si cette possibilitĂ© revient plus tard), mais plus aucune UI ne permet de la crĂ©er. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
9e076f8cf9 |
Refonte : vrai widget "Personnage" visuel (remplace l'UI de la Phase 7)
L'utilisateur a testĂ© la Phase 7 (sprites pilotĂ©s via un menu dĂ©roulant + zone de texte dans la logique de flow) et l'a rejetĂ©e Ă raison : ce n'est pas comme ça qu'un moteur de jeu (Phaser, Unity) gĂšre un personnage. Cette refonte remplace tout le flux d'AUTEURING par un vrai widget visuel â le moteur d'exĂ©cution de la Phase 7 (runSpriteAnimation, activeSpriteAnimations, orientation, kind="sprite" de la Timeline) reste inchangĂ©. Nouveau widget "personnage" (screens/widgets/registry.py) : - Rendu serveur dĂ©diĂ© (special_render, screens/rendering/render_personnage.py) qui affiche la pose "idle" dĂšs le HTML gĂ©nĂ©rĂ© â jamais une image cassĂ©e Ă configurer aprĂšs coup. - Toutes ses donnĂ©es (source Forge ou sprites propres au crĂ©ateur, quel personnage/quelles animations) vivent dans une seule clĂ© JSON _personnage_data (screens/rendering/personnage_data.py), sans aucune migration de schĂ©ma (mĂȘme patron que c_clause_list). - Nouvelle Ă©chappatoire "custom_panel", symĂ©trique Ă "special_render" mais pour le panneau de propriĂ©tĂ©s : ce widget affiche une galerie/un import de sprites sur-mesure plutĂŽt que les contrĂŽles gĂ©nĂ©riques. Galerie visuelle de personnages Forge (VRAIES miniatures, pas un emoji) : - Panneau gauche "đ Personnages" : pose un personnage dĂ©jĂ configurĂ©, animĂ© immĂ©diatement. - Panneau droit (propriĂ©tĂ©s) : change le personnage Forge de l'Ă©lĂ©ment sĂ©lectionnĂ©, ou importe les animations d'un sprite personnalisĂ©. - static/js/screen_edit/personnage-preview.js : lance l'aperçu animĂ© de CHAQUE personnage du canevas dĂšs le chargement de la page et aprĂšs toute sauvegarde â le cĆur de la demande ("je dois voir ça bouger"). SĂ©lecteur d'animation Ă miniatures (nĆud de flow "Jouer une animation" et clip de Timeline "sprite") : remplace le menu dĂ©roulant/la zone de texte par une grille de vraies miniatures, scopĂ©e aux SEULES animations du personnage rĂ©ellement ciblĂ© (ELEMENT_ANIMATIONS_MAP, rĂ©solu cĂŽtĂ© serveur Ă partir des propriĂ©tĂ©s de cet Ă©lĂ©ment prĂ©cis) â jamais un catalogue global. Le format stockĂ© sur le nĆud/le clip ({frames, fps, loop}) est inchangĂ©, seule l'UI d'Ă©dition change. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
d2c2f20b65 |
Phase 7 : personnage animé par sprites (poses/images successives)
Ajoute une nouvelle action de flow "jouer_animation_sprite" qui joue une
séquence de PNG sur un élément image (cycle en boucle type marche, ou
une fois type saut) â rĂ©utilise target_element_id (dĂ©jĂ whitelistĂ©) et
data_value en JSON {frames, fps, loop}, exactement le patron déjà établi
par "jouer_son" (Phase 6) et "alea" (Phase 2) : aucune nouvelle colonne,
aucune migration de schéma.
Ajoute une propriété d'élément "orientation" (Modifier un élément) pour
retourner un personnage en miroir (gauche/droite/bascule) sans nécessiter
une 2e feuille de sprites "vue de dos" â mĂȘme patron que "surbrillance"/
"désactivé".
Ătend aussi la Timeline d'animation existante (kind="sprite", aux cĂŽtĂ©s
de "animate_css"/"custom") pour qu'un personnage puisse animer tout seul
dÚs l'affichage de l'écran (ex. idle en boucle perpétuelle), pas
seulement en rĂ©action Ă un Ă©vĂ©nement â rĂ©utilise la colonne gĂ©nĂ©rique
custom_keyframes (JSON) et le réglage iteration_count déjà là pour la
boucle, aucune migration non plus. Les deux entrées (action de flow et
clip de timeline) partagent le mĂȘme moteur cĂŽtĂ© client
(runSpriteAnimation()/activeSpriteAnimations dans static/js/play/
actions.js, indexĂ© par nĆud DOM plutĂŽt que par id d'Ă©lĂ©ment pour
supporter plusieurs instances d'un mĂȘme Ă©cran-modĂšle animĂ©es
indépendamment).
Une bibliothĂšque de sprites Forge (2 personnages Kenney CC0, sous-
ensemble curé idle/marche/saut copié dans static/characters/) est
proposée dans l'éditeur, mais un créateur peut tout aussi bien utiliser
ses propres sprites uploadĂ©s (mĂȘme route d'upload gĂ©nĂ©rique que le son
de la Phase 6).
PérimÚtre volontairement limité à ce qui a été demandé : pas d'avatar
modulable en couches (assemblage cheveux/haut/bas par le joueur),
écarté du plan initial à la demande de l'utilisateur.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
||
|
|
76331f9285 |
Phase 6 : son (musique de fond par écran + action "Jouer un son")
Ajoute deux mĂ©canismes distincts, Ă la demande de l'utilisateur qui a prĂ©cisĂ© qu'un son doit pouvoir ĂȘtre attachĂ© Ă une SCĂNE (pas seulement jouĂ© ponctuellement par une action de flow, comme prĂ©vu initialement) : - Musique de fond par Ă©cran (nouveau champ background_music_url sur _screens, ALTER TABLE nullable) : rĂ©glĂ©e dans le panneau gauche de l'Ă©diteur (URL ou envoi de fichier, rĂ©utilise la route d'upload gĂ©nĂ©rique existante), enregistrĂ©e en AJAX au mĂȘme patron que le format d'aperçu (screen_set_aspect.py). DĂ©marrĂ©e en boucle Ă l'affichage de l'Ă©cran et arrĂȘtĂ©e au changement d'Ă©cran (runScreenBackgroundMusic(), appelĂ©e depuis showScreen() dans static/js/play/screens.js) â un seul Audio actif Ă la fois, jamais cumulĂ© avec une musique restĂ©e d'un Ă©cran prĂ©cĂ©dent. - Action de flow "jouer_son" (aux cĂŽtĂ©s des actions existantes) : effet sonore ponctuel, non bouclĂ©, dĂ©clenchable sur n'importe quel nĆud DĂ©clencheur. RĂ©utilise data_value (dĂ©jĂ un champ texte gĂ©nĂ©rique sur le nĆud action, comme pour "attendre") plutĂŽt qu'une nouvelle colonne dĂ©diĂ©e â fire-and-forget cĂŽtĂ© client (runActionNode), ne bloque jamais la suite du graphe. Les deux rĂ©utilisent le mĂȘme mĂ©canisme d'upload de fichier dĂ©jĂ en place ailleurs dans l'Ă©diteur (ex. source d'une vidĂ©o), sans nouvelle route. Ceci complĂšte les 6 phases du plan d'extension du moteur (Ă©tat par joueur, hasard/maths, clavier/minuteur, ajout de ligne, position/ collision, son) : Forge Engine peut dĂ©sormais couvrir des jeux bien au-delĂ du narratif/puzzle/quiz (action, arcade, jeux Ă contrainte de temps). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
9e263435e6 |
Phase 5 : position/déplacement d'élément + condition de collision
Ajoute le positionnement absolu (pos_x/pos_y, rĂ©utilise left/top en % dĂ©jĂ en place) et relatif (pos_x_relatif/pos_y_relatif, ajoute un delta Ă la position actuelle plutĂŽt que de l'Ă©craser) comme nouvelles propriĂ©tĂ©s de l'action "Modifier un Ă©lĂ©ment". Ajoute une nouvelle source de condition "collision" (aux cĂŽtĂ©s de "objet"/"variable") : deux Ă©lĂ©ments (cond_element_a/cond_element_b, ALTER TABLE sans contrainte FK, mĂȘme patron que block_id/ trigger_custom_event_id) dont on compare les rectangles Ă l'Ă©cran via getBoundingClientRect() cĂŽtĂ© client (elementsOverlap(), dans conditions.js). Pas d'opĂ©rateur/valeur Ă choisir : le chevauchement EST directement le boolĂ©en vrai/faux du nĆud â le crĂ©ateur relie le port "Faux" pour "ne se touchent pas", exactement comme pour n'importe quelle autre condition (design plus simple que rĂ©interprĂ©ter Ă©gal/diffĂ©rent, qui n'a pas de sens pour superieur/inferieur). delete_element.py et flow_nodes_referencing_element.py nettoient dĂ©sormais aussi les nĆuds de collision rĂ©fĂ©rençant un Ă©lĂ©ment supprimĂ© (ou l'un de ses descendants), pour rester cohĂ©rents avec le nettoyage dĂ©jĂ en place pour trigger_element_id/target_element_id. CombinĂ© Ă la Phase 3 (minuteur rĂ©current) et au dĂ©placement au clavier, ça couvre des jeux type casse-briques/Pong/ramasse-objets sans construire un vrai moteur physique (pas de vĂ©locitĂ©/accĂ©lĂ©ration/ gravitĂ© continues, cadrage volontairement limitĂ©). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
92fbfbc9dd |
Phase 4 : ajout dynamique d'une ligne à l'exécution
Nouveau sentinel LAST_INSERTED_ROW_ID = -3 (screens/flow/constants.py), aux cĂŽtĂ©s de CLICKED_ROW_ID = -1 â mĂȘme principe : un id de ligne n'existe qu'APRĂS l'insertion, jamais connu Ă la crĂ©ation du nĆud. Permet d'enchaĂźner un nĆud "Ajouter une ligne" (crĂ©e une ligne VIDE) puis un ou plusieurs nĆuds "Modifier une donnĂ©e" dĂ©jĂ existants (ciblant "â DerniĂšre ligne ajoutĂ©e" dans le sĂ©lecteur "Ligne concernĂ©e", partagĂ© avec les conditions) pour renseigner ses champs â rĂ©utilise 100% du mĂ©canisme actuel, aucun nouveau format de payload multi-champs. screens/data_actions/apply_add_row_action.py : db.get_definition + db.insert_row(slug, definition, {}, player_id) â la ligne appartient au joueur qui agit pour un objet per_player (Phase 1). Nouvelles routes (crĂ©ateur ET publique, comme prĂ©vu dĂšs la Phase 1) : POST /game/<slug>/flow/nodes/<id>/run-add-row et son miroir /jouer/<slug>/.../run-add-row â renvoient {"ok", "row_id"}. routes/flow/flow_node_run_data.py (+ son miroir public) : rĂ©sout aussi LAST_INSERTED_ROW_ID (en plus de CLICKED_ROW_ID dĂ©jĂ en place) via last_inserted_row_id transmis par le client. static/js/play/actions.js : runActionNode branche "ajouter_ligne" -> fetch la nouvelle route, pose window.lastInsertedRowId, puis refreshRuntimeData() (un RĂ©pĂ©teur liĂ© affiche la nouvelle ligne au prochain rendu, confirmĂ© par l'audit prĂ©alable â aucun ajustement du mĂ©canisme de rafraĂźchissement nĂ©cessaire). La branche "modifier_donnee" transmet dĂ©sormais aussi last_inserted_row_id, comme clicked_row_id. templates/screen_edit.html + static/js/screen_edit/flow-editor.js : nouveau type d'action "Ajouter une ligne Ă un objet" (juste un sĂ©lecteur d'objet, aucun champ Ă remplir â le rappel du fonctionnement enchaĂźnĂ© est affichĂ© directement dans le formulaire) ; le sĂ©lecteur "Ligne concernĂ©e" (partagĂ© Condition/Modifier une donnĂ©e) gagne l'option "â DerniĂšre ligne ajoutĂ©e" Ă cĂŽtĂ© de "đ±ïž Ligne cliquĂ©e". VĂ©rifiĂ© : 259 tests passent (4 nouveaux, dont un bout-en-bout via HTTP qui enchaĂźne rĂ©ellement les deux nĆuds et vĂ©rifie le champ renseignĂ©, et un qui verrouille l'isolation par joueur de la ligne créée), 13 tests node:test toujours au vert, syntaxe JS validĂ©e. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
727c3c97ba |
Phase 3 : déclencheur clavier + minuteur récurrent
screens/flow/constants.py : TRIGGER_EVENTS += "clavier" (Ă l'appui sur une touche) et "minuteur" (Toutes les X millisecondes) â ni Ă©lĂ©ment ni Ă©cran prĂ©cis pour les deux, comme "evenement" dĂ©jĂ en place. FLOW_NODE_FIELDS += trigger_key/trigger_interval_ms. ensure_flow_schema.py : ALTER TABLE pour les 2 colonnes (patron trigger_custom_event_id). templates/screen_edit.html + static/js/screen_edit/flow-editor.js : - "clavier" : un champ "Touche Ă surveiller" qui capture lui-mĂȘme la touche pressĂ©e (onkeydown sur l'input, captureFlowTriggerKey()) plutĂŽt que de faire deviner la syntaxe attendue (ev.key du navigateur, ex. "ArrowUp", "a", " " pour Espace). - "minuteur" : un simple champ numĂ©rique (millisecondes). - nodeLabel() affiche "âšïž Touche « X »"/"â±ïž Toutes les N ms" sur le nĆud. static/js/play/triggers.js (moteur de jeu) : - bindKeyboardTriggers() : UN SEUL window.addEventListener('keydown', ...) posĂ© une fois pour tout le jeu (voir l'amorçage en fin de templates/play.html) â mĂȘme patron de scan global que dispatchGameEvent() pour "Sur un Ă©vĂ©nement personnalisĂ©". - runScreenTimerTriggers(screenId) : gĂ©rĂ© PAR ĂCRAN (appelĂ© depuis showScreen(), static/js/play/screens.js) â dĂ©marre les setInterval des nĆuds "minuteur" de l'Ă©cran affichĂ©, arrĂȘte d'abord tous ceux de l'affichage prĂ©cĂ©dent (mĂȘme principe que runAnimationTimeline) pour ne jamais accumuler des minuteurs sur des Ă©crans quittĂ©s. VĂ©rifiĂ© : 255 tests passent (5 nouveaux, dont un qui verrouille que la touche Espace â trĂšs probablement utilisĂ©e en jeu â n'est pas filtrĂ©e comme une valeur "vide" par add_flow_node.py), 13 tests node:test toujours au vert, syntaxe JS validĂ©e sur tous les fichiers de static/js/play/ et static/js/screen_edit/. Comme le reste du graphe de logique cĂŽtĂ© client, le comportement RĂEL d'un keydown/setInterval n'est pas testable sans navigateur â test manuel recommandĂ© (touche assignĂ©e Ă un saut, minuteur faisant avancer un compteur). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
7476ed229e |
Phase 2 : hasard + opérations mathématiques
screens/labels/data_operations.py : 6 nouvelles opĂ©rations pour "Modifier une donnĂ©e"/"Modifier une variable" â multiplier, diviser, modulo (garde-fou division par zĂ©ro : valeur inchangĂ©e plutĂŽt qu'une ZeroDivisionError qui interromprait le graphe), minimum/maximum (borne la valeur ACTUELLE â utile pour une variable globale, qui n'a pas de min_value/max_value comme un champ d'objet), et alea (tire un nombre alĂ©atoire entre deux bornes). screens/data_actions/compute_operation.py (dĂ©jĂ factorisĂ© en Phase 0, donc une seule implĂ©mentation pour apply_data_action.py/ apply_variable_action.py) : implĂ©mente les 6. "alea" est la seule Ă deux opĂ©randes â rĂ©utilise data_value au format "min,max" plutĂŽt qu'une nouvelle colonne de nĆud (bornes remises dans l'ordre si inversĂ©es). random.uniform pour un rĂ©sultat dĂ©cimal, random.randint pour un entier. static/js/screen_edit/flow-editor.js + templates/screen_edit.html : petit indice visuel â le champ "Valeur / montant" du formulaire de nĆud affiche "min,max (ex. 1,6)" quand "alea" est choisi, pour ne pas laisser deviner ce format Ă deux nombres, diffĂ©rent de toutes les autres opĂ©rations. Sinon aucun nouveau champ/changement de schĂ©ma nĂ©cessaire, le <select> Ă©tait dĂ©jĂ gĂ©nĂ©rĂ© depuis DATA_OPERATIONS. VĂ©rifiĂ© : 250 tests passent (19 dans test_compute_operation.py, dont un qui a dĂ» ĂȘtre corrigĂ© â il utilisait "multiplier" comme exemple d'opĂ©ration INCONNUE, devenu un mauvais exemple maintenant qu'elle existe), 13 tests node:test toujours au vert, syntaxe JS validĂ©e. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
fe807ba51e |
Phase -1 (suite) : découpe l'éditeur de screen_edit.html en modules JS
MĂȘme chantier que le commit prĂ©cĂ©dent (moteur de jeu, play.html) â templates/screen_edit.html Ă©tait un unique fichier HTML+CSS+JS de 3312 lignes, tout l'Ă©diteur (arborescence, panneaux flottants, canevas, formulaire de propriĂ©tĂ©s, Ă©diteur de flow Ă nĆuds, blocs de logique, timeline d'animation) vivant dans UN SEUL <script>. Ce fichier est plus imbriquĂ© que play.html : de nombreux appels s'exĂ©cutent au niveau racine du script (pas seulement des dĂ©clarations de fonctions), et JavaScript hoiste les dĂ©clarations `function` sur TOUT le script â un appel au niveau racine peut donc rĂ©fĂ©rencer une fonction dĂ©clarĂ©e PLUS LOIN dans le mĂȘme fichier. DĂ©couper naĂŻvement casserait cet ordre implicite. Un audit dĂ©diĂ© (analyse ligne par ligne de chaque appel racine + son graphe d'appel transitif) a identifiĂ© 3 rĂ©fĂ©rences "en avance" rĂ©elles, toutes regroupĂ©es dans la mĂȘme zone (initBuilderPanel()/toggleActionFields() â bindAspectButtons/ toggleElementPropertyValue/onDataDefinitionChange) â le dĂ©coupage respecte cette contrainte : chaque fichier est une TRANCHE SĂQUENTIELLE de l'original (jamais une rĂ©organisation), et cette zone spĂ©cifique reste un seul fichier (panel-init.js) pour que le hoisting continue de fonctionner exactement comme avant. 5 fichiers sous static/js/screen_edit/ : - tree-panels.js â arborescence, menu contextuel, panneaux flottants gauche/droite, galerie d'icĂŽnes, modale de suppression/choix d'icĂŽne, glisser-dĂ©poser du canevas, panneau de propriĂ©tĂ©s (autosave). - panel-init.js â (rĂ©)initialisation du panneau central aprĂšs chaque changement de sĂ©lection, filtres de rĂ©pĂ©teur/donnĂ©e liĂ©e, condition de visibilitĂ©, champs d'action du formulaire de nĆud. - flow-editor.js â Ă©diteur de flow Ă nĆuds (rendu du graphe, formulaire d'ajout de nĆud, blocs de logique â currentBlockNodes/Edges). - tabs-and-blocks.js â onglets du centre, panneaux flottants gĂ©nĂ©riques (drag/resize/plein Ă©cran), modale d'un bloc de logique. - animation-timeline.js â timeline d'animation (clips Animate.css/ personnalisĂ©s). Toutes les donnĂ©es injectĂ©es par Jinja (GAME_SLUG, SCREEN_ID, DEFINITIONS_DATA, FLOW_NODES_INITIAL, ELEMENTS_LABELS, CUSTOM_EVENTS_MAP, ANIM_CLIPS...) sont posĂ©es UNE FOIS par un petit <script> inline restĂ© dans le template, avant les <script src> â mĂȘme patron que static/js/play/. Le seul bout de logique restĂ© inline est la toute petite IIFE d'ouverture initiale (?tab=/?block=), qui dĂ©pend directement de request.args et doit s'exĂ©cuter aprĂšs que tous les fichiers soient chargĂ©s. tests/conftest.py : screen_edit_js_bundle() (mĂȘme principe que play_js_bundle(), Phase -1 prĂ©cĂ©dente) â 4 tests qui vĂ©rifiaient la prĂ©sence de telle fonction/chaĂźne dans le HTML de l'Ă©diteur (le JS y Ă©tait inline) sont mis Ă jour pour chercher dans ce bundle. Un des deux Ă©checs rĂ©vĂ©lait un test dĂ©jĂ fragile (assert "Ligne cliquĂ©e" in html vĂ©rifiait en rĂ©alitĂ© le TEXTE SOURCE d'un <script> inline, jamais du HTML rĂ©ellement rendu â ce texte ne peut plus s'y trouver une fois la fonction qui le construit dynamiquement dĂ©placĂ©e dans un fichier externe) : corrigĂ© pour vĂ©rifier le bundle JS + la disponibilitĂ© de la route sĂ©parĂ©ment. VĂ©rifiĂ© : 215 tests passent, syntaxe JS validĂ©e sur les 5 nouveaux fichiers (node --check) et sur les <script> inline restants (rendus via le client de test). Test manuel recommandĂ© (Ă©dition complĂšte d'une scĂšne : arborescence, propriĂ©tĂ©s, glisser-dĂ©poser, logique de flow, blocs, timeline) avant de considĂ©rer ce dĂ©coupage dĂ©finitivement sans risque â comme pour play.html, ce fichier n'a pas de harnais de test DOM automatisĂ©. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> |
||
|
|
dbada333d5 |
Phase -1 : découpe le moteur de play.html en modules JS + premiers tests JS
templates/play.html était un unique fichier HTML+CSS+JS de 1069 lignes,
tout le moteur de jeu vivant dans UN SEUL <script>, sans aucune
couverture de test sur cette logique (seuls le rendu HTML et la syntaxe
JS étaient vérifiés). La feuille de route à venir (état par joueur,
hasard, clavier/minuteur, position/collision, son â voir le plan) va
justement faire grossir ce moteur : "un fichier = une fonction, un
dossier = une responsabilité" s'applique aussi au JS, pas seulement au
Python â le moment de dĂ©couper est avant d'ajouter encore plus de code,
pas aprĂšs.
Découpage en 6 fichiers sous static/js/play/, calqués sur les sections
déjà présentes dans le code (aucune réorganisation de logique, une pure
extraction) : screens.js (affichage d'écran, timeline d'animation),
conditions.js (Ă©valuation des conditions â la partie 100% PURE, sans
DOM, la plus testable), actions.js (exécution des actions), triggers.js
(recherche des nĆuds dĂ©clencheurs, attache des Ă©couteurs), bindings.js
(résolution des {{champ}}, rafraßchissement des données), flow-engine.js
(parcours du graphe, événements personnalisés).
Zéro nouvel outillage : plusieurs <script src> dans l'ordre, partageant
le mĂȘme espace global qu'avant (aucun bundler, aucune Ă©tape de build).
Les 2 URLs de route dont ces fichiers ont besoin (flow_node_run_data/
run_variable, runtime_payload) ne peuvent plus ĂȘtre injectĂ©es par Jinja
directement dans le code (un fichier statique n'est jamais passé par le
moteur de templates) â elles sont maintenant posĂ©es une fois dans
window.FORGE_PLAY_URLS par le petit <script> inline restant dans
play.html, qui ne porte plus que les données Jinja (gameData) et
l'amorçage (bindClicks() etc. au chargement).
publish/build_package.py : ajoute static/js/play Ă la liste des fichiers
copiés dans l'exécutable exporté (le mode jouable en dépend désormais).
Premiers tests JS (static/js/play/__tests__/conditions.test.js, lancés
via `node --test`, zĂ©ro nouvelle dĂ©pendance npm â decision prise avec
l'utilisateur de commencer par la logique PURE seulement, pas par une
couverture DOM via jsdom) : compareValues, resolveVariablePath,
evaluateConditionClause/Node, exactement la logique que les phases Ă
venir (opérations mathématiques, condition de collision) vont étendre.
tests/conftest.py : nouveau helper play_js_bundle() (concatĂšne tout
static/js/play/*.js) â 13 tests existants qui vĂ©rifiaient la prĂ©sence de
telle fonction/chaĂźne dans le HTML de /game/<slug>/play (tout le JS y
était inline avant ce découpage) sont mis à jour pour chercher dans ce
bundle à la place ; les tests qui vérifient un CSS/HTML réellement resté
dans play.html (forgeHighlight, forgeDisabled, #playFrame...) continuent
de chercher dans le HTML.
Vérifié : 215 tests pytest passent (aucune régression comportementale,
juste une réorganisation), 13 tests node:test passent, node --check sur
chacun des 6 nouveaux fichiers. Test manuel recommandé (jeu joué de bout
en bout : navigation, clic, survol, répéteur, condition, animation)
avant de considérer le découpage définitivement sans risque.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|