# How to fix the overbounce bug

**URL:** <https://discourse.ioquake.org/t/how-to-fix-the-overbounce-bug/1897>\
**Category:** dev\
**Created:** [November 21, 2023, 6:16am UTC](https://discourse.ioquake.org/t/how-to-fix-the-overbounce-bug/1897 "2023-11-21T06:16:26Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![zturtleman](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.ioquake.org/zturtleman/32/718_2.png) [@zturtleman](https://discourse.ioquake.org/u/zturtleman)\
**Post date:** [November 21, 2023, 6:16am UTC](https://discourse.ioquake.org/t/how-to-fix-the-overbounce-bug/1897/1 "2023-11-21T06:16:26Z")

</div>

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)

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

```

I fixed overbounce in Spearmint by changing them to the below

```auto
	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.

---

<div class="post-metadata">

**Author:** ![zturtleman](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.ioquake.org/zturtleman/32/718_2.png) [@zturtleman](https://discourse.ioquake.org/u/zturtleman)\
**Post date:** [November 21, 2023, 6:55am UTC](https://discourse.ioquake.org/t/how-to-fix-the-overbounce-bug/1897/2 "2023-11-21T06:55:32Z")

</div>

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.

```auto
#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.

```auto
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.

```auto
    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).

---

<div class="post-metadata">

**Author:** ![an\_origamian](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.ioquake.org/an_origamian/32/737_2.png) [@an\_origamian](https://discourse.ioquake.org/u/an_origamian)\
**Post date:** [December 12, 2023, 4:23am UTC](https://discourse.ioquake.org/t/how-to-fix-the-overbounce-bug/1897/3 "2023-12-12T04:23:16Z")

</div>

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

There are three things at work here. As you mentioned, `VectorNormalize` and `PM_ClipVelocity` are involved in the bug. The last is that `PM_GroundTrace` says you are walking if 1) you are within 0.25 units of the ground, 2) you are not moving upward or away from the ground, and 3) the ground is not a steep slope. Colliding with the ground is not required. So if you are falling on to flat ground and are within 0.25 units of the ground, but haven’t actually touched the ground plane yet, then you are on the ground. `PM_AirMove` is designed to perform all collisions with the ground and `PM_WalkMove` is only designed for movement after `PM_AirMove` has already handled the collision, but in this uncommon case, `PM_WalkMove` has to handle the collision.

The “proper” way to fix this is to set `pml.walking` in `PM_SlideMove` right after a collision with the ground has occurred. This requires a lot of changes, especially to stair stepping, so a workaround like you wrote above is the best option IMO. Another workaround is to prevent `PM_WalkMove` from handling ground collisions by setting a flag that indicates when the player was just in air.

And here’s a third way. It’s been a while, but I think this is all I had to do in OpenArena’s Ratmod:

```c
// set groundentity
PM_GroundTrace();
if (pml.groundPlane && (pm->pmove_ratflags & RAT_NOOVERBOUNCE)) {
	PM_OneSidedClipVelocity(pm->ps->velocity, pml.groundTrace.plane.normal, pm->ps->velocity, OVERCLIP);
}

```

It handles the collision before it gets to the movement functions.  
`PmoveSingle` calls `PM_GroundTrace` twice. This is the first call to it. `pmove_ratflags` is set from a cvar. `PM_OneSidedClipVelocity` only projects the velocity if it is moving against the normal. `PM_ClipVelocity` might work in its place, but I don’t recall if I’ve tested it.

---

<div class="post-metadata">

**Author:** ![WofWca](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.ioquake.org/wofwca/32/795_2.png) [@WofWca](https://discourse.ioquake.org/u/WofWca)\
**Post date:** [October 13, 2026, 3:06pm UTC](https://discourse.ioquake.org/t/how-to-fix-the-overbounce-bug/1897/4 "2026-10-13T15:06:13Z")

</div>

Disclaimer: I’m not an expert on movement, and didn’t do much research, and other people have probably solved this problem way better than me (or maybe not).

Another manifestation of the overbounce bug, IMO, is this thing where shooting down at someone standing below you (directly below, or slightly to the side) will make them slide parallel to the ground at full knockback speed, as if the knockback was directed parallel to the ground and not towards the ground. The same can be done by shooting a rocket into a low ceiling above you.

I believe neither the “is on the ground?” check nor the “is the speed below 1” check is not going to be enough then, because you’re already on the ground, and the speed can indeed be above 1.

According to the comment above the two lines responsible for the bug, I think the most thorough fix would be to set the player’s “input” (`wishdir`) velocity according to the ground plane.  
But… Isn’t that what we’re already doing?

```c
// project moves down to flat plane
pml.forward[2] = 0;
pml.right[2] = 0;

// project the forward and right directions onto the ground plane
PM_ClipVelocity (pml.forward, pml.groundTrace.plane.normal, pml.forward, OVERCLIP );
PM_ClipVelocity (pml.right, pml.groundTrace.plane.normal, pml.right, OVERCLIP );
//
VectorNormalize (pml.forward);
VectorNormalize (pml.right);

```

Can’t we then just not run the responsible lines? I tested it, and the `VectorLength(ps.velocity)` on slopes is indeed equal to `g_speed`.

Obviously that would change how movement behaves, not only in the “overbounce bug” part, but I think it just makes sense for the ground plane to clip velocity the same way walls clip it.
