Bughunting: Slopes thumbnail

Bughunting: Slopes

Published 2015-11-03

My little "pet project" over the last little while, so to speak, has been ROM hacking Sonic R, the less-than-loved racing game from 1997. Why? So I can make it better. It has potential, it just needs a bit of work.

One of those "bits of work" happens to be fixing some glaring issues. Now, even though I titled this series "Sonic R Bughunting", Sonic R is a rather glitch-free game. It will pretty much never crash via normal gameplay, or abnormal gameplay at that. The problem is that it was programmed in a bit of a rush, and it shows. Some things simply weren't thought out as well as they could have been. For example, the player's interaction with sloped floors is a bit flawed.

First, let's explain a few concepts. Characters (up to 5 of them) have all of their information stored in RAM. This includes the character's location, direction, identity and velocity, among many, many other things. The location information in particular is modified by a function I'm calling "Character Physics", or "CharPhysics" for short. CharPhysics is called for one character at a time, and takes the chunk of memory containing that character's information as input.

Simplified flowchart of CharPhysics' checking routines

The function takes the character's identity, looks it up against a table of known coefficients for various attributes (Acceleration, turn speed, etc.) and puts it in local memory to work with later. This allows it to a different acceleration and top speeds and the sort for Sonic compared to, say, Tails. It then looks at the character's environment and actions: Is the player jumping? Underwater? On ice? On a slope? If the character is currently in any of those situations, it modifies the coefficients accordingly. For example, on ice, the player's turning speed and traction is cut in half, making it harder to control the character.

Diagram showing slopes of different levels and Sonic's reaction to them.

Our issue stems in the code that checks if the character is on a slope. Normally, if the player is on a slope less than a certain angle (about 10-15 degrees), the game assumes the ground is more or less flat and skips the slope calculations. Of course, that doesn't sound like an issue at all. If that code was removed, the character would start sliding down even the slightest incline as if it were polished with wax. (You experimenters out there: remove that code [I'll tell you where it is later] and load up Radical City. The results will be amusing.)

Sinewave-like hills, with the middle (with the largest slope) shaded in red to show that slope physics start applying there.

The problem with that line of thinking comes with things like hills and banked curves. On slight inclines like these, the expectation is that the player follows them. The player should speed up when going down a hill, slow down when going up one, turn with a curved road, etc. And the player does... if the slope is larger than the cutoff point. You see, the cut-off point was designed so that when you're standing still or just starting to move, you don't slide down a shallow hill, but you do slide down a steep hill. However, this logic is being applied no matter what you're doing on that slope. Because of this over-generalization, things like small hills or banked curves do absolutely nothing. You'll just plow straight though them as if they were flat ground.

The dreaded Resort Island canyon

The most prominent example of this is in Resort Island, in the canyon bit. This is normally impossible to cross without slamming into a wall, because the banked turns don't guide you past the walls as expected. As JonTron puts it, "I have to give [the developers] credit, because they programmed MAGNETS into the walls."

Sinewave hill with speed cutoff and one without.

Now, how would you fix such a thing? You can't get rid of the cutoff, or else Sonic will fall flat on his face every time he stops on something as simple as a handicap ramp. My proposed solution is to only apply the cutoff when the player is under a certain speed. It makes sense logically; you tend to have more footing when standing still compared to when you're running, where a misstep could send you to the ground easily due to your momentum. This speed cutoff should be fast enough so that the character has enough momentum to not immediately fall off the slope they were just standing on, yet slow enough so that strategically slowing down won't completely mess up physics.

Here's the fun bit: the patch! At $485E20 (offset $76220) in the EXE (PC, 1998), change

cmp     word ptr [ebx+72h], 0 ; Skip if jumping
jz      CharPhysics_SlopeEnd
mov     eax, [ebx+0B4h] ; Skip if not on big hill
sar     eax, 10h
cmp     eax, 0FFFFF138h ; Cutoff angle
jle     CharPhysics_SlopeEnd
mov     eax, [ebx+40h]  ; In water?
sar     eax, 10h
test    eax, eax
jnz     short CharPhysics_SlopeEnd
test    byte ptr [ebp+Input+1], 10001b ; Accelerating with fwd or button?
jz      short loc_485E8C
mov     eax, [ebx+84h]
sar     eax, 10h
test    eax, eax
jg      short loc_485E8C ; Unknown
mov     dh, byte ptr [ebp+Input]
test    dh, 8           ; Pressing left
jz      short loc_485E69
test    dh, 80h         ; Pressing right
jnz     short loc_485E8C

to

cmp     word ptr [ebx+72h], 0 ; Skip if jumping
jz      CharPhysics_SlopeEnd
cmp     [ebx+44], 00020000    ; Speed cutoff
jg      CharPhysics_SlopeCheckSkip

mov     eax, [ebx+0B4h] ; Skip if not on big hill
sar     eax, 10h
cmp     eax, 0FFFFF138h ; Cutoff angle
jle     CharPhysics_SlopeEnd

CharPhysics_SlopeCheckSkip:
test    byte ptr [ebp+Input+1], 10001b ; Accelerating with fwd or button?
jz      short loc_485E8C
mov     eax, [ebx+84h]
sar     eax, 10h
test    eax, eax
jg      short loc_485E8C ; Unknown
mov     dh, byte ptr [ebp+Input]
test    dh, 8           ; Pressing left
jz      short loc_485E69
test    dh, 80h         ; Pressing right
jnz     short loc_485E8C

Or, download this IPS patch file that incorporates the changes above. (To use, grab a copy of Lunar IPS or the like and open it with that.)

(Eagle-eyed readers will notice that the new code is missing the underwater check. It was removed to make way for the new code. This was OK because it is physically impossible to have an underwater slope in Sonic R, making it entirely pointless.)

Of course, this is no magic bullet to fixing the game. Let's not pretend these tiny slopes are going to make a huge difference. However, it does make the game just feel a little better to play.