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>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
5ead091c75
commit
70c2b8df05
@@ -269,25 +269,18 @@ function openAnimClipForm(clip) {
|
||||
var cssText = Object.keys(k.props || {}).map(function(p) { return p + ': ' + k.props[p] + ';'; }).join(' ');
|
||||
addAnimKeyframeRow(k.percent, cssText);
|
||||
});
|
||||
// Phase 8 — clip.custom_keyframes stocke {frames, fps} en JSON pour un
|
||||
// clip kind="sprite" (pas une liste d'images-clés CSS, voir ci-dessus) ;
|
||||
// le NOM de l'animation, lui, n'est pas stocké (seules ses frames le
|
||||
// sont) — retrouvé ici par comparaison avec les animations actuelles du
|
||||
// personnage propriétaire, pour présélectionner la bonne miniature.
|
||||
// Phase 8 — clip.custom_keyframes stocke {animation, fps} en JSON pour un
|
||||
// clip kind="sprite" (pas une liste d'images-clés CSS, voir ci-dessus) :
|
||||
// le NOM de l'animation, pas ses frames — résolues à l'exécution depuis
|
||||
// le personnage ACTUELLEMENT assigné (voir resolveSpriteFrames() dans
|
||||
// static/js/play/actions.js), jamais figées ici.
|
||||
var spriteData = {};
|
||||
if (clip.kind === 'sprite' && clip.custom_keyframes) {
|
||||
try { spriteData = JSON.parse(clip.custom_keyframes); } catch (e) { /* ignore */ }
|
||||
}
|
||||
document.getElementById('anim-clip-sprite-fps').value = spriteData.fps || 8;
|
||||
toggleAnimClipKind();
|
||||
if (clip.kind === 'sprite') {
|
||||
var ownerAnimations = ELEMENT_ANIMATIONS_MAP[clip.element_id] || {};
|
||||
var matchedName = Object.keys(ownerAnimations).find(function(name) {
|
||||
var f = ownerAnimations[name];
|
||||
return spriteData.frames && f.length === spriteData.frames.length && f.every(function(u, i) { return u === spriteData.frames[i]; });
|
||||
}) || null;
|
||||
populateAnimClipSpriteGrid(matchedName);
|
||||
}
|
||||
if (clip.kind === 'sprite') populateAnimClipSpriteGrid(spriteData.animation);
|
||||
document.querySelectorAll('.animClip').forEach(function(b) {
|
||||
b.classList.toggle('selected', parseInt(b.dataset.clipId, 10) === clip.id);
|
||||
});
|
||||
@@ -313,16 +306,15 @@ function saveAnimClipForm() {
|
||||
};
|
||||
if (kind === 'custom') fields.custom_keyframes = collectAnimKeyframes();
|
||||
if (kind === 'sprite') {
|
||||
// Phase 8 — l'animation choisie (miniature cliquée, voir
|
||||
// populateAnimClipSpriteGrid()) est résolue en liste de frames ICI ;
|
||||
// {frames, fps} stocké dans custom_keyframes reste inchangé depuis la
|
||||
// Phase 7 (moteur d'exécution intact, seule l'UI a changé) — la boucle
|
||||
// reste portée par iteration_count (1/2/3/infinite, ci-dessus).
|
||||
var clipForFields = ANIM_CLIPS.find(function(c) { return c.id === animCurrentClipId; });
|
||||
var ownerAnimations = clipForFields ? (ELEMENT_ANIMATIONS_MAP[clipForFields.element_id] || {}) : {};
|
||||
var animName = document.getElementById('anim-clip-sprite-animation-name').value;
|
||||
// Phase 8 — stocke le NOM de l'animation choisie (miniature cliquée,
|
||||
// voir populateAnimClipSpriteGrid()), PAS ses frames — résolues à
|
||||
// l'exécution depuis le personnage ACTUELLEMENT assigné (voir
|
||||
// resolveSpriteFrames() dans static/js/play/actions.js), pour que
|
||||
// changer de personnage Forge affecte aussi les clips déjà posés dans
|
||||
// la Timeline. La boucle reste portée par iteration_count
|
||||
// (1/2/3/infinite, ci-dessus).
|
||||
fields.custom_keyframes = {
|
||||
frames: ownerAnimations[animName] || [],
|
||||
animation: document.getElementById('anim-clip-sprite-animation-name').value,
|
||||
fps: parseInt(document.getElementById('anim-clip-sprite-fps').value, 10) || 8,
|
||||
};
|
||||
}
|
||||
@@ -371,7 +363,11 @@ function previewAnimClip() {
|
||||
} else if (clip.kind === 'sprite') {
|
||||
var spriteData = {};
|
||||
try { spriteData = JSON.parse(clip.custom_keyframes || '{}'); } catch (e) { /* ignore */ }
|
||||
var frames = spriteData.frames || [];
|
||||
// Phase 8 — résout le NOM de l'animation depuis les animations
|
||||
// ACTUELLES du personnage (même logique que resolveSpriteFrames()
|
||||
// côté jeu, mais ELEMENT_ANIMATIONS_MAP n'existe que dans l'éditeur).
|
||||
var ownerAnimations = ELEMENT_ANIMATIONS_MAP[clip.element_id] || {};
|
||||
var frames = ownerAnimations[spriteData.animation] || [];
|
||||
var fps = spriteData.fps > 0 ? spriteData.fps : 8;
|
||||
if (animPreviewSpriteInterval) { clearInterval(animPreviewSpriteInterval); animPreviewSpriteInterval = null; }
|
||||
if (frames.length) {
|
||||
|
||||
Reference in New Issue
Block a user