Warning: Long / mathematical post, with pictures.
Well, I dived into the source code to see why your frame rate would affect how high / far you can jump. I will define "wu" as quake3 world units. From the source code, I got the following constants:
Gravity = 800 wu / s2
Run velocity = 320 wu / s
Jump velocity = 270 wu / s
Quake3 has to model your velocity as a piecewise linear approximation to the actual curve, due to limited knowledge. Specifically, it doesn't know if you are going to hit a wall / missile / other player, or if you are going to use air acceleration to change directions. This means it cannot precalculate your entire path and just move you along it; it has to see what direction you are currently going and move you along that direction for a single time step. The time step (in seconds) is approximately 1 / frame rate. Time step is always less than 0.066 seconds.
I also found that, if g_syncronousClients is 0, the server uses the client's frame rate to get the time step for predicting the client's movement! This is probably to ensure consistency between client and server. It is crucial for frame-rate dependent jumps to work.
Recall from physics that gravity is an acceleration. It works on your z velocity vector. Quake3 uses the average vertical velocity over a time step, accounting for the change in velocity between the start and the end of the time step due to gravity. There is no cap on vertical velocity that I could see; air is frictionless, so you have no terminal velocity.
Anyway, I simulated this using floating point arithmetic. The result is depicted in the following picture:
Black = ideal trajectory
Red = 120 fps
Green = 100 fps
Yellow = 85 fps
Blue = 80 fps
Purple = 60 fps
Cyan = 40 fps
White = 15 fps
As you can see, each trajector follows the ideal curve almost exactly. This is not quite what I expected. Then I realized that space in the quake3 world is discrete rather than continuous, so I repeated the experiment, this time clamping to integer values. Using the same settings, I got these results:
So, the framerate dependency comes from two factors:
1) Approximating the actual continuous function with a piecewise linear function.
2) Round-off error.
There is no way around either of these factors. The only thing you can do is make the time step a constant, but that would complicate the client-side rendering.
Okay, I stop typing now.
Edit: Made the second picture actually the second picture.
WARNING: EXTREMELY long post with only 1 smiley -- but it's worth it, as I answer pretty much every physics / FPS question in quake 3.
Earlier this evening, Ctrl told me that he was dissatisfied with my previous explanation of why your fps affects jumping. So, I did some more research. Specifically, I wrote a mod to measure your jumping.
Here are the key points that I discovered:
(1) The final velocity vectors are clamped to integers every time the player movement code is called. This gets called once per frame for fast clients, but never less than 20 times per second.
(2) This conversion is done by the equation (int)x, where "x" is a floating point value.
(3) The integer conversion done by the Q3 vm *IS NOT ANSI COMPLIENT*. ANSI C specifies that integer conversion is done by ignoring the fraction. The Q3 vm does it by rounding to the nearest integer.
This third point explains exactly why DLL's have slower speeds and jump heights than QVM's. In a QVM, the rounding is to nearest integer, so errors will tend to cancel out. In a DLL, rounding is always towards 0, so errors always reduce your speed and will always accumulate. The rounding error in a DLL always acts as extra friction. I expect you can get around this problem in DLL's by rewriting the "SnapVector" macro to emulate the QVM's rounding method.
Now, back to the jumps. In theory, the rounding errors should cancel out over the number of frames in a typical jump. This assumes that the fraction can be any value with equal probability. However, in practice, this is not the case. Each frame tends to be the same time as the previous one. The change in velocity is the acceleration times the frame time. Acceleration is due to gravity, and is therefore constant. Frame time is also nearly constant, so the change in velocity is also nearly constant in each frame. Since velocity always starts as an integer, and the change is always nearly the same, under a constant fps rounding has nearly the same error each frame. With a constant frame rate, Q3's rounding errors will tend to accumulate. For some frame rates, this will always round down; for others, it will always round up.
Based on this, the ideal frame rate for jumping distance is the highest one your computer can maintain that gets a fraction remainder near 0.5, but always greater than 0.5. Q3's gravity is 800, so you want the fractional part of 800/fps to be greater than 0.5. To protect against frame rate fluctuations, you'd also want nearby frame rates to have fractions greater than 0.5. Lastly, you want there to be as many frames as possible, so that the most possible positive error gets accumulated.
Matching our conditions with the table, we predict that the best frame rates would be about 29, 41, 83, 92, 120, 140, and 170. Notice that these numbers are very near the values that people have found experimentally. This match between theory and experience lends credence to the model used. ..........
Q3 measures frame time in an integer number of milliseconds. This means that there are only certain frame rates that it can hit... specifically, frame rates of 1000 / N, where N is an integer number of milliseconds. Given this, you can find viable frame rates by varying N. So, the possible frame rates are:
200
166-167
142-143
125
111-112
100
90-91
83-84
76-77
71-72
66-67
62-63
58-59
55-56
52-53
50
47-48
45-46
43-44
41-42
40
38-39
37-38
35-36
34-35
33-34
32-33
31-32
30-31
29-30
28-29
27-28
26-27
25-26
25
24-25
23-24
22-23
21-22
20-21
20
The lines with multiple numbers are ones that do not evenly divide 1000, so for example you cannot exactly hit 142 or 143 fps but must toggle between them.
So, when testing values for com_maxfps, these are the only numbers it really makes sense to try:
200
166
142
125
111
100
90
83
76
71
66
62
58
55
52
50
47
45
43
40-41
37-38
20-35