Actions

SCHG How-to

Fix the Special Stage jumping physics

From Sonic Retro

(Original guide by Mercury)

The jumping physics in the Special Stages in Sonic 1 are very unfair designed. Unlike the physics you have in the levels, where you can variable your jump height by releasing the jump button at any time, the Special Stage will always bring you to the very top of your jump height.

Hivebrain's Sonic 1 disassembly (2005)

Open sonic1.asm and go to Obj09_InAir. Replace this:

		bsr.w	nullsub_2

With this:

		bsr.w	Obj09_JumpHeight

Now, find the nullsub_2 label, which should be only a few lines below. This is the deprecated routine, and we need to replace it with a new one, custom one, Obj09_JumpHeight. So replace all this:

nullsub_2:				; XREF: Obj09_InAir
		rts	
; End of function nullsub_2

; ===========================================================================
; ---------------------------------------------------------------------------
; unused subroutine to limit Sonic's upward vertical speed
; ---------------------------------------------------------------------------
		move.w	#-$400,d1
		cmp.w	$12(a0),d1
		ble.s	locret_1BBB4
		move.b	($FFFFF602).w,d0
		andi.b	#$70,d0
		bne.s	locret_1BBB4
		move.w	d1,$12(a0)

locret_1BBB4:
		rts

With this:

; ===========================================================================
; ---------------------------------------------------------------------------
; Subroutine to limit Sonic's upward vertical speed
; ---------------------------------------------------------------------------

Obj09_JumpHeight:			; XREF: Obj09_InAir
		move.b	($FFFFF602).w,d0	; is the jump button up?
		andi.b	#$70,d0
		bne.s	locret_1BBB4		; if not, branch to return
		btst	#7,$22(a0)		; did Sonic jump or is he just falling or hit by a bumper?
		beq.s	locret_1BBB4		; if not, branch to return
		move.b	($FFFFF780).w,d0	; get SS angle
		andi.b	#$FC,d0
		neg.b	d0
		subi.b	#$40,d0
		jsr	(CalcSine).l			
		move.w	$12(a0),d2		; get Y speed
		muls.w	d2,d0			; multiply Y speed by sin
		asr.l	#8,d0			; find the new Y speed
		move.w	$10(a0),d2		; get X speed
		muls.w	d2,d1			; multiply X speed by cos
		asr.l	#8,d1			; find the new X speed
		add.w	d0,d1			; combine the two speeds
		cmpi.w	#$400,d1		; compare the combined speed with the jump release speed
		ble.s	locret_1BBB4		; if it's less, branch to return
		move.b	($FFFFF780).w,d0
		andi.b	#$FC,d0
		neg.b	d0
		subi.b	#$40,d0
		jsr	(CalcSine).l
		muls.w	#$400,d1
		asr.l	#8,d1
		move.w	d1,$10(a0)
		muls.w	#$400,d0
		asr.l	#8,d0
		move.w	d0,$12(a0)		; set the speed to the jump release speed
		bclr	#7,$22(a0)		; clear "Sonic has jumped" flag
 
locret_1BBB4:
		rts

Next, go to the Obj09_OnWall label, and add this line directly after it:

		bclr	#7,$22(a0)	; clear "Sonic has jumped" flag

Now, go to the Obj09_Jump label, and after this line:

		bset	#1,$22(a0)

Add this one:

		bset	#7,$22(a0)	; set "Sonic has jumped" flag

Finally, go to the Obj09_ChkBumper label, and after this line:

		bset	#1,$22(a0)

Add this one:

		bclr	#7,$22(a0)	; clear "Sonic has jumped" flag

Sonic 1 disassembly (GitHub version)

Open _incObj\09 Sonic in Special Stage.asm and go to Obj09_InAir. Replace this:

		bsr.w	nullsub_2

With this:

		bsr.w	Obj09_JumpHeight

Now, find the nullsub_2 label, which should be only a few lines below. This is the deprecated routine, and we need to replace it with a new one, custom one, Obj09_JumpHeight. So replace all this:

nullsub_2:				; XREF: Obj09_InAir
		rts	
; End of function nullsub_2

; ===========================================================================
; ---------------------------------------------------------------------------
; unused subroutine to limit Sonic's upward vertical speed
; ---------------------------------------------------------------------------
		move.w	#-$400,d1
		cmp.w	obVelY(a0),d1
		ble.s	locret_1BBB4
		move.b	(v_jpadhold2).w,d0
		andi.b	#btnABC,d0
		bne.s	locret_1BBB4
		move.w	d1,obVelY(a0)

locret_1BBB4:
		rts

With this:

; ===========================================================================
; ---------------------------------------------------------------------------
; Subroutine to limit Sonic's upward vertical speed
; ---------------------------------------------------------------------------

; ||||||||||||||| S U B	R O U T	I N E |||||||||||||||||||||||||||||||||||||||

Obj09_JumpHeight:			; XREF: Obj09_InAir
		move.b	(v_jpadhold2).w,d0	; is the jump button up?
		andi.b	#btnABC,d0
		bne.s	locret_1BBB4		; if not, branch to return
		btst	#7,obStatus(a0)		; did Sonic jump or is he just falling or hit by a bumper?
		beq.s	locret_1BBB4		; if not, branch to return
		move.b	(v_ssangle).w,d0	; get SS angle
		andi.b	#$FC,d0
		neg.b	d0
		subi.b	#$40,d0
		jsr	(CalcSine).l			
		move.w	obVelY(a0),d2		; get Y speed
		muls.w	d2,d0			; multiply Y speed by sin
		asr.l	#8,d0			; find the new Y speed
		move.w	obVelX(a0),d2		; get X speed
		muls.w	d2,d1			; multiply X speed by cos
		asr.l	#8,d1			; find the new X speed
		add.w	d0,d1			; combine the two speeds
		cmpi.w	#$400,d1		; compare the combined speed with the jump release speed
		ble.s	locret_1BBB4		; if it's less, branch to return
		move.b	(v_ssangle).w,d0
		andi.b	#$FC,d0
		neg.b	d0
		subi.b	#$40,d0
		jsr	(CalcSine).l
		muls.w	#$400,d1
		asr.l	#8,d1
		move.w	d1,obVelX(a0)
		muls.w	#$400,d0
		asr.l	#8,d0
		move.w	d0,obVelY(a0)		; set the speed to the jump release speed
		bclr	#7,obStatus(a0)		; clear "Sonic has jumped" flag

locret_1BBB4:
		rts

Next, go to the "Obj09_OnWall" label, and add this line directly after it:

		bclr	#7,obStatus(a0)	; clear "Sonic has jumped" flag

Now, go to the "Obj09_Jump" label, and after this line:

		bset	#1,obStatus(a0)

Add this one:

		bset	#7,obStatus(a0)	; set "Sonic has jumped" flag

Finally, go to the "Obj09_ChkBumper" label, and after this line:

		bset	#1,obStatus(a0)

Add this one:

		bclr	#7,obStatus(a0)	; clear "Sonic has jumped" flag


Original quote by Mercury on the
Sonic Retro
Forums
:
A lot of people hate the Sonic 1 Special Stages. Watching YouTube playthroughs, you'll hear them profaned quite often.

I'm pretty good at beating them, myself, but I still hate them. Because I'm scientifically minded, I tried to figure out just why this is so.

Then it hit me - Sonic doesn't have a variable jump height. Unlike when he's in the normal zones, you can't just let go of the jump button to mitigate his upward velocity.

Having noticed this, it struck me that it was most likely the largest contributor to the hate for the Special Stages. Because you don't have as much control over Sonic as you've become accustomed to from playing the normal zones, it's frustrating and unfair. Trying to navigate through cramped paths winds up being an exercise in hitting every bumper and and reverse block through no fault of you own - Sonic just makes a full jump no matter what you do.

Well, it was clear - I had to remedy this. I looked in the disassembly, and lo! - there was an unused routine for reducing Sonic's jump height in the Special Stages. Of course, merely reactivating the routine would be far too simple a fix - there had to be more than that. After all, if it worked, why would it be deprecated in the first place?

It turns out the unused routine is the same as the one from Sonic's normal code. It's deprecated because, in the Special Stages, Sonic's in a rotating maze, where up isn't always up. The routine is simply impotent at any angle other than 0.

Well, Yuji Naka might be lazy, but I'm not - so I wrote a new routine that actually does work, at any angle.

This is what I did, and if you follow the steps, you can, too. This is for the GitHub disassembly:


Guide


Basically, what it does is it combines the X and Y speed using cos and sin to find how fast he's moving upward at the current angle. It compares this combined speed with the jump release speed ($400) and if it's greater, then it uses similar code to the normal jump to make Sonic move at $400 in the current angle. Simple.

Here's the built rom with the fix applied.

Mercury

SCHG How-To Guide: Sonic the Hedgehog (16-bit)
Fixing Bugs
Fix Demo Playback | Fix a Race Condition with Pattern Load Cues | Fix the SEGA Sound | Display the Press Start Button Text | Fix the Level Select Menu | Fix the Hidden Points Bug | Fix Accidental Deletion of Scattered Rings | Fix Ring Timers | Fix the Walk-Jump Bug | Correct Drowning Bugs | Fix the Death Boundary Bug | Fix the Camera Follow Bug | Fix Song Restoration Bugs | Fix the HUD Blinking | Fix the Level Select Graphics Bug
Changing Design Choices
Change Spike Behavior | Fix Special Stage Jumping Physics | Improve the Fade In\Fade Out Progression Routines | Fix Scattered Rings' Underwater Physics | Remove the Speed Cap | Port the REV01 Background Effects | Port Sonic 2's Level Art Loader | Retain Rings Between Acts | Add Sonic 2 (Simon Wai Prototype) Level Select | Improve ObjectMove Subroutines
Adding Features
Add Spin Dash ( Part 1 / Part 2 / Part 3 / Part 4 ) | Add Eggman Monitor
Sound Features
Expand Music Index From $94 to $9F | Extend Music Slots | Play Different Songs Per Act | Expand Music Index to Start at $00 | Port Sonic 2 Final Sound Driver | Port Sonic 3's Sound Driver
Extending the Game
Load Chunks From ROM | Add Extra Characters | Make an Alternative Title Screen | Use Dynamic Tilesets | Make GHZ Load Alternate Art | Add a New Zone | Set Up the Goggle Monitor | Add New Moves | Add a Dynamic Collision System | Dynamic Special Stage Walls System | Extend Sprite Mappings and Art Limit | Enigma Credits
Miscellaneous
Convert the Hivebrain 2005 Disassembly to ASM68K
Split Disassembly Guides
Set Up a Split Disassembly | Basic Level Editing | Basic Art Editing | Basic ASM Editing (Spin Dash)