Ideal bhop explained

Started by FAME
01/07/2016 15:54
Sticky
Page 1 of 2
FAME01/07/2016 15:54(edited 06/02/2019 19:15)

Recently I got asked some questions about bhopping so I decided to post this to forum, hopefully more people will find it useful.

Before starting, here are a few things you need to know:

1. When you perform a jump using mouse wheel it executes both +jump and -jump commands within one frame.
2. When client sends +jump command, engine sets IN_JUMP bit to cmd->buttons variable and when client sends -jump engine removes IN_JUMP bit.
3. FOG is abbreviation for 'frames on ground'.
4. Velocity.z is a player's vertical speed.

Unlike HL1 and KZmod where you can roll mouse wheel like a crazy without any consequences, in order to achieve decent bunnyhop in CS 1.6 you need to do it with a certain speed. When you scroll jump being on the ground, engine checks if you already had IN_JUMP button in previous frame and if so, it won't allow you to jump as long as your previous frame has IN_JUMP button. That's the reason why we can't autobhop just by holding spacebar.

Here's an example to understand it better:

Groundflag        Command(s)
                    ...
        0                  +jump;-jump - jump could be done if player were on the ground
        0
        0                  +jump;-jump - jump could be done if player were on the ground
        1                  +jump;-jump - player just hit the ground but at this point engine does not allow him to jump because he had IN_JUMP in previous frame
        1
        1                  +jump;-jump - everything is ok, executing jump code...
        0
        0                  +jump;-jump - jump could be done if player were on the ground
                    ...

Only because player scrolled too fast, it led him to bhop with 3 FOG which in most cases will ruin your run.

Meanwhile, ideal scroll distribution looks like that:

Groundflag        Command(s)
                    ...
        0                  +jump;-jump - jump could be done if player were on the ground
        0
        0                  +jump;-jump - jump could be done if player were on the ground
        0
        1                  +jump;-jump - everything is ok, executing jump code...
        0
        0                  +jump;-jump - jump could be done if player were on the ground
        0
        0                  +jump;-jump - jump could be done if player were on the ground
                ...

But even with scroll distribution like that we are not safe from failing bhop because of:

a) PM_PreventMegaBunnyJumping() engine function ( https://github.com/SDLash3D/cs16-client/blob/06684811463fc0079f4b65720158665e79646fb7/pm_shared/pm_shared.c#L2038 ). After passing all of PM_Jump() checks there remains only PM_PreventMegaBunnyJumping(). This function decides whether to reduce your XYZ velocity or not.

First it calculates maximum speed cap for equipped weapon:

maxscaledspeed = BUNNYJUMP_MAX_SPEED_FACTOR * pmove->maxspeed;

If you have knife or USP in hands, it can be translated to:

maxscaledspeed = 1.2 * 250;

Then it checks if our XYZ speed (variable spd) is more than maxscaledspeed and if so, it multiplies our velocity vector by maxscaledspeed / spd * 0.8.

Some examples:

1. We have USP in hands, velocity.x = 286.132, veloicty.y = -11.622, veloicty.z = 0.0.

maxscaledspeed = 1.2 * 250 = 300.0
spd = squareroot(velocity.x^2 + velocity.y^2 + velocity.z^2) = 286.367


Since spd < maxscaledspeed, speed remains the same.

2. We have scout in hands, velocity.x = -54.337, veloicty.y = 311.328, veloicty.z = 0.0.

maxscaledspeed = 1.2 * 260 = 312.0
spd = squareroot(velocity.x^2 + velocity.y^2 + velocity.z^2) = 316.034


We have spd > maxscaledspeed therefore the whole velocity vector will be multiplied by maxscaledspeed / spd * 0.8:

velocity.x = -54.337 * 0.789 = -42.914
velocity.y = 311.328 * 0.789 = 245.883
velocity.z = 0.0 * 0.789 = 0.0

speed = squareroot(-42.914^2 + 245.883^2 + 0.0^2) = 249.599


So, our speed went from 316.034 to 249.599 after PM_PreventMegaBunnyJumping() function call.

Most of you have experienced situation when speed was cropped even though it was less than 300.0, there's an explanation for this. At the point of PM_PreventMegaBunnyJumping() call engine doesn't know yet that we are on the ground and there's no need to reduce our velocity.z as if we were in the air, so it reduces velocity.z on 4.0 (ent_gravity * pmove->movevars->gravity * 0.5 * pmove->frametime). It adds 4.0 back to velocity.z later though.

So although your XY speed was less than 300.0, your XYZ speed wasn't. This means that in examples above our velocity.z should have been -4.0 instead of 0.0 if we want to get accurate results.

b) PM_Friction() and PM_WalkMove() engine functions ( https://github.com/SDLash3D/cs16-client/blob/06684811463fc0079f4b65720158665e79646fb7/pm_shared/pm_shared.c#L1057 & https://github.com/SDLash3D/cs16-client/blob/06684811463fc0079f4b65720158665e79646fb7/pm_shared/pm_shared.c#L926 ). As you already could understand, we still can get 2 FOG bhops even with ideal scroll distribution. On such occasions, both PM_Friction() and PM_WalkMove() will strike.


fuser2 is a jump modificator that slows us down after performing a jump. After this, engine sets its value to 1315.789428 and each frame afterwards decreases it by current 'msec' value. If your FPS is 100 then your msec is 10, this means that to completely get rid of slowdown effect you need to wait 1.315 seconds.



PM_Friction() is a common ground friction function that reduces our speed on squareroot(velocity.x^2 + velocity.y^2) * 4 * 0.01 each frame we are on the ground (this does not apply for 1 FOG bhops).

PM_WalkMove() checks if fuser2 value is bigger than 0.0 and if so, both velocity.x and velocity.y will be multiplied by (100.0 - pmove->fuser2 * 0.001 * 19.0) * 0.01.

As an example here we have 1 FOG bhop with 300.0 speed and 60.022 speed loss because of PM_PreventMegaBunnyJumping(), let's calculate how much speed in theory we could save as if it was 2 FOG bhop.

Frame  Groundflag  Command(s)  Velocity.x  Velocity.y  Velocity.z  HSpeed
   107              1            +jump;-jump      300.0             0.0               -4.0             300.0
   108              0                                     239.978         0.0                0.0             239.978

PM_Friction(): Using formula we get squareroot(300.0^2 + 0.0^2) * 4 * 0.01 = 12.0 speed loss.

PM_WalkMove(): Let's say before this jump we had other jump that lasted 67 frames, just as normal non-stand-up bhop. At this point our fuser2 value is 645.789 (1315.789 - 67 * 10). Now, using formula we can know that PM_WalkMove() will make us lose (hspeed - hspeed * 0.877) = 35.4 speed.

It sums up to 47.4 speed loss in total so we can clearly say that in case with ~300 speed it's better to have 2 FOG bhop and lose 47.4 speed instead of doing 1 FOG bhop and losing 60 speed. Bhops with as low speed loss as possible are known as ideal or perfect bhops.

P.S. If you jump off really high building and spend 1.315+ seconds in air PM_WalkMove() won't affect your next bhop since fuser2 will be equal to 0.0.

P.S.S. I've made a small plugin that outputs in chat information about ideal bhops: https://nofile.io/f/QdqnjdwgeWH/ideal_bunnyhop.amxx In a short session of few minutes I've managed to get 10 ideal bhops in a row but it can be even more if you are lucky enough. This plugin is NOT safe for WR recording.

shxKh01/07/2016 16:21

N1 FAME,thanks a lot for the detailed explanation of complicated engine!1

SKY-RIM01/07/2016 17:41

Fame htf did you even understand all of that. Gj for the explanation and hard work.
#adminofthedecade

Keo mapper01/07/2016 20:44

nice one fame, a lot of things explained.

Ducu01/07/2016 20:59

gj for this theory lesson mate

Roach01/07/2016 21:35

I'm no pro or mathematician but I would like to ask FAME if a perfect duck after a perfect bhop is going to reduce or keep speed the same if we do another perfect jump(not a bhop because obvious) just after the duck because I have been ridiculed a few times for trying to learn cj after bhop.

Wh1te BearD^^02/07/2016 02:58

I really appreciate what you did here fame, about time that someone dedicate him self into something as complex as this and try explain it to the community, we may not understand it now but more importantly its a start to something will heavily help us in the future as it will be clear along the time.

Roach04/07/2016 21:42

Loved the plugin but would like to know on which factors are you deciding the "Ideal Percentage".

JocA Banned05/07/2016 11:03

THanks for explanation...is there any way to explain how 'stairs' work?

FAME05/07/2016 21:13(edited 15/03/2020 15:31)

Originally posted by Roach
I'm no pro or mathematician but I would like to ask FAME if a perfect duck after a perfect bhop is going to reduce or keep speed the same if we do another perfect jump(not a bhop because obvious) just after the duck because I have been ridiculed a few times for trying to learn cj after bhop.

Unlike bhop where you can get 1 FOG, in the best case you will get 2 FOG during gstrafe/duck-tap. This mean your speed will be reduced due to PM_WalkMove() and PM_Friction() just as if it was 2+ FOG bhop. Though it still can be useful in situations like this: https://youtu.be/6ZJTBKAiHUY?t=26s

Originally posted by DemonstratorBG
As far as i understood:
While bhoping keep your speed below 299.9 (try not to land with more than 299)

Yes. To be exact max. speed cap is 299.973 and not 300.0 as many people might think.

Originally posted by Roach
Loved the plugin but would like to know on which factors are you deciding the "Ideal Percentage".

From coding perspective, circumstances for ideal bhop look like that:

if (XYZ_speed < max_weapon_speed * 1.2 && (FOG == 1 || FOG >= 2 && prev_speed > max_weapon_speed * 1.2))
    ideal_bhop = true;
else
    ideal_bhop = false;

It can be translated as:

If XYZ speed is less than max_weapon_speed * 1.2 and FOG counter equals to 1

OR

If XYZ speed is less than max_weapon_speed * 1.2 and FOG counter is 2 or more and XYZ speed in previous frame is more than max_weapon_speed * 1.2

THEN your bhop is considered as ideal, otherwise it will be counted as failed.

And to get percentage we simply do ideal_bhops / total_bhops * 100.0.

Thanks for feedback guys!

Roadrunner mapper06/10/2016 14:33

Wow, awesome reading!

random Tournament #319/10/2016 16:48

Not sure, but might it help coders to code new codes :{ if u know wut i mean

Gorbachev_x mapper25/11/2016 21:48

Math is really damn beautiful!

Pax19/12/2017 13:10

From this, it can be concluded that the ideal scrolling frequency for 100 fps is 50 s^-1, right?
If there is a program (preferably an online application) that calculates the frequency when we scroll, we could use that to train and adjust our scrolling frequency so it's approximately equal to 50 s^-1
Is anyone aware of a such program?

FAME19/12/2017 14:40

In CS realities ideal scrolling rate is 1 scroll each 0.02 seconds. Even though it's possible to train yourself to scroll slower or faster, the easiest decision would be to find a suitable mouse for you.

By the way, you can try infamous "+jump;wait" script to see whether you need to scroll faster or slower. If you feel like your bhops became better with it — you need to scroll slower, otherwise — faster.

SilverNinja mapper19/12/2017 22:32

Originally posted by FAME
P.S.S. I've made a small plugin that outputs in chat information about ideal bhops: http://www95.zippyshare.com/v/Tl5aajxU/file.html In a short session of few minutes I've managed to get 10 ideal bhops in a row but it can be even more if you are lucky enough. This plugin is NOT safe for WR recording.[/i]


Hi, you can please reupload this? File has expired.

FAME20/12/2017 09:23

Here you go: https://drive.google.com/file/d/1nW-o4DxUooqvgjp9vf7M0aWjqoWgnCWc/view?usp=sharing

SilverNinja mapper20/12/2017 22:38

Thank!

MuneEb21/12/2017 23:10

OMG!you're really good at mathematics.BTW I didn't understand a single word

JocA Banned22/12/2017 14:37

munee dont worry, he's proffesor of math

Page 1 of 2

Please log in to post replies.