Fix monitor collision bug
From Sonic Retro
(Original guide by redhotsonic)
There's a bug in Sonic 2 that's not in any other game. The monitor's collision. Well, I say collision, but it's actually fine. It's the touch_collision that's actually wrong. The thing is, in the original Sonic 2 game, it's very hard to spot this bug. Mainly because the monitors are all placed on a flat floor. But whereas many of us have changed the level layout of our hacks, some of you may have placed them on hills or slopes (or at the beginning/end of hills or slopes), the bug may be noticeable.
The bug? You go straight through the monitors without destroying it. This can be a pain in the ass. What makes it worse, is that if you've placed a monitor on a hill but near a wall, there's a great chance you can go through that wall. This isn't meant to happen obviously.
To see for yourself, enable debug and put a monitor in the air. Now, go run and jump to it. If you come from either side of the monitor while going up, you will go through the monitor. That's why this bug is more common on hills/slopes. If you're rolling up a hill into the side of the monitor, chances are you'll go through it without destroying it. But why? Let's look at the code and see why:
<asm>
- loc_3F73C
Touch_Monitor:
tst.w y_vel(a0) ; is Sonic moving upwards? bpl.s loc_3F768 ; if not, branch move.w y_pos(a0),d0 subi.w #$10,d0 cmp.w y_pos(a1),d0 bcs.s return_3F78A neg.w y_vel(a0) ; reverse Sonic's y-motion move.w #-$180,y_vel(a1) tst.b routine_secondary(a1) bne.s return_3F78A move.b #4,routine_secondary(a1) ; set the monitor's routine counter rts
</asm>
It does this because there are checks to see if you've hit the bottom of the monitor and if so, to knock the monitor down. But if you go through the sides, it does this check, and it thinks you've missed the bottom when going up to hit it from underneath. Because of this, it branches to an rts.
In the code above, it asks if you're moving up and if not, branch away to destroy the monitor. But if moving up, it's doing the check to see if you're going to hit the bottom of the monitor. If you do, it will reverse Sonic's y_vel, and change the routine of the monitor so it can be knocked and fall. BUT, if you miss the bottom, it will branch to "return_3F78A" which is an rts. So, if you're hitting the side of the monitor but in an upwards state, it will branch to the rts; making Sonic go through the monitor and not breaking it. As soon as Sonic's y_vel hits 0 or more (starts to fall down), the monitor will break, because of the very first command. So, we need to make the monitor breakable by hitting the side of it but still going up. Here's how. Go to the code I just showed you above, and change it to this:
<asm>
- loc_3F73C
Touch_Monitor:
tst.w y_vel(a0) ; is Sonic moving upwards? bpl.s loc_3F768 ; if not, branch move.w y_pos(a0),d0 subi.w #$10,d0 cmp.w y_pos(a1),d0 bcs.s loc_3F768 ; Changed to loc_3F768 instead of return_3F78A neg.w y_vel(a0) ; reverse Sonic's y-motion move.w #-$180,y_vel(a1) tst.b routine_secondary(a1) bne.s return_3F78A move.b #4,routine_secondary(a1) ; set the monitor's routine counter rts
</asm>
All we've done here is changed "return_3F78A" to "loc_3F768". So now, when moving up but hitting the sides of the monitor, instead of it branching to "rts", it branches to the "break monitor" coding! This means you will now destroy them. You can still hit the bottom of the monitor and it will knock over. Any other way of hitting the monitor remains unaffected.
Additional Step
It's now fixed, but there remains one design flaw (which is actually present in S3K). You know when you hit the monitor, like jumping on it, Sonic's y_vel negates, so he appears to bounce. But with our new fix, if you destroy the monitor from the sides going up, Sonic's y_vel still negates, so he shoots down. It does this in S3K also. To me, this looks unnatural. If you like this, ignore this step. If you don't want Sonic's y_vel to negate when going up, but still to negate when going down, it's simple to do so.
Go to "loc_3F768:" and you should see this: <asm> loc_3F768:
cmpa.w #MainCharacter,a0 beq.s + tst.w (Two_player_mode).w beq.s return_3F78A
+
cmpi.b #2,anim(a0) bne.s return_3F78A neg.w y_vel(a0) ; reverse Sonic's y-motion move.b #4,routine(a1) move.w a0,parent(a1)
return_3F78A:
rts
</asm>
See that? It's negating Sonic's y_vel no matter what when you destroy the monitor. We don't want it to when we're going up, we want to carry on going up. So, change it to this: <asm> loc_3F768:
cmpa.w #MainCharacter,a0 beq.s + tst.w (Two_player_mode).w beq.s return_3F78A
+
cmpi.b #2,anim(a0) bne.s return_3F78A tst.w y_vel(a0) ; is Sonic moving upwards? blt.s + ; if so, branch, we want Sonic to carry on moving up ; So, Sonic is moving down instead? neg.w y_vel(a0) ; reverse Sonic's y-motion, to give him that bounce off the monitor
+
move.b #4,routine(a1) move.w a0,parent(a1)
return_3F78A:
rts
</asm>
Now, it checks if you're moving up, and if so, do NOT negate Sonic's y_vel. If you're moving down, it won't branch, and negate his y_vel, giving him his bounce.
All done!
NOTE: Again, this bug is only in Sonic 2. In Sonic 1, it branches to a subroutine to make the sides of the monitor completely solid, but you can't destroy the monitor. In Sonic 3 and Knuckles, the coding for hitting the monitor underneath no longer exists. If you hit it underneath in S3K, it will get destroyed. So no bug there.