Horizontal Movement
At any point, Mario may be moving left or right, determined by the sign of his speed
(Player_X_Speed). His speed is measured in subpixels/frame. In non-water areas, his
speed is hard
capped at ±40 when RunningTimer is set, or soft capped at ±24 otherwise. In water
areas, his speed
is always hard capped at ±24, and soft capped at ±16 on the ground. Note that a "soft cap" refers to
the fact
that in practice it is possible to exceed this cap by holding either no directional inputs or the
opposite of
the directional input corresponding to the direction Mario is traveling. An exception exists during
the pipe
entrance cutscenes (where GameEngineSubroutine == 7) where Mario's speed is hard capped
to 12.
Mario's subspeed (Player_X_MoveForce) is only used in calculating acceleration, and
does not
contribute to updating his position.
RunningTimer is always set to 10 when B is held, Mario is on the ground and the
directional inputs
held correspond to the direction Mario is moving
(Left_Right_Buttons == Player_MovingDir, where
Left_Right_Buttons is set to SavedJoypadBits & 0x03). It decrements by
1 every
frame these conditions aren't met.
Acceleration
Explanation
Mario's horizontal acceleration is stored in FrictionAdderHigh and
FrictionAdderLow,
following this algorithm:
$00 := 0
if Player_State != 0 then /* if Mario is in the air */
if Player_XSpeedAbsolute < 25 then
$00 := $00 + 1
if RunningSpeed then /* RunningSpeed is set if Player_XSpeedAbsolute >= 28 and is only unset if Left_Right_Buttons == Player_MovingDir and Player_XSpeedAbsolute < 28 */
$00 := $00 + 1
end
end
else /* if Mario is on the ground */
if AreaType == 0 then /* if in water area */
$00 := $00 + 1
else if Left_Right_Buttons != Player_MovingDir then
$00 := $00 + 1
if RunningSpeed then
$00 := $00 + 1
end
else if !(A_B_Buttons & #B_Button) and !RunningTimer then /* If B button is not pressed and RunningTimer is not set */
$00 := $00 + 1
end
end
FrictionAdderLow := FrictionData[$00] /* [0xe4, 0x98, 0xd0] for Mario, [0xb4, 0x68, 0xa0] for Luigi */
FrictionAdderHigh := 0
if PlayerFacingDir != Player_MovingDir then
/* Double Acceleration */
FrictionAdderLow := FrictionAdderLow << 1 /* Rolls bit 7 into carry */
FrictionAdderHigh := FrictionAdderHigh << 1 /* Rolls carry bit in */
end
Note that pressing L+R sets PlayerFacingDir to 3 (0b00000011), but
Player_MovingDir is
based solely on the sign of Mario's speed. This means that when L+R is pressed, Mario's horizontal
acceleration
will always be double it's original value.
Mario's horizontal acceleration is then applied first to subspeed then to his speed depending on the
sign of his
speed and directional inputs (note that if Mario's speed is zero, his subspeed will not be updated
if no buttons
are held; see the last entry of #Speed Resets). His speed is then capped
to his
maximum speed which depends on if RunningTimer is set.
Fast Acceleration
As Mario accelerates faster when he is facing the opposite direction he is going, one can
deliberately take
advantage of this, performing a trick called a fast acceleration. TASes usually perform what's
called a L+R fast
acceleration, where the inputs L+R, R+A are performed at the beginning of a section.
RTA runners,
who are either console runners physically unable to press the Left and Right D-pad buttons together,
or emulator
players who follow a no L+R rule present on most speedrun leaderboards can still perform a fast
acceleration, by
performing the inputs L, [nothing], R+A. Note that [nothing] means no Left, Right, or A
inputs, as
at least one other button has to be held (see the last entry of #Speed
Resets). The
same idea is then applied by both RTA runners and TASers to gain maximum running speed quicker.
Subspeed Manipulation
As there exists no possible acceleration value that sets FrictionAdderLow to 0 (on
NTSC), Mario's
subspeed will fluctuate even when his speed is capped. His subspeed can be frozen by releasing all
directional
inputs while in the air, useful for manipulating the speed at which Mario will accelerate. A common
example is
releasing right for one frame in order to maintain a subspeed of 0xF0 following a L+R fast
acceleration,
allowing Mario to reach the maximum running speed slightly faster.
Speed Resets
Mario's speed is reset to zero when:
- Mario collides with a wall
- Mario collides with the left side of the screen while holding either Left (0x02), L+R (0x03) or nothing (0x00)
- Mario dies
Mario's speed and subspeed are reset to zero when:
- Mario enters a screen transition
- Mario's absolute speed is less than 11, and any button (other than A and the direction Mario is traveling) is pressed
Miscellaneous
-
When Mario collects a powerup, the game will consider Mario to be on the ground for exactly one
frame,
allowing
RunningTimerto be set. This can be exploited as you can obtain running speed in the air after grabbing a powerup. This is used in the category "1-1 as Fire Mario" where the fire flower grab is called a darG grab. - Leftward movement is fundamentally different than rightward movement, as e.g. +0xD0 speed is stored as 00 D0, however -0xD0 speed is stored as FF 30 (upper byte signed two's complement), allowing Mario to move one subpixel left after only one frame of input from standby (00 00). This allows for fast y-subpixel manipulation off the left edge of a platform, as Mario can oscillate between -1 and 1 speed very quickly.
Wind (SMB2J)
In SMB2J, depending on whether the most significant bit of the frame counter is set, wind pushes
Mario by one
pixel every two or four frames (FrameCounter & 0b10000000 ? 2 : 4). This can be
exploited as a
method to clip into walls with much more leniency than normal, or even to fully clip while Mario is
traveling
downwards, which is otherwise impossible.
