How to fix the overbounce bug

There are trick jumps that can be done in certain places by “overbounce” which increase velocity to a huge value and throws the player back into the air after hitting the ground.

Anyway, I mainly test the game on q3dm1 and I got really tired of just jumping in q3dm1 court yard causing overbounce. Jump and when the player lands, they jump again.

To get rid of it, in code/game/bg_pmove.c there is two places with the following (PM_WalkMove, PM_WaterMove)

	VectorNormalize(pm->ps->velocity);
	VectorScale(pm->ps->velocity, vel, pm->ps->velocity);

I fixed overbounce in Spearmint by changing them to the below

	if ( pm->pmove_overbounce || VectorLength(pm->ps->velocity) > 1 ) {
		VectorNormalize(pm->ps->velocity);
		VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
	}

I added pm->pmove_overbounce which is set from a cvar (“pmove_overbounce”) before calling Pmove() in game/cgame to allow enabling/disabling it. You can remove “pm->pmove_overbounce ||” to just disable overbounce.

2 Likes

In 2017, I wrote an explanation that I never finished/posted.

Overview

Causing the overbounce bug is simple. Stand still on a flat surface and jump repeatedly. It may take 1 to 10 jumps or so. If the player automatically jumps again when they hit the ground, congratulations you’ve experienced the overbounce bug! It shouldn’t matter where the player is but I usually test in the courtyard of the q3dm1 map.

People have found ways to exploit the overbounce bug by falling from certain locations but that’s outside my area of interest. The key point is no horizontal movement when the player hits the ground.

What went wrong

Player walking and running maintains velocity force when going up and down slopes. The following code is from PM_WalkMove() in code/game/bg_pmove.c.

#define    OVERCLIP                1.001f

	vel = VectorLength(pm->ps->velocity);

	// slide along the ground plane
	PM_ClipVelocity(pm->ps->velocity,
		pml.groundTrace.plane.normal, 
		pm->ps->velocity,
		OVERCLIP);

	// don't decrease velocity when going up or down a slope
	VectorNormalize(pm->ps->velocity);
	VectorScale(pm->ps->velocity, vel, pm->ps->velocity);

The velocity is clipped against the ground plane. The clipped velocity is then normalized to a unit length of 1 and scaled back to the original velocity’s vector length. This allows the original velocity vector length to be maintained in the clipped direction.

The problem is PM_ClipVelocity() can clip too much and incorrectly change the direction. The falling velocity (0, 0, -270) clipped against an upward facing surface (0, 0, 1) becomes velocity (0, 0, 0.27). The velocity is then normalized to (0, 0, 1) and multiplied by the original velocity vector length (270) resulting in final velocity (0, 0, 270). The player is then launched back into the air.

Let’s have a look at PM_ClipVelocity() in code/game/bg_pmove.c.

void PM_ClipVelocity( vec3_t in, vec3_t normal, vec3_t out,
			float overbounce ) {
	float	backoff;
	float	change;
	int		i;
	
	backoff = DotProduct (in, normal);
	
	if ( backoff < 0 ) {
		backoff *= overbounce;
	} else {
		backoff /= overbounce;
	}

	for ( i=0 ; i<3 ; i++ ) {
		change = normal[i]*backoff;
		out[i] = in[i] - change;
	}
}

(I think there should be more written here? 2017 me didn’t write it.)

Solution

My solution for the problem is to only normalize and rescale the velocity if the clipped velocity length is more than 1 unit. It works unless the player’s falling velocity is over 1000 which I don’t think can happen.

    if ( pmove_overbounce || VectorLength(pm->ps->velocity) > 1 ) {
        // don't decrease velocity when going up or down a slope
        VectorNormalize(pm->ps->velocity);
        VectorScale(pm->ps->velocity, vel, pm->ps->velocity);
    }

Note: Quake III Arena bg_pmove.c source code is available under the Q3SDK end user license agreement and the GNU General Public License version 2 or later.

2023 tl;rd if clipped velocity VectorLength(pm->ps->velocity) is less than 1, it may of flipped direction and basically impacted directly into the surface normal and velocity should probably be 0 (which trap_SnapVector() probably does).

1 Like