Actions

SCHG How-to

Difference between revisions of "Fix bugs relating to Super Sonic"

From Sonic Retro

(almost done for now)
(done for now)
Line 68: Line 68:
 
What's happening is that first Sonic jumps and starts the transformation sequence. This stops his movement. Then, since the timer has stopped, his palette cycle is set to fade out, but since it hasn't finished fading in, Sonic's movement is never restored. Thus he just hangs there forever.
 
What's happening is that first Sonic jumps and starts the transformation sequence. This stops his movement. Then, since the timer has stopped, his palette cycle is set to fade out, but since it hasn't finished fading in, Sonic's movement is never restored. Thus he just hangs there forever.
  
==Wrong speed under water==
+
==Wrong speed when transforming under water==
 
When transforming under water, Sonic's speed is set to his above water speed.
 
When transforming under water, Sonic's speed is set to his above water speed.
  
Line 90: Line 90:
 
Under the last shown line add this:
 
Under the last shown line add this:
 
<asm> btst #6,status(a0) ; Check if underwater, return if not
 
<asm> btst #6,status(a0) ; Check if underwater, return if not
beq.s return_1ABA4
+
beq.s +
 
move.w #$500,(Sonic_top_speed).w
 
move.w #$500,(Sonic_top_speed).w
 
move.w #$18,(Sonic_acceleration).w
 
move.w #$18,(Sonic_acceleration).w
 
move.w #$80,(Sonic_deceleration).w
 
move.w #$80,(Sonic_deceleration).w
 +
+
 
</asm>
 
</asm>
 
Now Super Sonic will have the right speed when transforming under water.
 
Now Super Sonic will have the right speed when transforming under water.

Revision as of 19:07, 15 May 2011

(Original guide by MoDule)

Super Sonic from Sonic 2 comes with quite a few bugs, some minor, while others can render the game unbeatable. This guide will show how to fix some of them. They have been arranged roughly by their severity in the event that they occur, starting at worst.

Stuck at the end of a level

At the end of a level, after reverting back to normal, if Sonic jumps he will trigger his transformation again and get stuck in the air. A full description of the bug can be found here.

Fixing the bug

Locate the Sonic_CheckGoSuper routine and add the following two lines at the beginning: <asm> tst.b (Update_HUD_timer).w ; has Sonic reached the end of the act? beq.s return_1ABA4 ; if yes, branch </asm> The code should now look something like this: <asm>; ||||||||||||||| S U B R O U T I N E |||||||||||||||||||||||||||||||||||||||

loc_1AB38
test_set_SS:

Sonic_CheckGoSuper: tst.b (Update_HUD_timer).w ; has Sonic reached the end of the act? beq.s return_1ABA4 ; if yes, branch

tst.b (Super_Sonic_flag).w ; is Sonic already Super? bne.s return_1ABA4 ; if yes, branch cmpi.b #7,(Emerald_count).w ; does Sonic have exactly 7 emeralds? bne.s return_1ABA4 ; if not, branch cmpi.w #50,(Ring_count).w ; does Sonic have at least 50 rings? blo.s return_1ABA4 ; if not, branch </asm> All this does is skip Sonic's transformation check if the level timer has stopped, which happens at the end of a level.

The bug explained

The main offender is this line in the above routine. <asm> move.b #$81,obj_control(a0) ; lock Sonic in place </asm> What this does is stop Sonic's movement completely for the duration of his transformation sequence. This isn't usually a problem, because his movement is restored once the transformation is done. The other part of the problem comes from here: <asm>; loc_1ABA6: Sonic_Super: tst.b (Super_Sonic_flag).w ; Ignore all this code if not Super Sonic beq.w return_1AC3C tst.b (Update_HUD_timer).w ; has level ended? beq.s Sonic_RevertToNormal ; <-- if yes, branch

[...]

Sonic_RevertToNormal: move.b #2,(Super_Sonic_palette).w ; <-- remove rotating palette

[...] </asm> and here: <asm>; sub_213E: PalCycle_SuperSonic: move.b (Super_Sonic_palette).w,d0 beq.s ++ ; rts ; return, if Sonic isn't super bmi.w PalCycle_SuperSonic_normal ; branch, if fade-in is done subq.b #1,d0 bne.s PalCycle_SuperSonic_revert ; <-- branch for values greater than 1

[...]

move.b #-1,(Super_Sonic_palette).w ; mark fade-in as done move.b #0,(MainCharacter+obj_control).w ; <-- restore Sonic's movement

[...]

loc_2188

PalCycle_SuperSonic_revert: ; runs the fade in transition backwards </asm> What's happening is that first Sonic jumps and starts the transformation sequence. This stops his movement. Then, since the timer has stopped, his palette cycle is set to fade out, but since it hasn't finished fading in, Sonic's movement is never restored. Thus he just hangs there forever.

Wrong speed when transforming under water

When transforming under water, Sonic's speed is set to his above water speed.

Fixing the bug

Locate the Sonic_CheckGoSuper routine. It should look like this: <asm>; loc_1AB38: test_set_SS: Sonic_CheckGoSuper: [...]

move.b #1,(Super_Sonic_palette).w move.b #$F,(Palette_timer).w move.b #1,(Super_Sonic_flag).w move.b #$81,obj_control(a0) move.b #AniIDSupSonAni_Transform,anim(a0) ; use transformation animation move.b #ObjID_SuperSonicStars,(SuperSonicStars+id).w ; load Obj7E (super sonic stars object) at $FFFFD040

move.w #$A00,(Sonic_top_speed).w move.w #$30,(Sonic_acceleration).w move.w #$100,(Sonic_deceleration).w ; <-- </asm> Under the last shown line add this: <asm> btst #6,status(a0) ; Check if underwater, return if not beq.s + move.w #$500,(Sonic_top_speed).w move.w #$18,(Sonic_acceleration).w move.w #$80,(Sonic_deceleration).w + </asm> Now Super Sonic will have the right speed when transforming under water. There might be a branch distance out of range error here. To fix this, either change the branch length from .s to .w a use a closer label with an rts after it.

The bug explained

The game doesn't check if Sonic is under water when transforming, so the speed values it gives him are too high.

Ring countdown too slow

The counter that keeps track of when to subtract a ring while super counts for 61 frames instead of 60 as seen explained here.

Fixing the bug

Locate the Sonic_Super routine. It should look like this: <asm>; loc_1ABA6: Sonic_Super: tst.b (Super_Sonic_flag).w ; Ignore all this code if not Super Sonic beq.w return_1AC3C tst.b (Update_HUD_timer).w ; has level ended? beq.s Sonic_RevertToNormal ; if yes, branch subq.w #1,(Super_Sonic_frame_count).w bpl.w return_1AC3C ; <-- move.w #60,(Super_Sonic_frame_count).w ; <-- Reset frame counter to 60

[...] </asm> Here either change the bpl to a bhi or the 60 to a 59.

The bug explained

The branch condition used allows for a range of 0 to 60, which is 61 elements long, thus the timer runs for 61 frames, instead of the intended 60.

Decelerate too quickly when rolling

Sonic decelerates considerably faster when rolling if he is super. He can actually come to a stop on shallow downward slopes.

Fixing the bug

Locate the Sonic_RollSpeed routine. It should look like this: <asm>; loc_1A7C6: Sonic_RollSpeed: move.w (Sonic_top_speed).w,d6 asl.w #1,d6 move.w (Sonic_acceleration).w,d5 asr.w #1,d5 ; natural roll deceleration = 1/2 normal acceleration move.w #$20,d4

[...] </asm> Here, replace the two lines <asm> move.w (Sonic_acceleration).w,d5 asr.w #1,d5 ; natural roll deceleration = 1/2 normal acceleration </asm> with <asm> moveq #3,d5 ; natural roll deceleration = 1/2 normal acceleration </asm> Now Sonic will always have the same roll deceleration. That includes when he has speed shoes.

The bug explained

The main idea behind the way this was coded was probably to have Sonic decelerate at the same speed he accelerates. The problem is when his acceleration is increased, which makes him decelerate more quickly, even when it seems like he shouldn't.

Transforming twice in the same level

If during the same level and on the same life Sonic reverts and transforms a second time his transformation sequence will be wrong. He won't be suspended in mid air long enough and his palette cycle will show one incorrect color.

Fixing the bug

Locate the label PalCycle_SuperSonic_revert. The code should look like this: <asm>; loc_2188: PalCycle_SuperSonic_revert: ; runs the fade in transition backwards ; run frame timer subq.b #1,(Palette_timer).w bpl.s - ; rts move.b #3,(Palette_timer).w

; decrement palette frame and update Sonic's palette lea (CyclingPal_SSTransformation).l,a0 move.w (Palette_frame).w,d0 subq.w #8,(Palette_frame).w ; previous frame bcc.s + ; branch, if it isn't the first frame move.b #0,(Palette_frame).w ; <-- move.b #0,(Super_Sonic_palette).w ; stop palette cycle + lea (Normal_palette+4).w,a1 move.l (a0,d0.w),(a1)+ move.l 4(a0,d0.w),(a1)

[...] </asm> Change the move.b at the indicated line to move.w. That's it.

The bug explained

The palette cycle frame counter is reset incorrectly. Due to the way the routine handles the counter, on the last iteration it turns negative. The code attempts to fix this, but it does it wrong. Since only the first byte of the word length counter is set to zero, the next time it's read the game will get a wrong palette index that's far too high, accounting for both the wrong palette and the missing movement lock.

Skipping one palette frame

The last entry in Super Sonic's palette cycle is skipped. Although this is barely noticeable with the original palette, if one were to change it to use more distinct colors it would become much more obvious.

Fixing the bug

Locate the label PalCycle_SuperSonic_normal. The code should look like this: <asm>; loc_21E6: PalCycle_SuperSonic_normal: ; run frame timer subq.b #1,(Palette_timer).w bpl.s - ; rts move.b #7,(Palette_timer).w

; increment palette frame and update Sonic's palette lea (CyclingPal_SSTransformation).l,a0 move.w (Palette_frame).w,d0 addq.w #8,(Palette_frame).w ; next frame cmpi.w #$78,(Palette_frame).w ; is it the last frame? blo.s + ; <-- if not, branch move.w #$30,(Palette_frame).w ; reset frame counter (Super Sonic's normal palette cycle starts at $30. Everything before that is for the palette fade) + lea (Normal_palette+4).w,a1 move.l (a0,d0.w),(a1)+ move.l 4(a0,d0.w),(a1)

[...] </asm> Change the blo to bls. Done.

The bug explained

The branch condition is wrong. $78 is a valid index in Super Sonic's palette cycle, but it's skipped over due to the branch not including it.

No transformation cycle under water

When Sonic transforms under water the initial palette cycle is not displayed.

Fixing the bug

Locate the PalCycle_SuperSonic routine. The instruction are already given in the disassembly: <asm>; sub_213E: PalCycle_SuperSonic: move.b (Super_Sonic_palette).w,d0 beq.s ++ ; rts ; return, if Sonic isn't super bmi.w PalCycle_SuperSonic_normal ; branch, if fade-in is done subq.b #1,d0 bne.s PalCycle_SuperSonic_revert ; branch for values greater than 1

; fade from Sonic's to Super Sonic's palette ; run frame timer subq.b #1,(Palette_timer).w bpl.s ++ ; rts move.b #3,(Palette_timer).w

; increment palette frame and update Sonic's palette lea (CyclingPal_SSTransformation).l,a0 move.w (Palette_frame).w,d0 addq.w #8,(Palette_frame).w ; 1 palette entry = 1 word, Sonic uses 4 shades of blue cmpi.w #$30,(Palette_frame).w ; has palette cycle reached the 6th frame? blo.s + ; if not, branch move.b #-1,(Super_Sonic_palette).w ; mark fade-in as done move.b #0,(MainCharacter+obj_control).w ; restore Sonic's movement + lea (Normal_palette+4).w,a1 move.l (a0,d0.w),(a1)+ move.l 4(a0,d0.w),(a1) ; note: the fade in for Sonic's underwater palette is missing. ; <-- ; branch to the code below (*) to fix this / rts </asm> Add a branch to the line marked here: <asm>; loc_2188: PalCycle_SuperSonic_revert: ; runs the fade in transition backwards ; run frame timer subq.b #1,(Palette_timer).w bpl.s - ; rts move.b #3,(Palette_timer).w

; decrement palette frame and update Sonic's palette lea (CyclingPal_SSTransformation).l,a0 move.w (Palette_frame).w,d0 subq.w #8,(Palette_frame).w ; previous frame bcc.s + ; branch, if it isn't the first frame

move.b #0,(Palette_frame).w

move.w #0,(Palette_frame).w ; [MoDule] correctly reset the frame index move.b #0,(Super_Sonic_palette).w ; stop palette cycle + lea (Normal_palette+4).w,a1 move.l (a0,d0.w),(a1)+ move.l 4(a0,d0.w),(a1) ; underwater palettes (*) ; <-- lea (CyclingPal_CPZUWTransformation).l,a0 cmpi.b #chemical_plant_zone,(Current_Zone).w beq.s + cmpi.b #aquatic_ruin_zone,(Current_Zone).w bne.s - ; rts lea (CyclingPal_ARZUWTransformation).l,a0 + lea (Underwater_palette+4).w,a1 move.l (a0,d0.w),(a1)+ move.l 4(a0,d0.w),(a1) rts </asm> The final code should then look like this: <asm>; sub_213E: PalCycle_SuperSonic: [...]

lea (Normal_palette+4).w,a1 move.l (a0,d0.w),(a1)+ move.l 4(a0,d0.w),(a1)

bra.s PalCycle_SuperSonic_water / rts

===========================================================================
loc_2188

PalCycle_SuperSonic_revert: ; runs the fade in transition backwards [...]

lea (Normal_palette+4).w,a1 move.l (a0,d0.w),(a1)+ move.l 4(a0,d0.w),(a1)

PalCycle_SuperSonic_water: lea (CyclingPal_CPZUWTransformation).l,a0 cmpi.b #chemical_plant_zone,(Current_Zone).w beq.s + cmpi.b #aquatic_ruin_zone,(Current_Zone).w bne.s - ; rts lea (CyclingPal_ARZUWTransformation).l,a0 + lea (Underwater_palette+4).w,a1 move.l (a0,d0.w),(a1)+ move.l 4(a0,d0.w),(a1) rts </asm>


The bug explained

It was most likely simply an oversight as it was fixed in later games. The code to fade Sonic's under water palette is there, it simply isn't used in this case.