Actions

SCHG How-to

Difference between revisions of "Speed Up Ring Loss Process (With Underwater)"

From Sonic Retro

m (adding link to Sonic 2 Recreation, a few other minor formatting/linking things)
m (Text replacement - "\[\[Category:SCHG How-tos.*" to "")
 
(8 intermediate revisions by 4 users not shown)
Line 1: Line 1:
''Guide written by [[redhotsonic]]. Also, special thanks to SpirituInsanum for his guide at [[Sonic Stuff Research Group|SSRG]].''
+
''Guide written by [[User:Redhotsonic|redhotsonic]]. Also, special thanks to SpirituInsanum for his guide at [[Sonic Stuff Research Group|SSRG]].''
  
'''NOTE''' - This guide is written for [[Sonic the Hedgehog 2 (16-bit)|''Sonic 2'']], using [[Xenowhirl]]’s [[Disassemblies#Revision_01|2007 disassembly]]. For [[Sonic the Hedgehog 2 (16-bit)|''Sonic 1'']], and/or other disassemblies, it shouldn't be too hard still if you follow this guide. Also, before using this guide, you may want to try the following guide first: [[SCHG_How-to:Fix_Scattered_Rings_Underwater_Physics#Sonic_2_fix|Underwater Physics Fix (Sonic 2)]]
+
'''NOTE''' - This guide is written for [[Sonic the Hedgehog 2 (16-bit)|''Sonic 2'']], using [[User:Xenowhirl|Xenowhirl]]’s [[Disassemblies#Revision_01|2007 disassembly]]. For [[Sonic the Hedgehog (16-bit)|''Sonic 1'']], and/or other disassemblies, it shouldn't be too hard still if you follow this guide. Also, before using this guide, you may want to try the following guide first: [[SCHG_How-to:Fix_Scattered_Rings_Underwater_Physics#Sonic_2_fix|Underwater Physics Fix (Sonic 2)]]
  
 
Now, after following the above guide, your rings should behave properly underwater. Now to fix another problem. When losing rings (especially when underwater...) it can cause considerable lag. This guide will now walk you through removing this lag, and speeding up the ring loss process considerably.
 
Now, after following the above guide, your rings should behave properly underwater. Now to fix another problem. When losing rings (especially when underwater...) it can cause considerable lag. This guide will now walk you through removing this lag, and speeding up the ring loss process considerably.
Line 9: Line 9:
 
The following code below is part of the scattered rings code. This code repeats itself a maximum of 32 times; each time for each ring (As 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 another... up to 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 while you are in the water, it can noticeably lag. Anyway, look below:
 
The following code below is part of the scattered rings code. This code repeats itself a maximum of 32 times; each time for each ring (As 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 another... up to 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 while you are in the water, it can noticeably lag. Anyway, look below:
  
<asm>
+
<syntaxhighlight lang="asm">
 
loc_120BA:
 
loc_120BA:
 
         _move.b #$37,0(a1) ; load obj37
 
         _move.b #$37,0(a1) ; load obj37
Line 48: Line 48:
 
         neg.w  d4
 
         neg.w  d4
 
         dbf    d5,loc_120B2
 
         dbf    d5,loc_120B2
</asm>
+
</syntaxhighlight>
  
 
Anyway, This is quite big and slow, and now, let's show you how to reduce it significantly; speeding the ring loss process, thus 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 other guide. If you're happy to do the same as me, you may skip ahead to step 4 (although you might want to read the first couple of steps so you know what's going on).
 
Anyway, This is quite big and slow, and now, let's show you how to reduce it significantly; speeding the ring loss process, thus 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 other guide. If you're happy to do the same as me, you may skip ahead to step 4 (although you might want to read the first couple of steps so you know what's going on).
Line 56: Line 56:
  
 
In the scattered rings object, go to "loc_120A2:" and the first line you should see is this:
 
In the scattered rings object, go to "loc_120A2:" and the first line you should see is this:
<asm>
+
<syntaxhighlight lang="asm">
 
         moveq  #$20,d0
 
         moveq  #$20,d0
</asm>
+
</syntaxhighlight>
  
 
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:
 
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:
  
<asm>
+
<syntaxhighlight lang="asm">
 
loc_120A2:
 
loc_120A2:
 
         moveq  #20,d0                  ; lose a max of 20 rings
 
         moveq  #20,d0                  ; lose a max of 20 rings
Line 73: Line 73:
 
         bcs.s  loc_120AA
 
         bcs.s  loc_120AA
 
         move.w  d0,d5
 
         move.w  d0,d5
</asm>
+
</syntaxhighlight>
  
 
Make sure MainCharacter is loaded to a2, otherwise it may interrupt with the rest of our work or the original code.
 
Make sure MainCharacter is loaded to a2, otherwise it may interrupt with the rest of our work or the original code.
Line 83: Line 83:
  
 
Go to "loc_120AA:" and change this:
 
Go to "loc_120AA:" and change this:
<asm>
+
<syntaxhighlight lang="asm">
 
loc_120AA:
 
loc_120AA:
 
         subq.w  #1,d5
 
         subq.w  #1,d5
 
         move.w  #$288,d4
 
         move.w  #$288,d4
 
         bra.s  loc_120BA
 
         bra.s  loc_120BA
</asm>
+
</syntaxhighlight>
  
 
to this:
 
to this:
<asm>
+
<syntaxhighlight lang="asm">
 
loc_120AA:
 
loc_120AA:
 
         subq.w  #1,d5
 
         subq.w  #1,d5
Line 97: Line 97:
 
         lea ($FFFFAA00).l,a4    ; Load $FFFFAA00 to a4
 
         lea ($FFFFAA00).l,a4    ; Load $FFFFAA00 to a4
 
         bra.s  loc_120BA
 
         bra.s  loc_120BA
</asm>
+
</syntaxhighlight>
  
 
$FFFFAA00 is Nemesis' 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.
 
$FFFFAA00 is Nemesis' 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:
 
Then go to "loc_12132:" and change this:
<asm>
+
<syntaxhighlight lang="asm">
 
loc_12132:
 
loc_12132:
 
         move.w  d2,x_vel(a1)
 
         move.w  d2,x_vel(a1)
Line 108: Line 108:
 
         neg.w  d4
 
         neg.w  d4
 
         dbf    d5,loc_120B2
 
         dbf    d5,loc_120B2
</asm>
+
</syntaxhighlight>
  
 
to this:
 
to this:
<asm>
+
<syntaxhighlight lang="asm">
 
loc_12132:
 
loc_12132:
 
         move.w  d2,x_vel(a1)
 
         move.w  d2,x_vel(a1)
Line 120: Line 120:
 
         move.w  d3,(a4)+        ; Move d3 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
 
         dbf    d5,loc_120B2
</asm>
+
</syntaxhighlight>
  
 
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.
 
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.
Line 131: Line 131:
 
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):
 
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):
  
<asm>
+
<syntaxhighlight lang="asm">
 
; ===========================================================================
 
; ===========================================================================
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
Line 144: Line 144:
 
                 even
 
                 even
 
; ===========================================================================
 
; ===========================================================================
</asm>
+
</syntaxhighlight>
  
 
It goes x_vel, y_vel, x_vel, y_vel, x_vel, y_vel, etc...
 
It goes x_vel, y_vel, x_vel, y_vel, x_vel, y_vel, etc...
Line 150: Line 150:
  
 
'''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. Here's a guide for 8 rings.
 
'''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. Here's a guide for 8 rings.
<asm>
+
<syntaxhighlight lang="asm">
 
; ===========================================================================
 
; ===========================================================================
 
; ---------------------------------------------------------------------------
 
; ---------------------------------------------------------------------------
Line 161: Line 161:
 
; ===========================================================================
 
; ===========================================================================
 
; ===========================================================================
 
; ===========================================================================
</asm>
+
</syntaxhighlight>
  
 
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
 
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
<asm>
+
<syntaxhighlight lang="asm">
 
BranchTo5_DeleteObject  
 
BranchTo5_DeleteObject  
 
         bra.w  DeleteObject
 
         bra.w  DeleteObject
</asm>
+
</syntaxhighlight>
  
 
===Step 4===
 
===Step 4===
Line 174: Line 174:
 
Now that we've got our new tables, let's use them.
 
Now that 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:
 
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:
<asm>
+
<syntaxhighlight lang="asm">
 
loc_120A2:
 
loc_120A2:
 
         lea    SpillRingData,a3        ; load the address of the array in a3
 
         lea    SpillRingData,a3        ; load the address of the array in a3
Line 187: Line 187:
 
         bcs.s  loc_120AA
 
         bcs.s  loc_120AA
 
         move.w  d0,d5
 
         move.w  d0,d5
</asm>
+
</syntaxhighlight>
  
 
Basically, it will load the above water 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.
 
Basically, it will load the above water 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.
  
 
Now go to "loc_120AA:" and change this:
 
Now go to "loc_120AA:" and change this:
<asm>
+
<syntaxhighlight lang="asm">
 
loc_120AA:
 
loc_120AA:
 
         subq.w  #1,d5
 
         subq.w  #1,d5
Line 198: Line 198:
 
         lea ($FFFFAA00).l,a4    ; Load $FFFFAA00 to a4
 
         lea ($FFFFAA00).l,a4    ; Load $FFFFAA00 to a4
 
         bra.s  loc_120BA
 
         bra.s  loc_120BA
</asm>
+
</syntaxhighlight>
  
 
to this:
 
to this:
<asm>
+
<syntaxhighlight lang="asm">
 
loc_120AA:
 
loc_120AA:
 
         subq.w  #1,d5
 
         subq.w  #1,d5
 
         bra.s  loc_120BA
 
         bra.s  loc_120BA
</asm>
+
</syntaxhighlight>
  
 
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.
 
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.
Line 213: Line 213:
  
 
Go to "loc_120BA:"
 
Go to "loc_120BA:"
<asm>
+
<syntaxhighlight lang="asm">
 
loc_120BA:
 
loc_120BA:
 
         _move.b #$37,0(a1) ; load obj37
 
         _move.b #$37,0(a1) ; load obj37
Line 253: Line 253:
 
         move.w  d3,(a4)+        ; DELETE ME    ; Move d3 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
 
         dbf    d5,loc_120B2
</asm>
+
</syntaxhighlight>
  
 
See where it says '''"DELETE ME"'''? Do what it says. These are not needed anymore, in fact, they slow 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:
 
See where it says '''"DELETE ME"'''? Do what it says. These are not needed anymore, in fact, they slow 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:
  
<asm>
+
<syntaxhighlight lang="asm">
 
       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)+,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
 
       move.w  (a3)+,y_vel(a1)        ; move the data contained in the array to the y velocity and increment the address in a3
</asm>
+
</syntaxhighlight>
  
 
So, you have something looking like this:
 
So, you have something looking like this:
<asm>
+
<syntaxhighlight lang="asm">
 
loc_120BA:
 
loc_120BA:
 
         _move.b #$37,0(a1) ; load obj37
 
         _move.b #$37,0(a1) ; load obj37
Line 282: Line 282:
 
         move.w  (a3)+,y_vel(a1)        ; move the data contained in the array to the y 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
 
         dbf    d5,loc_120B2
</asm>
+
</syntaxhighlight>
  
 
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 like they're in water. Feel free to change these both back to 32... or to whatever you like.
 
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 like they're in water. Feel free to change these both back to 32... or to whatever you like.
Line 292: Line 292:
  
 
Go to "loc_120BA:" (part of the scattered rings object) and delete this line:
 
Go to "loc_120BA:" (part of the scattered rings object) and delete this line:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.b  #3,priority(a1)
 
         move.b  #3,priority(a1)
</asm>
+
</syntaxhighlight>
  
 
When the rings are being created, it won't have to move 3 to it's priority anymore, saving a command per ring created and slightly speeding it up (you'll probably won't notice any difference, but this isn't what I am trying to accomplish here).
 
When the rings are being created, it won't have to move 3 to it's priority anymore, saving a command per ring created and slightly speeding it up (you'll probably won't notice any difference, but this isn't what I am trying to accomplish here).
Line 301: Line 301:
  
 
Go to "loc_121B8" and you should see a command:
 
Go to "loc_121B8" and you should see a command:
<asm>
+
<syntaxhighlight lang="asm">
 
         bra.w  DisplaySprite
 
         bra.w  DisplaySprite
</asm>
+
</syntaxhighlight>
  
 
We are going to replace this with a new code, similar to how S3K does it. In S2, before it can display the sprite, it converts the object's priority into a word, and then displays it. It does calculations that are about 3 lines long to convert it into a word. When lots of objects use this code every single frame (Sonic and Tails constantly for example), it can be a slow process.
 
We are going to replace this with a new code, similar to how S3K does it. In S2, before it can display the sprite, it converts the object's priority into a word, and then displays it. It does calculations that are about 3 lines long to convert it into a word. When lots of objects use this code every single frame (Sonic and Tails constantly for example), it can be a slow process.
Line 312: Line 312:
  
 
Priority = 3 (byte)
 
Priority = 3 (byte)
<asm>
+
<syntaxhighlight lang="asm">
 
DisplaySprite:
 
DisplaySprite:
 
         lea    (Sprite_Table_Input).w,a1
 
         lea    (Sprite_Table_Input).w,a1
Line 327: Line 327:
 
return_16510:
 
return_16510:
 
         rts
 
         rts
</asm>
+
</syntaxhighlight>
  
 
Now, Priority = $180 (word)
 
Now, Priority = $180 (word)
Line 334: Line 334:
  
 
'''WAS:'''
 
'''WAS:'''
<asm>
+
<syntaxhighlight lang="asm">
 
loc_121B8:
 
loc_121B8:
  
Line 344: Line 344:
 
         bcs.s  BranchTo5_DeleteObject
 
         bcs.s  BranchTo5_DeleteObject
 
         bra.w  DisplaySprite
 
         bra.w  DisplaySprite
</asm>
+
</syntaxhighlight>
  
 
'''''And change that to this:'''''
 
'''''And change that to this:'''''
<asm>
+
<syntaxhighlight lang="asm">
 
loc_121B8:
 
loc_121B8:
  
Line 356: Line 356:
 
         cmp.w  y_pos(a0),d0
 
         cmp.w  y_pos(a0),d0
 
         bcs.s  BranchTo5_DeleteObject
 
         bcs.s  BranchTo5_DeleteObject
         lea    (Sprite_Table_Input).w,a1
+
         lea    (Sprite_Table_Input).w,a1       ; To be changed
 
         move.w  priority(a0),d0                ; To be deleted
 
         move.w  priority(a0),d0                ; To be deleted
 
         lsr.w  #1,d0                          ; To be deleted
 
         lsr.w  #1,d0                          ; To be deleted
 
         andi.w  #$380,d0                        ; To be deleted
 
         andi.w  #$380,d0                        ; To be deleted
         adda.w  d0,a1                          ; To be changed
+
         adda.w  d0,a1                          ; To be deleted
 
         cmpi.w  #$7E,(a1)
 
         cmpi.w  #$7E,(a1)
 
         bcc.s  +
 
         bcc.s  +
Line 368: Line 368:
 
+
 
+
 
         rts
 
         rts
</asm>
+
</syntaxhighlight>
  
 
'''"To be changed"''' means that line is going to be edited and the '''"To be deleted"''', well, guess what we will do with that? =P
 
'''"To be changed"''' means that line is going to be edited and the '''"To be deleted"''', well, guess what we will do with that? =P
  
 
Change
 
Change
<asm>
+
<syntaxhighlight lang="asm">
         adda.w d0,a1                           ; To be changed
+
         lea    (Sprite_Table_Input).w,a1       ; To be changed
</asm>
+
</syntaxhighlight>
  
 
To this:
 
To this:
<asm>
+
<syntaxhighlight lang="asm">
        adda.w  #$180,a1
+
      lea    Sprite_Table_Input+$180,a1
</asm>
+
</syntaxhighlight>
  
As we now know 3 would equal $180, we can just add $180 straight to a1. No more calculations every single frame, we've already done it!
+
As we now know 3 would equal $180, we can just add straight to a1. No more calculations every single frame, we've already done it!
 
You should have something like this now:
 
You should have something like this now:
<asm>
+
<syntaxhighlight lang="asm">
 
loc_121B8:
 
loc_121B8:
  
Line 393: Line 393:
 
         cmp.w  y_pos(a0),d0
 
         cmp.w  y_pos(a0),d0
 
         bcs.s  BranchTo5_DeleteObject
 
         bcs.s  BranchTo5_DeleteObject
         lea    (Sprite_Table_Input).w,a1
+
         lea    Sprite_Table_Input+$180,a1
        adda.w  #$180,a1
 
 
         cmpi.w  #$7E,(a1)
 
         cmpi.w  #$7E,(a1)
 
         bcc.s  +
 
         bcc.s  +
Line 402: Line 401:
 
+
 
+
 
         rts
 
         rts
</asm>
+
</syntaxhighlight>
  
 
So, what have we done? Well, we saved a line of it branching somewhere for a start, it's already there, so that's a plus! Also, we're moving $180 straight to a1, rather than doing them 3 lines of calculations! What a time saver! ALL done. That was easy, eh? Now your scattered rings will be even quicker, hopefully not slowing anything else down!
 
So, what have we done? Well, we saved a line of it branching somewhere for a start, it's already there, so that's a plus! Also, we're moving $180 straight to a1, rather than doing them 3 lines of calculations! What a time saver! ALL done. That was easy, eh? Now your scattered rings will be even quicker, hopefully not slowing anything else down!
Line 410: Line 409:
  
 
If you look at the code you just edited:
 
If you look at the code you just edited:
<asm>
+
<syntaxhighlight lang="asm">
 
Obj_37_sub_4:
 
Obj_37_sub_4:
 
         addq.b  #2,routine(a0)
 
         addq.b  #2,routine(a0)
Line 425: Line 424:
 
BranchTo5_DeleteObject  
 
BranchTo5_DeleteObject  
 
         bra.w  DeleteObject
 
         bra.w  DeleteObject
</asm>
+
</syntaxhighlight>
  
 
You can see a command move 1 to priority, and displaying it again. This is for when you collect the rings (when the rings turn into them sparkly effects). You can do something extremely similar here if you like. It will make quite a bit of a difference if you collect a lot of scattered rings at the same time, otherwise, it won't do too much. If you want it to do the same, then you can. But it won't be $180 again! When 1 has been through DisplaySprite's calculations, it will equal $80 instead!
 
You can see a command move 1 to priority, and displaying it again. This is for when you collect the rings (when the rings turn into them sparkly effects). You can do something extremely similar here if you like. It will make quite a bit of a difference if you collect a lot of scattered rings at the same time, otherwise, it won't do too much. If you want it to do the same, then you can. But it won't be $180 again! When 1 has been through DisplaySprite's calculations, it will equal $80 instead!
  
 
So, at "Obj_37_sub_4:", delete this line/command:
 
So, at "Obj_37_sub_4:", delete this line/command:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.b  #1,priority(a0)
 
         move.b  #1,priority(a0)
</asm>
+
</syntaxhighlight>
  
 
Then, at "Obj_37_sub_6:", replace:
 
Then, at "Obj_37_sub_6:", replace:
<asm>
+
<syntaxhighlight lang="asm">
 
         bra.w  DisplaySprite
 
         bra.w  DisplaySprite
</asm>
+
</syntaxhighlight>
  
 
with this:
 
with this:
<asm>
+
<syntaxhighlight lang="asm">
 
         lea    (Sprite_Table_Input).w,a1
 
         lea    (Sprite_Table_Input).w,a1
 
         adda.w  #$80,a1
 
         adda.w  #$80,a1
Line 450: Line 449:
 
+
 
+
 
         rts
 
         rts
</asm>
+
</syntaxhighlight>
  
 
So you have something like this:
 
So you have something like this:
<asm>
+
<syntaxhighlight lang="asm">
 
Obj_37_sub_4:
 
Obj_37_sub_4:
 
         addq.b  #2,routine(a0)
 
         addq.b  #2,routine(a0)
Line 471: Line 470:
 
+
 
+
 
         rts
 
         rts
</asm>
+
</syntaxhighlight>
  
 
Done: you've made it slightly faster again!
 
Done: you've made it slightly faster again!
  
 
{{S2Howtos}}
 
{{S2Howtos}}
[[Category:SCHG How-tos|Speed Up Ring Loss Process (With Underwater)]]
+
|Speed Up Ring Loss Process (With Underwater)]]

Latest revision as of 11:10, 25 August 2018

Guide written by redhotsonic. Also, special thanks to SpirituInsanum for his guide at SSRG.

NOTE - This guide is written for Sonic 2, using Xenowhirl’s 2007 disassembly. For Sonic 1, and/or other disassemblies, it shouldn't be too hard still if you follow this guide. Also, before using this guide, you may want to try the following guide first: Underwater Physics Fix (Sonic 2)

Now, after following the above guide, your rings should behave properly underwater. Now to fix another problem. When losing rings (especially when underwater...) it can cause considerable lag. This guide will now walk you through removing this lag, and speeding up the ring loss process considerably.

Part 1

The following code below is part of the scattered rings code. This code repeats itself a maximum of 32 times; each time for each ring (As 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 another... up to 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 while you are in the water, it can noticeably lag. Anyway, look below:

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

Anyway, This is quite big and slow, and now, let's show you how to reduce it significantly; speeding the ring loss process, thus 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 other guide. If you're happy to do the same as me, you may 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 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' 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, go to the 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, 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. 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 that 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 above water 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.

Now 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, in fact, they slow 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 like they're in water. Feel free to change these both back to 32... or to whatever you like.

The ring loss process will now be much faster! And if in water, it shouldn't slow down (as much). To continue speeding up the process EVEN MORE, continue to Part 2, below.

Part 2

Ok, so I ported the S3&K priority manager into my hack [Sonic 2 Recreation], and you can make the scattered rings object do something extremely similar. So this is only making one object do it, but because of the amount of rings you can lose, this actually makes a BIG difference (as usual, especially underwater).

Go to "loc_120BA:" (part of the scattered rings object) and delete this line:

        move.b  #3,priority(a1)

When the rings are being created, it won't have to move 3 to it's priority anymore, saving a command per ring created and slightly speeding it up (you'll probably won't notice any difference, but this isn't what I am trying to accomplish here).

Now you're probably thinking "WTF! The rings aren't going to be displayed now!" Well, in a way, you're wrong. It will still be displayed, but with a priority of 0. Now, we do not want that, we still want it to be 3, and we're about to fix it, and this will be what speeds it up dramatically.

Go to "loc_121B8" and you should see a command:

        bra.w   DisplaySprite

We are going to replace this with a new code, similar to how S3K does it. In S2, before it can display the sprite, it converts the object's priority into a word, and then displays it. It does calculations that are about 3 lines long to convert it into a word. When lots of objects use this code every single frame (Sonic and Tails constantly for example), it can be a slow process.

Now, imagine you just got hurt and lost 32 rings, each one of them 32 rings branches to DisplaySprite, does the calculations, then displays the sprite; every single frame! All 32 of them! This slows it down quite a bit! Now, you can't just turn the scattered ring's priority into a word, otherwise it will over-write the scattered rings's "width_pixel". So, what do we do? Well, we can just copy part of the DisplaySprite's coding and insert it into the scattered rings' object coding.

Now, normally, after the rings have jumped to DisplaySprite to convert it's priority into a word, it becomes $180. Look at this:

Priority = 3 (byte)

DisplaySprite:
        lea     (Sprite_Table_Input).w,a1
        move.w  priority(a0),d0                 ; To be deleted
        lsr.w   #1,d0                           ; To be deleted
        andi.w  #$380,d0                        ; To be deleted
        adda.w  d0,a1                           ; To be changed
        cmpi.w  #$7E,(a1)
        bcc.s   return_16510
        addq.w  #2,(a1)
        adda.w  (a1),a1
        move.w  a0,(a1)

return_16510:
        rts

Now, Priority = $180 (word)

So, we're going to copy this code, then edit it a bit, then move it to the scattered rings object. So copy it, then move it to where the branch was in the scattered rings object. So you have something looking like this:

WAS:

loc_121B8:

        tst.b   (Ring_spill_anim_counter).w
        beq.s   BranchTo5_DeleteObject
        move.w  (Camera_Max_Y_pos_now).w,d0
        addi.w  #$E0,d0
        cmp.w   y_pos(a0),d0
        bcs.s   BranchTo5_DeleteObject
        bra.w   DisplaySprite

And change that to this:

loc_121B8:

        tst.b   (Ring_spill_anim_counter).w
        beq.s   BranchTo5_DeleteObject
        move.w  (Camera_Max_Y_pos_now).w,d0
        addi.w  #$E0,d0
        cmp.w   y_pos(a0),d0
        bcs.s   BranchTo5_DeleteObject
        lea     (Sprite_Table_Input).w,a1       ; To be changed
        move.w  priority(a0),d0                 ; To be deleted
        lsr.w   #1,d0                           ; To be deleted
        andi.w  #$380,d0                        ; To be deleted
        adda.w  d0,a1                           ; To be deleted
        cmpi.w  #$7E,(a1)
        bcc.s   +
        addq.w  #2,(a1)
        adda.w  (a1),a1
        move.w  a0,(a1)
+
        rts

"To be changed" means that line is going to be edited and the "To be deleted", well, guess what we will do with that? =P

Change

        lea     (Sprite_Table_Input).w,a1       ; To be changed

To this:

       lea     Sprite_Table_Input+$180,a1

As we now know 3 would equal $180, we can just add straight to a1. No more calculations every single frame, we've already done it! You should have something like this now:

loc_121B8:

        tst.b   (Ring_spill_anim_counter).w
        beq.s   BranchTo5_DeleteObject
        move.w  (Camera_Max_Y_pos_now).w,d0
        addi.w  #$E0,d0
        cmp.w   y_pos(a0),d0
        bcs.s   BranchTo5_DeleteObject
        lea     Sprite_Table_Input+$180,a1
        cmpi.w  #$7E,(a1)
        bcc.s   +
        addq.w  #2,(a1)
        adda.w  (a1),a1
        move.w  a0,(a1)
+
        rts

So, what have we done? Well, we saved a line of it branching somewhere for a start, it's already there, so that's a plus! Also, we're moving $180 straight to a1, rather than doing them 3 lines of calculations! What a time saver! ALL done. That was easy, eh? Now your scattered rings will be even quicker, hopefully not slowing anything else down!

Part 2.5 (optional)

This is optional; you do not have to do it if you do not want to.

If you look at the code you just edited:

Obj_37_sub_4:
        addq.b  #2,routine(a0)
        move.b  #0,collision_flags(a0)
        move.b  #1,priority(a0)
        bsr.w   sub_11FC2

Obj_37_sub_6:
        lea     (byte_1237A).l,a1
        bsr.w   AnimateSprite
        bra.w   DisplaySprite
; ===========================================================================

BranchTo5_DeleteObject 
        bra.w   DeleteObject

You can see a command move 1 to priority, and displaying it again. This is for when you collect the rings (when the rings turn into them sparkly effects). You can do something extremely similar here if you like. It will make quite a bit of a difference if you collect a lot of scattered rings at the same time, otherwise, it won't do too much. If you want it to do the same, then you can. But it won't be $180 again! When 1 has been through DisplaySprite's calculations, it will equal $80 instead!

So, at "Obj_37_sub_4:", delete this line/command:

        move.b  #1,priority(a0)

Then, at "Obj_37_sub_6:", replace:

        bra.w   DisplaySprite

with this:

        lea     (Sprite_Table_Input).w,a1
        adda.w  #$80,a1
        cmpi.w  #$7E,(a1)
        bcc.s   +
        addq.w  #2,(a1)
        adda.w  (a1),a1
        move.w  a0,(a1)
+
        rts

So you have something like this:

Obj_37_sub_4:
        addq.b  #2,routine(a0)
        move.b  #0,collision_flags(a0)
        bsr.w   sub_11FC2

Obj_37_sub_6:
        lea     (byte_1237A).l,a1
        bsr.w   AnimateSprite
        lea     (Sprite_Table_Input).w,a1
        adda.w  #$80,a1
        cmpi.w  #$7E,(a1)
        bcc.s   +
        addq.w  #2,(a1)
        adda.w  (a1),a1
        move.w  a0,(a1)
+
        rts

Done: you've made it slightly faster again!

SCHG How-To Guide: Sonic the Hedgehog 2 (16-bit)
Fixing Bugs
Fix Demo Playback | Fix a Race Condition with Pattern Load Cues | Fix Super Sonic Bugs | Use Correct Height When Roll Jumping | Fix Jump Height Bug When Exiting Water | Fix Screen Boundary Spin Dash Bug | Correct Drowning Bugs | Fix Camera Y Position for Tails | Fix Tails Subanimation Error | Fix Tails' Respawn Speeds | Fix Accidental Deletion of Scattered Rings | Fix Ring Timers | Fix Rexon Crash | Fix Monitor Collision Bug | Fix EHZ Deformation Bug | Correct CPZ Boss Attack Behavior | Fix Bug in ARZ Boss Arrow's Platform Behavior | Fix ARZ Boss Walking on Air Glitch | Fix ARZ Boss Sprite Behavior | Fix Multiple CNZ Boss Bugs | Fix HTZ Background Scrolling Mountains | Fix OOZ Launcher Speed Up Glitch | Fix DEZ Giant Mech Collision Glitch | Fix Boss Deconstruction Behavior | Fix Speed Bugs | Fix 14 Continues Cheat | Fix Debug Mode Crash | Fix 99+ Lives | Fix Sonic 2's Sega Screen
Design Choices
Remove the Air Speed Cap | Disable Floor Collision While Dying | Modify Super Sonic Transformation Methods & Behavior | Enable/Disable Tails in Certain Levels | Collide with Water After Being Hurt | Retain Rings When Returning at a Star Post | Improve the Fade In\Fade Out Progression Routines | Fix Scattered Rings' Underwater Physics | Insert LZ Water Ripple Effect | Restore Lost CPZ Boss Feature | Prevent SCZ Tornado Spin Dash Death | Improve ObjectMove Subroutines | Port S3K Rings Manager | Port S3K Object Manager | Port S3K Priority Manager | Edit Level Order with ASM‎ | Alter Ring Requirements in Special Stages | Make Special Stage Characters Use Normal DPLCs | Speed Up Ring Loss Process | Change spike behaviour in Sonic 2
Adding Features
Create Insta-kill and High Jump Monitors | Create Clone and Special Stage Monitors | Port Knuckles
Sound Features
Expand Music Index to Start at $00 | Port Sonic 1 Sound Driver | Port Sonic 2 Clone Driver | Port Sonic 3 Sound Driver | Port Flamewing's Sonic 3 & Knuckles Sound Driver | Expand the Music Index to Start at $00 (Sonic 2 Clone Driver Version) | Play Different Songs Per Act
Extending the Game
Extend the Level Index Past $10 | Extend the Level Select | Extend Water Tables | Add Extra Characters | Free Up 2 Universal SSTs

|Speed Up Ring Loss Process (With Underwater)]]