Any bugs, let me know! For a Sonic 2 guide, keep reading this post. For a Sonic 1 guide, read this post. For a Sonic 3 and Knuckles guide, read this post. (This isn't actually a bug as such, it's a design change. This guide is for Sonic 2) Ah, the scattered rings when you get hurt. It can cause slowdown to your game, and even mess up the under water palette. Now, when Sonic is underwater, his movement and his gravity is changed so he moves slower to act like he's in water. But when he is hurt under water, isn't it funny that the scattered rings still act the same as if you weren't in water? In this very short tutorial, I will tell you how to make the rings act correctly under water. I am using Xenowhirl's Disassembly. For other disassemblies, it must be extremely similar to this so I'm sure you won't have a problem if you follow this guide. Remember, always back up your disassembly first before following guides. I will not be held responsible if anything goes wrong. First, go to "loc_120BA:" (part of the scattered rings object code) and find this bit of coding: Code (ASM): bsr.w JmpTo4_CalcSine move.w d4,d2 lsr.w #8,d2 Just underneath, paste this: Code (ASM): tst.b (Water_flag).w ; Does the level have water? beq.s + ; If not, branch and skip underwater checks move.w (Water_Level_2).w,d6 ; Move water level to d6 cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level? bgt.s + ; If not, branch and skip underwater commands asr.w #$1,d0 ; Half d0. Makes the ring's x_vel bounce to the left/right slower asr.w #$1,d1 ; Half d1. Makes the ring's y_vel bounce up/down slower + So, you should end up with something looking like this: Code (ASM): loc_120BA: _move.b #$37,0(a1) ; load obj37 addq.b #2,routine(a1) move.b #8,y_radius(a1) move.b #8,x_radius(a1) move.w x_pos(a0),x_pos(a1) move.w y_pos(a0),y_pos(a1) move.l #Obj25_MapUnc_12382,mappings(a1) move.w #$26BC,art_tile(a1) bsr.w Adjust2PArtPointer2 move.b #$84,render_flags(a1) move.b #3,priority(a1) move.b #$47,collision_flags(a1) move.b #8,width_pixels(a1) move.b #-1,(Ring_spill_anim_counter).w tst.w d4 bmi.s loc_12132 move.w d4,d0 bsr.w JmpTo4_CalcSine move.w d4,d2 lsr.w #8,d2 tst.b (Water_flag).w ; Does the level have water? beq.s + ; If not, branch and skip underwater checks move.w (Water_Level_2).w,d6 ; Move water level to d6 cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level? bgt.s + ; If not, branch and skip underwater commands asr.w #$1,d0 ; Half d0. Makes the ring's x_vel bounce to the left/right slower asr.w #$1,d1 ; Half d1. Makes the ring's y_vel bounce up/down slower + asl.w d2,d0 asl.w d2,d1 move.w d0,d2 move.w d1,d3 addi.b #$10,d4 bcc.s loc_12132 subi.w #$80,d4 bcc.s loc_12132 move.w #$288,d4 This ensures that when underwater, the rings x_vel and y_vel are halved so they appear to be moving slower. Next, go to label "Obj_37_sub_2:" and under Code (ASM): move.b (Ring_spill_anim_frame).w,mapping_frame(a0) bsr.w ObjectMove addi.w #$18,y_vel(a0) Put this: Code (ASM): tst.b (Water_flag).w ; Does the level have water? beq.s + ; If not, branch and skip underwater checks move.w (Water_Level_2).w,d6 ; Move water level to d6 cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level? bgt.s + ; If not, branch and skip underwater commands subi.w #$E,y_vel(a0) ; Reduce gravity by $E ($18-$E=$A), giving the underwater effect for the rings + You can change the value of the gravity if you like, but I found $E to suit. So, you should have something looking like this: Code (ASM): Obj_37_sub_2: move.b (Ring_spill_anim_frame).w,mapping_frame(a0) bsr.w ObjectMove addi.w #$18,y_vel(a0) tst.b (Water_flag).w ; Does the level have water? beq.s + ; If not, branch and skip underwater checks move.w (Water_Level_2).w,d6 ; Move water level to d6 cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level? bgt.s + ; If not, branch and skip underwater commands subi.w #$E,y_vel(a0) ; Reduce gravity by $E ($18-$E=$A), giving the underwater effect for the rings + bmi.s loc_121B8 move.b ($FFFFFE0F).w,d0 add.b d7,d0 andi.b #7,d0 bne.s loc_121B8 tst.b render_flags(a0) bpl.s loc_121D0 jsr (RingCheckFloorDist).l tst.w d1 bpl.s loc_121B8 add.w d1,y_pos(a0) move.w y_vel(a0),d0 asr.w #2,d0 sub.w d0,y_vel(a0) neg.w y_vel(a0) This ensures when underwater, the gravity is not as great. Otherwise, the rings will gain speed and then it won't give the desired underwater effect. And that's it. Save, build and test. I don't think I've ever seen anyone do this in their hack. I've just applied to it in my Sonic 2 Recreation hack. I hope to see this in more hacks from now on! In case, here is a Sonic 2 Rom, only edited to include the underwater Scattered rings. To see for yourself. Cheers, redhotsonic
I would've never even noticed this had you not pointed it out... Good spot. Page worthy of the SCHG, I think. Come to think of it... has such a fix been done, or should be done, to Sonic's 1 and 3? I highly doubt this is an isolated incident only occuring in Sonic 2.
A pet peeve of mine is when people write guides that amount to "paste this here and there". You wrote this in an effort to teach someone how to do it, yes? Then why not explain it a bit better?
I do agree with that. At the very least, some tags next to the lines of code would suffice, telling us what the code does. Someone like myself who is novice at ASM will look at that and simply copy and paste without knowing what I'm doing or why I'm changing this.
Sure, as you wish. Also, I had a facepalm moment. I forgot to make them do an extra check to see if the level has water or not. Otherwise, in other levels like EHZ or whatever, the rings may act if their underwater lol. I've fixed this in the first post. Cheers
Thanks for the that, RHS. Now somebody that uses the SVN disasms has a fighting chance to implement this themselves if they wanted to.
Thanks, that would be a nice edition if anyone did want to add it to the SCHG. This has never been applied to any Sonic game IIRC. Can't be too sure about Knuckles Chaotix as never played that game. You're welcome. It shouldn't be hard at all to apply using a SVN disassembly. Someone from SSRG has ported it into his Sonic 1 hack using my Sonic 2 guide so it can't be hard =P
Tell you what, I'll get on it in the morning. It's night for me here right now, so wait a few hours. I'll put something together for you. (That is, if someone doesn't beat me to it) Any chance you can put together a tutorial for the same thing for Sonic's 1 and 3?
Sure, here is one for Sonic 1. I can'T find a disassembly for S3, but there's one for S3K. So I will include that next, although this requires a little more work, so will post that one later. Sonic 1 and Sonic 3 and Knuckles guides are using SVN disassemblies, seeming as that's what most of you use (for some strange reason). SONIC 1 First, In your disassembly, go to "_incObj\25 & 37 Rings.asm" Go to "@makerings:" (part of the scattered rings object code) and find this bit of coding: Code (ASM): bsr.w CalcSine move.w d4,d2 lsr.w #8,d2 Just underneath, paste this: Code (ASM): tst.b ($FFFFF64C).w ; Does the level have water? beq.s @skiphalvingvel ; If not, branch and skip underwater checks move.w ($FFFFF646).w,d6 ; Move water level to d6 cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level? bgt.s @skiphalvingvel ; If not, branch and skip underwater commands asr.w #$1,d0 ; Half d0. Makes the ring's x_vel bounce to the left/right slower asr.w #$1,d1 ; Half d1. Makes the ring's y_vel bounce up/down slower @skiphalvingvel: So, you should end up with something looking like this: Code (ASM): @makerings: move.b #id_RingLoss,0(a1) ; load bouncing ring object addq.b #2,obRoutine(a1) move.b #8,obHeight(a1) move.b #8,obWidth(a1) move.w obX(a0),obX(a1) move.w obY(a0),obY(a1) move.l #Map_Ring,obMap(a1) move.w #$27B2,obGfx(a1) move.b #4,obRender(a1) move.b #3,obPriority(a1) move.b #$47,obColType(a1) move.b #8,obActWid(a1) move.b #-1,(v_ani3_time).w tst.w d4 bmi.s @loc_9D62 move.w d4,d0 bsr.w CalcSine move.w d4,d2 lsr.w #8,d2 tst.b ($FFFFF64C).w ; Does the level have water? beq.s @skiphalvingvel ; If not, branch and skip underwater checks move.w ($FFFFF646).w,d6 ; Move water level to d6 cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level? bgt.s @skiphalvingvel ; If not, branch and skip underwater commands asr.w #$1,d0 ; Half d0. Makes the ring's x_vel bounce to the left/right slower asr.w #$1,d1 ; Half d1. Makes the ring's y_vel bounce up/down slower @skiphalvingvel: asl.w d2,d0 asl.w d2,d1 move.w d0,d2 move.w d1,d3 addi.b #$10,d4 bcc.s @loc_9D62 subi.w #$80,d4 bcc.s @loc_9D62 move.w #$288,d4 This ensures that when underwater, the rings x_vel and y_vel are halved so they appear to be moving slower. Next, go to label "RLoss_Bounce:" and under Code (ASM): move.b (v_ani3_frame).w,obFrame(a0) bsr.w SpeedToPos addi.w #$18,obVelY(a0) Put this: Code (ASM): tst.b ($FFFFF64C).w ; Does the level have water? beq.s @skipbounceslow ; If not, branch and skip underwater checks move.w ($FFFFF646).w,d6 ; Move water level to d6 cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level? bgt.s @skipbounceslow ; If not, branch and skip underwater commands subi.w #$E,obVelY(a0) ; Reduce gravity by $E ($18-$E=$A), giving the underwater effect @skipbounceslow: You can change the value of the gravity if you like, but I found $E to suit. So, you should have something looking like this: Code (ASM): RLoss_Bounce: ; Routine 2 move.b (v_ani3_frame).w,obFrame(a0) bsr.w SpeedToPos addi.w #$18,obVelY(a0) tst.b ($FFFFF64C).w ; Does the level have water? beq.s @skipbounceslow ; If not, branch and skip underwater checks move.w ($FFFFF646).w,d6 ; Move water level to d6 cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level? bgt.s @skipbounceslow ; If not, branch and skip underwater commands subi.w #$E,obVelY(a0) ; Reduce gravity by $E ($18-$E=$A), giving the underwater effect @skipbounceslow: bmi.s @chkdel move.b (v_vbla_byte).w,d0 add.b d7,d0 andi.b #3,d0 bne.s @chkdel jsr ObjFloorDist tst.w d1 bpl.s @chkdel add.w d1,obY(a0) move.w obVelY(a0),d0 asr.w #2,d0 sub.w d0,obVelY(a0) neg.w obVelY(a0) This ensures when underwater, the gravity is not as great. Otherwise, the rings will gain speed and then it won't give the desired underwater effect.
Double post, I thought it'd be better to have a seperate post for this, so on my first post, I can add quick links, so please forgive me. Anyway, here is your guide for adding gravity for scattered rings underwater for S3K. Again, SVN disassembly. Sonic 3 and Knuckles Go to "off_1A658:" (the routines of the scattered rings). You'll see that it's first job is to go to "loc_1A67A". That needs changing to "loc_1A68C". So, from this: Code (ASM): off_1A658: dc.w loc_1A67A-off_1A658 dc.w loc_1A75C-off_1A658 dc.w loc_1A7C2-off_1A658 dc.w loc_1A7D6-off_1A658 dc.w loc_1A7E4-off_1A658 And replace with this: Code (ASM): off_1A658: dc.w loc_1A68C-off_1A658 dc.w loc_1A75C-off_1A658 dc.w loc_1A7C2-off_1A658 dc.w loc_1A7D6-off_1A658 dc.w loc_1A7E4-off_1A658 This will skip "loc_1A67A" commands. This is because this location pushes the scattered rings object (or the reverse scattered rings object) to d6. For our water code, we need d6, because all other data registers are being used. It doesn't do this in Sonic 1 or 2 because there's no such thing as "reverse gravity". The scattered rings object needs to be moved to a1, but we can't do it here, because shortly after, a1 is being used for the amount of rings. Next, go to "off_1A670:" This is extremely similar to what we just done, but for reverse gravity. so no point explaining what it's doing here as I just did that. Anyway, you should see: Code (ASM): off_1A670: dc.w loc_1A67A-off_1A670 dc.w loc_1A7E8-off_1A670 dc.w loc_1A7C2-off_1A670 dc.w loc_1A7D6-off_1A670 dc.w loc_1A7E4-off_1A670 And replace with: Code (ASM): off_1A670: dc.w loc_1A68C-off_1A670 dc.w loc_1A7E8-off_1A670 dc.w loc_1A7C2-off_1A670 dc.w loc_1A7D6-off_1A670 dc.w loc_1A7E4-off_1A670 Again, this will skip "loc_1A67A" commands. Explained why above. Next, go to "loc_1A67A:" and you should see this. Code (ASM): loc_1A67A: move.l #Obj_Bouncing_Ring,d6 tst.b (Reverse_gravity_flag).w beq.s loc_1A68C move.l #Obj_Bouncing_Ring_Reverse_Gravity,d6 Notice "Bouncing_ring" (and the reverse one) are being pushed to d6. We need d6, so, delete it. It's no longer needed. Trust me. We have now free'd d6. Although we've lost pushing the scattered rings into place. Do not worry, that's next. Next, go to "loc_1A6B6:". The very first command should be "addq.b #2,5(a1)" Just before that, add this: Code (ASM): move.l #Obj_Bouncing_Ring,(a1) tst.b (Reverse_gravity_flag).w beq.s loc_1A6B6CONTINUE move.l #Obj_Bouncing_Ring_Reverse_Gravity,(a1) loc_1A6B6CONTINUE: Yes, you might recognise this. It's part of that code you just this second deleted. We're pushing the scattered rings back into place. But instead of putting it to d6, we're putting it to a1. a1 is now available as it's not being used again for a while. So you have something looking at this: Code (ASM): loc_1A6B6: move.l #Obj_Bouncing_Ring,(a1) tst.b (Reverse_gravity_flag).w beq.s loc_1A6B6CONTINUE move.l #Obj_Bouncing_Ring_Reverse_Gravity,(a1) loc_1A6B6CONTINUE: addq.b #2,5(a1) move.b #8,$1E(a1) move.b #8,$1F(a1) move.w $10(a0),$10(a1) move.w $14(a0),$14(a1) move.l #Map_Ring,$C(a1) move.w #$A6BC,$A(a1) move.b #$84,4(a1) move.w #$180,8(a1) move.b #$47,$28(a1) move.b #8,7(a1) move.b #-1,(Ring_spill_anim_counter).w tst.w d4 bmi.s loc_1A728 move.w d4,d0 jsr (GetSineCosine).l move.w d4,d2 lsr.w #8,d2 asl.w d2,d0 asl.w d2,d1 move.w d0,d2 move.w d1,d3 addi.b #$10,d4 bcc.s loc_1A728 subi.w #$80,d4 bcc.s loc_1A728 move.w #$288,d4 Still looking at "loc_1A6B6:" look and find this bit of coding: Code (ASM): jsr (GetSineCosine).l move.w d4,d2 lsr.w #8,d2 Just underneath, paste this: Code (ASM): tst.b (Water_flag).w ; Does the level have water? beq.s skiphalvingvel ; If not, branch and skip underwater checks move.w (Water_level).w,d6 ; Move water level to d6 cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level? bgt.s skiphalvingvel ; If not, branch and skip underwater commands asr.w #$1,d0 ; Half d0. Makes the ring's x_vel bounce to the left/right slower asr.w #$1,d1 ; Half d1. Makes the ring's y_vel bounce up/down slower skiphalvingvel: So, you should end up with something looking like this: Code (ASM): loc_1A6B6: move.l #Obj_Bouncing_Ring,(a1) tst.b (Reverse_gravity_flag).w beq.s loc_1A6B6CONTINUE move.l #Obj_Bouncing_Ring_Reverse_Gravity,(a1) loc_1A6B6CONTINUE: addq.b #2,5(a1) move.b #8,$1E(a1) move.b #8,$1F(a1) move.w $10(a0),$10(a1) move.w $14(a0),$14(a1) move.l #Map_Ring,$C(a1) move.w #$A6BC,$A(a1) move.b #$84,4(a1) move.w #$180,8(a1) move.b #$47,$28(a1) move.b #8,7(a1) move.b #-1,(Ring_spill_anim_counter).w tst.w d4 bmi.s loc_1A728 move.w d4,d0 jsr (GetSineCosine).l move.w d4,d2 lsr.w #8,d2 tst.b (Water_flag).w ; Does the level have water? beq.s skiphalvingvel ; If not, branch and skip underwater checks move.w (Water_level).w,d6 ; Move water level to d6 cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level? bgt.s skiphalvingvel ; If not, branch and skip underwater commands asr.w #$1,d0 ; Half d0. Makes the ring's x_vel bounce to the left/right slower asr.w #$1,d1 ; Half d1. Makes the ring's y_vel bounce up/down slower skiphalvingvel: asl.w d2,d0 asl.w d2,d1 move.w d0,d2 move.w d1,d3 addi.b #$10,d4 bcc.s loc_1A728 subi.w #$80,d4 bcc.s loc_1A728 move.w #$288,d4 This ensures that when underwater, the rings x_vel and y_vel are halved so they appear to be moving slower. Next, go to label "loc_1A75C:" and under Code (ASM): move.b (Ring_spill_anim_frame).w,$22(a0) bsr.w MoveSprite2 addi.w #$18,$1A(a0) Put this: Code (ASM): tst.b (Water_flag).w ; Does the level have water? beq.s skiphalvinggrav ; If not, branch and skip underwater checks move.w (Water_level).w,d6 ; Move water level to d6 cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level? bgt.s skiphalvinggrav ; If not, branch and skip underwater commands subi.w #$E,$1A(a0) ; Reduce gravity by $E ($18-$E=$A), giving the underwater effect skiphalvinggrav: You can change the value of the gravity if you like, but I found $E to suit. So, you should have something looking like this: Code (ASM): loc_1A75C: move.b (Ring_spill_anim_frame).w,$22(a0) bsr.w MoveSprite2 addi.w #$18,$1A(a0) tst.b (Water_flag).w ; Does the level have water? beq.s skiphalvinggrav ; If not, branch and skip underwater checks move.w (Water_level).w,d6 ; Move water level to d6 cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level? bgt.s skiphalvinggrav ; If not, branch and skip underwater commands subi.w #$E,$1A(a0) ; Reduce gravity by $E ($18-$E=$A), giving the underwater effect skiphalvinggrav: bmi.s loc_1A7B0 move.b (V_int_run_count+3).w,d0 add.b d7,d0 andi.b #7,d0 bne.s loc_1A7B0 tst.b 4(a0) bpl.s loc_1A79C jsr (sub_F994).l tst.w d1 bpl.s loc_1A79C add.w d1,$14(a0) move.w $1A(a0),d0 asr.w #2,d0 sub.w d0,$1A(a0) neg.w $1A(a0) This ensures when underwater, the gravity is not as great. Otherwise, the rings will gain speed and then it won't give the desired underwater effect. This last bit is optional. You only need to do this if you're planning to have reverse gravity underwater. You'd be a bit mental if you do have this, but you never know =P Go to label "loc_1A7E8:" and under Code (ASM): move.b (Ring_spill_anim_frame).w,$22(a0) bsr.w MoveSprite_TestGravity2 addi.w #$18,$1A(a0) Put this: Code (ASM): tst.b (Water_flag).w ; Does the level have water? beq.s skiphalvingrevgrav ; If not, branch and skip underwater checks move.w (Water_level).w,d6 ; Move water level to d6 cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level? bgt.s skiphalvingrevgrav ; If not, branch and skip underwater commands subi.w #$E,$1A(a0) ; Reduce gravity by $E ($18-$E=$A), giving the underwater effect skiphalvingrevgrav: Again, you can change the value of the reverse gravity if you like. So, you should have something looking like this: Code (ASM): loc_1A7E8: move.b (Ring_spill_anim_frame).w,$22(a0) bsr.w MoveSprite_TestGravity2 addi.w #$18,$1A(a0) tst.b (Water_flag).w ; Does the level have water? beq.s skiphalvingrevgrav ; If not, branch and skip underwater checks move.w (Water_level).w,d6 ; Move water level to d6 cmp.w y_pos(a0),d6 ; Is the ring object underneath the water level? bgt.s skiphalvingrevgrav ; If not, branch and skip underwater commands subi.w #$E,$1A(a0) ; Reduce gravity by $E ($18-$E=$A), giving the underwater effect skiphalvingrevgrav: bmi.s loc_1A83C move.b (V_int_run_count+3).w,d0 add.b d7,d0 andi.b #7,d0 bne.s loc_1A83C tst.b 4(a0) bpl.s loc_1A828 jsr (sub_FCA0).l tst.w d1 bpl.s loc_1A828 sub.w d1,$14(a0) move.w $1A(a0),d0 asr.w #2,d0 sub.w d0,$1A(a0) neg.w $1A(a0) This ensures when underwater, the gravity is not as great when it's in reverse gravity mode. That should be it =) Now, with all this and my ASM guide I did years ago and my contributions to the SCHG, where's my tech status already? =P (joke)
I really can't emphasize enough that you should use lsr/asr instead of divu/divs whenever you are dividing by a power of 2. Meaning you really, really should change all instances of Code (Text): divu.w #$2,d* by Code (Text): lsr.w #$1,d* divu is almost 20 times slower than lsr in the particular case of division by 2.
20x slower? Bloody hell. That's very slow, also, as this code has got to repeat itself for each ring you lose, that must make it extremely slower. Thanks for the tip =P I've applied it to all three guides. EDIT: I've now applied it to all 3 guides. If you have used these for your hack, please change: Code (ASM): divu.w #$2,d0 ; Half d0. Makes the ring's x_vel bounce to the left/right slower divu.w #$2,d1 ; Half d1. Makes the ring's y_vel bounce up/down slower to this: Code (ASM): lsr.w #$1,d0 ; Half d0. Makes the ring's x_vel bounce to the left/right slower lsr.w #$1,d1 ; Half d1. Makes the ring's y_vel bounce up/down slower
Now that I looked more closely at the code, I think it should also be asr instead or lsr (just as it should have been divs instead of divu).
I'm not at home at the moment so I can't test it out, but what will be the difference? To me, once all bits have shifted to the right, the new bit introduced may be a 1 instead of a 0 (depends if it's a negative value).
This is very interesting, I can't believe I didn't remember the rings having the wrong gravity when underwater. Good work fixing them! They ended up not adding water to any level, so no need to check. Which makes me wonder if there could be any remnants (not counting graphics) of water implementation in the game...
ASR shifts arithmetically, which means -4 will become -2 after shifting. %1111111111111100 -> %1111111111111110 LSR shifts logically ignoring source data's sign, so -4 will eventually become 32766! %1111111111111100 -> %0111111111111110 If you're calculating signed numbers, there will be a huge bug with it. However, if you were dealing with shifting left (in case you wanted to multiply something by power of 2), there would be no difference between ASL and LSL, expect for ASL would set Overflow bit if sign bit was changed after operation.
I see. I have tried it and have noticed a slight difference. With lsr, some rings were in the same place as another (one directly behind another), and ASR seems to fix this. I will apply this to the guide.
http://info.sonicret...erwater_Physics Here you go. I just copied and pasted from the OP. Slapped headers on the thing, and added the page to the How-To guide, categories, and the template at the bottom. If anyone knows how to put in a contents table, please do it, cuz it could use one. I just figured it would be fine on one page... seeing as how this is essentially the same guide for all three games, for the most part. EDIT: Why the fuck does my Wiki Edits stay at 1??? I have certainly made way more than one... both major and minor.