Thursday, September 14, 2017

Can't catch me if I'm already gone

Like Hydrocity Zone, Carnival Night Zone too features a one-way obstacle you wouldn't expect to fall back through, but which the layout accounts for anyway. This time, it's the hover fans scattered throughout both acts of the level.


In the above scenario, when you take damage over a row of fans, they won't catch you, and you'll fall into an otherwise empty room. There's a red spring in the corner to make sure you can get back out, which is kind of overkill, considering you can get within range of the fans simply by jumping.

I don't know what they were so worried about, because if you do this close to the bottom of the stage, you'll fall straight into Knuckles' path, where you'll likely get stuck anyway due to all the Knuckles-only breakable walls.

As for why it happens: although the fans are part of the level's blocks much like the water slides in Hydrocity Zone, they actually have no collision and are instead controlled by invisible objects placed directly over them. In turn, these objects don't affect the player when their routine counter is set to 4 or above. Previously, we saw how routines 6 and above are used when the player has died; routine 4 occurs when the player is falling back from taking damage.
sub_31E96:
    ...
    cmpi.b  #4,5(a1)
    bhs.w   locret_31F2E
    tst.b   $2E(a1)
    bne.s   locret_31F2E
    ...
I'm not sure why the object behaves that way, but there you go.

Something similar happens in Flying Battery Zone, where none of the screen event code runs if the player is dead. This leads to some visual weirdness if you save yourself from death by entering debug mode: since the screen events aren't running, the foreground plane isn't redrawn as you move around the stage.


This one I can explain: the screen event code is responsible for checking whether you're inside or outside the ship, and triggering all the different effects, such as loading palettes, changing backgrounds and doing the necessary level layout modifications. It does this by loading player 1's X and Y coordinates into the d0 and d1 registers upfront and comparing them against a bunch of different ranges.
FBZ1_ScreenEvent:
    cmpi.b  #6,($FFFFB005).w
    blo.s   loc_5242E
    rts                                     ; Don't do any special events while Sonic is dying
; ---------------------------------------------------------------------------

loc_5242E:
    lea     FBZ1_LayoutModRange(pc),a1
    move.w  ($FFFFB010).w,d0
    move.w  ($FFFFB014).w,d1
    move.w  ($FFFFEED2).w,d2
    jmp     loc_52442(pc,d2.w)
; ---------------------------------------------------------------------------

loc_52442:
    bra.w   FBZ1SE_Normal
; ---------------------------------------------------------------------------
    bra.w   FBZ1SE_LayoutMod1
; ---------------------------------------------------------------------------
    bra.w   FBZ1SE_LayoutMod2
; ---------------------------------------------------------------------------
    ...
If this code ran while during the death animation, the player could potentially enter one of those ranges while falling off the screen, causing the background to change while the camera is stuck where the player originally died. The simplest solution is to check for routine 6 or above before doing anything, and although it fudges up debug mode a bit, it has no visible effect when the camera freezes normally.

3 comments:

  1. Here's a reminder: tomorrow is the blog's 100th post!

    ReplyDelete
  2. What are all the routines, may I ask?

    ReplyDelete
    Replies
    1. 0 is the init routine, which sets up the object's SST. 2 is normal operation. 4 and above are explained above. The routine byte is always an even value.

      Delete