TODO: most of the technical explanation was copy pasted with small fixes. someone should verify if it doesn't need changing.
Accelerated hop, or Ahop for short is the main movement technique in New Engine versions of Portal, Half-Life 2, and the Episodes. It also works in Half-Life: Alyx, when launched without VR.
It replaces bunnyhopping, which was patched together with the release of Portal and Half-Life 2: Episode Two. It is also significantly faster.
In order to patch bunnyhopping, valve implemented a speedcap into the games. This speed cap was implemented horribly, and worked by adding negative velocity to the player. However, instead of relying on the actual movement on the player, it relied on the input to determine the direction the player was going. It was quickly discovered that this could be abused. It can also only be applied 100% forwards or 100% backwards. Never to the side.
Ahop can be divided into 3 primary variants, Accelerated Back Hopping (ABH), Accelerated Side Hopping (AFH), and Accelerated Forwards Hopping (AFH).
In order to perform an ahop, the player must be over the speed cap of the current movement type (walking, ducking, sprinting), while also tricking the game that they're moving in a different direction that they actually are.
The higher the base speed of the movement type, the higher the speed cap, meaning the game will apply less negative velocity to slow the player down, which in turn means that the speed boost will be smaller. A table of the speed caps in different movement types is available:
Conditions | Speed limit | First possible ABH speed | ||
---|---|---|---|---|
With suit | Without suit | With suit | Without suit | |
While crouching | 209 | 165 | 495 | 285 |
While walking | 225 | 225 | 479 | n/a (2) |
While running | 285 | 419 | ||
While sprinting | 352 | n/a (1) | n/a (3) |
Since the HEV suit isn't obtainable in portal, the player can only crouch and walk.
Notes:
(1) You cannot sprint without a suit.
(2) The only states you can enter without the suit is walking and crouching.
(3) You cannot sprint while jumping. TODO: (3) is false. determine the real speed cap and insert it.
In order to perform an ABH, the player needs to be going backwards over the speed cap without holding w. The game will by default assume that the player is going forwards, adding backwards speed to slow the player down, not knowing that they're already going backwards.
The easiest way to achieve this result is to sprint backwards, let go of s after jumping, and continuing to jump.
https://www.youtube.com/watch?v=vdNYggZr5nM
AFH is the opposite of ABH. It is significantly harder to perform, as when W and S are not held - the game assumes the player is moving forwards. This means that to trick the game into going backwards, S needs to be pressed whenever a jump is made. Alternatively, a singificantly harder to learn but faster method utilizing the +strafe command exists.
The +strafe command, available in the menu as "Strafe Modifier" allows the player to control their player with the mouse, with analogue input. The game will take this input into consideration when checking the direction of player movement.
By holding +strafe, and moving their mouse down at the same time, the player can sacrifice the ability to move their camera for an AFH with the same speed as an ABH.
Accelerated Side hopping is a variant of AFH that doesn't require the player to quickly tap S or press +strafe whenever they jump.
This however, comes with downsides. The speed boost from ASH is noticeably smaller than the one from AFH, and the boost isn't applied in the same direction as the one the player is travelling. This leads to the player making arcs in air.
AFH is performed by holding S and one of the side directional keys at the same time, while also looking around 45° to the right, when holding A, or left when holding D.
https://www.youtube.com/watch?v=Lrnp88Dq7TU
Accelerated hopping is a result of valve not taking into account the direction of the player's speed when capping it, and tying it to the Jumpboost code.
Whenever the player jumps, in the game, the game will apply a speed boost forward, if the player is holding W, or backwards when the player is holding S. In Old Engine, this resulted in Bunnyhopping. Instead of installing a global speed cap for the player, valve simply (tried) to prevent the jumpboost speed from building up every jump.
This means that even if the code worked as intended, jumping sideways would have no speed cap regardless.
The game goes through a few steps to decide how much to add to or subtract from the player's speed.
float flSpeedBoostPerc = ( !pMoveData->m_bIsSprinting && !player->m_Local.m_bDucked ) ? 0.5f : 0.1f;`
float flSpeedAddition = fabs( mv->m_flForwardMove * flSpeedBoostPerc );
//mv->m_flForwardMove is the normal speed we move while in the state we jumped in, so when we do a sprint jump, it is 320; a running jump is 190;...
3. Calculate the maximum speed flMaxSpeed for bounding the player's speed later on.
float flMaxSpeed = mv->m_flMaxSpeed + ( mv->m_flMaxSpeed * flSpeedBoostPerc );
float flNewSpeed = ( flSpeedAddition + mv->m_vecVelocity.Length2D() );
// If we're over the maximum, we want to only boost as much as will get us to the goal speed
if ( flNewSpeed > flMaxSpeed )
{
flSpeedAddition -= flNewSpeed - flMaxSpeed;
}
After all the steps above, the game takes the newly acquired flSpeedAddition and apply it onto the player's current speed.
ABH will be used for the primary explanation as it is the simplest form of ahop.
The most standard ABH is done by doing a backward sprintjump, crouching in midair, landing then continuing to jump without holding any directional keys. In the calculations below, we'll assume that inputs are frame-perfect without any loss of speed.
Let's imagine a player doing crouched ABHs in a straight line. When the player begins their run up by sprinting backwards and then jumping, their speed would be negative. Right before the next jump in the ABH sequence, their speed would be at 352 UPS. Ideally at this point, since they're crouched, mv->m_flForwardMove should be at 190; but because we are not holding any directional keys, it is 0 instead. This means our additional speed we get will also be 0.
float flSpeedAddition = fabs( mv->m_flForwardMove * flSpeedBoostPerc ) = fabs(0 * 0.1) = 0
As a result, our new speed will also be the same as the old which would be
float flNewSpeed = ( flSpeedAddition + mv->m_vecVelocity.Length2D() ) = 0 + 352 = 352
Because our speed is over the threshold when landing (we land at 352 UPS which is higher than the 209 UPS threshold when crouching), the game does the speed bounding routine. However, since our flSpeedAddition is 0, this will come out as negative.
flSpeedAddition -= flNewSpeed - flMaxSpeed
= flSpeedAddition - (flNewSpeed - flMaxSpeed)
= 0 - (352 - 209)
= -143
And since we're moving backwards and our speed is negative, our new speed will be increased. Our new speed in this case is 495. After 2 another jumps, the speed increase will be massive
//(2nd ABH jump)
flSpeedAddition -= flNewSpeed - flMaxSpeed
= flSpeedAddition - (flNewSpeed - flMaxSpeed)
= 0 - (352 + 143 - 209)
= -286
//(3rd ABH jump)
flSpeedAddition -= flNewSpeed - flMaxSpeed
= flSpeedAddition - (flNewSpeed - flMaxSpeed)
= 0 - (352 + 143 + 286 - 209)
= -579
After these 2 jumps, our speed would be 1360 UPS.
When S is held, or the player is moving backwards with +strafe, mv->m_flForwardMove is negative, which shouldn't interfere in the speed capping as the function uses its absolute value. However at the very bottom there's the following bit of code:
if ( mv->m_flForwardMove < 0.0f )
flSpeedAddition *= -1.0f;
Which effectively turns the speed reduction amount from being negative to positive, thus accelerating you.
//(before)
flSpeedAddition -= flNewSpeed - flMaxSpeed
= flSpeedAddition - (flNewSpeed - flMaxSpeed)
= 0 - (352 - 209)
= -143
//(after)
flSpeedAddition *= -1.0f
flSpeedAddition = -143 * -1
flSpeedAddition = 143
143 UPS is added to your previous positive speed instead of the -143 from before.