How to fix timers on Scattered rings (S1, S2 & S3K)

Discussion in 'Tutorials Archive' started by redhotsonic, Sep 23, 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 discovered a bug in Sonic 2 Recreation with the Scattered Rings object, and yet again, it exists in all Sonic 1, Sonic 2 and Sonic 3 and Knuckles! So, I made a fix.


    In this guide

    • Bug fix for Sonic 1 SVN Disassembly
    • Bug fix for Sonic 2 XenoWhirl's Disassembly
    • Bug fix for Sonic 2 SVN Disassembly
    • Bug fix for Sonic 3 and Knuckles SVN Disassembly








    The Bug and the fix


    [​IMG]


    When you get hurt, you lose rings (duh). All the rings have a timer of $FF which is set to a RAM address. All the scattered rings read from this RAM address. Each frame, that timer counts down by #1. When the timer finally reaches 0, the scattered rings delete themselves. This seems fair enough.


    Now, imagine you collect some rings, but get hurt again:


    [​IMG]


    Some more scattered rings are created. The thing is, these scattered rings set the RAM timer to $FF again, so they can count down. Fine, but them rings you loss earlier, if they haven't been deleted yet, their timer is also set to $FF.


    So, for example; you lose rings and the timer sets to $FF. Some frames later, that timer is on $10, so those rings are going to be deleted any moment now, but you get hurt again and lose more rings. Those scattered rings that were about to be deleted, are now going to stay a lot longer.


    And then if you have badniks explode with a ring coming out instead of an animal like I do, this will also interrupt with the RAM timer (depends on your coding, this is not included in the guide).


    [​IMG]


    Sonic 2 Recreation - showing a ring coming out of a badnik


    That's not meant to happen. What we want is the rings we're about to be deleted to keep their original timer! So when we lose more rings, the old scattered rings will still have their own timer. Let's fix this!


    Sonic 1 fix - SVN Disassembly


    First, open your "25 & 37 Rings.asm" file and go to "@makerings:" label. Find this line:



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



    This is where the RAM timer is set. Although, setting the RAM here is a waste of time. Depending on how many rings are being scattered, this is how many times it's being written. Example, if you lose 10 rings, it writes #-1 to this RAM 10 times. Why? It only needs to be written once. So we'll move this. For now, delete this line.


    Next, go to the "@resetcounter:" label. Find this line:



    sfx sfx_RingLoss ; play ring loss sound



    Just before it, add this:



    moveq #-1,d0 ; Move #-1 to d0
    move.b d0,obDelayAni(a0) ; Move d0 to new timer


    move.b d0,(v_ani3_time).w ; Move d0 to old timer (for animated purposes)



    This is where we're setting our timer. "obDelayAni(a0)" is our brand new timer. This means each scattered ring will now have its own personal timer. We must set the old timer still, for animated purposes. Without setting the old timer, the rings won't spin. Anyway, setting the timer here, it will only be written once; saving a bit of time when creating the rings.


    So, our new timer has been set to $FF. We still need to make it count down. So, find the "@chkdel:" label. See this?



    tst.b (v_ani3_time).w
    beq.s RLoss_Delete



    You may delete it! No longer needed, as we're not using this as the main timer anymore. Instead, replace it with this:



    subq.b #1,obDelayAni(a0) ; Subtract 1
    beq.w DeleteObject ; If 0, delete



    Now, the new personal timer will be subtracted every frame. Once it reaches 0, it will delete itself. This timer cannot be interrupted when you lose more rings, so these will delete themselves when they're supposed to!


    Done


    Sonic 2 fix - XenoWhirl's Disassembly


    First, open your ASM file and go to "loc_120BA:" label. Find this line:



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



    This is where the RAM timer is set. Although, setting the RAM here is a waste of time. Depending on how many rings are being scattered, this is how many times it's being written. Example, if you lose 10 rings, it writes #-1 to this RAM 10 times. Why? It only needs to be written once. So we'll move this. For now, delete this line.


    Next, go to the "loc_12142:" label. Find this line:



    move.w #$C6,d0
    jsr (PlaySoundStereo).l



    Just before it, add this:



    moveq #-1,d0 ; Move #-1 to d0
    move.b d0,objoff_1F(a0) ; Move d0 to new timer


    move.b d0,(Ring_spill_anim_counter).w ; Move d0 to old timer (for animated purposes)



    This is where we're setting our timer. "objoff_1F(a0)" is our brand new timer. This means each scattered ring will now have its own personal timer. We must set the old timer still, for animated purposes. Without setting the old timer, the rings won't spin. Anyway, setting the timer here, it will only be written once; saving a bit of time when creating the rings.


    So, our new timer has been set to $FF. We still need to make it count down. So, find the "loc_121B8:" label. See this?



    tst.b (Ring_spill_anim_counter).w
    beq.s BranchTo5_DeleteObject



    You may delete it! No longer needed, as we're not using this as the main timer anymore. Instead, replace it with this:



    subq.b #1,objoff_1F(a0) ; Subtract 1
    beq.w DeleteObject ; If 0, delete



    Now, the new personal timer will be subtracted every frame. Once it reaches 0, it will delete itself. This timer cannot be interrupted when you lose more rings, so these will delete themselves when they're supposed to!


    Done


    Sonic 2 fix - SVN Disassembly


    First, open your ASM file and go to "Obj37_Init:" label. On the 3rd + label, find this line:



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



    This is where the RAM timer is set. Although, setting the RAM here is a waste of time. Depending on how many rings are being scattered, this is how many times it's being written. Example, if you lose 10 rings, it writes #-1 to this RAM 10 times. Why? It only needs to be written once. So we'll move this. For now, delete this line.


    From the "Obj37_Init:", find the 5th + label. Find this line:



    move.w #SndID_RingSpill,d0
    jsr (PlaySoundStereo).l



    Just before it, add this:



    moveq #-1,d0 ; Move #-1 to d0
    move.b d0,objoff_1F(a0) ; Move d0 to new timer


    move.b d0,(Ring_spill_anim_counter).w ; Move d0 to old timer (for animated purposes)



    This is where we're setting our timer. "objoff_1F(a0)" is our brand new timer. This means each scattered ring will now have its own personal timer. We must set the old timer still, for animated purposes. Without setting the old timer, the rings won't spin. Anyway, setting the timer here, it will only be written once; saving a bit of time when creating the rings.


    So, our new timer has been set to $FF. We still need to make it count down. So, find the "loc_121B8:" label. See this?



    tst.b (Ring_spill_anim_counter).w
    beq.s Obj37_Delete



    You may delete it! No longer needed, as we're not using this as the main timer anymore. Instead, replace it with this:



    subq.b #1,objoff_1F(a0) ; Subtract 1
    beq.w DeleteObject ; If 0, delete



    Now, the new personal timer will be subtracted every frame. Once it reaches 0, it will delete itself. This timer cannot be interrupted when you lose more rings, so these will delete themselves when they're supposed to!


    Done


    Sonic 3 and Knuckles fix - SVN Disassembly


    First, open your ASM file and go to "loc_1A6B6:" label. Find this line:



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



    This is where the RAM timer is set. Although, setting the RAM here is a waste of time. Depending on how many rings are being scattered, this is how many times it's being written. Example, if you lose 10 rings, it writes #-1 to this RAM 10 times. Why? It only needs to be written once. So we'll move this. For now, delete this line.


    Next, go to the "loc_1A738:" label. Find this line:



    move.w #$FFB9,d0
    jsr (Play_Sound_2).l



    Just before it, add this:



    moveq #-1,d0 ; Move #-1 to d0
    move.b d0,height_pixels(a0) ; Move d0 to new timer


    move.b d0,(Ring_spill_anim_counter).w ; Move d0 to old timer (for animated purposes)



    This is where we're setting our timer. "anim_frame_timer(a0)" is our brand new timer. This means each scattered ring will now have its own personal timer. We must set the old timer still, for animated purposes. Without setting the old timer, the rings won't spin. Anyway, setting the timer here, it will only be written once; saving a bit of time when creating the rings.


    So, our new timer has been set to $FF. We still need to make it count down. So, find the "loc_1A79C:" label. See this?



    tst.b (Ring_spill_anim_counter).w
    beq.s loc_1A7E4



    You may delete it! No longer needed, as we're not using this as the main timer anymore. Instead, replace it with this:



    subq.b #1,height_pixels(a0) ; Subtract 1
    beq.w Delete_Current_Sprite ; If 0, delete



    Now, the new personal timer will be subtracted every frame. Once it reaches 0, it will delete itself. This timer cannot be interrupted when you lose more rings, so these will delete themselves when they're supposed to!


    WAIT!


    Unlike the other games, you have to do one more thing to Sonic 3 and Knuckles. You know the lightning shield? When you have rings attracting to you, and you get hurt and lose the shield, the rings that were being attracted are turned into scattered rings. We need to set the new timer for this.


    Go to "loc_1A88C:" and find:



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



    and just above or below this line, add this line:



    move.b #-1,height_pixels(a0) ; Move #$FF to new timer



    WAIT!


    There's one more. Again, this is S3K only. You know the Egg Prisons you can jump on in Flying Battery? Some of them release rings, which are scattered rings again. They need their own timer. So, go to "loc_89D44:" and find the line:



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



    and just above or below this line, add this line:



    move.b #-1,height_pixels(a0) ; Move #$FF to new timer



    NOTE: I do not hack S3K, but I am sure that the height_pixels SST is free for scattered rings. After trying this myself, I see no problems. If there are errors, let me know.


    Done


    Ta-dah!
     
    Nat The Porcupine likes this.
Thread Status:
Not open for further replies.