Tuesday, July 25, 2017

I was never here

The breakable wall object, like most other solid objects in the game, checks for collision with Sonic and pals by calling the SolidObjectFull function. This function takes in a bunch of parameters through the d1-d4 registers, as well as the solid object in the a0 register and a player object in the a1 register.
loc_21568:
    move.w  ($FFFFB018).w,$30(a0)
    move.w  ($FFFFB062).w,$32(a0)
    moveq   #0,d1
    move.b  7(a0),d1
    addi.w  #$B,d1
    moveq   #0,d2
    move.b  6(a0),d2
    move.w  d2,d3
    addq.w  #1,d3
    move.w  $10(a0),d4
    jsr     (SolidObjectFull).l
    tst.b   $2C(a0)
    bpl.s   loc_215A4
    ...
After executing this function however, if either player was touching the object, then they get pushed outside the object's bounding box, and their speed is killed. This is obviously bad in the case where the player breaks down the wall, which is why prior to calling the function, the breakable wall object records the horizontal velocity for both players.

If you look at the first two lines of loc_21568 above, the RAM values at $B018 and $B062 are stored in offsets $30 and $32 of the breakable wall's SST. $B000 is the RAM address of player 1's SST, normally identified by the Player_1 label. An object's X velocity is kept at offset $18, so $B000 + $18 = player 1's X velocity at $B018. The same principle applies to player 2, whose SST is directly after player 1's at $B04A and is normally identified by the Player_2 label.
loc_215A4:
    swap    d6
    andi.w  #3,d6
    bne.s   loc_215B2

loc_215AC:
    jmp     (Sprite_OnScreen_Test).l
; ---------------------------------------------------------------------------

loc_215B2:
    lea     (Player_1).w,a1
    move.w  $30(a0),d1
    move.w  d6,d0
    andi.w  #1,d0
    beq.s   loc_2162A
    tst.b   (Super_Sonic_Knux_flag).w
    bne.s   loc_215F4
    ...
A bit later, the stored velocity is recalled into register d1, which I handwaved in my previous post. None of the following code actually runs unless one of two bits in register d6 are set, which get set by the SolidObjectFull function depending if the object was touched by player 1, player 2, or both.

Note that just checking bit 5 of $2A isn't enough: not only is it not set when the player is in mid-air, but it remains set as long the player is still touching the object, so if you stood with your back to a wall and spindashed away, it would break.
sub_2165A:
    move.w  d1,$18(a1)
    addq.w  #4,$10(a1)
    movea.l $34(a0),a4
    move.w  $10(a0),d0
    cmp.w   $10(a1),d0
    blo.s   loc_2167A
    subi.w  #8,$10(a1)
    movea.l $38(a0),a4
            
loc_2167A:  
    move.w  $18(a1),$1C(a1)
    bclr    #5,$2A(a1)
    move.l  #loc_21692,(a0)
    ...
Finally, if the wall did break, d1 is written back into the player's horizontal velocity at $18(a1), almost as if the wall had never been there in the first place. A slight tug is also given to the player's horizontal position at $10(a1), moving them four pixels towards the center of the wall. Bit 5 of $2A(a1) is also cleared in order to prevent the player from switching over to the pushing animation.

No comments:

Post a Comment