Actions

SCHG How-to

Difference between revisions of "Fix Speed Bugs in Sonic 2"

From Sonic Retro

(New page for RHS fix. I will create another one for Sonic 1, as I applied a fix for HG Sonic 1 using this guide.)
(No difference)

Revision as of 07:28, 17 August 2013

In Sonic 2, there are multiple speed issue bugs with Sonic and Tails, but first, let's look at Sonic. It's uncommon, but not exactly rare, and when Super Sonic is enabled, it becomes more apparent. But sometimes, Sonic's top speed, acceleration and deceleration can be set wrong. The most common place for the bugs to occur is around the area of ARZ1 where the speed shoes are placed.

With people's hacks putting stuff speed shoes underwater and other places in their layouts, these bugs can happen more often. Such issues include:

  • If Super Sonic and you hit speed shoes, his top speed goes a little bit higher which is fine, but his acceleration and deceleration decreases. Technically, it's applying normal Sonic's speed shoes speed.
  • If you get speed shoes, then go underwater, speed shoes's speed is lost. Even if you get back out of the water, Sonic's speed is normal.
  • If Super Sonic and you get speed shoes, if you go underwater, speed shoes's speed are lost. Even if you get out of the water, Super Sonic's speed is normal.
  • If underwater and you get speed shoes, Sonic's "out-of-water" speed shoes speeds apply, making you go way too fast underwater. Getting out of water or even back in results in the speed shoes speeds being lost, even with speed shoes still applied.
  • If Super Sonic and underwater and you get speed shoes, Normal Sonic's "out-of-water" speed shoes speed apply, making you go way too fast underwater. Getting out of water or even back in and the speed shoes speeds are lost, even with speed shoes applied, but applies Super Sonic's speed settings.
  • If underwater and you get speed shoes, bug above applies but if you never leave the water, once speed shoes wear off, Sonic's normal speed applies, even though you're still underwater, making you still too fast.
  • If Super and underwater and you get speed shoes, the bug 2 bullets above applies but if you never leave the water, once speed shoes wear off, Super Sonic's speed applies, even though you're still underwater, making you still too fast.
  • If underwater and then you transform into Super Sonic, Super Sonic's "out-of-water" speed applies, making you way too fast.
  • If you have speed shoes and then you transform into Super Sonic, Super Sonic's speed applies, making you lose speed shoes speed, making you that bit slower than you should be.
  • If underwater when you have speed shoes and then you transform into Super Sonic, Super Sonic's "out-of-water" speed applies, and you lose your speed shoes speed.
  • If underwater when you have speed shoes and you're Super Sonic and you transform back to normal Sonic, normal Sonic's "underwater" speed applies, making you lose your speed shoes.

A lot, eh? The main issue is that the game never checks to see if you have the speed shoes applied or not. Or when becoming Super and etc, it's not checking if underwater, etc, etc. It would have to do a few checks itself to get it right and I suspect Sonic Team didn't have time to do it as it wasn't particularly important. But we're going to fix these ourselves, and it's much easier than you think.

Please note: I am using Xenowhirl's Sonic 2 disassembly, although this shouldn't be hard to follow if you're using the HG disassembly. KingofHarts note: I've applied this to the HG Sonic 1 disassembly. There should be little to no compatibility issues.

Part 1: Creating new speeds

First of all, we need to make some new speed settings. Here are some of Sonic's speeds settings listed in Sonic 2 currently: <asm>

NORMAL
       move.w  #$600,(Sonic_top_speed).w
       move.w  #$C,(Sonic_acceleration).w
       move.w  #$80,(Sonic_deceleration).w
NORMAL SPEED SHOES
       move.w  #$C00,(Sonic_top_speed).w
       move.w  #$18,(Sonic_acceleration).w
       move.w  #$80,(Sonic_deceleration).w
       
SUPER SONIC
       move.w  #$A00,(Sonic_top_speed).w
       move.w  #$30,(Sonic_acceleration).w
       move.w  #$100,(Sonic_deceleration).w
       
NORMAL UNDERWATER
       move.w  #$300,(Sonic_top_speed).w
       move.w  #6,(Sonic_acceleration).w
       move.w  #$40,(Sonic_deceleration).w
SUPER SONIC UNDERWATER
       move.w  #$500,(Sonic_top_speed).w
       move.w  #$18,(Sonic_acceleration).w
       move.w  #$80,(Sonic_deceleration).w

</asm> As you can see, the game doesn't have speed settings for "Normal underwater with speed shoes", "Super Sonic with speed shoes" or "Super Sonic underwater with speed shoes". No wonder why all these bugs exist. Now, we have to come up with these speeds, which I have already done with some maths involved. To get "Normal underwater with speed shoes", I thought, how does normal Sonic get speed shoes speed? Well, everything is doubled except his deceleration, which stays the same. So I did the same and got this new speed setting.

<asm>

NORMAL UNDERWATER SPEED SHOES
       move.w  #$600,(Sonic_top_speed).w
       move.w  #$C,(Sonic_acceleration).w
       move.w  #$40,(Sonic_deceleration).w

</asm>

It's almost identical to Sonic's normal speed, except his deceleration is slower. To get "Super Sonic with speed shoes", I left the top speed setting it already does alone, but made it not decrease Super Sonic's acceleration and deceleration, making him that tiny bit faster: <asm>

SUPER SONIC SPEED SHOES
       move.w  #$C00,(Sonic_top_speed).w
       move.w  #$30,(Sonic_acceleration).w
       move.w  #$100,(Sonic_deceleration).w

</asm>

To get "Super Sonic underwater with speedshoes", I followed the same method on how "Normal" does it, and got this: <asm>

SUPER SONIC UNDERWATER SPEED SHOES
       move.w  #$A00,(Sonic_top_speed).w
       move.w  #$30,(Sonic_acceleration).w
       move.w  #$80,(Sonic_deceleration).w

</asm> You do not need to add any of the ASM above into your hack, YET. Just note these for now.

Part 2: Creating a new table

Now we've got all our speeds, we need to put it all into a table, which I have already done on your behalf. You may use the table below, modifying it to your liking.

<asm>

===========================================================================
----------------------------------------------------------------------------
Speed Settings Array
This array defines what speeds the character should be set to
----------------------------------------------------------------------------
blank top_speed acceleration deceleration  ; #  ; Comment

Speedsettings:

       dc.w    $0,     $600,           $C,             $80             ; $00   ; Normal
       dc.w    $0,     $C00,           $18,            $80             ; $08   ; Normal Speedshoes
       dc.w    $0,     $300,           $6,             $40             ; $16   ; Normal Underwater
       dc.w    $0,     $600,           $C,             $40             ; $24   ; Normal Underwater Speedshoes
       dc.w    $0,     $A00,           $30,            $100            ; $32   ; Super
       dc.w    $0,     $C00,           $30,            $100            ; $40   ; Super Speedshoes
       dc.w    $0,     $500,           $18,            $80             ; $48   ; Super Underwater
       dc.w    $0,     $A00,           $30,            $80             ; $56   ; Super Underwater Speedshoes
===========================================================================

</asm>

This will be our new table. We'll insert this into the ASM file shortly. First, let's go onto the next step.

Part 3: Creating a new subroutine to apply the speeds

So, we need to create a new subroutine to grab the right speed under Sonic's conditions:

<asm>

===========================================================================
---------------------------------------------------------------------------
Subroutine to collect the right speed setting for a character
a0 must be character
a1 will be the result and have the correct speed settings
a2 is characters' speed
---------------------------------------------------------------------------
||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||

ApplySpeedSettings:

       moveq   #0,d0                           ; Quickly clear d0
       tst.w   speedshoes_time(a0)             ; Does character have speedshoes?
       beq.s   +                               ; If not, branch
       addq.b  #1,d0                           ; Quickly add 1 to d0

+

       btst    #6,status(a0)                   ; Is the character underwater?
       beq.s   +                               ; If not, branch
       addq.b  #2,d0                           ; Quickly add 2 to d0

+

       cmpa.w  #MainCharacter,a0               ; Is it Tails currently following this code?
       bne.s   +                               ; If so, branch and ignore next question
       tst.b   (Super_Sonic_flag).w            ; Is the character Super?
       beq.s   +                               ; If not, branch
       addq.b  #4,d0                           ; Quickly add 4 to d0

+

       add.b   d0,d0                           ; Multiply itself
       add.b   d0,d0                           ; And again
       add.b   d0,d0                           ; And again
       lea     Speedsettings(pc,d0.w),a1       ; Load correct speed settings into a1
       addq.l  #2,a1                           ; Increment a1 by 2 quickly
       move.l  (a1)+,(a2)+                     ; Set character's new top speed and acceleration
       move.w  (a1),(a2)                       ; Set character's deceleration
       rts                                     ; Finish subroutine
===========================================================================

</asm>

What this will do is perform checks for stuff like if you're underwater, have speed shoes or are Super and depending on the results, it will add bytes to d0. Once finished with the questions, whatever d0 is, it will multiply itself 3 times. With the new result in d0 after that multiplication, it will then grab data from the table with its starting position being whatever d0 is. For example, say you're normal Sonic and underwater. After the questions, d0 should be 2. Multiply itself 3 times, d0 becomes 16. If you then look for 16 on the table, you'll see that it does indeed equal "Normal Underwater". So it got the right speed setting.

Add this new subroutine to your code, and add the table right below it. The best place to put all of this code is between Sonic's code and Tails' code, preferably right above Tails' code.

Part 4: Replacing code

Now we have our new code, we need to make some jumps to it. First, go to "Obj01_Init:". You only need to do this bit if you have checkpoints starting underwater, otherwise, you can skip this bit of the step. You MUST follow the rest of this step from when you see "************" onward. Anyway, under the label "Obj01_Init:", find and delete these lines: <asm>

       move.w  #$600,(Sonic_top_speed).w       ; set Sonic's top speed
       move.w  #$C,(Sonic_acceleration).w      ; set Sonic's acceleration
       move.w  #$80,(Sonic_deceleration).w     ; set Sonic's deceleration

</asm>

and replace with this <asm>

       lea     (Sonic_top_speed).w,a2  ; Load Sonic_top_speed into a2
       bsr.w   ApplySpeedSettings      ; Fetch Speed settings

</asm>

************

Under "Obj01_ChkShoes:" find and delete these lines: <asm>

       move.w  #$600,(Sonic_top_speed).w
       move.w  #$C,(Sonic_acceleration).w
       move.w  #$80,(Sonic_deceleration).w
       tst.b   (Super_Sonic_flag).w
       beq.s   Obj01_RmvSpeed
       move.w  #$A00,(Sonic_top_speed).w
       move.w  #$30,(Sonic_acceleration).w
       move.w  #$100,(Sonic_deceleration).w

</asm>

and replace with this <asm>

       lea     (Sonic_top_speed).w,a2  ; Load Sonic_top_speed into a2
       bsr.w   ApplySpeedSettings      ; Fetch Speed settings

</asm>

Next, under "Obj01_InWater:" find these lines and delete: <asm>

       move.w  #$300,(Sonic_top_speed).w
       move.w  #6,(Sonic_acceleration).w
       move.w  #$40,(Sonic_deceleration).w
       tst.b   (Super_Sonic_flag).w
       beq.s   +
       move.w  #$500,(Sonic_top_speed).w
       move.w  #$18,(Sonic_acceleration).w
       move.w  #$80,(Sonic_deceleration).w

+ </asm>

and replace with: <asm>

       lea     (Sonic_top_speed).w,a2  ; Load Sonic_top_speed into a2
       bsr.w   ApplySpeedSettings      ; Fetch Speed settings

</asm>

Next, under "Obj01_OutWater:" find these lines and delete: <asm>

       move.w  #$600,(Sonic_top_speed).w
       move.w  #$C,(Sonic_acceleration).w
       move.w  #$80,(Sonic_deceleration).w
       tst.b   (Super_Sonic_flag).w
       beq.s   +
       move.w  #$A00,(Sonic_top_speed).w
       move.w  #$30,(Sonic_acceleration).w
       move.w  #$100,(Sonic_deceleration).w

+ </asm>

and replace with: <asm>

       lea     (Sonic_top_speed).w,a2  ; Load Sonic_top_speed into a2
       bsr.w   ApplySpeedSettings      ; Fetch Speed settings

</asm>

Next, go to "Sonic_CheckGoSuper:" and find and delete these lines: <asm>

       move.w  #$A00,(Sonic_top_speed).w
       move.w  #$30,(Sonic_acceleration).w
       move.w  #$100,(Sonic_deceleration).w

</asm>

and replace with: <asm>

       lea     (Sonic_top_speed).w,a2          ; Load Sonic_top_speed into a2
       bsr.w   ApplySpeedSettings              ; Fetch Speed settings

</asm>

Next, go to "Sonic_RevertToNormal:" and find and delete these lines: <asm>

       move.w  #$600,(Sonic_top_speed).w
       move.w  #$C,(Sonic_acceleration).w
       move.w  #$80,(Sonic_deceleration).w
       btst    #6,status(a0)   ; Check if underwater, return if not
       beq.s   return_1AC3C
       move.w  #$300,(Sonic_top_speed).w
       move.w  #6,(Sonic_acceleration).w
       move.w  #$40,(Sonic_deceleration).w

</asm>

and replace with this slightly different code: <asm>

       lea     (Sonic_top_speed).w,a2          ; Load Sonic_top_speed into a2
       bra.w   ApplySpeedSettings              ; Fetch Speed settings and return

</asm>

Now onto Tails. Seeing as how Tails uses the exact same speeds, we can use the exact same table. NOTE: If Tails DOES use different speeds, though, you will want to create a new table. Keep that in mind. So, go to "Obj02_Init:". You only need to do this bit if you have checkpoints starting underwater, otherwise, you can skip this bit of the step. You MUST follow the rest of this step from when you see "11:27, 17 August 2013 (UTC)11:27, 17 August 2013 (UTC)~~" onwards. Anyway, under the label "Obj02_Init:", find and delete these lines: <asm>

       move.w  #$600,(Tails_top_speed).w       ; set Tails' top speed
       move.w  #$C,(Tails_acceleration).w      ; set Tails' acceleration
       move.w  #$80,(Tails_deceleration).w     ; set Tails' deceleration

</asm>

and replace with this slightly different code: <asm>

       lea     (Tails_top_speed).w,a2  ; Load Tails_top_speed into a2
       bsr.w   ApplySpeedSettings      ; Fetch Speed settings

</asm>

11:27, 17 August 2013 (UTC)11:27, 17 August 2013 (UTC)~~

Next, go to "Obj02_ChkShoes:" and find and delete these lines: <asm>

       move.w  #$600,(Tails_top_speed).w
       move.w  #$C,(Tails_acceleration).w
       move.w  #$80,(Tails_deceleration).w

</asm>

and replace with: <asm>

       lea     (Tails_top_speed).w,a2  ; Load Tails_top_speed into a2
       bsr.w   ApplySpeedSettings      ; Fetch Speed settings

</asm>

Next, find "Obj02_InWater:" and find and delete these lines: <asm>

       move.w  #$300,(Tails_top_speed).w
       move.w  #6,(Tails_acceleration).w
       move.w  #$40,(Tails_deceleration).w

</asm>

and replace with: <asm>

       lea     (Tails_top_speed).w,a2  ; Load Tails_top_speed into a2
       bsr.w   ApplySpeedSettings      ; Fetch Speed settings

</asm>

Next, under "Obj02_OutWater:", find and delete: <asm>

       move.w  #$600,(Tails_top_speed).w
       move.w  #$C,(Tails_acceleration).w
       move.w  #$80,(Tails_deceleration).w

</asm>

and replace with: <asm>

       lea     (Tails_top_speed).w,a2  ; Load Tails_top_speed into a2
       bsr.w   ApplySpeedSettings      ; Fetch Speed settings

</asm>

That's Tails. Let's edit the speed shoes object code. Locate label "super_shoes:" and find and delete these lines: <asm>

       move.w  #$C00,(Sonic_top_speed).w
       move.w  #$18,(Sonic_acceleration).w
       move.w  #$80,(Sonic_deceleration).w

</asm>

and replace with this new code: <asm>

       movem.l a0-a2,-(sp)             ; Move a0, a1 and a2 onto stack
       lea     (MainCharacter).w,a0    ; Load Sonic to a0
       lea     (Sonic_top_speed).w,a2  ; Load Sonic_top_speed into a2
       jsr     ApplySpeedSettings      ; Fetch Speed settings
       movem.l (sp)+,a0-a2             ; Move a0, a1 and a2 from stack

</asm>

Then finally, go to "loc_12A10" and delete these lines: <asm>

       move.w  #$C00,(Tails_top_speed).w
       move.w  #$18,(Tails_acceleration).w
       move.w  #$80,(Tails_deceleration).w

</asm>

and replace with this slightly different new code: <asm>

       movem.l a0-a2,-(sp)             ; Move a0, a1 and a2 onto stack
       lea     (MainCharacter).w,a0    ; Load Tails to a0
       lea     (Tails_top_speed).w,a2  ; Load Tails_top_speed into a2
       jsr     ApplySpeedSettings      ; Fetch Speed settings
       movem.l (sp)+,a0-a2             ; Move a0, a1 and a2 from stack

</asm>

All done. Please note, do not edit under the label "loc_3AA22:". This is the only speed settings that can be left alone. This is when Sonic jumps onto the Tornado at the end of WFZ. This code applies to Super Sonic too (so Super Sonic goes slower) but this is to stop Super Sonic over-shooting the Tornado and falling to his death. The speed only applies when Sonic is about to jump onto the plane.

Be warned! If you have put in other places where you've made the characters change their speed, you'll want to edit them too!

Part 5: Fixing a new bug

With the above fixes applied, a new bug has been introduced. The bug? If you're Super Sonic and you die whilst Super, the "Super_Sonic_flag" will still remain set. This means, once the level restarts, the speed settings will fetch speeds with Super Sonic in mind, because of the flag. So if you go underwater or get spped shoes, it will still think you're Super and take that into account. And if you followed the two steps for when Sonic and Tails get created, then the Super speeds will apply then too.

Also whilst Super Sonic, if you pause the game and press A to quit, the flag will remain set. You could select Tails in options and Tails will have Super speeds. Anyway, let's fix this.

Find "Level_ClrRam:" and directly underneath it before the ClearRAM macros, put this: <asm>

       clr.b   (Super_Sonic_flag).w

</asm>

There we go, all bugs should be fixed. Now, when you enter/exit water, enter/exit speed shoes and/or enter/exit Super, the speeds should all be set correctly!