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.
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.
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.)
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 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."
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.