This time we're going to face

`fuser2`

parameter that was introduced in CS 1.6, so we will have to use

pm_shared.cpp from

**ReGameDLL** project instead of

*pm_shared.cpp* from

**HLSDK**.

Let's consider

*PM_PlayerMove* function from the article about strafe physics in more detail:

As you can see, there are several types of movement, and the functions we have considered, such as

*PM_Friction*,

*PM_WalkMove* and

*PM_AirMove*, are called for type

`MOVETYPE_WALK`

. With

*PM_Duck*, which is responsible for the duck process, we will get acquainted in the next article, but for now we will be interested in the call of

*PM_Jump* before

*PM_Friction*. Note that this call occurs if

`IN_JUMP`

bit is set in the

`cmd.buttons`

variable that stores the states of the buttons. When we press the jump button,

+jump command is sent to the engine, and

`IN_JUMP`

bit becomes 1. When the button is released, the

–jump command is sent, and at the same frame

`IN_JUMP`

is reset. Read more about this and why we can jump using the scroll, under this

spoiler

Let's take a look at *input.cpp* and find the function *InitInput*. Here we find that command +jump initiates a call of the function *IN_JumpDown*, which changes the variable `state`

of the jump button: the first and second bits of `state`

are set. The first bit means that the button is pressed in general, and the second - that the button was pressed just now. The –jump command initiates a call of *IN_JumpUp*, which clears the first bit (button is no longer pressed) and sets the third bit (the button is released just now).

Further in *CL_ButtonBits* we find that the `IN_JUMP`

bit in `cmd.buttons`

is set if `state`

has the first or the second bit set. Thus, if we jumped with the help of Space for example, then `IN_JUMP`

will be set starting from the frame when we pressed the Space. At the frame when the Space was released, the `IN_JUMP`

bit has been cleared. If we jumped with the help of a scroll, then the commands +jump and –jump were sent one after another at the same frame. Then at this frame the first bit of `state`

is zero, that is, the jump button is considered not pressed. However, since the second bit of `state`

is set, then `IN_JUMP`

in `cmd.buttons`

will also be set, which means *PM_Jump* will be called. That's why we can jump with the scroll.

Now it's time to see

*PM_Jump*:

Note that if there was a bit

`IN_JUMP`

at the previous frame, then there won't be a jump at the current frame. Otherwise, we could do bhop just by holding the

Space. Moreover, this condition also means that the command

+jump;-jump sent with the scroll will push us off from the ground only if there was no jump command at the previous frame. How does this affect our bhop?

We introduce the term

**FOG** (frames on the ground) - the number of frames that player spends on the ground during one bhop.

Theoretically bhop can be done with

Space, but human reaction is not good enough to send a jump command in time. By scrolling we send several jump commands at once, and it allows us to minimize FOG and lose less speed. However, too fast scrolling will lead to the fact that several jump commands can go in a row, which means that only the first one can cause a jump, the rest will be useless. A good distribution of jump commands is when we alternate frames with

+jump and without it. In this case, we guarantee 1 or 2 FOG. In practice, the distribution for every player is different, but, nevertheless, with time everyone succeeds in minimizing the number of bhops with more than 2 FOG.

### Horizontal speed

However does lesser FOG always means lesser loss of speed? First of all, it should be clarified that we are interested in horizontal speed now. After all, horizontal speed before hitting the ground is what lj stats shows us as a bhop prestrafe. For simplicity we will not pay attention to the variable

`fuser2`

for now, and also let the vertical speed on the ground be zero. Consider two cases:

1) 1 FOG bhop. A successful jump occurred at the same frame when we were on the ground. Inside

*PM_Jump*, the variable

`pmove->onground`

became equal to -1, therefore

*PM_Friction* in

*PM_PlayerMove* will not be called. The only function that can affect horizontal speed is

*PM_PreventMegaBunnyJumping* in

*PM_Jump*. Here's what it looks like:

Here

`pmove->maxspeed`

(with usp or knife) is 250 units/s, so

`maxscaledspeed = 300`

units/s. If the speed is more than 300 it becomes equal to

`maxscaledspeed * 0.8 = 240`

units/s (keeping the direction). Otherwise speed does not change at all!

2) 2 FOG bhop. A successful jump occurred one frame after landing. At the first frame on the ground, only

*PM_Friction* affected us taking away 4 percent of the speed, and at the second - only

*PM_PreventMegaBunnyJumping*. That is, at the first frame the speed will be lost in any case, but if after that it turns out to be not more than 300 units/s, then the second frame will pass without loss.

Thus, we could do without a loss of speed if we performed a bhop of 1 FOG at a speed of not more than 300 units/s. However, in practice, the large distances between the blocks and the need to change direction during bhop force us to pick up speed over 300. And then it turns out to be more profitable to make 2 FOG bhop! For example, let our speed before landing be 310 units/s. Then 1 FOG bhop will cut it to 240, and 2 FOG bhop will cut it to 297.6 units/s. Below we will correct these calculations taking into account the vertical speed and the variable

`fuser2`

, but generally the result will be the same.

While we can somehow control the speed with strafes, it is physiologically impossible to fully control the amount of FOG. Scrolling can give us a high chance of making FOG less than 3, however whether it is 1 or 2 FOG - partly player experience, partly random element. On the one hand, this dependence on luck is not very pleasant when it comes to competition. On the other hand, the unpredictability of FOG teaches the player to assume before each bhop the possibility of the worst option and to quickly make a decision after bhop. And therein lies another highlight of kreedz, because of which it became interesting to such a large number of people.

### Vertical speed

Each frame reduces the vertical speed due to simulated gravity, which is specified by the cvar

sv_gravity, an analogue of the acceleration of gravity. This happens in the functions

*PM_AddCorrectGravity* and

*PM_FixupGravityVelocity*, which essentially do the following:

Here

`ent_gravity`

can be considered equal to 1,

`pmove->movevars->gravity`

is exactly the value of

sv_gravity (800 by default), and the duration of the frame in seconds

`pmove->frametime`

at 100 fps is 0.01. In total, the vertical speed inside the function will decrease by 4 units/s. If

*PM_Jump* is not called inside

*PM_PlayerMove*, then

*PM_AddCorrectGravity* and

*PM_FixupGravityVelocity* will reduce the vertical speed by 8 units/s, and then, if we are on the ground, it will be reset. If

*PM_Jump* is called, then the vertical speed in it becomes

`sqrt (2 * 800 * 45) = 268.33`

unit/s, and then decreases by 8 units/s due to two calls of

*PM_FixupGravityVelocity* (inside

*PM_Jump* and later in

*PM_PlayerMove*).

From here it is easy to get the height of the jump. We can assume that on the ground the initial speed is

`V0 = sqrt (2 * 800 * 45)`

unit/s, the acceleration is directed down and equal to

`a = 800`

units/s^2 . Then the height will be

`H = V0 ^ 2 / (2 * a) = 2 * 800 * 45 / (2 * 800) = 45`

units.

Note that if fps decreases, then a smaller number of frames will be necessary for the jump, however, due to the fact that the formula uses

`pmove->frametime`

, the height of the jump will remain almost the same. If you look again at the article about strafe physics, you will find that the same principle is laid down in the functions

*PM_Accelerate*,

*PM_AirAccelerate* and

*PM_Friction*.

And one more important point: when we make a jump, we have

*PM_AddCorrectGravity* called before

*PM_PreventMegaBunnyJumping*, so the vertical speed at this moment will not be zero, but -4 units/s. Consequently, the total speed will be more than 300 when the horizontal component is greater than

`sqrt (300^2 - 4^2) = 299.973`

unit/s. So when jump statistics says that a good prestrafe must be less than 300 units/s, it deceives you a little.

### fuser2

And now the most interesting part. In the article about HighJump physics we became more familiar with the friction in the GoldSource engine. During the evolution of Counter-Strike, the parameter

`fuser2`

was added to this friction, which affects all three components of speed. At the moment of the jump in

*PM_Jump* function the variable

`fuser2`

becomes equal to

`1315.789429`

(looks like a magic number, but I suppose it is

`100 * 1000 * 19.0 / 4.0`

). Then each frame in

*PM_PlayerMove* we have a call of

*PM_ReduceTimers*, which along with other things reduces

`fuser2`

by the length of the frame in milliseconds (i.e. at 100 fps 10 is subtracted from

`fuser2`

). If within 1.31 seconds a new jump does not occur, then

`fuser2`

reaches zero and no longer affects physics.

However even on a flat surface

`fuser2`

does not have time to be reset between bhops. Because of this in

*PM_Jump*, before the call of

*PM_FixupGravityVelocity*, the vertical speed is truncated. The horizontal speed at the beginning of the function

*PM_WalkMove* also changes in the same way:

*PM_WalkMove* is called only on the ground, and we feel it when doing normal jumps on any climb section. The less time we spent in the air, the longer we have to pause after landing before the next jump. Now you understand that the

`fuser2`

variable is to blame for it.

Let's go back to a flat surface. First, we just run and jump. At this moment,

`fuser2`

is zero, so at 100 fps we know for sure that the vertical speed is 268.33 units/s, and that we will stay in the air for 66 frames (we are not considering stand-up bhop yet), and that the maximum jump height is 45 units. Suppose that by the end of the flight we reached a speed of 310 units/s. As it was found earlier, it would be more profitable for us to make 2 FOG bhop. Let's check if this result changes with the account of

`fuser2`

.

At the first frame on the ground

*PM_Friction* will take 4 percent from the horizontal speed, leaving us 297.6 units/s. After 66 frames in the air and 1 frame on the ground

`fuser2 =1315.789429 - 67 * 10 = 645.789429`

, therefore after

*PM_WalkMove* the horizontal speed will become equal to

`297.6 * (100.0 - 645.789429 * 0.001 * 19.0) * 0.01 = 261.08`

unit/s. At the second frame inside

*PM_PreventMegaBunnyJumping*, we find that

`261.08 < 299.973`

, so the horizontal speed will not change. In the case of 1 FOG, the speed would be cut to

`299.973 * 0.8 = 239.98`

units/s, so 2 FOG in this case really remained more profitable. By the way, if we did DropBhop, during which we had to pick up a fairly large horizontal speed, then 3+ FOG bhop could be even more profitable.

As for the vertical speed, at the second frame on the ground

`fuser2`

will decrease by another 10, and the vertical speed before calling

*PM_FixupGravityVelocity* will be

`268.33 * (100.0 - 635.789429 * 0.001 * 19.0) * 0.01 = 235.92`

unit/s. Then, after bhop we will spend 58 frames in the air, and the jump height will be just 34.78 units. At the next bhop, depending on

`fuser2`

and the number of FOG, we will be in the air for about 56-58 frames.

If we used a stand-up bhop instead of the usual one on a flat surface, we would have spent more (about 64-65) frames in the air between jumps cause of ducking. Due to this,

`fuser2`

would have time to decrease more, and jumps would be about 1 unit higher, and the speed in the case of 2 FOG bhop would not decrease so much. However, this does not mean that stand-up bhop is always better than usual one, because it takes more time. That is why experienced players often try to do a regular bhop instead of a stand-up if possible.

We will return to the topic of stand-up bhop and explore the work of duck in the next article.

Kpoluk@Juicesince I insert code fragments a lot, I'm trying to engage the same terms that used there. I saw "stamina" in Source SDK notes, and I suppose it does some similar but not completely the same things as fuser2 in GoldSrc. So if you know something about differences you can describe it here, would be a good supplement.Juice