Aller au contenu principal

Session 120 — Extraction de GLFT_AlienAnims_l

Problème

Observation utilisateur sur le niveau de test test_alien_bullets : plusieurs
aliens semblent visuellement identiques, alors que dans le jeu original ils
ont des variants distincts (Guard bleu, ‘Ard Guard rouge avec lampes,
Ashnarg vert, etc.). Hypothèse initiale « sprites partagés par gfxType »
incorrecte.

Analyse

Après lecture de defs.i, hires.s et newaliencontrol.s :

  • La struct ASM AlienT n’a effectivement qu’un champ GFXType (pas de
    WAD individuel par alien). Le mapping gfxType → WAD du LevelSceneBuilder
    est donc fidèle.

  • Mais chaque alien a sa propre table GLFT_AlienAnims_l[type] (2420
    octets dans le binaire) qui spécifie quelles frames du WAD partagé il
    utilise. C’est par ce biais que Guard et ‘Ard Guard, qui partagent le WAD
    guard, peuvent être visuellement différents : Guard utilise les frames
    0-18, ‘Ard Guard utilise une autre plage (19-36 ou similaire).

  • Structure d’une frame d’anim alien (11 octets, voir defs.i A_FrameLen) :

  • byte 0 : wadFrame → numéro de frame WAD à afficher (clé)
  • byte 1 : flipMirror (signed, négatif = mirror)
  • bytes 2-3 : scaleHint (taille/luminosité)
  • byte 4 : doActionFlag (trigger tir/hit)
  • byte 5 : finishedFlag (=1 à la dernière frame, loop)
  • byte 6 : moveSpeed
  • byte 7 : soundIndex (sample SFX)
  • bytes 8-10 : réservés

  • Total par alien : 11 options × 20 frames × 11 octets = 2420 octets
    (NUM_ALIEN_DEFS × A_AnimLen = 20 × 2420 = 48400 octets).

Bug bonus découvert

En calculant l’offset de GLFT_AlienAnims_l, j’ai trouvé que
OFS_GUN_GIVE dans LnkParser valait 0x7FF8 au lieu de 0x8000 :

OFS_AMMO_GIVE        = 0x7AD8
+ NUM_OBJECT_DEFS * AmmoGiveLen (30 * 44 = 1320)
= 0x8000               <-- valeur correcte

L’erreur de 8 octets faisait que getGunGive(defIdx) lisait dans les 8
derniers octets de l’entrée AmmoGive[29] au lieu du début de
GunGive[0]. Effet : les flags Shield/JetPack/Weapons des objets étaient
décalés. Cas peu visible en pratique car les valeurs résiduelles étaient
souvent 0, mais c’était latent.

Modifications

LnkParser.java :

  • Fix OFS_GUN_GIVE = 0x8000 (était 0x7FF8)
  • Ajout OFS_ALIEN_ANIMS = 0x82D0 (= 0x8000 + 30*24)
  • Ajout des constantes A_FRAME_LEN, A_OPT_LEN, A_ANIM_LEN,
    A_FRAMES_PER_OPTION, A_OPTIONS_PER_ALIEN
  • Ajout du record AlienAnimFrame avec les 10 champs documentés
  • Ajout des méthodes :
  • getAlienAnimFrame(alienIdx, optIdx, frameIdx)AlienAnimFrame
  • getAlienAnimOption(alienIdx, optIdx)AlienAnimFrame[20]
  • getAlienIdleFrame(alienIdx) → int (raccourci pour
    [opt=0][frame=0].wadFrame)
  • getAlienAnimByte(alienIdx, optIdx, frameIdx, byteOff) → int

LevelJsonExporter.java :

  • Ajout du champ idleFrame dans chaque entrée d’aliens[] de
    definitions.json
  • Ajout d’une nouvelle section alienAnims[] qui exporte la table
    complète (20 aliens × 11 options × 20 frames). Format compact :
    [wadFrame, flipMirror, finishedFlag, doActionFlag] par frame.
    Taille résultante : ~30-40 ko ajoutés à definitions.json.

LevelSceneBuilder.java :

  • Extension du record interne AlienDef avec le champ idleFrame
  • Lecture de idleFrame dans loadAlienDefs() (fallback 0 si absent
    pour compatibilité avec d’anciens definitions.json)
  • Utilisation de adef.idleFrame() dans addItems() pour passer la bonne
    frame à tryLoadSprite(). Avant : toujours 0. Maintenant : la frame
    vraie de l’alien dans son WAD partagé.

Pipeline pour tester

./gradlew convertLevels    # regénère definitions.json avec idleFrame
./gradlew buildScenes      # regénère les .j3o avec les bonnes frames
./gradlew run --args="--test-level ALIEN_BULLETS"

Résultat attendu

Dans le niveau test test_alien_bullets, les 16 aliens devraient maintenant
apparaître avec leurs sprites distincts du jeu original :

  • gfxType 3 (Guard, ‘Ard Guard, Triclaw, Insect Boss) : 4 variants
    visuellement différents au lieu de 4 copies du Guard
  • gfxType 2 (Droid, Ashnarg, AlienPriest, BigInsect, Tough Triclaw) :
    5 variants distincts au lieu de 5 copies du Droid
  • gfxType 4 (well ard guard, Insectalien) : 2 variants distincts

Reste à faire (V2)

La fix de cette session ne touche que la frame de repos (idleFrame).
L’animation dynamique (marche, attaque, hit, mort) utilise toujours la
convention hardcodée dans AlienAnimTable.java (frames 0-3 = TOWARDS,
4-7 = RIGHT, etc.), ce qui peut donner des animations incorrectes pour
les aliens dont la table d’anim utilise un offset différent.

La V2 consistera à :

  1. Créer AlienAnimsLoader.java qui charge definitions.json::alienAnims
    au démarrage du jeu
  2. Adapter AlienAnimTable.pickFrame() pour utiliser la vraie table par
    alien (avec fallback sur la convention si absente)
  3. Utiliser doActionFlag et finishedFlag pour piloter précisément les
    triggers de tir/hit dans AlienAI.java (au lieu de FIRE_FRAME = 4
    hardcodé)

Bénéfices V2 : animations 100% fidèles + timing de tir exact + sons par
frame (offset 7 dans la struct).

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *