Corrige : une animation déjà posée dans la logique ignorait le changement de personnage
Build and deploy / test-python (push) Successful in 1m39s
Build and deploy / test-js (push) Successful in 7s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped

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>
This commit is contained in:
william
2026-08-31 08:56:20 +02:00
co-authored by Claude Sonnet 5
parent 5ead091c75
commit 70c2b8df05
6 changed files with 112 additions and 39 deletions
+37
View File
@@ -101,6 +101,43 @@ def test_play_page_exposes_sprite_animation_runtime(client, game):
assert "activeSpriteAnimations" in js
assert "stopAllSpriteAnimations" in js
assert "forgeFlipped" in js
# Phase 8 — résolution à l'exécution depuis le personnage actuellement
# assigné (voir résolution du bug ci-dessous), pas des frames figées.
assert "resolveSpriteFrames" in js
assert "personnage_animations" in js
def test_swapping_forge_character_updates_already_configured_flow_action(client, game):
"""Régression : un nœud "Jouer une animation" configuré pour un
personnage devait rester bloqué sur les frames de l'ANCIEN personnage
même après avoir changé le personnage Forge de l'élément dans
l'éditeur (signalé par l'utilisateur). data_value ne stocke plus que
le NOM de l'animation — resolveSpriteFrames() la résout à l'exécution
depuis gameData.personnage_animations, reconstruit à chaque
chargement de full_game_payload() à partir du personnage ACTUEL."""
screen_id = _create_screen(client, game)
resp = client.post(
f"/game/{game}/screens/{screen_id}/elements/add",
data={"widget": "personnage", "forge_character": "male-adventurer"},
follow_redirects=False,
)
el_id = int(re.search(r"selected=(\d+)", resp.headers["Location"]).group(1))
_add_flow_node(
client, game, screen_id, node_type="action", action_type="jouer_animation_sprite",
target_element_id=el_id, data_value=json.dumps({"animation": "walk", "fps": 8, "loop": True}),
)
payload = screens.full_game_payload(game)
assert payload["personnage_animations"][str(el_id)]["walk"][0].startswith("/static/characters/male-adventurer/")
client.post(
f"/game/{game}/elements/{el_id}/personnage",
data=json.dumps({"source": "forge", "forge_character": "zombie"}),
content_type="application/json",
)
payload = screens.full_game_payload(game)
assert payload["personnage_animations"][str(el_id)]["walk"][0].startswith("/static/characters/zombie/")
def test_screen_edit_exposes_sprite_animation_editors(client, game):