I was trying to get rid of some of the most annoying slowdowns in the game, when I got this idea. Since it's extremely versatile, I thought I could share it without taking the risk to see the exact same thing in all hacks. So, the condition to use this is essentially that you'll make your own pattern (and possibly give credits but I don't care much about that). I won't give the data to avoid copy/paste, but the process is detailed and simple. Actually, it's extremely simplistic, it only requires to find a good equation, use your favorite spreadsheet software and modify a few details in the code. The idea is to use some pre-computed data stored in a simple array rather than do useless calculations (that would always lead to the exact same result). This way, you get rid of a waste of hundreds of processor cycles. So this is also a simple example of how to use an array. THE CODE Here's the original lost ring code (from the svn), it's Obj37 in Hivebrain's disassembly: RingLoss: ; XREF: Obj_Index moveq #0,d0 move.b obRoutine(a0),d0 move.w RLoss_Index(pc,d0.w),d1 jmp RLoss_Index(pc,d1.w) ; =========================================================================== RLoss_Index: dc.w RLoss_Count-RLoss_Index dc.w RLoss_Bounce-RLoss_Index dc.w RLoss_Collect-RLoss_Index dc.w RLoss_Sparkle-RLoss_Index dc.w RLoss_Delete-RLoss_Index ; =========================================================================== RLoss_Count: ; Routine 0 movea.l a0,a1 moveq #0,d5 move.w (v_rings).w,d5 ; check number of rings you have moveq #32,d0 cmp.w d0,d5 ; do you have 32 or more? bcs.s @belowmax ; if not, branch move.w d0,d5 ; if yes, set d5 to 32 @belowmax: subq.w #1,d5 move.w #$288,d4 ; << THIS LINE (1) bra.s @makerings ; =========================================================================== @loop: bsr.w FindFreeObj bne.w @resetcounter @makerings: move.b #id_RingLoss,0(a1) ; load bouncing ring object addq.b #2,obRoutine(a1) move.b #8,obHeight(a1) move.b #8,obWidth(a1) move.w obX(a0),obX(a1) move.w obY(a0),obY(a1) move.l #Map_Ring,obMap(a1) move.w #$27B2,obGfx(a1) move.b #4,obRender(a1) move.b #3,obPriority(a1) move.b #$47,obColType(a1) move.b #8,obActWid(a1) move.b #-1,(v_ani3_time).w ; this is "move.b #-1,($FFFFFFA6).w" in Hivebrain's disassembly tst.w d4 ; << THIS LINE (2) bmi.s @loc_9D62 ; << THIS LINE (2) move.w d4,d0 ; << THIS LINE (2) bsr.w CalcSine ; << THIS LINE (2) move.w d4,d2 ; << THIS LINE (2) lsr.w #8,d2 ; << THIS LINE (2) asl.w d2,d0 ; << THIS LINE (2) asl.w d2,d1 ; << THIS LINE (2) move.w d0,d2 ; << THIS LINE (2) move.w d1,d3 ; << THIS LINE (2) addi.b #$10,d4 ; << THIS LINE (2) bcc.s @loc_9D62 ; << THIS LINE (2) subi.w #$80,d4 ; << THIS LINE (2) bcc.s @loc_9D62 ; << THIS LINE (2) move.w #$288,d4 ; << THIS LINE (2) @loc_9D62: ; << THIS LINE (2) move.w d2,obVelX(a1) ; << THIS LINE (2) move.w d3,obVelY(a1) ; << THIS LINE (2) neg.w d2 ; << THIS LINE (2) neg.w d4 ; << THIS LINE (2) dbf d5,@loop ; repeat for number of rings (max 31) @resetcounter: move.w #0,(v_rings).w ; reset number of rings to zero move.b #$80,(f_ringcount).w ; update ring counter move.b #0,(v_lifecount).w sfx sfx_RingLoss ; play ring loss sound RLoss_Bounce: ; Routine 2 move.b (v_ani3_frame).w,obFrame(a0) bsr.w SpeedToPos addi.w #$18,obVelY(a0) bmi.s @chkdel move.b (v_vbla_byte).w,d0 add.b d7,d0 andi.b #3,d0 bne.s @chkdel jsr ObjFloorDist tst.w d1 bpl.s @chkdel add.w d1,obY(a0) move.w obVelY(a0),d0 asr.w #2,d0 sub.w d0,obVelY(a0) neg.w obVelY(a0) @chkdel: tst.b (v_ani3_time).w beq.s RLoss_Delete 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 bra.w DisplaySprite ; =========================================================================== RLoss_Collect: ; Routine 4 addq.b #2,obRoutine(a0) move.b #0,obColType(a0) move.b #1,obPriority(a0) bsr.w CollectRing RLoss_Sparkle: ; Routine 6 lea (Ani_Ring).l,a1 bsr.w AnimateSprite bra.w DisplaySprite ; =========================================================================== RLoss_Delete: ; Routine 8 bra.w DeleteObject The lines with "<< THIS LINE" as a comment are the ones we're going to change. Let's do this first: Change the line marked with a "(1)" with: lea SpillingRingData,a3 ; load the address of the array in a3 You can choose another name, of course. And change all the lines marked with a "(2)" with this code: move.w (a3)+,$12(a1) ; move the data contained in the array to the y velocity and increment the address in a3 move.w (a3)+,$10(a1) ; move the data contained in the array to the x velocity and increment the address in a3 That's it for the asm part. Short, isn't it? THE DATA Open the spreadsheet software of your choice. - In a first column, put 0 to "n" (32 in the original game). - In a second column, you have to set the absolute speed of the rings. The maximum absolute speed should be approximately -1000 to -1200 (not in hex).It's negative because of the coordinate system of Sonic. You can use either all the same values, or different ones (eg: -1200, -800, -1150, -775...) - In the third column, you have to put the angle equation: The parameters should be something like this: a=pi/2+b/t*n+c Where: "a" is the angle, "b" is the total angle covered by your rings (a full circle is 2*pi), "t" is the maximum number of rings that will appear, "n" is the number of the current ring, "c" is a bias, 0 if you want your first ring to move vertically, another value if you want it to go left/right. - In the fourth column, you'll put the vertical speed: v=sin(a)*s Where: "v" is the vertical speed "a" is the angle (third column) "s" is the absolute speed of the ring (second column) - In the fifth column, you'll put the horizontal speed: h=cos(a)*s Where: "h" is the horizontal speed "a" is the angle (third column) "s" is the absolute speed of the ring (second column) Now, setup the 4th and 5th columns to show 0 decimals. Copy their content in your text editor, and arrange things so it looks like this: SpillingRingData: dc.w -1100, 0 dc.w -847, -67 dc.w -847, 67 dc.w -1086, -174 ; (...) ; (...) ; (...) even You don't have to turn the decimal values into hexadecimal values, the assembler will do it for you. Make sure you have as many lines as the maximum amount of rings you can lose. If you want your rings to got in both directions, make lines for only half of the rings, duplicate all lines (excepted the purely vertical ones) and negate the horizontal speed. You'll only have to change the absolute speeds and the total angle to design tons of great patterns, and with a few more changes you can even have several patterns in the same game. Put your new array wherever you want in your code (for example before or after the object). Enjoy!