[S1,S2,S3K] How to fix the scattered rings underwater

Discussion in 'Approved' started by redhotsonic, Mar 24, 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
    For a Sonic 2 guide, keep reading this post.


    For a Sonic 1 guide, read this post.


    For a Sonic 3 and Knuckles guide, read this post.


    Once you've followed this guide


    To speed up the ring loss process with this underwater guide, read this post. (Sonic 2 only, but you could still read it and you may be able to apply it to other Sonic games, shouldn't be too hard).


    (This isn't actually a bug as such, it's a design change. This guide is for Sonic 2)


    [​IMG]


    Ah, the scattered rings when you get hurt. It can cause slowdown to your game, and even mess up the under water palette. Now, when Sonic is underwater, his movement and his gravity is changed so he moves slower to act like he's in water. But when he is hurt under water, isn't it funny that the scattered rings still act the same as if you weren't in water?


    [​IMG]


    In this very short tutorial, I will tell you how to make the rings act correctly under water. I am using Xenowhirl's Disassembly. For other disassemblies or other Sonic games, I am not covering, but it must be extremely similar to this so I'm sure you won't have a problem if you follow this guide. Remember, always back up your disassembly first before following guides. I will not be held responsible if anything goes wrong.


    First, go to "loc_120BA:" (part of the scattered rings object code) and find this bit of coding:



    bsr.w JmpTo4_CalcSine
    move.w d4,d2


    lsr.w #8,d2



    Just underneath, paste this:



    tst.b (Water_flag).w ; Does the level have water?
    beq.s + ; If not, branch and skip underwater checks


    move.w (Water_Level_2).w,d6 ; Move water level to d6


    cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level?


    bgt.s + ; If not, branch and skip underwater commands


    asr.w #$1,d0 ; Half d0. Makes the ring's x_vel bounce to the left/right slower


    asr.w #$1,d1 ; Half d1. Makes the ring's y_vel bounce up/down slower


    +



    So, you should end up with something looking like this:



    loc_120BA:
    _move.b #$37,0(a1) ; load obj37


    addq.b #2,routine(a1)


    move.b #8,y_radius(a1)


    move.b #8,x_radius(a1)


    move.w x_pos(a0),x_pos(a1)


    move.w y_pos(a0),y_pos(a1)


    move.l #Obj25_MapUnc_12382,mappings(a1)


    move.w #$26BC,art_tile(a1)


    bsr.w Adjust2PArtPointer2


    move.b #$84,render_flags(a1)


    move.b #3,priority(a1)


    move.b #$47,collision_flags(a1)


    move.b #8,width_pixels(a1)


    move.b #-1,(Ring_spill_anim_counter).w


    tst.w d4


    bmi.s loc_12132


    move.w d4,d0


    bsr.w JmpTo4_CalcSine


    move.w d4,d2


    lsr.w #8,d2


    tst.b (Water_flag).w ; Does the level have water?


    beq.s + ; If not, branch and skip underwater checks


    move.w (Water_Level_2).w,d6 ; Move water level to d6


    cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level?


    bgt.s + ; If not, branch and skip underwater commands


    asr.w #$1,d0 ; Half d0. Makes the ring's x_vel bounce to the left/right slower


    asr.w #$1,d1 ; Half d1. Makes the ring's y_vel bounce up/down slower


    +


    asl.w d2,d0


    asl.w d2,d1


    move.w d0,d2


    move.w d1,d3


    addi.b #$10,d4


    bcc.s loc_12132


    subi.w #$80,d4


    bcc.s loc_12132


    move.w #$288,d4



    This ensures that when underwater, the rings x_vel and y_vel are halved so they appear to be moving slower.


    Next, go to label "Obj_37_sub_2:" and under



    move.b (Ring_spill_anim_frame).w,mapping_frame(a0)
    bsr.w ObjectMove


    addi.w #$18,y_vel(a0)



    Put this:



    tst.b (Water_flag).w ; Does the level have water?
    beq.s + ; If not, branch and skip underwater checks


    move.w (Water_Level_2).w,d6 ; Move water level to d6


    cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level?


    bgt.s + ; If not, branch and skip underwater commands


    subi.w #$E,y_vel(a0) ; Reduce gravity by $E ($18-$E=$A), giving the underwater effect for the rings


    +



    You can change the value of the gravity if you like, but I found $E to suit.


    So, you should have something looking like this:



    Obj_37_sub_2:

    move.b (Ring_spill_anim_frame).w,mapping_frame(a0)


    bsr.w ObjectMove


    addi.w #$18,y_vel(a0)


    tst.b (Water_flag).w ; Does the level have water?


    beq.s + ; If not, branch and skip underwater checks


    move.w (Water_Level_2).w,d6 ; Move water level to d6


    cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level?


    bgt.s + ; If not, branch and skip underwater commands


    subi.w #$E,y_vel(a0) ; Reduce gravity by $E ($18-$E=$A), giving the underwater effect for the rings


    +


    bmi.s loc_121B8


    move.b ($FFFFFE0F).w,d0


    add.b d7,d0


    andi.b #7,d0


    bne.s loc_121B8


    tst.b render_flags(a0)


    bpl.s loc_121D0


    jsr (RingCheckFloorDist).l


    tst.w d1


    bpl.s loc_121B8


    add.w d1,y_pos(a0)


    move.w y_vel(a0),d0


    asr.w #2,d0


    sub.w d0,y_vel(a0)


    neg.w y_vel(a0)



    This ensures when underwater, the gravity is not as great. Otherwise, the rings will gain speed and then it won't give the desired underwater effect.


    And that's it. Save, build and test.


    I don't think I've ever seen anyone do this in their hack. I've just applied to it in my Sonic 2 Recreation hack. I hope to see this in more hacks from now on!


    In case, here is a Sonic 2 Rom, only edited to include the underwater Scattered rings. To see for yourself.


    Cheers,


    redhotsonic


    EDIT: I forgot to add something to this guide that you will need to update if you applied this to your hack before 9.30am 24/03 (GMT). Sorry! It's been applied now.
     
    Last edited by a moderator: Apr 30, 2012
  2. Pokepunch

    Pokepunch That guy who posts on occasion Member

    Joined:
    Aug 7, 2009
    Messages:
    270
    Location:
    UK
    Thanks for this redhotsonic, I edited it to work with Sonic 1:


    Go to "Obj37_MakeRings:". You will find this:




    Obj37_MakeRings: ; XREF: Obj37_CountRings


    move.b #$37,0(a1) ; load bouncing ring object


    addq.b #2,$24(a1)


    move.b #8,$16(a1)


    move.b #8,$17(a1)


    move.w 8(a0),8(a1)


    move.w $C(a0),$C(a1)


    move.l #Map_obj25,4(a1)


    move.w #$27B2,2(a1)


    move.b #4,1(a1)


    move.b #3,$18(a1)


    move.b #$47,$20(a1)


    move.b #8,$19(a1)


    move.b #-1,($FFFFFEC6).w


    tst.w d4


    bmi.s loc_9D62


    move.w d4,d0


    bsr.w CalcSine


    move.w d4,d2


    lsr.w #8,d2


    asl.w d2,d0


    asl.w d2,d1


    move.w d0,d2


    move.w d1,d3


    addi.b #$10,d4


    bcc.s loc_9D62


    subi.w #$80,d4


    bcc.s loc_9D62


    move.w #$288,d4



    Now after the following lines:




    bsr.w CalcSine


    move.w d4,d2


    lsr.w #8,d2



    Add this:




    tst.b ($FFFFF64C).w ; Does the level have water?


    beq.s Obj37_MRCont ; If not, branch and skip underwater checks


    move.w ($FFFFF646).w,d6 ; Move water level to d6


    cmp.w $C(a0),d6 ; Is the ring object underneath the water level?


    bgt.s Obj37_MRCont ; If not, branch and skip underwater commands


    asr.w #$1,d0 ; Half d0. Makes the ring's x_vel bounce to the left/right slower


    asr.w #$1,d1 ; Half d1. Makes the ring's y_vel bounce up/down slower


    Obj37_MRCont:



    You will end up with this:




    Obj37_MakeRings: ; XREF: Obj37_CountRings


    move.b #$37,0(a1) ; load bouncing ring object


    addq.b #2,$24(a1)


    move.b #8,$16(a1)


    move.b #8,$17(a1)


    move.w 8(a0),8(a1)


    move.w $C(a0),$C(a1)


    move.l #Map_obj25,4(a1)


    move.w #$27B2,2(a1)


    move.b #4,1(a1)


    move.b #3,$18(a1)


    move.b #$47,$20(a1)


    move.b #8,$19(a1)


    move.b #-1,($FFFFFEC6).w


    tst.w d4


    bmi.s loc_9D62


    move.w d4,d0


    bsr.w CalcSine


    move.w d4,d2


    lsr.w #8,d2


    tst.b ($FFFFF64C).w ; Does the level have water?


    beq.s Obj37_MRCont ; If not, branch and skip underwater checks


    move.w ($FFFFF646).w,d6 ; Move water level to d6


    cmp.w $C(a0),d6 ; Is the ring object underneath the water level?


    bgt.s Obj37_MRCont ; If not, branch and skip underwater commands


    asr.w #$1,d0 ; Half d0. Makes the ring's x_vel bounce to the left/right slower


    asr.w #$1,d1 ; Half d1. Makes the ring's y_vel bounce up/down slower


    Obj37_MRCont:


    asl.w d2,d0


    asl.w d2,d1


    move.w d0,d2


    move.w d1,d3


    addi.b #$10,d4


    bcc.s loc_9D62


    subi.w #$80,d4


    bcc.s loc_9D62


    move.w #$288,d4



    Now go to "Obj37_Bounce:" and after this:




    move.b ($FFFFFEC7).w,$1A(a0)


    bsr.w SpeedToPos


    addi.w #$18,$12(a0)



    Add this:




    tst.b ($FFFFF64C).w ; Does the level have water?


    beq.s Obj37_BCont ; If not, branch and skip underwater checks


    move.w ($FFFFF646).w,d6 ; Move water level to d6


    cmp.w $C(a0),d6 ; Is the ring object underneath the water level?


    bgt.s Obj37_BCont ; If not, branch and skip underwater commands


    subi.w #$E,$12(a0) ; Reduce gravity by $E ($18-$E=$A), giving the underwater effect for the rings


    Obj37_BCont:



    You should now have this:




    Obj37_Bounce: ; XREF: Obj37_Index


    move.b ($FFFFFEC7).w,$1A(a0)


    bsr.w SpeedToPos


    addi.w #$18,$12(a0)


    tst.b ($FFFFF64C).w ; Does the level have water?


    beq.s Obj37_BCont ; If not, branch and skip underwater checks


    move.w ($FFFFF646).w,d6 ; Move water level to d6


    cmp.w $C(a0),d6 ; Is the ring object underneath the water level?


    bgt.s Obj37_BCont ; If not, branch and skip underwater commands


    subi.w #$E,$12(a0) ; Reduce gravity by $E ($18-$E=$A), giving the underwater effect for the rings


    Obj37_BCont:


    bmi.s Obj37_ChkDel


    move.b ($FFFFFE0F).w,d0


    add.b d7,d0


    andi.b #3,d0


    bne.s Obj37_ChkDel


    jsr ObjHitFloor


    tst.w d1


    bpl.s Obj37_ChkDel


    add.w d1,$C(a0)


    move.w $12(a0),d0


    asr.w #2,d0


    sub.w d0,$12(a0)


    neg.w $12(a0)



    This is now fixed for Sonic 1!
     
    Last edited by a moderator: Mar 28, 2012
  3. redhotsonic

    redhotsonic Also known as RHS Member

    Joined:
    Aug 10, 2007
    Messages:
    2,969
    Location:
    England
    I know cookietheguineapig has added one for Sonic 1, I've put one here for the SVN disassembly. I've added more guides for the same thing for S1 and S3K using SVN disassemblies!


    SONIC 1


    First, In your disassembly, go to "_incObj25 & 37 Rings.asm"


    Go to "@makerings:" (part of the scattered rings object code) and find this bit of coding:



    bsr.w CalcSine
    move.w d4,d2


    lsr.w #8,d2



    Just underneath, paste this:



    tst.b ($FFFFF64C).w ; Does the level have water?
    beq.s @skiphalvingvel ; If not, branch and skip underwater checks


    move.w ($FFFFF646).w,d6 ; Move water level to d6


    cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level?


    bgt.s @skiphalvingvel ; If not, branch and skip underwater commands


    asr.w #$1,d0 ; Half d0. Makes the ring's x_vel bounce to the left/right slower


    asr.w #$1,d1 ; Half d1. Makes the ring's y_vel bounce up/down slower


    @skiphalvingvel:



    So, you should end up with something looking like this:



    @makerings:
    move.b #id_RingLoss,0(a1) ; load bouncing ring object


    addq.b #2,obRoutine(a1)


    move.b #8,obHeight(a1)


    move.b #8,obWidth(a1)


    move.w obX(a0),obX(a1)


    move.w obY(a0),obY(a1)


    move.l #Map_Ring,obMap(a1)


    move.w #$27B2,obGfx(a1)


    move.b #4,obRender(a1)


    move.b #3,obPriority(a1)


    move.b #$47,obColType(a1)


    move.b #8,obActWid(a1)


    move.b #-1,(v_ani3_time).w


    tst.w d4


    bmi.s @loc_9D62


    move.w d4,d0


    bsr.w CalcSine


    move.w d4,d2


    lsr.w #8,d2


    tst.b ($FFFFF64C).w ; Does the level have water?


    beq.s @skiphalvingvel ; If not, branch and skip underwater checks


    move.w ($FFFFF646).w,d6 ; Move water level to d6


    cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level?


    bgt.s @skiphalvingvel ; If not, branch and skip underwater commands


    asr.w #$1,d0 ; Half d0. Makes the ring's x_vel bounce to the left/right slower


    asr.w #$1,d1 ; Half d1. Makes the ring's y_vel bounce up/down slower


    @skiphalvingvel:


    asl.w d2,d0


    asl.w d2,d1


    move.w d0,d2


    move.w d1,d3


    addi.b #$10,d4


    bcc.s @loc_9D62


    subi.w #$80,d4


    bcc.s @loc_9D62


    move.w #$288,d4



    This ensures that when underwater, the rings x_vel and y_vel are halved so they appear to be moving slower.


    Next, go to label "RLoss_Bounce:" and under



    move.b (v_ani3_frame).w,obFrame(a0)
    bsr.w SpeedToPos


    addi.w #$18,obVelY(a0)



    Put this:



    tst.b ($FFFFF64C).w ; Does the level have water?
    beq.s @skipbounceslow ; If not, branch and skip underwater checks


    move.w ($FFFFF646).w,d6 ; Move water level to d6


    cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level?


    bgt.s @skipbounceslow ; If not, branch and skip underwater commands


    subi.w #$E,obVelY(a0) ; Reduce gravity by $E ($18-$E=$A), giving the underwater effect


    @skipbounceslow:



    You can change the value of the gravity if you like, but I found $E to suit.


    So, you should have something looking like this:



    RLoss_Bounce: ; Routine 2
    move.b (v_ani3_frame).w,obFrame(a0)


    bsr.w SpeedToPos


    addi.w #$18,obVelY(a0)


    tst.b ($FFFFF64C).w ; Does the level have water?


    beq.s @skipbounceslow ; If not, branch and skip underwater checks


    move.w ($FFFFF646).w,d6 ; Move water level to d6


    cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level?


    bgt.s @skipbounceslow ; If not, branch and skip underwater commands


    subi.w #$E,obVelY(a0) ; Reduce gravity by $E ($18-$E=$A), giving the underwater effect


    @skipbounceslow:


    bmi.s @chkdel


    move.b (v_vbla_byte).w,d0


    add.b d7,d0


    andi.b #3,d0


    bne.s @chkdel


    jsr ObjFloorDist


    tst.w d1


    bpl.s @chkdel


    add.w d1,obY(a0)


    move.w obVelY(a0),d0


    asr.w #2,d0


    sub.w d0,obVelY(a0)


    neg.w obVelY(a0)



    This ensures when underwater, the gravity is not as great. Otherwise, the rings will gain speed and then it won't give the desired underwater effect.
     
    Last edited by a moderator: Mar 28, 2012
    Nat The Porcupine likes this.
  4. redhotsonic

    redhotsonic Also known as RHS Member

    Joined:
    Aug 10, 2007
    Messages:
    2,969
    Location:
    England
    Double post, I thought it'd be better to have a seperate post for this, so on my first post, I can add quick links, so please forgive me.


    Anyway, here is your guide for adding gravity for scattered rings underwater for S3K. Again, SVN disassembly.


    Sonic 3 and Knuckles


    Go to "off_1A658:" (the routines of the scattered rings). You'll see that it's first job is to go to "loc_1A67A". That needs changing to "loc_1A68C".


    So, from this:



    off_1A658: dc.w loc_1A67A-off_1A658

    dc.w loc_1A75C-off_1A658


    dc.w loc_1A7C2-off_1A658


    dc.w loc_1A7D6-off_1A658


    dc.w loc_1A7E4-off_1A658



    And replace with this:



    off_1A658: dc.w loc_1A68C-off_1A658

    dc.w loc_1A75C-off_1A658


    dc.w loc_1A7C2-off_1A658


    dc.w loc_1A7D6-off_1A658


    dc.w loc_1A7E4-off_1A658



    This will skip "loc_1A67A" commands. This is because this location pushes the scattered rings object (or the reverse scattered rings object) to d6. For our water code, we need d6, because all other data registers are being used. It doesn't do this in Sonic 1 or 2 because there's no such thing as "reverse gravity". The scattered rings object needs to be moved to a1, but we can't do it here, because shortly after, a1 is being used for the amount of rings.


    Next, go to "off_1A670:" This is extremely similar to what we just done, but for reverse gravity. so no point explaining what it's doing here as I just did that. Anyway, you should see:



    off_1A670: dc.w loc_1A67A-off_1A670

    dc.w loc_1A7E8-off_1A670


    dc.w loc_1A7C2-off_1A670


    dc.w loc_1A7D6-off_1A670


    dc.w loc_1A7E4-off_1A670



    And replace with:



    off_1A670: dc.w loc_1A68C-off_1A670

    dc.w loc_1A7E8-off_1A670


    dc.w loc_1A7C2-off_1A670


    dc.w loc_1A7D6-off_1A670


    dc.w loc_1A7E4-off_1A670



    Again, this will skip "loc_1A67A" commands. Explained why above.


    Next, go to "loc_1A67A:" and you should see this.



    loc_1A67A:

    move.l #Obj_Bouncing_Ring,d6


    tst.b (Reverse_gravity_flag).w


    beq.s loc_1A68C


    move.l #Obj_Bouncing_Ring_Reverse_Gravity,d6



    Notice "Bouncing_ring" (and the reverse one) are being pushed to d6. We need d6, so, delete it. It's no longer needed. Trust me. We have now free'd d6. Although we've lost pushing the scattered rings into place. Do not worry, that's next.


    Next, go to "loc_1A6B6:". The very first command should be "addq.b #2,5(a1)" Just before that, add this:



    move.l #Obj_Bouncing_Ring,(a1)
    tst.b (Reverse_gravity_flag).w


    beq.s loc_1A6B6CONTINUE


    move.l #Obj_Bouncing_Ring_Reverse_Gravity,(a1)


    loc_1A6B6CONTINUE:



    Yes, you might recognise this. It's part of that code you just this second deleted. We're pushing the scattered rings back into place. But instead of putting it to d6, we're putting it to a1. a1 is now available as it's not being used again for a while.


    So you have something looking at this:



    loc_1A6B6:
    move.l #Obj_Bouncing_Ring,(a1)


    tst.b (Reverse_gravity_flag).w


    beq.s loc_1A6B6CONTINUE


    move.l #Obj_Bouncing_Ring_Reverse_Gravity,(a1)


    loc_1A6B6CONTINUE:


    addq.b #2,5(a1)


    move.b #8,$1E(a1)


    move.b #8,$1F(a1)


    move.w $10(a0),$10(a1)


    move.w $14(a0),$14(a1)


    move.l #Map_Ring,$C(a1)


    move.w #$A6BC,$A(a1)


    move.b #$84,4(a1)


    move.w #$180,8(a1)


    move.b #$47,$28(a1)


    move.b #8,7(a1)


    move.b #-1,(Ring_spill_anim_counter).w


    tst.w d4


    bmi.s loc_1A728


    move.w d4,d0


    jsr (GetSineCosine).l


    move.w d4,d2


    lsr.w #8,d2


    asl.w d2,d0


    asl.w d2,d1


    move.w d0,d2


    move.w d1,d3


    addi.b #$10,d4


    bcc.s loc_1A728


    subi.w #$80,d4


    bcc.s loc_1A728


    move.w #$288,d4



    Still looking at "loc_1A6B6:" look and find this bit of coding:



    jsr (GetSineCosine).l
    move.w d4,d2


    lsr.w #8,d2



    Just underneath, paste this:



    tst.b (Water_flag).w ; Does the level have water?
    beq.s skiphalvingvel ; If not, branch and skip underwater checks


    move.w (Water_level).w,d6 ; Move water level to d6


    cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level?


    bgt.s skiphalvingvel ; If not, branch and skip underwater commands


    asr.w #$1,d0 ; Half d0. Makes the ring's x_vel bounce to the left/right slower


    asr.w #$1,d1 ; Half d1. Makes the ring's y_vel bounce up/down slower


    skiphalvingvel:



    So, you should end up with something looking like this:



    loc_1A6B6:
    move.l #Obj_Bouncing_Ring,(a1)


    tst.b (Reverse_gravity_flag).w


    beq.s loc_1A6B6CONTINUE


    move.l #Obj_Bouncing_Ring_Reverse_Gravity,(a1)


    loc_1A6B6CONTINUE:


    addq.b #2,5(a1)


    move.b #8,$1E(a1)


    move.b #8,$1F(a1)


    move.w $10(a0),$10(a1)


    move.w $14(a0),$14(a1)


    move.l #Map_Ring,$C(a1)


    move.w #$A6BC,$A(a1)


    move.b #$84,4(a1)


    move.w #$180,8(a1)


    move.b #$47,$28(a1)


    move.b #8,7(a1)


    move.b #-1,(Ring_spill_anim_counter).w


    tst.w d4


    bmi.s loc_1A728


    move.w d4,d0


    jsr (GetSineCosine).l


    move.w d4,d2


    lsr.w #8,d2


    tst.b (Water_flag).w ; Does the level have water?


    beq.s skiphalvingvel ; If not, branch and skip underwater checks


    move.w (Water_level).w,d6 ; Move water level to d6


    cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level?


    bgt.s skiphalvingvel ; If not, branch and skip underwater commands


    asr.w #$1,d0 ; Half d0. Makes the ring's x_vel bounce to the left/right slower


    asr.w #$1,d1 ; Half d1. Makes the ring's y_vel bounce up/down slower


    skiphalvingvel:


    asl.w d2,d0


    asl.w d2,d1


    move.w d0,d2


    move.w d1,d3


    addi.b #$10,d4


    bcc.s loc_1A728


    subi.w #$80,d4


    bcc.s loc_1A728


    move.w #$288,d4



    This ensures that when underwater, the rings x_vel and y_vel are halved so they appear to be moving slower.


    Next, go to label "loc_1A75C:" and under



    move.b (Ring_spill_anim_frame).w,$22(a0)
    bsr.w MoveSprite2


    addi.w #$18,$1A(a0)



    Put this:



    tst.b (Water_flag).w ; Does the level have water?
    beq.s skiphalvinggrav ; If not, branch and skip underwater checks


    move.w (Water_level).w,d6 ; Move water level to d6


    cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level?


    bgt.s skiphalvinggrav ; If not, branch and skip underwater commands


    subi.w #$E,$1A(a0) ; Reduce gravity by $E ($18-$E=$A), giving the underwater effect


    skiphalvinggrav:



    You can change the value of the gravity if you like, but I found $E to suit.


    So, you should have something looking like this:



    loc_1A75C:
    move.b (Ring_spill_anim_frame).w,$22(a0)


    bsr.w MoveSprite2


    addi.w #$18,$1A(a0)


    tst.b (Water_flag).w ; Does the level have water?


    beq.s skiphalvinggrav ; If not, branch and skip underwater checks


    move.w (Water_level).w,d6 ; Move water level to d6


    cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level?


    bgt.s skiphalvinggrav ; If not, branch and skip underwater commands


    subi.w #$E,$1A(a0) ; Reduce gravity by $E ($18-$E=$A), giving the underwater effect


    skiphalvinggrav:


    bmi.s loc_1A7B0


    move.b (V_int_run_count+3).w,d0


    add.b d7,d0


    andi.b #7,d0


    bne.s loc_1A7B0


    tst.b 4(a0)


    bpl.s loc_1A79C


    jsr (sub_F994).l


    tst.w d1


    bpl.s loc_1A79C


    add.w d1,$14(a0)


    move.w $1A(a0),d0


    asr.w #2,d0


    sub.w d0,$1A(a0)


    neg.w $1A(a0)



    This ensures when underwater, the gravity is not as great. Otherwise, the rings will gain speed and then it won't give the desired underwater effect.


    This last bit is optional. You only need to do this if you're planning to have reverse gravity underwater. You'd be a bit mental if you do have this, but you never know =P


    Go to label "loc_1A7E8:" and under



    move.b (Ring_spill_anim_frame).w,$22(a0)
    bsr.w MoveSprite_TestGravity2


    addi.w #$18,$1A(a0)



    Put this:



    tst.b (Water_flag).w ; Does the level have water?
    beq.s skiphalvingrevgrav ; If not, branch and skip underwater checks


    move.w (Water_level).w,d6 ; Move water level to d6


    cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level?


    bgt.s skiphalvingrevgrav ; If not, branch and skip underwater commands


    subi.w #$E,$1A(a0) ; Reduce gravity by $E ($18-$E=$A), giving the underwater effect


    skiphalvingrevgrav:



    Again, you can change the value of the reverse gravity if you like.


    So, you should have something looking like this:



    loc_1A7E8:

    move.b (Ring_spill_anim_frame).w,$22(a0)


    bsr.w MoveSprite_TestGravity2


    addi.w #$18,$1A(a0)


    tst.b (Water_flag).w ; Does the level have water?


    beq.s skiphalvingrevgrav ; If not, branch and skip underwater checks


    move.w (Water_level).w,d6 ; Move water level to d6


    cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level?


    bgt.s skiphalvingrevgrav ; If not, branch and skip underwater commands


    subi.w #$E,$1A(a0) ; Reduce gravity by $E ($18-$E=$A), giving the underwater effect


    skiphalvingrevgrav:


    bmi.s loc_1A83C


    move.b (V_int_run_count+3).w,d0


    add.b d7,d0


    andi.b #7,d0


    bne.s loc_1A83C


    tst.b 4(a0)


    bpl.s loc_1A828


    jsr (sub_FCA0).l


    tst.w d1


    bpl.s loc_1A828


    sub.w d1,$14(a0)


    move.w $1A(a0),d0


    asr.w #2,d0


    sub.w d0,$1A(a0)


    neg.w $1A(a0)



    This ensures when underwater, the gravity is not as great when it's in reverse gravity mode.


    That should be it =)
     
    Last edited by a moderator: Mar 28, 2012
  5. redhotsonic

    redhotsonic Also known as RHS Member

    Joined:
    Aug 10, 2007
    Messages:
    2,969
    Location:
    England
    Sorry for the triple post, but this is kinda important to anyone who has applied this to their hacks, so I wanted to get your attention.


    I got told a bit of advice to change the divu's to lsr's. Apparently, divu's can be almost 20x slower than lsr. So, in your hack from the guide, please change:



    divu.w #$2,d0 ; Half d0. Makes the ring's x_vel bounce to the left/right slower
    divu.w #$2,d1 ; Half d1. Makes the ring's y_vel bounce up/down slower



    to this:



    lsr.w #$1,d0 ; Half d0. Makes the ring's x_vel bounce to the left/right slower
    lsr.w #$1,d1 ; Half d1. Makes the ring's y_vel bounce up/down slower



    Notice, it's now using lsr by 1, instead of divu by 2.


    This has also been applied to all 3 guides above


    Cheers,


    redhotsonic
     
    Last edited by a moderator: Mar 27, 2012
  6. Pokepunch

    Pokepunch That guy who posts on occasion Member

    Joined:
    Aug 7, 2009
    Messages:
    270
    Location:
    UK
    I will edit my post too then.
     
  7. redhotsonic

    redhotsonic Also known as RHS Member

    Joined:
    Aug 10, 2007
    Messages:
    2,969
    Location:
    England
    Another edit, I'm afraid.


    lsr was faster than divu, but it brought a slight bug. When the rings spawn, some of them spawn directly behind another, so when collecting one, you actually get 2. I've been told, to fix this, replace:



    lsr.w #$1,d0 ; Half d0. Makes the ring's x_vel bounce to the left/right slower
    lsr.w #$1,d1 ; Half d1. Makes the ring's y_vel bounce up/down slower



    With this:



    asr.w #$1,d0 ; Half d0. Makes the ring's x_vel bounce to the left/right slower
    asr.w #$1,d1 ; Half d1. Makes the ring's y_vel bounce up/down slower



    This has also been applied to all 3 guides above

    You put lsr.w #$2 instead of lsr.w #$1. Also, you'll need to change it to asr #$1 now anyway.
     
    Last edited by a moderator: Mar 28, 2012
    Nat The Porcupine likes this.
  8. Pokepunch

    Pokepunch That guy who posts on occasion Member

    Joined:
    Aug 7, 2009
    Messages:
    270
    Location:
    UK
    Ok thanks for that.
     
  9. SpirituInsanum

    SpirituInsanum Well-Known Member Member

    Joined:
    Feb 11, 2010
    Messages:
    642
    I read this to see how I could adapt it to my optimized ring scattering routine (and the solution is so easy I'm not sure it's really relevant to post it), and noticed your code could be optimized, from a processing standpoint.


    Right now, for every ring it creates, it checks whether the created ring is above water or not, which means it has to process the whole underwater check about 30 times when you lose your rings. This could cause a slowdown, or at least it's unnecessary.


    A simple solution is to move the water check before the loop, and create a second routine only for the underwater rings. So when you check whether the ring is above or under the water, branch either to the "create rings above water" or to the "create rings under water" routines.
     
  10. redhotsonic

    redhotsonic Also known as RHS Member

    Joined:
    Aug 10, 2007
    Messages:
    2,969
    Location:
    England
    Warning: This guide is for Sonic 2, and I am using the Xenowhirl 2007 disassembly. For Sonic 1 and/or other disassemblies, it shouldn't be too hard still if you follow this guide. ALSO, REMEMBER TO BACK-UP BEFORE MAKING ANY CHANGES. I'm not held responsible for any screw-ups to your hack.


    Also: If you want your scattered rings to act if they're underwater like Sonic does, it is best to follow my underwater guide first (see first post).


    If you want to use the tables I've supplied in this guide, again, you WILL need to follow my underwater guide first (see first post)


    I bought you the underwater scattered rings guide and SpirituInsanum told us how to speed up the ring loss process. Together, we bring you "How to speed up the ring loss process with underwater".



    loc_120BA:
    _move.b #$37,0(a1) ; load obj37


    addq.b #2,routine(a1)


    move.b #8,y_radius(a1)


    move.b #8,x_radius(a1)


    move.w x_pos(a0),x_pos(a1)


    move.w y_pos(a0),y_pos(a1)


    move.l #Obj25_MapUnc_12382,mappings(a1)


    move.w #$26BC,art_tile(a1)


    bsr.w Adjust2PArtPointer2


    move.b #$84,render_flags(a1)


    move.b #3,priority(a1)


    move.b #$47,collision_flags(a1)


    move.b #8,width_pixels(a1)


    move.b #-1,(Ring_spill_anim_counter).w


    tst.w d4


    bmi.s loc_12132


    move.w d4,d0


    bsr.w JmpTo4_CalcSine


    move.w d4,d2


    lsr.w #8,d2


    asl.w d2,d0


    asl.w d2,d1


    move.w d0,d2


    move.w d1,d3


    addi.b #$10,d4


    bcc.s loc_12132


    subi.w #$80,d4


    bcc.s loc_12132


    move.w #$288,d4


    loc_12132:


    move.w d2,x_vel(a1)


    move.w d3,y_vel(a1)


    neg.w d2


    neg.w d4


    dbf d5,loc_120B2



    This is part of the scattered rings code. This code repeats itself a maximum of 32 times; each time for each ring (and like said, it will make you scatter a max of 32 rings, but still set your counter to 0). What this code does, is works out how high and how far to scatter a ring, then then whether to make it fly left or right. Once it's done that, it does it again for another ring, and then another, and then another until it either reaches your ring count, or 32. Whilst it's doing this, nothing else can be done. It's not much of a problem when there's not much about, but if you lose a lot of rings when there's so many other objects about and/or in water, it can go slow for a second.


    Anyway, This is quite big and slow, and I'm about to show you how to reduce it significantly; speeding the ring loss process, freeing the processor for other work. Instead of doing lots of calculations on the fly over and over, we will use a pre-made table to insert how high, how far and which way we want our rings to go. This will be a lot quicker as we already have the calculations done this way.


    But how do we come up with our table? Well, I'm about to show you. But first, in this guide, I will be using two pre-made tables myself. These are for a max of 20 rings above water, and 8 rings below water. Also, the ring spill underwater will be using the effect from my underwater guide above. If you're happy to do the same as me, skip ahead to step 4 (although you might want to read the first couple of steps so you know what's going on).


    Step 1 - Change the max amount of rings to spill


    In the scattered rings object, go to "loc_120A2:" and the first line you should see is this:



    moveq #$20,d0



    This is hexadecimal for 32, this is the max number of rings it will spill. Take the $ sign out (so it's not hexadecimal anymore), and then change the number to the amount of rings you want it to spill at maximum. If you want it to lose a max of 44 rings for example, then simply change the 20 to 44. If you want to change the number of max rings spill for when underwater (as things get slower underwater) we can add a check to see if underwater and if so, to change the number. Say you want to lose 20 rings over water but only 8 when in the water, you would do this:



    loc_120A2:
    moveq #20,d0 ; lose a max of 20 rings


    lea (MainCharacter).w,a2 ; a2=character


    btst #6,status(a2) ; is Sonic underwater?


    beq.s + ; if not, branch


    moveq #8,d0 ; lose a max of 8 rings when underwater


    +


    cmp.w d0,d5


    bcs.s loc_120AA


    move.w d0,d5



    Make sure the Maincharacter is loaded to a2, otherwise it may interrupt with the rest of our work or the original code.


    You DON'T have to change the max for underwater, but it is advisable, because things get very slow underwater. The more rings there are, the slower things will get, because more processing power goes in to making them bounce and etc. That's purely the only reason.


    Step 2 - Make our tables for pre-calculated figures


    go to "loc_120AA:" and change this:



    loc_120AA:
    subq.w #1,d5


    move.w #$288,d4


    bra.s loc_120BA



    to this:



    loc_120AA:
    subq.w #1,d5


    move.w #$288,d4


    lea ($FFFFAA00).l,a4 ; Load $FFFFAA00 to a4


    bra.s loc_120BA



    $FFFFAA00 is nemesis's decompression buffer's RAM. It's not used during gameplay, so we can use this. All we've done is copied the RAM address to a4.


    Then go to "loc_12132:" and change this:



    loc_12132:
    move.w d2,x_vel(a1)


    move.w d3,y_vel(a1)


    neg.w d2


    neg.w d4


    dbf d5,loc_120B2



    to this:



    loc_12132:
    move.w d2,x_vel(a1)


    move.w d3,y_vel(a1)


    neg.w d2


    neg.w d4


    move.w d2,(a4)+ ; Move d2 to a4 then increment a4 by a word


    move.w d3,(a4)+ ; Move d3 to a4 then increment a4 by a word


    dbf d5,loc_120B2



    Just added two lines here. Basically, once one ring has calculated it's x_vel and y_vel when it's going to spill out, it's copied it's values to a4, then a4 has incremented so that the next value can be added. The code repeats itself for the next ring and again, once it's got it's calculations, it's then copied to a4, and keeps doing this for all rings spilled.


    Step 3 - Build and test. Let's get our new table!


    Save, build and test. Should be no errors. Now, using your emulator (I reccomend Regen), go to the RAM viewer (Tools > RAM viewer) and go to $FFFFAA00. Load any level up. During level loading, the numbers at this RAM address will go crazy, but once the title cards have gone away, it should stop and not do anything (there will still be numbers there).


    Now, collect the max number of rings (or more) and then get hurt and lose your rings. They'll scatter everywhere as usual, but all it's values has just been moved to $FFFFAA00. Ta-dah! There's our new table! What you need to do now is open notepad and write your new values in using words. Give it a label too! Here is a guide on how to do it (mine is a max of 20 rings):



    ; ===========================================================================
    ; ---------------------------------------------------------------------------


    ; Ring Spawn Array


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


    SpillRingData: dc.w $00C4,$FC14, $FF3C,$FC14, $0238,$FCB0, $FDC8,$FCB0 ; 4


    dc.w $0350,$FDC8, $FCB0,$FDC8, $03EC,$FF3C, $FC14,$FF3C ; 8


    dc.w $03EC,$00C4, $FC14,$00C4, $0350,$0238, $FCB0,$0238 ; 12


    dc.w $0238,$0350, $FDC8,$0350, $00C4,$03EC, $FF3C,$03EC ; 16


    dc.w $0062,$FE0A, $FF9E,$FE0A, $011C,$FE58, $FEE4,$FE58 ; 20


    even


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



    It goes x_vel, y_vel, x_vel, y_vel, x_vel, y_vel, x_vel, y_vel, etc, etc.


    REMEMBER, if you're doing less rings for underwater, you need to get the max number of rings again, go underwater then get hurt. You'll get another new table. For my underwater, the max is 8. And again, I'm using the underwater scattered rings guide above. Here's a guide for 8 rings.



    ; ===========================================================================
    ; ---------------------------------------------------------------------------


    ; Ring Spawn Array Underwater


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


    SpillRingDataU: dc.w $0064,$FE08, $FF9C,$FE08, $011C,$FE58, $FEE4,$FE58 ; 4


    dc.w $01A8,$FEE4, $FE58,$FEE4, $01F8,$FF9C, $FE08,$FF9C ; 8


    even


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


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



    Notice I've changed the label for the underwater table. I've just stuck a U after it. You will need to insert these (or your own, whatever) into your ASM file. A good place to put it, find "BranchTo5_DeleteObject" and stick it after



    BranchTo5_DeleteObject
    bra.w DeleteObject



    Step 4 - Loading our new tables


    Now we've got our new tables, let's use them.


    Go back to "loc_120A2:". This is where we will make it load our new tables. The tables will load into a3. Change it to make it look like this:



    loc_120A2:
    lea SpillRingData,a3 ; load the address of the array in a3


    moveq #20,d0 ; lose a max of 20 rings


    lea (MainCharacter).w,a2 ; a2=character


    btst #6,status(a2) ; is Sonic underwater?


    beq.s + ; if not, branch


    lea SpillRingDataU,a3 ; load the UNDERWATER address of the array in a3


    moveq #8,d0 ; lose a max of 8 rings underwater


    +


    cmp.w d0,d5


    bcs.s loc_120AA


    move.w d0,d5



    Basically, it will load the overwater table and make 20 the max rings. It then checks if your underwater and if so, load the underwater table and change the max to 8. If not underwater, it will skip doing that bit and carry on with normal. REMEMBER to change the label and max rings to whatever you gave it. It only does this once every time you lose rings, it doesn't repeat itself unlike the calculations below it.


    Go to "loc_120AA:" and change this:



    loc_120AA:
    subq.w #1,d5


    move.w #$288,d4


    lea ($FFFFAA00).l,a4 ; Load $FFFFAA00 to a4


    bra.s loc_120BA



    to this:



    loc_120AA:
    subq.w #1,d5


    bra.s loc_120BA



    The Nemesis RAM bit isn't needed anymore; that was just for creating our tables. The "move.w #$288,d4" isn't needed anymore either as that's part of the calculations of spilling the rings.


    Step 5 - Using the data from our new tables


    Go to "loc_120BA:"



    loc_120BA:
    _move.b #$37,0(a1) ; load obj37


    addq.b #2,routine(a1)


    move.b #8,y_radius(a1)


    move.b #8,x_radius(a1)


    move.w x_pos(a0),x_pos(a1)


    move.w y_pos(a0),y_pos(a1)


    move.l #Obj25_MapUnc_12382,mappings(a1)


    move.w #$26BC,art_tile(a1)


    bsr.w Adjust2PArtPointer2


    move.b #$84,render_flags(a1)


    move.b #3,priority(a1)


    move.b #$47,collision_flags(a1)


    move.b #8,width_pixels(a1)


    move.b #-1,(Ring_spill_anim_counter).w


    tst.w d4 ; DELETE ME


    bmi.s loc_12132 ; DELETE ME


    move.w d4,d0 ; DELETE ME


    bsr.w JmpTo4_CalcSine ; DELETE ME


    move.w d4,d2 ; DELETE ME


    lsr.w #8,d2 ; DELETE ME


    asl.w d2,d0 ; DELETE ME


    asl.w d2,d1 ; DELETE ME


    move.w d0,d2 ; DELETE ME


    move.w d1,d3 ; DELETE ME


    addi.b #$10,d4 ; DELETE ME


    bcc.s loc_12132 ; DELETE ME


    subi.w #$80,d4 ; DELETE ME


    bcc.s loc_12132 ; DELETE ME


    move.w #$288,d4 ; DELETE ME


    loc_12132: ; DELETE ME


    move.w d2,x_vel(a1) ; DELETE ME


    move.w d3,y_vel(a1) ; DELETE ME


    neg.w d2 ; DELETE ME


    neg.w d4 ; DELETE ME


    move.w d2,(a4)+ ; DELETE ME ; Move d2 to a4 then increment a4 by a word


    move.w d3,(a4)+ ; DELETE ME ; Move d3 to a4 then increment a4 by a word


    dbf d5,loc_120B2



    See where it says "DELETE ME"? Do what it says. These are not needed anymore! This is what slows the ring loss down! This calculates the ring loss speeds and etc. Instead, we using our tables. Now, where you just deleted that table, insert this instead:



    move.w (a3)+,x_vel(a1) ; move the data contained in the array to the x velocity and increment the address in a3
    move.w (a3)+,y_vel(a1) ; move the data contained in the array to the y velocity and increment the address in a3



    So, you have something looking like this:



    loc_120BA:
    _move.b #$37,0(a1) ; load obj37


    addq.b #2,routine(a1)


    move.b #8,y_radius(a1)


    move.b #8,x_radius(a1)


    move.w x_pos(a0),x_pos(a1)


    move.w y_pos(a0),y_pos(a1)


    move.l #Obj25_MapUnc_12382,mappings(a1)


    move.w #$26BC,art_tile(a1)


    bsr.w Adjust2PArtPointer2 ; This is only needed for two player


    move.b #$84,render_flags(a1)


    move.b #3,priority(a1)


    move.b #$47,collision_flags(a1)


    move.b #8,width_pixels(a1)


    move.b #-1,(Ring_spill_anim_counter).w


    move.w (a3)+,x_vel(a1) ; move the data contained in the array to the x velocity and increment the address in a3


    move.w (a3)+,y_vel(a1) ; move the data contained in the array to the y velocity and increment the address in a3


    dbf d5,loc_120B2



    ALL DONE! Phew! Let me explain though. What it does now, is load a word from a3 (the table) and inserts it into x_vel, and then increment a3 by a word. Then it loads a word from a3 again (which has been incremented, so it won't be the same word) and inserts it into y_vel, then increment a3 again. Now one ring has it's speeds. It now knows how high to jump, how far to jump and which way to go, a hell of a lot quicker then doing all them calculations!


    If you have used my tables I've supplied, then when out of water, you can only lose a max of 20 rings, and when in water, you can only lose a max of 8 rings, and it will act if like they're in water.


    The ring loss process will now be much faster! And if in water, it shouldn't slow down (as much)


    Any questions/comments/etc, reply!


    Enjoy!


    Credits


    SpirituInsanum - For the idea and speed loss patterns guide. Also for helping me come up with this guide.


    Me - For underwater scattered rings.


    EDIT: Here is a ROM of Sonic 2 with this guide and my underwater guide combined.
     
    Last edited by a moderator: Apr 30, 2012
    Nat The Porcupine likes this.
Thread Status:
Not open for further replies.