| Crit puck flame trail (jdc 2026-10-25-r23/r24): while the critical puck flashes
| ($FFF060 lifetime, critical_flash.s) the puck's path is kept for 9 frames and
| six flame sprites follow it at 2..7 frames ago, big to small; after the flash ends
| (save, goal, possession) the tail dies out over 24 frames (2026-10-26b: jdc, too short).
| History holds camera-independent coordinates. Project with the CURRENT camera
| when drawing; storing past screen coordinates collapsed the flames together
| whenever the camera tracked a fast shot (r33, Mini EDM-CHI reproduction).
| Other views ($C2EC bit7) normalize the native sprite position into the same space.
| +0 record: hook $15F06 (end of the sprite list build: cmpa.w #$C018,a6 / bne.w $15F12) ->
|    jsr / bne.s $15F12; ends with the same compare so the flags are the native ones.
|    r7: also draws crowd.s's bars there, at the END of the list (+28), while it has room.
| +4 draw: called by crowd.s at the START of the next list (r24: at the end the 64-sprite
|    list was often full, so the trail only showed sometimes; at the start the flames lie on
|    top of the goalie).
| 2026-10-26-r7: the game intro builds ~90 sprites itself, right up to the list's end $C2E8;
| anything drawn first pushed it over $C2E8..$C2EF ($C2EC bit0 = pause): "Resume Game"
| froze before the faceoff. TL is menu RAM, so menu text there drew flames in the intro.
| Now the afterglow needs GOK (set only by a real crit), and every sprite of ours stops at
| 80 ($C298).
| Tiles $7FC..$7FE (VRAM $FF80, the free tail of the full-screen H-scroll table) through the
| native VBlank DMA queue (a5).
    .equ LIFE,0xFFF060
    .equ PUCK,0xFFFFB74A
    .equ HIST,0xFFF600                 | 9 x (y, x) camera-independent anchors, newest first
    .equ HN,0xFFF630                   | entries in HIST
    .equ HT,0xFFF632                   | tile upload countdown
    .equ TL,0xFFF634                   | afterglow frames left
    .equ GOK,0xFFF636                  | $A5C3 while TL comes from a real crit (r7)
    .equ TILES,TILES_ADDR
    .equ TILE,0x7FC                    | VRAM $FF80..$FFDF (a DMA ending at $10000 wrapped onto tile 0)
    .text
    .globl _start
_start:
    bra.w record
    bra.w draw
record:
    movem.l %d0-%d5/%a0-%a2,-(%sp)
    tst.w LIFE
    beq.s 0f
    move.w #24,TL                      | afterglow after the flash ends (r2026-10-26b)
    move.w #0xA5C3,GOK
    bra.s 1f
0:  cmpi.w #24,TL                    | r37: TL/HT/HN are menu RAM; anything out of range is text
    bls.s 00f
    clr.w TL
00: tst.w TL
    bne.s 01f
    clr.w HN                           | no critical puck: forget the trail
    clr.w GOK
    bra.w 9f
01: subq.w #1,TL
1:  cmpi.w #31,HT
    bls.s 10f
    clr.w HT
10: cmpi.w #9,HN
    bls.s 11f
    clr.w HN
11: subq.w #1,HT                       | tiles: now and every 32 frames
    bpl.s 2f
    move.w #31,HT
    move.l #TILES,(%a5)+
    move.w #3*16,(%a5)+
    move.w #TILE*32,(%a5)+
2:  btst #7,0xFFFFC2EC                 | normal rink camera: the puck's screen position directly
    bne.s 3f                           | (the 64-sprite list may not hold the puck in a crowd)
    move.w PUCK,%d0                    | x anchor: puck x + 285
    addi.w #285,%d0
    move.w #233,%d1                   | y anchor: 233 - puck y - height
    sub.w PUCK+0x14,%d1
    sub.w PUCK+0x18,%d1
    bra.s 4f
3:  movea.w #0xC018,%a0                | other views: find the puck's sprite (tile = puck +$12)
    move.w PUCK+0x12,%d2
    andi.w #0x7FF,%d2
31: cmpa.l %a6,%a0
    bcc.w 9f
    move.w 4(%a0),%d0
    andi.w #0x7FF,%d0
    cmp.w %d2,%d0
    beq.s 32f
    addq.l #8,%a0
    bra.s 31b
32: move.w (%a0),%d1
    move.w 6(%a0),%d0
    sub.w 0xFFFFBD18,%d1              | native screen -> camera-independent anchor
    add.w 0xFFFFBD1C,%d0
4:  lea HIST+32,%a1                    | push (y, x): shift the 8 older ones down
    lea HIST+36,%a2
    moveq #7,%d2
5:  move.l -(%a1),-(%a2)
    dbra %d2,5b
    move.w %d1,HIST
    move.w %d0,HIST+2
    cmpi.w #9,HN
    bcc.s 9f
    addq.w #1,HN
9:  movem.l (%sp)+,%d0-%d5/%a0-%a2
    jsr 0x2E001C                       | crowd.s bars at the END of the list, while it has room
    jsr 0x2ED004                       | shotmeter.s bar and speed (r27), same rule
    cmpa.w #0xC018,%a6                 | the replaced native compare (flags for its bne)
    rts

draw:                                  | a6/d6 = the list (start of the frame): flames from HIST
    movem.l %d0-%d5/%a1,-(%sp)
    moveq #6,%d1                       | flames: 6 during the flash, then fewer as it dies out
    tst.w LIFE                         | (LIFE is set by a real crit only)
    bne.s 0f
    cmpi.w #0xA5C3,GOK                 | afterglow only after a real crit, never menu text in TL
    bne.s 9f
    move.w TL,%d1
    cmpi.w #24,%d1
    bhi.s 9f
    lsr.w #2,%d1
    beq.s 9f
0:  moveq #0,%d5                       | sprite i = HIST[i+1] (2..7 frames ago)
1:  move.w %d5,%d0
    addq.w #1,%d0
    cmp.w HN,%d0
    bcc.s 9f
    cmpa.w #0xC298,%a6                 | 80 sprites
    bcc.s 9f
    lsl.w #2,%d0
    lea HIST,%a1
    move.w (%a1,%d0.w),%d2             | y
    add.w 0xFFFFBD18,%d2              | project old positions with this frame's camera
    move.w 2(%a1,%d0.w),%d3            | x
    sub.w 0xFFFFBD1C,%d3
    move.w %d5,%d4                     | tile: big, big, medium, medium, small, small
    lsr.w #1,%d4
    addi.w #0x2000+TILE,%d4            | palette 1 (the crit red is colour A there)
    move.w %d2,(%a6)+
    clr.b (%a6)+
    move.b %d6,(%a6)+
    move.w %d4,(%a6)+
    move.w %d3,(%a6)+
    addq.w #1,%d6
    addq.w #1,%d5
    cmp.w %d1,%d5
    bcs.s 1b
9:  movem.l (%sp)+,%d0-%d5/%a1
    rts
