NaN Bounce

From P2SR Wiki

NaN Bounce

Overview

NaN Bounce is a glitch that allows the player to teleport to world's origin (coordinates X:0, Y:0, Z:0) by bouncing on a repulsion gel while being in a funnel and near a portal environment. It was found by Discord user goatleg#6619 on April 7th 2023.

Execution

Original discovery of NaN Bounce by goatleg

In order to perform the NaN bounce, you have to meet these conditions:

  • You need to be in a funnel.
  • You need to be in a portal environment (intersect the portal's bounding box)
  • You need to be midair without jumping or bouncing

Once these conditions are met, bouncing on the perfectly horizontal floor with repulsion gel on it will result in the player teleporting to the world's origin.

Usage

So far, there is no useful way to utilize this glitch in Portal 2 SP or Co-op, nor in any of the more popular Portal 2 mods. Barely any maps have an useful location placed at world's origin, and even less have required elements (funnel and repulsion gel) to even perform it in the first place.

Explanation

All of real numbers in the game are stored in a floating point data type. This data type has a special value called NaN (not a number) which is used as a result of calculations which have no valid numerical answer, such as square root of a negative number or a division by zero. Consequently, if we perform any calculation on a NaN, the result of that is also NaN.

When player is bouncing the first time from the horizontal floor with a repulsion gel on it by landing on it without jumping, the game is using a different function to calculate vertical velocity of the player[1]. Part of this function involves dividing the Z component of the velocity by current player's gravity. However, when player is in a funnel, their gravity is nearly equal to zero. This is where NaN is created and stored in a velocity component.

This was predicted by the developers, as later on, when the game does the movement calculations, velocity gets checked multiple times for NaNs, which are reset to 0 as a safety measure [2]. Fortunately, this can be bypassed. These checks are done on a copy of the velocity vector, not the original, which means if NaN managed to get produced, it is cleared on the copy, but still is in the original until it's overwritten by the copy when it's applied to player's position.

Funnily enough, funnel movement code is using the original velocity vector for its calculations[3], meaning that after all these checks, NaN value gets back to the copy of velocity, with an additional benefit of all components (not just Z, but X and Y as well) becoming NaN as well due to the funnel code performing mathematical operations using all of the components. This, however, isn't enough, as the player needs to be in a portal environemnt as well in order to avoid the last of the velocity check. Once it's time to apply velocity on top of the position, all position components become a NaN value, which, upon the next check, the game resets to 0, teleporting the player to world's origin.

Notes

  1. UpdatePaintZ() function in portal_player_shared.cpp, [1]
  2. CheckVelocity() function in gamemovement.cpp, [2]
  3. CalculateFrameMovement() function in trigger_tractorbeam_shared.cpp, [3]