Actions

SCHG How-to

Fix Speed Bugs in Sonic 2

From Sonic Retro

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' 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' 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:

; 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 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.

; 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 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:

; 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, 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.

; ===========================================================================
; ----------------------------------------------------------------------------
; Speed Settings Array

; This array defines what speeds the character should be set to
; ----------------------------------------------------------------------------
;		top_speed	acceleration	deceleration	; #	; Comment
Speedsettings:
	dc.w	$600,		$C,		$80		; $00	; Normal
	dc.w	$C00,		$18,		$80		; $08	; Normal Speedshoes
	dc.w	$300,		$6,		$40		; $16	; Normal Underwater
	dc.w	$600,		$C,		$40		; $24	; Normal Underwater Speedshoes
	dc.w	$A00,		$30,		$100		; $32	; Super
	dc.w	$C00,		$30,		$100		; $40	; Super Speedshoes
	dc.w	$500,		$18,		$80		; $48	; Super Underwater
	dc.w	$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.

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:

; ===========================================================================
; ---------------------------------------------------------------------------
; 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	#6,d0				; Quickly add 6 to d0
+
	btst	#6,status(a0)			; Is the character underwater?
	beq.s	+				; If not, branch
	addi.b	#12,d0				; Add 12 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
	addi.b	#24,d0				; Add 24 to d0
+
	lea	Speedsettings(pc,d0.w),a1	; Load correct speed settings into a1
	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 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:

	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. 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 "************" 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

If you followed this guide, then there's one more place to edit: Find label "loc_1BC68:" and 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:

	move.l	a1,-(sp)		; Backup a1
	lea	(Tails_top_speed).w,a2	; Load Tails_top_speed into a2
	bsr.w	ApplySpeedSettings	; Fetch Speed settings
	move.l	(sp)+,a1		; Restore a1

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

	cmpa.w	#MainCharacter,a1
	bne.s	loc_12A10
	cmpi.w	#2,(Player_mode).w	; Maybe set if the char is Tails?
	beq.s	loc_12A10
	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 main character to a0
	cmpa.w	a0,a1			; Did the main character break the monitor?
	bne.s	loc_12A10		; If not, branch
	cmpi.w	#2,(Player_mode).w	; Is player using Tails?
	beq.s	loc_12A10		; If yes, branch
	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 new code:

	tst.w	(Two_player_mode).w	; Is this two-player mode?
	beq.s	.nottwoplayer		; If not, branch
	lea	(Sidekick).w,a0		; If so, Tails isn't the main character; use correct RAM
.nottwoplayer:
	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!

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:

	clr.b	(Super_Sonic_flag).w

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!

SCHG How-To Guide: Sonic the Hedgehog 2 (16-bit)
Fixing Bugs
Fix Demo Playback | Fix a Race Condition with Pattern Load Cues | Fix Super Sonic Bugs | Use Correct Height When Roll Jumping | Fix Jump Height Bug When Exiting Water | Fix Spin Dash Code and Add Spin Dash Speeds | Fix Screen Boundary Spin Dash Bug | Correct Drowning Bugs | Fix Camera Y Position for Tails | Fix Tails Subanimation Error | Fix Tails' Respawn Speeds | Fix Accidental Deletion of Scattered Rings | Fix Ring Timers | Fix Rexon Crash | Fix Monitor Collision Bug | Fix EHZ Deformation Bug | Correct CPZ Boss Attack Behavior | Fix Bug in ARZ Boss Arrow's Platform Behavior | Fix ARZ Boss Walking on Air Glitch | Fix ARZ Boss Sprite Behavior | Fix Multiple CNZ Boss Bugs | Fix HTZ Background Scrolling Mountains | Fix OOZ Launcher Speed Up Glitch | Fix DEZ Giant Mech Collision Glitch | Fix Boss Deconstruction Behavior | Fix Speed Bugs
Design Choices
Remove the Air Speed Cap | Disable Floor Collision While Dying | Modify Super Sonic Transformation Methods & Behavior | Enable/Disable Tails in Certain Levels | Collide with Water After Being Hurt | Retain Rings When Returning at a Star Post | Improve the Fade In\Fade Out Progression Routines | Fix Scattered Rings' Underwater Physics | Insert LZ Water Ripple Effect | Restore Lost CPZ Boss Feature | Prevent SCZ Tornado Spin Dash Death | Improve ObjectMove Subroutines | Port S3K Rings Manager | Port S3K Object Manager | Port S3K Priority Manager | Edit Level Order with ASM‎ | Alter Ring Requirements in Special Stages | Make Special Stage Characters Use Normal DPLCs | Speed Up Ring Loss Process | Add beta spindash to Sonic 2 | Change spike behaviour in Sonic 2
Adding Features
Create Insta-kill and High Jump Monitors | Create Clone and Special Stage Monitors | Port Knuckles
Sound Features
Port Sonic 1 Sound Driver | Port Sonic 2 Clone Driver | Port Sonic 3 Sound Driver | Expand the Music Index to Start at $00 (Sonic 2 Clone Driver Version)
Extending the Game
Extend the Level Index Past $10 | Extend the Level Select | Extend Water Tables | Add Extra Characters | Free Up 2 Universal SSTs