Actions

SCHG How-to

Difference between revisions of "Fix Ring Timers"

From Sonic Retro

m (Text replacement - "</asm>" to "</syntaxhighlight>")
m (Text replacement - "<asm>" to "<syntaxhighlight lang="asm">")
Line 13: Line 13:
  
 
First, open your "Sonic1.asm" file and go to "Obj37_MakeRings:" label. Find this line:
 
First, open your "Sonic1.asm" file and go to "Obj37_MakeRings:" label. Find this line:
<asm>
+
<syntaxhighlight lang="asm">
 
                 move.b  #-1,($FFFFFEC6).w
 
                 move.b  #-1,($FFFFFEC6).w
 
</syntaxhighlight>
 
</syntaxhighlight>
Line 20: Line 20:
  
 
Next, go to the "Obj37_ResetCounter:" label. Find these line:
 
Next, go to the "Obj37_ResetCounter:" label. Find these line:
<asm>
+
<syntaxhighlight lang="asm">
 
move.w #$C6,d0
 
move.w #$C6,d0
 
jsr (PlaySound_Special).l ; play ring loss sound
 
jsr (PlaySound_Special).l ; play ring loss sound
Line 26: Line 26:
  
 
Just before it, add this:
 
Just before it, add this:
<asm>
+
<syntaxhighlight lang="asm">
 
                 moveq  #-1,d0                  ; Move #-1 to d0
 
                 moveq  #-1,d0                  ; Move #-1 to d0
 
                 move.b  d0,$1F(a0)      ; Move d0 to new timer
 
                 move.b  d0,$1F(a0)      ; Move d0 to new timer
Line 35: Line 35:
  
 
So, our new timer has been set to $FF. We still need to make it count down. So, find the "Obj37_ChkDel:" label. See this?
 
So, our new timer has been set to $FF. We still need to make it count down. So, find the "Obj37_ChkDel:" label. See this?
<asm>
+
<syntaxhighlight lang="asm">
 
tst.b ($FFFFFEC6).w
 
tst.b ($FFFFFEC6).w
 
beq.w Obj37_Delete
 
beq.w Obj37_Delete
Line 41: Line 41:
  
 
You may delete it! No longer needed, as we're not using this as the main timer anymore. Instead, replace it with this:
 
You may delete it! No longer needed, as we're not using this as the main timer anymore. Instead, replace it with this:
<asm>
+
<syntaxhighlight lang="asm">
 
                 subq.b  #1,$1F(a0)      ; Subtract 1
 
                 subq.b  #1,$1F(a0)      ; Subtract 1
 
                 beq.w  DeleteObject            ; If 0, delete
 
                 beq.w  DeleteObject            ; If 0, delete
Line 50: Line 50:
  
 
First, open your "25 & 37 Rings.asm" file and go to "@makerings:" label. Find this line:
 
First, open your "25 & 37 Rings.asm" file and go to "@makerings:" label. Find this line:
<asm>
+
<syntaxhighlight lang="asm">
 
                 move.b  #-1,(v_ani3_time).w
 
                 move.b  #-1,(v_ani3_time).w
 
</syntaxhighlight>
 
</syntaxhighlight>
Line 57: Line 57:
  
 
Next, go to the "@resetcounter:" label. Find this line:
 
Next, go to the "@resetcounter:" label. Find this line:
<asm>
+
<syntaxhighlight lang="asm">
 
                 sfx    sfx_RingLoss    ; play ring loss sound
 
                 sfx    sfx_RingLoss    ; play ring loss sound
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
Just before it, add this:
 
Just before it, add this:
<asm>
+
<syntaxhighlight lang="asm">
 
                 moveq  #-1,d0                  ; Move #-1 to d0
 
                 moveq  #-1,d0                  ; Move #-1 to d0
 
                 move.b  d0,obDelayAni(a0)      ; Move d0 to new timer
 
                 move.b  d0,obDelayAni(a0)      ; Move d0 to new timer
Line 71: Line 71:
  
 
So, our new timer has been set to $FF. We still need to make it count down. So, find the "@chkdel:" label. See this?
 
So, our new timer has been set to $FF. We still need to make it count down. So, find the "@chkdel:" label. See this?
<asm>
+
<syntaxhighlight lang="asm">
 
                 tst.b  (v_ani3_time).w
 
                 tst.b  (v_ani3_time).w
 
                 beq.s  RLoss_Delete
 
                 beq.s  RLoss_Delete
Line 77: Line 77:
  
 
You may delete it! No longer needed, as we're not using this as the main timer anymore. Instead, replace it with this:
 
You may delete it! No longer needed, as we're not using this as the main timer anymore. Instead, replace it with this:
<asm>
+
<syntaxhighlight lang="asm">
 
                 subq.b  #1,obDelayAni(a0)      ; Subtract 1
 
                 subq.b  #1,obDelayAni(a0)      ; Subtract 1
 
                 beq.w  DeleteObject            ; If 0, delete
 
                 beq.w  DeleteObject            ; If 0, delete
Line 87: Line 87:
  
 
First, open your ASM file and go to "loc_120BA:" label. Find this line:
 
First, open your ASM file and go to "loc_120BA:" label. Find this line:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.b  #-1,(Ring_spill_anim_counter).w
 
         move.b  #-1,(Ring_spill_anim_counter).w
 
</syntaxhighlight>
 
</syntaxhighlight>
Line 94: Line 94:
  
 
Next, go to the "loc_12142:" label. Find this line:
 
Next, go to the "loc_12142:" label. Find this line:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  #$C6,d0
 
         move.w  #$C6,d0
 
         jsr    (PlaySoundStereo).l
 
         jsr    (PlaySoundStereo).l
Line 100: Line 100:
  
 
Just before it, add this:
 
Just before it, add this:
<asm>
+
<syntaxhighlight lang="asm">
 
         moveq  #-1,d0                          ; Move #-1 to d0
 
         moveq  #-1,d0                          ; Move #-1 to d0
 
         move.b  d0,objoff_1F(a0)                ; Move d0 to new timer
 
         move.b  d0,objoff_1F(a0)                ; Move d0 to new timer
Line 109: Line 109:
  
 
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?
 
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?
<asm>
+
<syntaxhighlight lang="asm">
 
         tst.b  (Ring_spill_anim_counter).w
 
         tst.b  (Ring_spill_anim_counter).w
 
         beq.s  BranchTo5_DeleteObject
 
         beq.s  BranchTo5_DeleteObject
Line 115: Line 115:
  
 
You may delete it! No longer needed, as we're not using this as the main timer anymore. Instead, replace it with this:
 
You may delete it! No longer needed, as we're not using this as the main timer anymore. Instead, replace it with this:
<asm>
+
<syntaxhighlight lang="asm">
 
         subq.b  #1,objoff_1F(a0)                ; Subtract 1
 
         subq.b  #1,objoff_1F(a0)                ; Subtract 1
 
         beq.w  DeleteObject                    ; If 0, delete
 
         beq.w  DeleteObject                    ; If 0, delete
Line 125: Line 125:
  
 
First, open your ASM file and go to "Obj37_Init:" label. On the 3rd + label, find this line:
 
First, open your ASM file and go to "Obj37_Init:" label. On the 3rd + label, find this line:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.b  #-1,(Ring_spill_anim_counter).w
 
         move.b  #-1,(Ring_spill_anim_counter).w
 
</syntaxhighlight>
 
</syntaxhighlight>
Line 132: Line 132:
  
 
From the "Obj37_Init:", find the 5th + label. Find this line:
 
From the "Obj37_Init:", find the 5th + label. Find this line:
<asm>
+
<syntaxhighlight lang="asm">
 
         move.w  #SndID_RingSpill,d0
 
         move.w  #SndID_RingSpill,d0
 
         jsr    (PlaySoundStereo).l
 
         jsr    (PlaySoundStereo).l
Line 138: Line 138:
  
 
Just before it, add this:
 
Just before it, add this:
<asm>
+
<syntaxhighlight lang="asm">
 
         moveq  #-1,d0                          ; Move #-1 to d0
 
         moveq  #-1,d0                          ; Move #-1 to d0
 
         move.b  d0,objoff_1F(a0)                ; Move d0 to new timer
 
         move.b  d0,objoff_1F(a0)                ; Move d0 to new timer
Line 146: Line 146:
  
 
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?
 
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?
<asm>
+
<syntaxhighlight lang="asm">
 
         tst.b  (Ring_spill_anim_counter).w
 
         tst.b  (Ring_spill_anim_counter).w
 
         beq.s  Obj37_Delete
 
         beq.s  Obj37_Delete
Line 152: Line 152:
  
 
You may delete it! No longer needed, as we're not using this as the main timer anymore. Instead, replace it with this:
 
You may delete it! No longer needed, as we're not using this as the main timer anymore. Instead, replace it with this:
<asm>
+
<syntaxhighlight lang="asm">
 
         subq.b  #1,objoff_1F(a0)                ; Subtract 1
 
         subq.b  #1,objoff_1F(a0)                ; Subtract 1
 
         beq.w  DeleteObject                    ; If 0, delete
 
         beq.w  DeleteObject                    ; If 0, delete
Line 162: Line 162:
  
 
First, open your ASM file and go to "loc_1A6B6:" label. Find this line:
 
First, open your ASM file and go to "loc_1A6B6:" label. Find this line:
<asm>
+
<syntaxhighlight lang="asm">
 
                 move.b  #-1,(Ring_spill_anim_counter).w
 
                 move.b  #-1,(Ring_spill_anim_counter).w
 
</syntaxhighlight>
 
</syntaxhighlight>
Line 169: Line 169:
  
 
Next, go to the "loc_1A738:" label. Find this line:
 
Next, go to the "loc_1A738:" label. Find this line:
<asm>
+
<syntaxhighlight lang="asm">
 
                 move.w  #$FFB9,d0
 
                 move.w  #$FFB9,d0
 
                 jsr    (Play_Sound_2).l
 
                 jsr    (Play_Sound_2).l
Line 175: Line 175:
  
 
Just before it, add this:
 
Just before it, add this:
<asm>
+
<syntaxhighlight lang="asm">
 
                 moveq  #-1,d0                          ; Move #-1 to d0
 
                 moveq  #-1,d0                          ; Move #-1 to d0
 
                 move.b  d0,height_pixels(a0)            ; Move d0 to new timer
 
                 move.b  d0,height_pixels(a0)            ; Move d0 to new timer
Line 184: Line 184:
  
 
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?
 
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?
<asm>
+
<syntaxhighlight lang="asm">
 
                 tst.b  (Ring_spill_anim_counter).w
 
                 tst.b  (Ring_spill_anim_counter).w
 
                 beq.s  loc_1A7E4
 
                 beq.s  loc_1A7E4
Line 190: Line 190:
  
 
You may delete it! No longer needed, as we're not using this as the main timer anymore. Now, find the "loc_1A7B0:" label. Just below it, add:
 
You may delete it! No longer needed, as we're not using this as the main timer anymore. Now, find the "loc_1A7B0:" label. Just below it, add:
<asm>
+
<syntaxhighlight lang="asm">
 
                 subq.b  #1,height_pixels(a0)            ; Subtract 1
 
                 subq.b  #1,height_pixels(a0)            ; Subtract 1
 
                 beq.w  Delete_Current_Sprite          ; If 0, delete
 
                 beq.w  Delete_Current_Sprite          ; If 0, delete
Line 202: Line 202:
  
 
Go to "loc_1A88C:" and find:
 
Go to "loc_1A88C:" and find:
<asm>
+
<syntaxhighlight lang="asm">
 
                 move.b  #-1,(Ring_spill_anim_counter).w
 
                 move.b  #-1,(Ring_spill_anim_counter).w
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
and just above or below this line, add this line:
 
and just above or below this line, add this line:
<asm>
+
<syntaxhighlight lang="asm">
 
                 move.b  #-1,height_pixels(a0)          ; Move #$FF to new timer
 
                 move.b  #-1,height_pixels(a0)          ; Move #$FF to new timer
 
</syntaxhighlight>
 
</syntaxhighlight>
Line 214: Line 214:
  
 
There's one more thing. Again, this is S3K only. You know the Egg Prisons you can jump on in Flying Battery Zone? Some of them release rings, which are scattered rings again. They need their own timer. So, go to "loc_89D44:" and find the line:
 
There's one more thing. Again, this is S3K only. You know the Egg Prisons you can jump on in Flying Battery Zone? Some of them release rings, which are scattered rings again. They need their own timer. So, go to "loc_89D44:" and find the line:
<asm>
+
<syntaxhighlight lang="asm">
 
                 move.b  #-1,(Ring_spill_anim_counter).w
 
                 move.b  #-1,(Ring_spill_anim_counter).w
 
</syntaxhighlight>
 
</syntaxhighlight>
  
 
and just above or below this line, add this line:
 
and just above or below this line, add this line:
<asm>
+
<syntaxhighlight lang="asm">
 
                 move.b  #-1,height_pixels(a0)          ; Move #$FF to new timer
 
                 move.b  #-1,height_pixels(a0)          ; Move #$FF to new timer
 
</syntaxhighlight>
 
</syntaxhighlight>
Line 231: Line 231:
  
 
To add this little feature (For Sonic 1 SVN users, though hackers of other games/disassemblies can easily follow suit) go to "@chkdel:" and change it to this:
 
To add this little feature (For Sonic 1 SVN users, though hackers of other games/disassemblies can easily follow suit) go to "@chkdel:" and change it to this:
<asm>
+
<syntaxhighlight lang="asm">
 
@chkdel:
 
@chkdel:
 
subq.b  #1,obDelayAni(a0)  ; Subtract 1  ; RHS Ring Timer fix
 
subq.b  #1,obDelayAni(a0)  ; Subtract 1  ; RHS Ring Timer fix
Line 257: Line 257:
  
 
To add this little feature (For Hivebrain) go to "Obj37_ChkDel:" and change it to this:
 
To add this little feature (For Hivebrain) go to "Obj37_ChkDel:" and change it to this:
<asm>
+
<syntaxhighlight lang="asm">
 
Obj37_ChkDel:
 
Obj37_ChkDel:
 
subq.b  #1,$1F(a0)  ; Subtract 1  ; RHS Ring Timer fix
 
subq.b  #1,$1F(a0)  ; Subtract 1  ; RHS Ring Timer fix
Line 281: Line 281:
 
In addition to KingofHarts' note, to do this in Sonic 2 (Xenowhirl) just replace "loc_121B8:" with this:
 
In addition to KingofHarts' note, to do this in Sonic 2 (Xenowhirl) just replace "loc_121B8:" with this:
  
<asm>
+
<syntaxhighlight lang="asm">
 
loc_121B8:
 
loc_121B8:
 
subq.b  #1,objoff_1F(a0)  ; Subtract 1  ; RHS Ring Timer fix
 
subq.b  #1,objoff_1F(a0)  ; Subtract 1  ; RHS Ring Timer fix

Revision as of 21:46, 20 December 2015

(Original guide by redhotsonic)

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. 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.

But wait, it gets better! If you have badniks explode with a ring coming out instead of an animal in your hack, like I do, this will also interrupt with the RAM timer.

That's not meant to happen. What we want is for the rings that are 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. I'll note now, that this bug exists in all 3 Mega Drive games, so let's go ahead and fix them all!

Sonic 1 fix - Hivebrain Disassembly

(Addition by Luigi Hero)

First, open your "Sonic1.asm" file and go to "Obj37_MakeRings:" label. Find this line:

                move.b  #-1,($FFFFFEC6).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 delete this line.

Next, go to the "Obj37_ResetCounter:" label. Find these line:

		move.w	#$C6,d0
		jsr	(PlaySound_Special).l ;	play ring loss sound

Just before it, add this:

                moveq   #-1,d0                  ; Move #-1 to d0
                move.b  d0,$1F(a0)       ; Move d0 to new timer
                move.b  d0,($FFFFFEC6).w      ; Move d0 to old timer (for animated purposes)

This is where we're setting our timer. "$1F(a0)" is our brand new timer. "$1F" is a local variable in the object's OST that exists only within the object, instead of a global variable like $FFFFFEC6. This means each scattered ring will now have its own personal timer, instead of following 1 singular global timer. We must still set the old timer though, for animation 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 "Obj37_ChkDel:" label. See this?

				tst.b	($FFFFFEC6).w
				beq.w	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,$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!

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 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. "obDelayAni" is a local variable in the object's OST that exists only within the object, instead of a global variable like v_ani3_time. This means each scattered ring will now have its own personal timer, instead of following 1 singular global timer. We must still set the old timer though, for animation 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!

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 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. "objoff_1F" is a local variable in the object's OST that exists only within the object, instead of a global variable like Ring_spill_anim_counter. This means each scattered ring will now have its own personal timer, instead of following 1 singular global timer. We must still set the old timer though, for animation 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!

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 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. "objoff_1F" is a local variable in the object's OST that exists only within the object, instead of a global variable like Ring_spill_anim_counter. This means each scattered ring will now have its own personal timer, instead of following 1 singular global timer. We must still set the old timer though, for animation 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!

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 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. "anim_frame_timer(a0)" is a local variable in the object's OST that exists only within the object, instead of a global variable like Ring_spill_anim_counter. This means each scattered ring will now have its own personal timer, instead of following 1 singular global timer. We must still set the old timer though, for animation 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. Now, find the "loc_1A7B0:" label. Just below it, add:

                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!

BUT WAIT!

Unlike the previous 2 games, you have to do something else to Sonic 3 and Knuckles. You know the lightning shield? When you have rings attracted 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

BUT WAIT!

There's one more thing. Again, this is S3K only. You know the Egg Prisons you can jump on in Flying Battery Zone? 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.

Lost Rings Flash In Sonic 1 SVN

(Addition by KingofHarts)

That should do it. This is especially useful for anyone who uses ReadySonic's ring loss flash effect, which makes lost rings flash during the final frames of their existence.

To add this little feature (For Sonic 1 SVN users, though hackers of other games/disassemblies can easily follow suit) go to "@chkdel:" and change it to this:

@chkdel:
		subq.b  #1,obDelayAni(a0)  ; Subtract 1   ; RHS Ring Timer fix
                beq.w   DeleteObject       ; If 0, delete ; RHS Ring Timer fix
		move.w	(v_limitbtm2).w,d0
		addi.w	#$E0,d0
		cmp.w	obY(a0),d0	   ; has object moved below level boundary?
		bcs.s	RLoss_Delete	   ; if yes, branch	
;Mercury Lost Rings Flash
		btst	#0, obDelayAni(a0) ; Test the first bit of the timer, so rings flash every other frame.
		beq.w	DisplaySprite      ; If the bit is 0, the ring will appear.
		cmpi.b	#80,obDelayAni(a0) ; Rings will flash during last 80 steps of their life.
		bhi.w	DisplaySprite      ; If the timer is higher than 80, obviously the rings will STAY visible.
		rts
;end Lost Rings Flash

As noted, the ring loss flash is from Mercury's ReadySonic, which I have implemented into Sonic 1 REV C. I have amended this to work with RHS' new fix. That's all there is to it.

Lost Rings Flash In Sonic 1 Hivebrain

(Addition by Luigi Hero)

That should do it. This is especially useful for anyone who uses ReadySonic's ring loss flash effect, which makes lost rings flash during the final frames of their existence.

To add this little feature (For Hivebrain) go to "Obj37_ChkDel:" and change it to this:

Obj37_ChkDel:
		subq.b  #1,$1F(a0)  ; Subtract 1   ; RHS Ring Timer fix
                beq.w   DeleteObject       ; If 0, delete ; RHS Ring Timer fix
		move.w	($FFFFF72E).w,d0
		addi.w	#$E0,d0
		cmp.w	$C(a0),d0	   ; has object moved below level boundary?
		bcs.s	Obj37_Delete	   ; if yes, branch	
;Mercury Lost Rings Flash
		btst	#0, $1F(a0) ; Test the first bit of the timer, so rings flash every other frame.
		beq.w	DisplaySprite      ; If the bit is 0, the ring will appear.
		cmpi.b	#80,$1F(a0) ; Rings will flash during last 80 steps of their life.
		bhi.w	DisplaySprite      ; If the timer is higher than 80, obviously the rings will STAY visible.
		rts
;end Lost Rings Flash

As noted, the ring loss flash is from Mercury's ReadySonic, which I have implemented into Sonic 1 REV C. I have amended this to work with RHS' new fix. That's all there is to it.

Lost Rings Flash In Sonic 2

(Addition by TheRetroGuy)

In addition to KingofHarts' note, to do this in Sonic 2 (Xenowhirl) just replace "loc_121B8:" with this:

loc_121B8:
		subq.b  #1,objoff_1F(a0)  ; Subtract 1   ; RHS Ring Timer fix
		beq.w   DeleteObject       ; If 0, delete ; RHS Ring Timer fix
		move.w	(Camera_Max_Y_pos_now).w,d0
		addi.w	#$E0,d0
		cmp.w	y_pos(a0),d0	   ; has object moved below level boundary?
		bcs.s	BranchTo5_DeleteObject
;Mercury Lost Rings Flash
		btst	#0, objoff_1F(a0) ; Test the first bit of the timer, so rings flash every other frame.
		beq.w	DisplaySprite      ; If the bit is 0, the ring will appear.
		cmpi.b	#80,objoff_1F(a0) ; Rings will flash during last 80 steps of their life.
		bhi.w	DisplaySprite      ; If the timer is higher than 80, obviously the rings will STAY visible.
		rts


SCHG How-To Guide: Sonic the Hedgehog (16-bit)
Fixing Bugs
Fix Demo Playback | Fix a Race Condition with Pattern Load Cues | Fix the SEGA Sound | Display the Press Start Button Text | Fix the Level Select Menu | Fix the Hidden Points Bug | Fix Accidental Deletion of Scattered Rings | Fix Ring Timers | Fix the Walk-Jump Bug | Correct Drowning Bugs | Fix the Death Boundary Bug | Fix the Camera Follow Bug | Fix Song Restoration Bugs | Fix the HUD Blinking | Fix the Level Select Graphics Bug | Fix a remember sprite related bug
Changing Design Choices
Change Spike Behavior | Collide with Water After Being Hurt | Fix Special Stage Jumping Physics | Improve the Fade In\Fade Out Progression Routines | Fix Scattered Rings' Underwater Physics | Remove the Speed Cap | Port the REV01 Background Effects | Port Sonic 2's Level Art Loader | Retain Rings Between Acts | Add Sonic 2 (Simon Wai Prototype) Level Select | Improve ObjectMove Subroutines | Port Sonic 2 Level Select
Adding Features
Add Spin Dash ( Part 1 / Part 2 / Part 3 / Part 4 ) | Add Eggman Monitor | Add Super Sonic | Add the Air Roll
Sound Features
Expand the Sound Index | Play Different Songs Per Act | Port Sonic 2 Final Sound Driver | Port Sonic 3's Sound Driver | Port Flamewing's Sonic 3 & Knuckles Sound Driver | Change The SEGA Sound
Extending the Game
Load Chunks From ROM | Add Extra Characters | Make an Alternative Title Screen | Use Dynamic Tilesets | Make GHZ Load Alternate Art | Make Ending Load Alternate Art | Add a New Zone | Set Up the Goggle Monitor | Add New Moves | Add a Dynamic Collision System | Dynamic Special Stage Walls System | Extend Sprite Mappings and Art Limit | Enigma Credits | Use Dynamic Palettes
Miscellaneous
Convert the Hivebrain 2005 Disassembly to ASM68K
Split Disassembly Guides
Set Up a Split Disassembly | Basic Level Editing | Basic Art Editing | Basic ASM Editing (Spin Dash)
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