Aller au contenu principal

Session 132duodecimus (suite 6) — Combat sprites strict ASM

Remplacement des sphères placeholder/flashs colores par les vrais sprites animes
de l’Amiga, avec timing strict 25 Hz selon les données {@code BulT_AnimData_vb}
et {@code BulT_PopData_vb} du GLF.

Reference ASM

newanims.s::ItsABullet (~ligne 1340) :
– Mode vol (ShotT_Status_b == 0) : avance d’une frame d’anim par tick 25 Hz
dans BulT_AnimData_vb (boucle a la fin via cmp.w BulT_AnimFrames_l+2(a6), d2).
– Mode pop (ShotT_Status_b != 0) : meme logique avec BulT_PopData_vb, mais
one-shot : FREE_ENT a0 quand d2 > BulT_PopFrames_l.

Format des 6 bytes par frame (route ASM .bitmapgraph / .glaregraph /
.additivegraph) :
– byte 0 : sprite ID (= index dans GLFT_ObjGfxNames_l, ecrit a obj+9)
– byte 1 : extra/scale (ecrit a obj+11)
– bytes 2-3 : frame index BE WORD dans le sprite atlas (obj+6)
– byte 4 : reserve (jamais lu directement)
– byte 5 : brightness (signed, anim_Brightness_w)

120 bytes / 6 = 20 frames max par anim.

Nouveaux fichiers

combat/BulletAnimAsset.java : decodeur des blocs 120 bytes.
– Record AnimFrame(spriteId, extra, frameIndex, brightness).
decode(byte[], int count) retourne une liste immutable.
loopedFrameIndex(elapsed, count) pour AnimData (mode vol).
oneShotFrameIndex(elapsed, count) pour PopData (mode impact, retourne -1
une fois fini).

combat/BulletAnimCache.java : cache des animations decodees pour les 20
BulletDefs. Decode au demarrage, lookup O(1) en runtime.

combat/BulletSpriteAtlas.java : resout (gfxIndex, frameIndex) en
Texture JME. Charge depuis
assets/Textures/objects/<wadName>/<wadName>_f<N>.png (= sortie de
WadConverter). Cache + warn-once des assets manquants. Lecture des
GLFT_ObjGfxNames_l directement depuis GlfDatabase.getRawData() pour ne
pas avoir besoin du LnkParser au runtime.

combat/BulletSpriteRenderer.java : cree des billboards JME (Quad
+ BillboardControl::Screen) pour les bullets en vol. Mode de blending selon
BulT_GraphicType_l :
– 0 = bitmap : alpha standard
– 1 = glare : additive
– 2 = additive : additive

updateAnimation(shot) avance la frame en lisant
BulletAnimAsset.loopedFrameIndex(shot.lifetime / 25Hz, frameCount). No-op si
la frame n’a pas change.

combat/ImpactAnimRenderer.java : cree et anime les billboards d’impact.
Même approche que BulletSpriteRenderer mais pour BulT_PopData_vb.
update(inst, tpf) retourne false quand l’anim est finie (equivalent
FREE_ENT a0 ASM) et le caller doit detacher la geometry.

tools/dump/GlfBulletAnimDump.java : outil diagnostic. Hex dump des 120
bytes AnimData/PopData pour les 20 BulletDefs avec interpretation
(spriteId, frameIndex, brightness). Utile pour valider le format.

Fichiers modifies

combat/BulletUpdateSystem.java :
– Champ spriteRenderer + setSpriteRenderer().
createBulletGeometry() tente d’abord le billboard, fallback sphere.
– Avance l’animation chaque frame via spriteRenderer.updateAnimation(shot).

combat/ImpactEffectSystem.java :
– Champ animRenderer + setAnimRenderer() + liste activeAnims.
spawnImpact() tente d’abord l’anim PopData, fallback sphere flash.
update() avance les anims actives + supprime celles finies.

app/GameAppState.java : câblage. Apres l’attache d’ImpactEffectSystem :

BulletSpriteAtlas spriteAtlas = new BulletSpriteAtlas(sa.getAssetManager(), glf);
BulletAnimCache   animCache   = new BulletAnimCache(glf);
bulletUpdateSystem.setSpriteRenderer(
    new BulletSpriteRenderer(sa.getAssetManager(), spriteAtlas, animCache));
impactSystem.setAnimRenderer(
    new ImpactAnimRenderer(sa.getAssetManager(), spriteAtlas, animCache));

build.gradle : nouvelle tache dumpGlfBulletAnim.

Tests

test/.../BulletAnimAssetTest.java : 14 tests JUnit
– decode produit les frames attendues (spriteId/extra/frameIndex/brightness)
– decode 0 frames -> liste vide
– decode liste immutable
– decode rejette buffer trop court / null / count hors bornes
loopedFrameIndex avance d’une frame par tick 25 Hz, boucle, gere 0
oneShotFrameIndex avance puis retourne -1, gere 0, clamp negatif
AnimFrame.isEmpty()
AnimFrame rejette frameIndex negatif

Comportement

Avec sprites convertis presents (cas nominal) :
– Bullets en vol = billboards animes face camera, blending selon graphicType
– Impacts/explosions = billboards animes one-shot (timing strict 25 Hz)
– Couleurs correctes (suite 3 + 4 ont fixe les .256pal additifs et ROCKETS/glare).

Sans sprites (jamais lance convertWads) :
– Atlas log debug « Sprite manquant » pour les frames absentes
– Renderers retournent null -> fallback sphere/flash
– Aucune regression

Bullets sans AnimData (BulletDef avec animFrames=0, ex: certains hitscan) :
BulletAnimCache.Entry.hasFlightAnim() = false
– Fallback sphere directement

Limite connue (a ulterieur)

Les bullets explosives (BulT_ExplosiveForce_l != 0) declenchent dans l’ASM
ComputeBlast qui spawn 9 « flame » auxiliaires (8 splutchs + l’explosion
centrale) ajoutees au AlienShotPool. Le port actuel ne spawn que
l’animation centrale. Les flammes peripheriques sont une session future
(ajouter le call a ComputeBlast dans le killBullet path).

Workflow de validation

./gradlew dumpGlfBulletAnim    # verifier les 6 bytes/frame du GLF
./gradlew test                  # 14 nouveaux tests doivent passer
./gradlew convertWads           # si pas deja fait : generer les PNG
./gradlew run                   # lancer le jeu, tirer

Observer visuellement :
1. Les bullets en vol affichent les vrais sprites Amiga animes
2. A l’impact mur/alien : animation d’explosion (rocket, plasma, lazer)
3. Couleurs coherentes (jaune/orange pour explosion, blanc-bleu plasma)
4. Si un sprite manque le placeholder sphere prend le relais

Laisser un commentaire

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