Actions

SCHG How-to

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:

; 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

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:

; 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

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:

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

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:

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

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.

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 Spin Dash Code and Add Spin Dash Speeds | 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
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
Adding Features
Create Insta-kill and High Jump Monitors | Create Clone and Special Stage Monitors | Port Knuckles
Sound Features
Port Sonic 1 Sound Driver | Port Sonic 2 Clone Driver | Port Sonic 3 Sound Driver | Expand the Music Index to Start at $00 (Sonic 2 Clone Driver Version)
Extending the Game
Extend the Level Index Past $10 | Extend the Level Select | Extend Water Tables | Add Extra Characters | Free Up 2 Universal SSTs