How to fix the drowning bugs - Sonic 1 and 2

Discussion in 'Tutorials Archive' started by redhotsonic, Jul 28, 2012.

Thread Status:
Not open for further replies.
  1. redhotsonic

    redhotsonic Also known as RHS Member

    Joined:
    Aug 10, 2007
    Messages:
    2,969
    Location:
    England
    Hello, guys. I come forth with another two (maybe three) bug fixes! These bugs are present in Sonic 1 and Sonic 2, but has been fixed in Sonic 3 (and Knuckles).


    There is a drowning bug. You know, you're underwater, timer starts, can't find air, you drown. But what happens if you get hurt as soon as you're about to drown? Well, this little video will demonstrate:



    If you're hurt and you haven't landed yet, when you drown, Sonic will still be using the gravity from his falling back from hurt state. He will also be able to detect the floors and walls. Hence why in this video, I was able to move about after drowning. However, there is a timer present, and it will force you to the bottom of the screen and restart within seconds.


    Also, in both Sonic 1 and 2, there is another bug, although, hard to pull off. If you drown around the 9:58 mark, then the timer hits 9:59 and you're still drowning, Sonic will all of a sudden zoom to the top of the screen then back down again, really fast, in his death animation. The Time over appears.


    In Sonic 2, there is an extra bug. When you and Tails (sidekick) drowns (normally), when Sonic reaches to the bottom of the screen, if Tails hasn't yet, he will suddenly start flying and move closer to where you drowned, but will continue to fall downwards.


    In Sonic 3 and Knuckles, all of these have been fixed. It doesn't matter if you're in the hurt state, Sonic will still fall normally and will ignore the floor. And unless Tails entered the water at the exact same time as you (like he does at the beginning of HCZ1), Tails will automatically start swimming to where you drowned but will not fall. If he did enter the water at the same time, he will drown with you, but won't have time to do that bug where he would start flying suddenly. Time over will still make an appearance if you drown near the 9:58 mark, but Sonic won't misbehave.


    Today, I'm going to show you how to fix these bugs in Sonic 1 and Sonic 2, the way Sonic 3 and Knuckles did it. It's not that hard, I've done all the research for you.


    I will show you Sonic 1 SVN, and for Sonic 2, both Xenowhirls 2007 and SVN disassembly.


    Sonic 1 fix - SVN Disassembly


    First, we need to make little edit to the drowning-routine. You need to open the "0A - Drowning Countdown.asm".


    Go to "@reduceair:" and just after the "move.w #0,obInertia(a0)" command, insert this:



    move.b #$A,obRoutine(a0) ; Force the character to drown



    And just after "move.b #1,(f_nobgscroll).w", insert this:



    move.b #0,(f_timecount).w ; Stop the timer immediately



    So you have this:



    @reduceair:
    subq.w #1,(v_air).w ; subtract 1 from air remaining


    bcc.w @gotomakenum ; if air is above 0, branch


    ; Sonic drowns here


    bsr.w ResumeMusic


    move.b #$81,(f_lockmulti).w ; lock controls


    sfx sfx_Drown ; play drowning sound


    move.b #$A,$34(a0)


    move.w #1,$36(a0)


    move.w #$78,$2C(a0)


    move.l a0,-(sp)


    lea (v_player).w,a0


    bsr.w Sonic_ResetOnFloor


    move.b #$17,obAnim(a0) ; use Sonic's drowning animation


    bset #1,obStatus(a0)


    bset #7,obGfx(a0)


    move.w #0,obVelY(a0)


    move.w #0,obVelX(a0)


    move.w #0,obInertia(a0)


    move.b #$A,obRoutine(a0) ; Force the character to drown


    move.b #1,(f_nobgscroll).w


    move.b #0,(f_timecount).w ; Stop the timer immediately


    movea.l (sp)+,a0


    rts



    Next, go to "@loc_13F86:" and change all this:



    @loc_13F86:
    subq.w #1,$2C(a0)


    bne.s @loc_13F94


    move.b #6,(v_player+obRoutine).w


    rts


    ; ===========================================================================


    @loc_13F94:


    move.l a0,-(sp)


    lea (v_player).w,a0


    jsr SpeedToPos


    addi.w #$10,obVelY(a0)


    movea.l (sp)+,a0


    bra.s @nochange



    To this:



    @loc_13F86:
    subq.w #1,$2C(a0)


    bne.s @nochange ; Make it jump straight to this location


    move.b #6,(v_player+obRoutine).w


    rts



    Right, that's the drowning-routine finished. Now, we need to make Sonic apply to this.


    Now, open the main ASM file. Go to "Sonic_Index:" and add this line at the end of the table:



    dc.w Sonic_Drowned-Sonic_Index



    So you have:



    Sonic_Index: dc.w Sonic_Main-Sonic_Index
    dc.w Sonic_Control-Sonic_Index


    dc.w Sonic_Hurt-Sonic_Index


    dc.w Sonic_Death-Sonic_Index


    dc.w Sonic_ResetLevel-Sonic_Index


    dc.w Sonic_Drowned-Sonic_Index



    Next, find "include "_incObjSonic Loops.asm"" and just below it, insert this:



    include "_incObjSonic Drowns.asm"



    Then, in the "_incObj" folder, make a new ASM file called "Sonic Drowns.asm" and insert this:



    ; ---------------------------------------------------------------------------
    ; Sonic when he's drowning


    ; ---------------------------------------------------------------------------


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


    Sonic_Drowned:


    bsr.w SpeedToPos ; Make Sonic able to move


    addi.w #$10,y_vel(a0) ; Apply gravity


    bsr.w Sonic_RecordPosition ; Record position


    bsr.s Sonic_Animate ; Animate Sonic


    bsr.w Sonic_LoadGfx ; Load Sonic's DPLCs


    bra.w DisplaySprite ; And finally, display Sonic



    There, all done. When you drown, everything will be normal. If you got hurt then drown, everything will still be normal and you won't be able to hit the floor, nor will you fall fast. Also, as soon as Sonic drowns, the timer will stop immediately, rather than continuing to countdown. Because of this, there is no way you can get Time Over when drowning.


    Sonic 2 fix - XenoWhirl's Disassembly


    First, we need to make little edit to the drowning-routine. Go to "Obj0A_ReduceAir:" and just after the "move.w #0,inertia(a0)" command, insert this:



    move.b #$C,routine(a0) ; Force the character to drown



    And just after "move.b #1,($FFFFEEDC).w", insert this:



    move.b #0,(Update_HUD_timer).w ; Stop the timer immediately



    So you have this:



    Obj0A_ReduceAir:
    subq.b #1,air_left(a2) ; subtract 1 from air remaining


    bcc.w BranchTo_Obj0A_MakeItem ; if air is above 0, branch


    move.b #$81,obj_control(a2) ; lock controls


    move.w #$32+$80,d0


    jsr (PlaySound).l ; play drowning sound


    move.b #$A,objoff_34(a0)


    move.w #1,objoff_36(a0)


    move.w #$78,objoff_2C(a0)


    movea.l a2,a1


    bsr.w ResumeMusic


    move.l a0,-(sp)


    movea.l a2,a0


    bsr.w Sonic_ResetOnFloor_Part2


    move.b #$17,anim(a0) ; use Sonic's drowning animation


    bset #1,status(a0)


    bset #7,art_tile(a0)


    move.w #0,y_vel(a0)


    move.w #0,x_vel(a0)


    move.w #0,inertia(a0)


    move.b #$C,routine(a0) ; Force the character to drown


    movea.l (sp)+,a0 ; load 0bj address ; restore a0 = obj0A


    cmpa.w #MainCharacter,a2


    bne.s + ; if it isn't player 1, branch


    move.b #1,($FFFFEEDC).w


    move.b #0,(Update_HUD_timer).w ; Stop the timer immediately


    +


    rts



    Next, go to "loc_1D708:" and change all this:



    loc_1D708:
    subq.w #1,objoff_2C(a0)


    bne.s +


    move.b #6,routine(a2)


    rts


    ; ---------------------------------------------------------------------------


    + move.l a0,-(sp)


    movea.l a2,a0


    jsr ObjectMove


    addi.w #$10,y_vel(a0)


    movea.l (sp)+,a0 ; load 0bj address


    bra.s loc_1D72C



    To this:



    loc_1D708:
    subq.w #1,objoff_2C(a0)


    bne.s loc_1D72C ; Make it jump straight to this location


    move.b #6,routine(a2)


    rts



    Right, that's the drowning-routine finished. Now, we need to make the characters apply to this.


    Sonic. Go to "Obj01_States:" and add this line at the end of the table:



    dc.w Obj01_Drowned - Obj01_States ;$C



    So you have:



    Obj01_States:
    dc.w Obj01_Init - Obj01_States ; 0


    dc.w Obj01_Control - Obj01_States ; 2


    dc.w Obj01_Hurt - Obj01_States ; 4


    dc.w Obj01_Dead - Obj01_States ; 6


    dc.w Obj01_Gone - Obj01_States ; 8


    dc.w Obj01_Respawning - Obj01_States ;$A


    dc.w Obj01_Drowned - Obj01_States ;$C



    Next, go to "Sonic_Animate:" and just above it, insert this:



    ; ---------------------------------------------------------------------------
    ; Sonic when he's drowning


    ; ---------------------------------------------------------------------------


    Obj01_Drowned:


    bsr.w ObjectMove ; Make Sonic able to move


    addi.w #$10,y_vel(a0) ; Apply gravity


    bsr.w Sonic_RecordPos ; Record position


    bsr.w Sonic_Animate ; Animate Sonic


    bsr.w LoadSonicDynPLC ; Load Sonic's DPLCs


    bra.w DisplaySprite ; And finally, display Sonic



    That's Sonic done. Tails is next. Go to "Obj02_States:" and again, insert this line at the end of the table:



    dc.w Obj02_Drowned - Obj02_States ;$C



    So you have:



    Obj02_States:
    dc.w Obj02_Init - Obj02_States ; 0


    dc.w Obj02_Control - Obj02_States ; 2


    dc.w Obj02_Hurt - Obj02_States ; 4


    dc.w Obj02_Dead - Obj02_States ; 6


    dc.w Obj02_Gone - Obj02_States ; 8


    dc.w Obj02_Respawning - Obj02_States ;$A


    dc.w Obj02_Drowned - Obj02_States ;$C



    Next, go to "Tails_Animate:" and just above it, insert this:



    ; ---------------------------------------------------------------------------
    ; Tails when he's drowning


    ; ---------------------------------------------------------------------------


    Obj02_Drowned:


    bsr.w ObjectMove ; Make Tails able to move


    addi.w #$10,y_vel(a0) ; Apply gravity


    bsr.w Tails_RecordPos ; Record position


    bsr.s Tails_Animate ; Animate Tails


    bsr.w LoadTailsDynPLC ; Load Tails's DPLCs


    bra.w DisplaySprite ; And finally, display Tails



    There, all done. When you drown, everything will be normal. If you got hurt then drown, everything will still be normal and you won't be able to hit the floor, nor will you fall fast. If Tails is with you, he will fly out of the water as soon as you drown. Unless he entered the water at the exact same time as you; then he will drown with you, but won't have time to suddenly fly again.


    Now, as soon as Sonic drowns, the timer will stop immediately, rather than continuing to countdown. Because of this, there is no way you can get Time Over when drowning.


    Sonic 2 fix - SVN Disassembly


    First, we need to make little edit to the drowning-routine. Go to "Obj0A_ReduceAir:" and just after the "move.w #0,inertia(a0)" command, insert this:



    move.b #$C,routine(a0) ; Force the character to drown



    And just after "move.b #1,(Deform_lock).w", insert this:



    move.b #0,(Update_HUD_timer).w ; Stop the timer immediately



    So you have this:



    Obj0A_ReduceAir:
    subq.b #1,air_left(a2) ; subtract 1 from air remaining


    bcc.w BranchTo_Obj0A_MakeItem ; if air is above 0, branch


    move.b #$81,obj_control(a2) ; lock controls


    move.w #SndID_Drown,d0


    jsr (PlaySound).l ; play drowning sound


    move.b #$A,objoff_34(a0)


    move.w #1,objoff_36(a0)


    move.w #$78,objoff_2C(a0)


    movea.l a2,a1


    bsr.w ResumeMusic


    move.l a0,-(sp)


    movea.l a2,a0


    bsr.w Sonic_ResetOnFloor_Part2


    move.b #$17,anim(a0) ; use Sonic's drowning animation


    bset #1,status(a0)


    bset #high_priority_bit,art_tile(a0)


    move.w #0,y_vel(a0)


    move.w #0,x_vel(a0)


    move.w #0,inertia(a0)


    move.b #$C,routine(a0) ; Force the character to drown


    movea.l (sp)+,a0 ; load 0bj address ; restore a0 = obj0A


    cmpa.w #MainCharacter,a2


    bne.s + ; if it isn't player 1, branch


    move.b #1,(Deform_lock).w


    move.b #0,(Update_HUD_timer).w ; Stop the timer immediately


    +


    rts



    Next, go to "loc_1D708:" and change all this:



    loc_1D708:
    subq.w #1,objoff_2C(a0)


    bne.s +


    move.b #6,routine(a2)


    rts


    ; ---------------------------------------------------------------------------


    + move.l a0,-(sp)


    movea.l a2,a0


    jsr (ObjectMove).l


    addi.w #$10,y_vel(a0)


    movea.l (sp)+,a0 ; load 0bj address


    bra.s loc_1D72C



    To this:



    loc_1D708:
    subq.w #1,objoff_2C(a0)


    bne.s loc_1D72C ; Make it jump straight to this location


    move.b #6,routine(a2)


    rts



    Right, that's the drowning-routine finished. Now, we need to make the characters apply to this.


    Sonic. Go to "Obj01_Index:" and add this line at the end of the table:



    offsetTableEntry.w Obj01_Drowned ; $C



    So you have:



    Obj01_Index: offsetTable
    offsetTableEntry.w Obj01_Init ; 0


    offsetTableEntry.w Obj01_Control ; 2


    offsetTableEntry.w Obj01_Hurt ; 4


    offsetTableEntry.w Obj01_Dead ; 6


    offsetTableEntry.w Obj01_Gone ; 8


    offsetTableEntry.w Obj01_Respawning ; $A


    offsetTableEntry.w Obj01_Drowned ; $C



    Next, go to "Sonic_Animate:" and just above it, insert this:



    ; ---------------------------------------------------------------------------
    ; Sonic when he's drowning


    ; ---------------------------------------------------------------------------


    Obj01_Drowned:


    bsr.w ObjectMove ; Make Sonic able to move


    addi.w #$10,y_vel(a0) ; Apply gravity


    bsr.w Sonic_RecordPos ; Record position


    bsr.w Sonic_Animate ; Animate Sonic


    bsr.w LoadSonicDynPLC ; Load Sonic's DPLCs


    bra.w DisplaySprite ; And finally, display Sonic



    That's Sonic done. Tails is next. Go to "Obj02_Index:" and again, insert this line at the end of the table:



    offsetTableEntry.w Obj02_Drowned ; $C



    So you have:



    Obj02_Index: offsetTable
    offsetTableEntry.w Obj02_Init ; 0


    offsetTableEntry.w Obj02_Control ; 2


    offsetTableEntry.w Obj02_Hurt ; 4


    offsetTableEntry.w Obj02_Dead ; 6


    offsetTableEntry.w Obj02_Gone ; 8


    offsetTableEntry.w Obj02_Respawning ; $A


    offsetTableEntry.w Obj02_Drowned ; $C



    Next, go to "Tails_Animate:" and just above it, insert this:



    ; ---------------------------------------------------------------------------
    ; Tails when he's drowning


    ; ---------------------------------------------------------------------------


    Obj02_Drowned:


    bsr.w ObjectMove ; Make Tails able to move


    addi.w #$10,y_vel(a0) ; Apply gravity


    bsr.w Tails_RecordPos ; Record position


    bsr.s Tails_Animate ; Animate Tails


    bsr.w LoadTailsDynPLC ; Load Tails's DPLCs


    bra.w DisplaySprite ; And finally, display Tails



    There, all done. When you drown, everything will be normal. If you got hurt then drown, everything will still be normal and you won't be able to hit the floor, nor will you fall fast. If Tails is with you, he will fly out of the water as soon as you drown. Unless he entered the water at the exact same time as you; then he will drown with you, but won't have time to suddenly fly again.


    Now, as soon as Sonic drowns, the timer will stop immediately, rather than continuing to countdown. Because of this, there is no way you can get Time Over when drowning.


    There you have it! All fixed! Enjoy!
     
  2. redhotsonic

    redhotsonic Also known as RHS Member

    Joined:
    Aug 10, 2007
    Messages:
    2,969
    Location:
    England
    Double post, thought I'd share this with people.

    Yes, this does fix this glitch. But this glitch you mention is heavily misunderstood. When you're drowning, you can't hit any more objects, even from the hurt state. It's impossible to hit them spikes when you're drowning.


    If you try it again, if you look very closely, Sonic actually drowns through the spikes just fine. It's the bottom of the screen that is causing it. If you get hurt, then drown, then hit the bottom of the screen, this will cause the losing 2 lives glitch; not the spikes itself. Try it. Use debug, make a spike or badnik underwater right by the bottom of the screen (CPZ or ARZ), then get hurt then drown; same thing will happen.


    Anyway, this glitch will now be fixed. I should explain how and why my fixes helps.


    Before the fix


    Object 0A (Small bubbles from Sonic's face while underwater), also controls the timer for when the drown jingle should start and when you should drown (Obj0A_ReduceAir:).


    "Obj0A_ReduceAir:" will make you drown, and once your time was up, it just pretty much do these commands:



    jsr ObjectMove
    addi.w #$10,y_vel(a0)



    This code is in the Obj0A object. So, it would basically make Sonic move downwards, and that's it. There is a quick timer running, and when it reaches 0, it will change Sonic's routine to dead, taking a life. That's how the drowning system works in Sonic 1 and 2, and it normally works fine.


    Because it's in the Obj0A code, Sonic's original code was still running. When Sonic gets hurt, his routine changes (to his hurt routine), and this is what causes the trouble. So, when Sonic now drowns, his hurt routine will still run.


    In Sonic's hurt routine, he can check for floors and walls. So if you get hurt, and then drown, he will wait 'til he hits the floor, and when he does, Sonic's routine will go back to normal, making Sonic himself normal and able to move. The Obj0A code is still running, so when that quick timer is up, it will force sonic to die and take a life.


    In his hurt state, Sonic will still check for boundaries, so if hurt then drown, as soon as he hits the bottom of the screen, Sonic will change to his dead routine instantly. The dead routine will take 1 life. That quick timer is still running and once up, it will restarts Sonic dead routine, so it will take another life.


    The reason why he moves so fast in these glitches is because his hurt/dead routine and Obj0A are both adding to his gravity, a bit too much.


    Sonic's hurt routine does not do the checks for Touch_Response, so even in his hurt routine, he can't interact with many objects, and that's why it wasn't the spikes causing the 2-lives glitch.


    With the fix


    With the fix above, we gave Sonic himself a brand new routine (Obj01_Drowned). Sonic's new routine, only contains this code:



    bsr.w ObjectMove ; Make Sonic able to move
    addi.w #$10,y_vel(a0) ; Apply gravity


    bsr.w Sonic_RecordPos ; Record position


    bsr.w Sonic_Animate ; Animate Sonic


    bsr.w LoadSonicDynPLC ; Load Sonic's DPLCs


    bra.w DisplaySprite ; And finally, display Sonic



    As you can see, there are no checks for floors, no checks for walls, no checks for level boundaries, etc. All it simply does is make Sonic move, add a little gravity, and display him. Because of this, there is NO WAY Sonic can now interact with anything.


    Then at the "Obj0A_ReduceAir:", we made it so as soon as Sonic drowns, it will force Sonic to his new routine (so if Sonic is hurt, the routine will be changed to the new drown routine anyway). Then in "Obj0A_ReduceAir:" we took out the code where it applies gravity to Sonic (no point now as Sonic has his routine).


    So now, hurt or not, when Sonic drowns, it will force Sonic to his new routine no matter what. His new routine has no checks for floors or boundaries, so Sonic can just fall and drown peacefully. The quick timer from Obj0A is still running, and once up, it will change to Sonic's dead routine, and take 1 life only.
     
  3. Mike B Berry

    Mike B Berry A grandiose return Member

    Joined:
    Jun 6, 2012
    Messages:
    377
    Location:
    New places, newer motivation
    Now As I've stated in my video here: http://www.youtube.com/watch?v=yCDkOBsv1KM at around 2:04 into the video I explain the drowning bug and the possible reasons as to why it is glitching.
     
    Last edited by a moderator: Aug 1, 2012
  4. redhotsonic

    redhotsonic Also known as RHS Member

    Joined:
    Aug 10, 2007
    Messages:
    2,969
    Location:
    England
    Not sure what you're trying to say about the drowning bug there. I've never seen that glitch before. Either way, does the guide above fix it?
     
  5. Mike B Berry

    Mike B Berry A grandiose return Member

    Joined:
    Jun 6, 2012
    Messages:
    377
    Location:
    New places, newer motivation
    No that's for an above water flag check when the drowning countdown bubbles are supposed to delete it's self after Sonic... well, drowns... Sonic 2 Nick arcade Beta had the same issue, if done correctly in any stage with water. The bug is present in the original rom too, so it kind a hints of where the bug occurs.
     
    Last edited by a moderator: Aug 2, 2012
  6. redhotsonic

    redhotsonic Also known as RHS Member

    Joined:
    Aug 10, 2007
    Messages:
    2,969
    Location:
    England
    Quick update!

     
  7. Psycho RFG

    Psycho RFG Well-Known Member Member

    Joined:
    Feb 22, 2011
    Messages:
    234
    Nice fix! I only noticed something you missed and it's that the bubbles that appears from Sonic's mouth are not loaded in your fix when drowing. Other than that, you did a great job with this bug.About Mike B Berry's bug, it happens in the original Sonic 1, not as garbled graphics, but you can see the number again loaded while you are drowing.
     
    Last edited by a moderator: Aug 5, 2012
  8. redhotsonic

    redhotsonic Also known as RHS Member

    Joined:
    Aug 10, 2007
    Messages:
    2,969
    Location:
    England

    That's what my last post was about =P
     
  9. Psycho RFG

    Psycho RFG Well-Known Member Member

    Joined:
    Feb 22, 2011
    Messages:
    234
    loool sorry, I didn't noticed that part. It's OK, thanks and sorry!!!


    Any idea of why the bug that Mike B Berry's told happens? It looks the countdown numbers code is still running when Sonic is drowing or something like that.
     
    Last edited by a moderator: Aug 5, 2012
  10. Mike B Berry

    Mike B Berry A grandiose return Member

    Joined:
    Jun 6, 2012
    Messages:
    377
    Location:
    New places, newer motivation
    I still think that it is because of a water check bug. But I'm no professional, I just check for bugs.
     
  11. Kensou

    Kensou Well-Known Member Member

    Joined:
    Aug 19, 2010
    Messages:
    59
    Omg, guys. I checked and I have that garbled graphics when the countdown ends too. Anyone has a clue of what's happening here?
     
  12. redhotsonic

    redhotsonic Also known as RHS Member

    Joined:
    Aug 10, 2007
    Messages:
    2,969
    Location:
    England
    Has this been happening with the new fix only? Or did it happen before? Also, screenshot please? I tried it in my hack and original Sonic 1 and 2 and do not encounter this countdown bug.
     
  13. Mike B Berry

    Mike B Berry A grandiose return Member

    Joined:
    Jun 6, 2012
    Messages:
    377
    Location:
    New places, newer motivation
    RHS. Please watch my video again in post #3... I tried to explain what happens.
     
  14. Psycho RFG

    Psycho RFG Well-Known Member Member

    Joined:
    Feb 22, 2011
    Messages:
    234
    No, it's not caused for your fix guide. Everything is fine in your fix. That happened even before add this fix. Maybe this is not the right thread since is not about your fix, but it is related with drowing. I checked in Sonic 1 and it happens too, not too much noticeable as in my hack (that shows garbled graphics too). But in original Sonic 1 you can see the countdown numbers graphics while you are drowing. I will upload a video latter to show it if you still can't reproduce the bug.
     
  15. Kensou

    Kensou Well-Known Member Member

    Joined:
    Aug 19, 2010
    Messages:
    59
    It was happening before too.
     
    Last edited by a moderator: Aug 6, 2012
Thread Status:
Not open for further replies.