Tuesday, October 1, 2019

One Steve Shield Limit

Reader Emi asked:
There's something that always bothered me in sonic 3 with super sonic
Why SS can't use the insta shield? Or why you can't use the insta shield with normal invicibility.
I'm not sure if that's a bug but it feels odd since the rest still can use their special moves as super but sonic can't
The reason for this is twofold.

First off, the insta-shield is implemented as regular power-up -- it is actually a separate object which spawns into the player 1 shield shot when playing as Sonic, and Sonic is also awarded one whenever he loses any other shield. This is because unlike other characters' special moves, the insta-shield must animate independently from Sonic, and it loads its art to the same VRAM slot reserved for shields and invincibility stars.

This is probably why Sonic can't use any shield moves while invincible. Since shields and invincibility share a single VRAM slot, they can't be displayed at the same time (thank goodness), and it would be pretty bad design to let players use shield moves without knowing ahead of time which move Sonic would actually perform. As such, all shield moves are disabled while Sonic's shield is hidden, and this includes Super Sonic.

Of course, you could reasonably argue that since Sonic's shield is hidden, the insta-shield should just override all other shield moves for the duration of the Super form. After all, it's what Hyper Sonic's jump dash does. That would probably work, if not for another VRAM conflict: similar to the insta-shield, the large sparks that trail behind Super characters at high speeds load their art to the same VRAM slot as shields and invincibility stars.

Actually, this is probably why they fixed that Sonic 2 bug where invincibility and Super would stack! It wasn't a problem in Sonic 2 because shields and invincibility used separate VRAM slots, since players could potentially have both at the same time in a 2P game. Anyway, it was probably too much trouble to mediate VRAM usage between the insta-shield and the sparks, so Super Sonic specifically has no jump moves. That much is intentional:
    bclr    #Status_RollJump,status(a0)             bclr    #Status_RollJump,status(a0)
    tst.b   (Super_Sonic_Knux_flag).w               tst.b   (Super_Sonic_Knux_flag).w
    beq.s   Sonic_FireShield                        beq.s   Sonic_FireShield
                                                    bmi.w   Sonic_HyperDash
    move.b  #1,double_jump_flag(a0)                 move.b  #1,double_jump_flag(a0)
    rts                                             rts
; --------------------------------------------- ; ---------------------------------------------

Sonic_FireShield:                               Sonic_FireShield:
    btst    #Status_Invincible,$2B(a0)              btst    #Status_Invincible,$2B(a0)
    bne.w   locret_12A20                            bne.w   locret_11A14
    btst    #Status_FireShield,$2B(a0)              btst    #Status_FireShield,$2B(a0)
    beq.s   Sonic_LightningShield                   beq.s   Sonic_LightningShield
Now, what probably wasn't intentional is how Super Sonic can trigger the bubble shield's bounce effect upon touching the ground in S3A!

Note how in the above bit of code, Sonic's double_jump_flag is still set upon a successful double jump, even when the Super_Sonic_Knux_flag is on. If Sonic happens to have a bubble shield, that will cause the Player_TouchFloor routine to trigger a bubble bounce, despite the initial downward plunge bit never actually taking place.

S&K adds a check for Super_Sonic_Knux_flag to this code, as well as a clumsy character ID check since characters other than Sonic sometimes run through this code too I guess:
    tst.b   double_jump_flag(a0)                    tst.b   double_jump_flag(a0)
    beq.s   locret_130BC                            beq.s   locret_12230
                                                    tst.b   character_id(a0)
                                                    bne.s   loc_1222A
                                                    tst.b   (Super_Sonic_Knux_flag).w
                                                    bne.s   loc_1222A
    btst    #Status_BublShield,$2B(a0)              btst    #Status_BublShield,$2B(a0)
    beq.s   loc_130B6                               beq.s   loc_1222A
    bsr.s   BubbleShield_Bounce                     bsr.s   BubbleShield_Bounce

loc_130B6:                                      loc_1222A:
    move.b  #0,double_jump_flag(a0)                 move.b  #0,double_jump_flag(a0)
What baffles me is, why include that code path at all? If you just remove the Super_Sonic_Knux_flag check, then the invincibility check in Sonic_FireShield will also catch Super Sonic and bail without setting the double_jump_flag. Yes, the game will continue checking the A, B and C buttons once every frame until Sonic lands back on the floor, but guess what? The game already does that for invincibility!

Okay, so the insta-shield is just a regular old object in the shield slot. Does that mean that if we award it to characters other than Sonic, they too can benefit from its unique properties? Not really.

You see, all the interesting stuff is done by the Sonic object itself. So long as his double_jump_flag is set to 1, Sonic's attack radius is increased beyond its normal range. The insta-shield simply plays its animation whenever the flag jumps from 0 to 1, and then at the end of the animation, it increases the flag to 2, returning Sonic's attack radius to normal.

Incidentally, this is the source of another S3A bug: the double_jump_flag is only cleared when Sonic touches the floor, so if the insta-shield's animation ends after Sonic has already landed, the flag will remain stuck at 2 until Sonic touches the floor again, and any double jumps prior to that will be considered to have been "already spent", as it were.

S&K fixes this by checking whether the double_jump_flag has already been cleared before attempting to set it to 2.

In my upcoming ROM hack, which I *swear* isn't cancelled, you can have characters other than Tails as your support character. Shields can still be awarded to player 1, though, so what do we do about the insta-shield art conflict?

Well, the spindash dust object is already working overtime as skid dust, water splashes and the drowning timer, so now it also serves as an insta-shield for player 2. Yeah, it disappears when player 2 jumps in/out of water and fails to show up at all when they're drowning, but as reader Emi notes, blocking the insta-shield altogether would just feel... odd.


  1. Is it possible that the character ID check might be to allow Knuckles to glide?

    1. The ID check is there because Knuckles shares a lot of functions with Sonic (ostensibly to save space) which in turn call Player_TouchFloor, falling into the trap of executing code meant only for Sonic.

      I didn't go into specifics within the blog post itself because it really wasn't relevant to the discussion at hand.

  2. I had always wondered about the bubble-bouncing Super Sonic. I'm sad that it was a bug that got fixed (because I always liked doing that) rather than a feature that got cut. Thanks for the write-up, good sir.

  3. Code-wise, how different are the crash handlers between Sonic 3 and 3K (and the 3C / S&K protos)?

    1. Look up ErrorTrap in both disassemblies. In S3 it's just a couple of nops and then a branch to itself, trapping you in an infinite loop. In S&K, the branch itself was nop'd out so it just falls through to EntryPoint, resetting the game.

  4. How would you improve the S&K leftovers subpage in the Sonic 3 tcrf.net article?

    1. I would rewrite everything from scratch and throw out everything to do with levels reading garbage data

  5. Sonic 3 for some reason has some sort of cartridge size limit that makes the game crash if level art that is stored beyond a certain point is loaded
    how do you fix that?

    1. actually it seems to not be able to load anything past there

  6. So with the recent discovery of a Sonic 3 Prototype, does this affect your plans for Sonic 3 Unlocked, and if so, how?

  7. what would be the best place to ask questions? because i have a few things that im curious about relating to sonic 3.

  8. On Marble Garden Act 2, the frame of Eggman that gets scaled for the "fly away" part has some... interesting design. I guess he's VERY surprised that Sonic's managed to follow him into the sky.
    Is the game looking at the wrong place for his palette, or improperly loading the CORRECT one, or something?

    1. It's drawn using the boss palette, which is why Robotnik looks a bit odd. Usually Robotnik is separate sprite that uses the player palette, but this wasn't possible to do in this instance.

    2. Hm... would it look better if it were drawn using the player palette?

  9. Why are there points in stages where the game erases almost all level chunks on the map (ie: near the end of both LBZ2 and MHZ2)? What purpose do these have?

    1. Game is not erases almost all level Chunks(LBZ2,MHZ2),game is load special layout which is intended for boss fight.

  10. Do you have a blog post on how blue sphere generates its stages?

  11. why are there people spamming arabic stuff

  12. You should maybe explain the super glitch that happens in lava reef zone, it would be interesting to know what causes the effects

  13. I found something that might interest you, there are unused tiles for Tails' tails found in Tails' art block. And that's about it.