Hello, I've been meaning to make this guide for ages but because of life reasons, I just didn't have the time. But recently, I have found more time and decided to get on with this.
In Sonic 2, there are multiple speed issue bugs with Sonic and Tails, but for now, let's look at it with Sonic's point of view. 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 this area of ARZ1 due to where the speed shoes are placed.
Like said, it can be uncommon, but with people's hacks putting speedshoes underwater and other places, the bugs can happen more often.
There are several speed issues:
- If Super Sonic and you hit speedshoes, his top speed goes a little bit higher which is fine, but his acceleration and deceleration decreases. Technically, it's applying normal Sonic's speedshoes speed.
- If Normal Sonic and you get speedshoes, if you go underwater, speedshoes's speed are lost. Even if you get out of the water, Sonic's speed is normal.
- If Super Sonic and you get speedshoes, if you go underwater, speedshoes's speed are lost. Even if you get out of the water, Super Sonic's speed is normal.
- If underwater and you get speedshoes, Sonic's "out-of-water" speedshoes speed apply, making you go way too fast underwater. Getting out of water or even back in and the speedshoes speeds are lost, even with speedshoes applied.
- If Super and underwater and you get speedshoes, Normal Sonic's "out-of-water" speedshoes speed apply, making you go way too fast underwater. Getting out of water or even back in and the speedshoes speeds are lost, even with speedshoes applied, but applies Super Sonic's speed settings.
- If underwater and you get speedshoes, bug above applies but if you never leave the water, once speedshoes 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 speedshoes, bug above the above applies but if you never leave the water, once speedshoes 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 speedshoes and then you transform into Super Sonic, Super Sonic's speed applies, making you lose speedshoes speed, making you that bit slower than you should be.
- If underwater when you have speedshoes and then you transform into Super Sonic, Super Sonic's "out-of-water" speed applies, and you lose your speedshoes speed.
- If underwater when you have speedshoes and you're Super Sonic and you transform back to normal Sonic, normal Sonic's "underwater" speed applies, making you lose your speedshoes.
A lot, eh? The main issue is that the game never checks to see if you have the speedshoes 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 easier than you think.
Please note: I am using Xenowhirls disassembly, although this shouldn't be hard to follow if you're using the HG disassembly.
Step 1 - Back-up your disassembly
Like all guides you're about to follow, make a back-up before making any changes! I am also not held responsible for any troubles this may bring to your hack.
Step 2 - 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:
; 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
As you can see, the game doesn't have speed settings for "Normal underwater with speedshoes", "Super Sonic with speedshoes" or "Super Sonic underwater with speedshoes". 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 speedshoes", I thought, how does normal Sonic get speedshoes speed? Well, everything is doubled except his deceleration, which stays the same. So I did the same and got this new speed setting.
; NORMAL UNDERWATER SPEED SHOES
move.w #$600,(Sonic_top_speed).w
move.w #$C,(Sonic_acceleration).w
move.w #$40,(Sonic_deceleration).w
It's almost identical to Sonic's normal speed, except his deceleration is slower. To get "Super Sonic with speedshoes", 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:
; SUPER SONIC SPEED SHOES
move.w #$C00,(Sonic_top_speed).w
move.w #$30,(Sonic_acceleration).w
move.w #$100,(Sonic_deceleration).w
To get "Super Sonic underwater with speedshoes", I followed the same method on how "Normal" does it, and got this:
; SUPER SONIC UNDERWATER SPEED SHOES
move.w #$A00,(Sonic_top_speed).w
move.w #$30,(Sonic_acceleration).w
move.w #$80,(Sonic_deceleration).w
You do not need to add any of the ASM above into your hack as yet.
Step 3 - Creating a new table
Now we've got all our speeds, we need to convert it into a table. Which I have already done on your behalf:
; ===========================================================================
; ----------------------------------------------------------------------------
; 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
; ===========================================================================
This will be our new table. We'll insert this into the ASM file shortly. First, let's go onto the next step.
Step 4 - Creating a new subroutine to apply speeds
So, we need to create a new subroutine to grab the right speed under Sonic's conditions. Here is what I come up with:
; ===========================================================================
; ---------------------------------------------------------------------------
; 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
; ===========================================================================
What this will do is ask questions like if you're underwater, have speedshoes or are Super and depending on the answers, 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.
So, here is our new code and table so far:
; ===========================================================================
; ---------------------------------------------------------------------------
; 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
; ===========================================================================
; ----------------------------------------------------------------------------
; 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
; ===========================================================================
Step 5 - Placing your new code in your ASM file
Thanks to step 4, we've got the majority of the code sorted. You now want to place that new code in your ASM file. My suggestion is to put it between Sonic and Tails object, seeming as they're both the ones that are going to use it more often. So, search for "obj02:" and place it just above.
Now, if you've put the code in between Sonic and Tails like I said above, the rest of my guide should be fine, although if you do decide to insert the code elsewhere, you may get some “branch out of range” errors, which you must fix yourself. Depending on how heavy you've edited Sonic and/or Tails code, you might get these errors anyway, but they're so simple to fix.
Step 6 - Replacing some old code in certain places
Now we have our new code, we need to make some jumps to it. So, let's go.
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 "************" onwards. Anyway, under the label "Obj01_Init:", find and delete these lines:
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
and replace with this
lea (Sonic_top_speed).w,a2 ; Load Sonic_top_speed into a2
bsr.w ApplySpeedSettings ; Fetch Speed settings
"************"
Under "Obj01_ChkShoes:" find and delete these lines:
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
and replace with this
lea (Sonic_top_speed).w,a2 ; Load Sonic_top_speed into a2
bsr.w ApplySpeedSettings ; Fetch Speed settings
Next, under "Obj01_InWater:" find these lines and delete:
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
+
and replace with:
lea (Sonic_top_speed).w,a2 ; Load Sonic_top_speed into a2
bsr.w ApplySpeedSettings ; Fetch Speed settings
Next, under "Obj01_OutWater:" find these lines and delete:
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
+
and replace with:
lea (Sonic_top_speed).w,a2 ; Load Sonic_top_speed into a2
bsr.w ApplySpeedSettings ; Fetch Speed settings
Next, go to "Sonic_CheckGoSuper:" and find and delete these lines:
move.w #$A00,(Sonic_top_speed).w
move.w #$30,(Sonic_acceleration).w
move.w #$100,(Sonic_deceleration).w
and replace with:
lea (Sonic_top_speed).w,a2 ; Load Sonic_top_speed into a2
bsr.w ApplySpeedSettings ; Fetch Speed settings
Next, go to "Sonic_RevertToNormal:" and find and delete these lines:
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
and replace with this slightly different code:
lea (Sonic_top_speed).w,a2 ; Load Sonic_top_speed into a2
bra.w ApplySpeedSettings ; Fetch Speed settings and return
Now onto Tails. Seeming as Tails uses the exact same speeds, we can use the exact same table. 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 "~~~~~~~~~~~~" onwards. Anyway, under the label "Obj02_Init:", find and delete these lines:
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
and replace with this slightly different code:
lea (Tails_top_speed).w,a2 ; Load Tails_top_speed into a2
bsr.w ApplySpeedSettings ; Fetch Speed settings
"~~~~~~~~~~~~"
Next, go to "Obj02_ChkShoes:" and find and delete these lines:
move.w #$600,(Tails_top_speed).w
move.w #$C,(Tails_acceleration).w
move.w #$80,(Tails_deceleration).w
and replace with:
lea (Tails_top_speed).w,a2 ; Load Tails_top_speed into a2
bsr.w ApplySpeedSettings ; Fetch Speed settings
Next, find "Obj02_InWater:" and find and delete these lines:
move.w #$300,(Tails_top_speed).w
move.w #6,(Tails_acceleration).w
move.w #$40,(Tails_deceleration).w
and replace with:
lea (Tails_top_speed).w,a2 ; Load Tails_top_speed into a2
bsr.w ApplySpeedSettings ; Fetch Speed settings
Next, under "Obj02_OutWater:", find and delete:
move.w #$600,(Tails_top_speed).w
move.w #$C,(Tails_acceleration).w
move.w #$80,(Tails_deceleration).w
and replace with:
lea (Tails_top_speed).w,a2 ; Load Tails_top_speed into a2
bsr.w ApplySpeedSettings ; Fetch Speed settings
That's Tails. Let's edit the speed shoes object code. Locate label "super_shoes:" and find and delete these lines:
move.w #$C00,(Sonic_top_speed).w
move.w #$18,(Sonic_acceleration).w
move.w #$80,(Sonic_deceleration).w
and replace with this new code:
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
Then finally, go to "loc_12A10" and delete these lines:
move.w #$C00,(Tails_top_speed).w
move.w #$18,(Tails_acceleration).w
move.w #$80,(Tails_deceleration).w
and replace with this slightly different new code:
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
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!
Step 7 - Fixing another 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 sppedshoes, 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:
clr.b (Super_Sonic_flag).w
There we go, all bugs should be fixed.
Now, when you enter/exit water, enter/exit speedshoes and/or enter/exit Super, the speeds should all be set correctly!
As per usual,
here is a ROM with the bugs fixed.
Cheers,
redhotsonic