Tuesday, October 31, 2017

Dynamic pattern load cues, part 1

Let's do a recap of how most objects get their graphics on screen. First, on level load, the stage's pattern load cues are processed and the object's art is queued for decompression. In case of the Tulipon enemy, the art pointers are included as part of Angel Island Zone's KosM PLC:
PLCKosM_AIZ:    dc.w 2
                dc.l ArtKosM_AIZ_MonkeyDude
                dc.w $A900
                dc.l ArtKosM_AIZ_Tulipon
                dc.w $A540
                dc.l ArtKosM_AIZ_CaterkillerJr
                dc.w $ABE0
After the KosM queue finishes processing, VRAM address $A540 will contain the art for the Tulipon enemy:

Okay, it doesn't look like much yet. But when a Tulipon object is spawned, it will call the SetUp_ObjAttributes function to initialize its SST to the following values:
ObjDat3_86E12:  dc.l Map_Tulipon
                dc.w $252A
                dc.w $200
                dc.b $C
                dc.b $18
                dc.b 0
                dc.b $23
Specifically, its art_tile attribute is set to $252A, which being a VDP pattern index of the form PCCYXAAAAAAAAAAA, indicates a base tile of $52A using palette line 1, otherwise known as the enemy palette. Each tile is $20 bytes long, so tile $52A begins at VRAM offset $52A * $20 = $A540, which lines up with where the art was decompressed to.

Additionally, the object's mappings attribute is set to Map_Tulipon, which points to sprite mapping data for the Tulipon enemy. This data can be found in the Map - Tulipon.asm file, and it assembles the enemy's graphics into more familiar frames of animation:

Let's look at the sprite definition for the first frame:
Frame_3616CA:   dc.w  6
                dc.b  $EA,   6,   0,   7, $FF, $F0
                dc.b  $EA,   6,   8,   7,   0,   0
                dc.b    8,   0,   0,   0, $FF, $E8
                dc.b    0,   6,   0,   1, $FF, $F0
                dc.b    0,   6,   8,   1,   0,   0
                dc.b    8,   0,   8,   0,   0, $10
This is kind of overwhelming, but bear with me. The first frame contains six sprite pieces, three of which are horizontal mirrors of the other three. You can see this by looking at the middle two bytes for each sprite definition, which make up another VDP pattern index. The mirrored sprite pieces are the ones with their third byte set to 8; this indicates the X bit on the pattern index is set.

On the other hand, the fourth byte is composed entirely of A bits, essentially making it the base tile for that sprite piece. Meanwhile, the low nybble of the second byte defines the piece's shape, out of the 16 possible shapes:

Okay so, the third and sixth pieces, with shape 0 and base tile 0, produce a single tile: these are the spikes sticking out from either side of the enemy. The fourth and fifth pieces, with shape 6 and base tile 1, produce six tiles arranged into a 2x3 rectangle using tiles 1 through 6: these are the enemy's spiky stalk. Finally, the first and second sprites, with shape 6 and base tile 7, produce the same rectangle using tiles 7 through $C: these are the enemy's red bulb.

As such, the first frame makes use of every tile from 0 through $C:

Now let's look at the definition for the second frame:
Frame_3616F0:   dc.w  6
                dc.b  $EA,   6,   0,  $D, $FF, $F0
                dc.b  $EA,   6,   8,  $D,   0,   0
                dc.b    8,   0,   0,   0, $FF, $E8
                dc.b    0,   6,   0,   1, $FF, $F0
                dc.b    0,   6,   8,   1,   0,   0
                dc.b    8,   0,   8,   0,   0, $10
Immediately, we can see that pieces 3 through 6 are identical to those of the previous frame. As for the first two pieces, although they have the same 2x3 shape as before, this time the base tile is $D, which means they use tiles $D through $12 instead. Thus, the tile usage is as follows:

Although the second mapping frame reuses the stalk from the first frame, it supplies its own bulb by simply bumping up the base tile to a value past the last tile of the previous frame.

Man, this post is getting way longer than I originally anticipated. Check back tomorrow when I hopefully get to the point.

Monday, October 30, 2017

The transformation glitch

A while ago, I mentioned how you can go through the rising lava in Lava Reef Zone 1 by transforming into Super Sonic right before you land on it. More generally, characters go through all solid objects while they're hanging in the air during their transformation sequence. In his glitches and oversights series, GoldS dubbed this the "transformation glitch".

So what's special about the transformation sequence that causes you to phase through solid objects? Something which I glossed over back when I talked about the Super monitor is how as part of the sequence, the object_control attribute at offset $2E of the player's SST is set to a particular value:
    move.b  #1,(Super_Hyper_palette_status).w   ; set Super/Hyper palette status to 'fading'
    move.b  #$F,(Palette_timer).w
    move.w  #60,(Super_Hyper_frame_count).w
    move.l  #Map_SuperSonic,mappings(a0)
    move.b  #$81,object_control(a0)
    move.b  #$1F,anim(a0)                       ; enter 'transformation' animation
The object_control attribute is a bitfield, whose flags instruct the player object to skip key routines such as movement, collision and animation. They're typically set when another object is controlling the player, hence the attribute's name.

In this case, a value of $81 means that bits 7 and 0 are set. Bit 7 causes object collision to be skipped, while bit 0 skips over the player's movement routines, removing the effects of gravity. This is different from the "standing on object" flag, which also essentially disables gravity, but leaves players in full control of their movement.

When the player's movement routines don't run, level collision effectively becomes disabled, due to the assumption that the player can't touch the level unless they're traveling toward it. This quirk is actually quite important, because it allows objects to pull the player straight through solid ground, such as the hooks and teacups in Launch Base Zone.

Okay, so the transformation sequence temporarily disables gravity by setting bit 0 of the object_control bitfield, so that the player hangs in the air a bit while the animation plays. But why does it also disable object collision by setting bit 7?

Because the alternative is worse.

When bit 0 is set, the player object assumes that it must be completely stationary, so it doesn't bother checking for level collisions. However, unless bit 7 is also set, other objects may break this assumption by pushing the player themselves, which will then send the player straight through solid ground.

Better safe than sorry.

Friday, October 27, 2017

Non-standard Eggheads, part 3

The last egghead oversight isn't a missing animation frame, nor is it actually an egghead. In Sky Sanctuary Zone, when Mecha Sonic pilots the boss contraption from Sonic 1's Green Hill Zone, he actually uses the same exact palette that is used later during his proper boss fight.

When he pilots the boss contraption from Sonic 2's Metropolis Zone, though, he uses a different palette which replaces the red tones used by the wrecking ball with shades of gray, which are useful for drawing the spinning capsules:

Beyond that, there's another difference. In the Metropolis Zone palette, Mecha Sonic is considerably more purple. This suggests a similar process to what happened with Knuckles' palette: first they split the Metropolis Zone boss off into its own palette, and later updated the main palette to be less purple, neglecting to keep the Metropolis palette in sync.

The madness doesn't stop there for Mecha Sonic. In the Blue Sphere results screen, he has yet another palette, which happens to be even less purple than the main one.

However, this palette has a completely different reason for existing. In the Blue Sphere results screen, the same palette is actually used to display both Mecha Sonic and the Sonic & Knuckles logos in the background. The darkest shades of red and blue were thus made less orange-y and purple-y in order to give the logos their strong, primary colors.

Thursday, October 26, 2017

Non-standard Eggheads, part 2

On the subject of the unused FBZ2 boss head, reader Silver Sonic 1992 asked:
Was this the intended use for that frame?
I guess I should've went into more detail regarding the nature of the bug. When the boss object begins swinging around the platform, the code at loc_7078A sets bit 2 of the bitfield at offset $38 of its SST:
    move.b  #8,5(a0)
    bset    #2,$38(a0)
    move.l  #loc_707EC,$34(a0)
Meanwhile, the head object tries to keep tabs on this bit by loading the RAM address for the boss' SST into register a1, which it has stored away at offset $44 of its own SST, and then checking the contents of offset $38. However, due to an oversight, it ends up checking offset $38 of register a0, which is its own SST, rather than register a1:
    clr.b   $22(a0)
    movea.w $44(a0),a1
    btst    #2,$38(a0)
    beq.s   loc_67C12
    move.b  #1,$22(a0)
Okay, now for today's bug. Lava Reef Zone's act 2 boss has a unique head object with only one idle frame, in which the art has been redrawn to give Eggman's whiskers extra lighting from the lava below.

However, due to an oversight, only two of the three frames are ever seen. When the boss is defeated, it simply displays the second frame, which is the regular damage frame. The oversight is hard to notice due to how quickly the boss sinks back into the lava.

The code that picks which mapping frame to display is at loc_79C1C:
    jsr     (Refresh_ChildPositionAdjusted).l
    movea.w $46(a0),a1
    move.b  #$F,$22(a0)
    btst    #6,$2A(a1)
    beq.s   loc_79C3E
    move.b  #$10,$22(a0)
    bra.w   loc_79C4C
; ---------------------------------------------------------------------------

    btst    #7,$2A(a1)
    beq.s   loc_79C4C
    move.b  #$11,$22(a0)

    jmp     (Child_Draw_Sprite2).l
Much like before, the head object is keeping tabs on the boss object, except this time the boss object's RAM address is at offset $46, and the bitfield the head object checks is the status bitfield at offset $2A.

Here's how this code works: it defaults to mapping frame $F, switches to frame $10 if bit 6 is set, and switches to frame $11 if bit 7 is set. Bit 6 is set when the boss takes damage, and bit 7 is set when it is defeated. However, when the boss takes its final hit, both bits are set, and the bra.w loc_79C4C instruction skips over the bit 7 check if bit 6 is already set.

To fix the bug, simply remove the bra.w instruction.

Wednesday, October 25, 2017

Non-standard Eggheads, part 1

I've unwittingly shown this before, but in each act 2 boss, Eggman's head has four frames of animation: the first two are played in a loop while the boss is idle, whereas the third and fourth frames are shown when it takes damage, and when it's defeated, respectively. Unlike in Sonic 1 and 2, Eggman doesn't giggle when the player takes damage.

Flying Battery Zone's boss has a different set of art, and the way it works is also a bit different. The first frame is shown while the player's position is being tracked, and flips horizontally to show Eggman actively searching for the player. The third and fourth frames work the same as before. Due to an oversight, however, the second frame is never used.

To fix this, go over to the Obj_FBZRobotnikHead object and replace the code at loc_67BFC with this:
    clr.b   $22(a0)
    movea.w $44(a0),a1
    btst    #2,$38(a1)
    beq.s   loc_67C12
    move.b  #1,$22(a0)
Rebuild the ROM, wait for the boss to begin swinging around the platform and...

Hey, it's our old pal from the Data Select screen!

Additionally, you can also go over to the Obj_FBZEndBoss object and replace the code at loc_706C2 with this:
    move.w  d0,$14(a0)
    move.b  #4,5(a0)
    move.b  #$84,$38(a0)
    move.w  #$5F,$2E(a0)
    move.l  #loc_707EC,$34(a0)
This will make the boss also use the missing frame during its initial swing.

Tuesday, October 24, 2017

Too many colors, part 2

Evidence suggests that the Data Select screen originally had a similar palette layout to the one in the Competition level select screen: the first palette line holds the colors for the background and menu elements and the fourth line is used to display the current save slot's preview picture. However, the third palette line contains colors used to display the Chaos Emeralds, rather than Knuckles' palette.

As a result, Sonic and Knuckles are forced to share the second palette line; the developers accomplished this by taking Sonic's palette and replacing three of its colors with Knuckles' three reds. Sonic's palette already has two shades of red which are a close enough match. The third red is squeezed in by sacrificing the lightest gray.

This gives us the final palette layout as follows. Interestingly, the emeralds are made to flicker by writing white to every color in the third palette line every third frame, suggesting the feature was a late addition:

Obviously, this setup requires that all the character sprites be touched up; generally speaking, the light gray is replaced by white, and Sonic's shoes get a bit of pink so they don't look as dark. However, they missed one small detail:

With the new palette, the light gray of Eggman's bald shine became a red dot on his forehead. I guess the man's taken.

Monday, October 23, 2017

Too many colors

The VDP's color RAM is working overtime while displaying Competition mode's level select screen. When first entering this screen, the first palette line holds colors for the background and menu elements, the second and third lines contain Sonic and Knuckles' palettes respectively, and the fourth line is used to display the grayscale level previews.

Then, once the characters have been selected and their sprites scrolled off-screen, Knuckles' colors are replaced with the colors for the currently highlighted level, while Sonic's palette continues being used to display level names and the best time text on the right hand side. The color palettes for the level previews have to be carefully designed in order to match the single grayscale palette used by every preview picture.

There's something subtly wrong with the grayscale palette itself, though. Do you see it?

Looking at the preview picture for Azure Lake, both the clouds and the part of the sky near the mountains appear pitch black under the grayscale palette.

If we examine the colors in the palette, we find the second color is $EEE, white, the fourth color is $AAA, light gray, but the color between those two is $000, black. If we replace that color with the more logical $CCC light gray, Azure Lake's grayscale preview picture looks a lot better:

Perhaps most dramatic is Desert Palace's preview picture. The original version (center, below) almost resembles an ink blot when compared with what it's actually supposed to look like:

Friday, October 20, 2017

Implicit unboxing conversions

Reader Silver Sonic 1992 asked:
The Twin hammer boxes had their art changed slightly between Sonic 3 and Sonic 3 & Knuckles?
Specifically, they're referring to my dissection of Knuckles' boss area in Launch Base Zone 1, and the changes made to it between Sonic 3 and Sonic 3 & Knuckles, which I illustrated using these two screenshots:

Let's take a step back. In act 2, these boxes are present alongside each of these ascending/descending hook objects:

Here we can see these boxes are clearly using the "Sonic 3" design, even though the screenshot was obviously taken in Sonic & Knuckles. Can you understand what is going on now?

Let's go back to the Sonic and Tails act 1 boss area:

This is an instance where sprites don't use background art. The sprite-based object that Eggman carries around, which then opens up to reveal the boss, uses a different set of graphics and mappings from the solid, chunk-based boxes that are permanent fixtures of the level.

It just so happens that the 16x16 blocks along the center of those chunks got swapped vertically somehow, resulting in a weird pattern which doesn't look like it could actually open. Would've been interesting if it did, though.

Thursday, October 19, 2017

The ghosts are already loose

When playing as Knuckles in Sandopolis Zone 2, the ghosts don't wait for Last_star_post_hit to have a non-zero value before spawning. And later, when you reach the capsule, you find that it's already been opened.

Continuing my point from last time, this is because Knuckles' story takes place after Sonic's, and no one re-trapped the ghosts in the capsule after Sonic and Tails freed them.

This isn't the only instance of level events in Sonic's story having a tangible effect on Knuckles' playthrough of the level.

When playing as Sonic or Tails, there's a cutscene at the end of Launch Base Zone 1 in which Knuckles throws a bomb into a building, causing it to collapse. Later, when Knuckles plays through the stage, the building has sunk into the floor, revealing the alternate path hidden behind a wall in Sonic and Tails' version of the stage.

In fact, the very same function is called during both Sonic and Knuckles' stories in order to remove the building from the level layout. That should cement any doubts on the issue, concretely proving that my claims are rock solid.