5. Ducking and jumping¶
Ducking and jumping are some of the fundamental player actions. Understanding the intricacies associated with these actions is vital to writing an effecting TAS and troubleshooting issues that might arise along the way.
Ducking is one of the most important actions in playing Half-Life. And yet, it has some of the more misunderstood behaviours. Ducking can be performed with the use of the duck key, which issues
+duck. The subsequent behaviour depends on whether the player is onground, and how long the duck key is held before releasing.
There are three ducking states in Half-Life, namely standing, in-duck, and ducked. The standing state is simply the player’s default state, when the duck key is not held. In this state, the player bounding box is set to be 72 units tall. The in-duck state refers to the transitional state between the standing and ducked states. Lastly, the ducked state is when the player is fully ducked, with a bounding box of 36 units height. The standing and ducked states are relatively straightforward, but what happens in the in-duck state is less obvious and potentially misleading.
Suppose the player presses the duck key. Suppose also the player is onground. The ducking state will instantly transition from standing to in-duck. As a result, the game will start a countdown of 0.4 seconds. Meanwhile, the game will begin to slowly animate the player vertical view offset downwards. This can be observed in-game as the camera position seemingly easing downwards when ducking. However, this is misleading: the player hull size has not changed thus far, and the height remains at 72 units throughout. Only when the 0.4 seconds time is up the game will set the ducking state to ducked and instantaneously set the hull height to 36 units. And only in the following frame does the FSU values (see Forwardmove, sidemove, and upmove) get scaled down by a factor of 0.333:
As long as the duck key is still being held, the ducking state will remain unchanged no matter what situation the player is in, and no matter how long. (Note that the factor of 0.333 is exact and actually used in the SDK. This is not an approximation made by this documentation.)
On the other hand, if the player is not onground (in the air), then the game will skip the in-duck state entirely, and transition immediately to the ducked state. This means that changing hull size is instantaneously as long as the player is in the air. This allows for very quick responses when navigating around narrow terrains. For example, the player can duck in the air to immediately dodge an obstructing object. Again, the FSU values will only get scaled down in the following frame.
Now let us backtrack and assume the player is onground. What if the player releases the duck key before the 0.4 seconds is up? The game will attempt to unduck. If the player is onground, the game will consider the location 18 units above the current player position, and the game will begin player tracing from the current position to 18 units above, taking the player hull size into account (72 units height in this case). If the area 18 units above the player is clear, then the unducking process is considered successful, and the player position will be instantaneously set to 18 units above the ground. This is peculiar behaviour forms the foundation of ducktapping (see Ducktapping).
The duckbug is one of the many ways of cancelling fall damage. Duckbug requires a slope, but otherwise works like a combination of jumpbug (see Jumpbug) and edgebug. Similar to the jumpbug, the player must unduck to go from being in the air to onground within a frame, though no jump is required. And similar to the edgebug, the player must be able to exit the onground layer of the slope before the end of the frame by “sliding off” the ground.
Referring to Fig. 5.5., the player begins at frame by being in ducked state and in the air at position with velocity . At the beginning of frame , the player would have moved to position , and considered to be in the air. But because
-duck is issued in this frame, the player switches to the standing bounding box. The bottom of the bounding box at happens to be within the ongrond layer, which causes the game to consider the player onground at the end of
PM_UnDuck, and cancelling the vertical velocity. At the end of frame , the player ends up at position , such that the bottom of the bounding box exits the onground layer, ending up at . Since the player is in the air in the beginning and at the end of frame , fall damage will not be inflicted.
There is a minimum horizontal speed required to exit the 2 units onground layer. The horizontal displacement in one frame, represented by the length , must be sufficient large. If is the horizontal speed, is the frame time, and is the angle of the slope, then the inequality
must be satisfied for duckbug to work at all. Observe that the greater the slope angle , the smaller the horizontal speed required. And if we have a perfectly horizontal ground with instead of a slope, then an infinite horizontal speed is required, as expected.
In addition to the horizontal speed, the frame rate must not be too high or too low. This is unlike jumpbug, where higher frame rates are generally better, and unlike edgebug, where lower frame rates are usually preferred. With duckbug, a frame rate too high makes it difficult to exit the 2 units onground layer, because the minimum required horizontal speed would too high. A frame rate too low makes it difficult to unduck onto the ground. In practice, we probably should pick a frame rate that is as high as possible while ensuring the inequality above is met. In most situations, there is not much leeway in altering the horizontal speed prior to a duckbug, therefore the frame rate is the most important control input.
Suppose a player is onground and the duckstate is in-duck or ducked. When the player then unducks,
PM_UnDuck will be called. This function will try to move the player origin up by 18 units, and will succeed in doing so provided sufficient space above the player. Suppose the player is in the ducked state. Then this upward displacement makes sense, as one can check that the difference in vertical player position (measured at the centre point of the hull in use) using the ducked and standing hulls when resting on the same ground level is exactly 18 units. If the function did not forcibly displace the player in such a way, the player would be stuck to the ground with 18 units buried below it. Interestingly, the behaviour of
PM_UnDuck does not distinguish between in-duck and ducked states, and it does not “notice” that the standing hull is used in the in-duck state. This results in the player incorrectly being displaced 18 units upwards when unducked in the in-duck state, while using the standing hull throughout. This lies the basis for ducktapping, where the player holds the duck key to get into the in-duck state from the standing state, and then releasing the duck key while the player is still in the in-duck state, preferably as soon as possible. The player will be teleported to 18 units in the air, and air movement physics will take over.
Ducktapping is an alternative to jumping in minimising or eliminating the effect of ground friction (see Ground friction) on the player’s horizontal speed. Ducktapping is especially critical in the presence of the bunnyhop cap (see Bunnyhop cap). Nonetheless, even without the bunnyhop cap, ducktapping can be useful in altering the player vertical position some time later to a more desirable location. For example, a tight vent may be hard to get in by a series of pure jumps because the player may not be in the right vertical position at the mouth of the vent. By doing ducktaps in place of jumping at appropriate points, however, the player vertical position may be manipulated to level with the vent, making entrance much easier. The downside of ducktapping, at least in its standard form, is that there is always one frame where the player is onground, and therefore subjected to ground friction. This may be eliminated in newer Half-Life engines by setting the frame rate to a very high value on landing, thereby causing the player frame rate (see Frame rate) to be zero, or , in at least the frame the player is onground. This has an effect of completely eliminating friction, while still allowing ducking and jumping physics to work as normal. If the frame in which the player is onground is made to have , and the player also gets displaced into the air in this frame, then ground friction can be bypassed completely.
When ducktapping, the vertical velocity remains unchanged and is usually zero, since the player needs to be on the ground in the prior frame which would set the vertical velocity to zero. Therefore, the time it takes for the player to fall back onto the ground by entering the 2 units onground layer can be found by solving
Assuming the entity gravity is 1 and the value of
sv_gravity is 800, then we have .
When the jump bit is set by issuing the
+jump command and the player is onground, the player will jump. To be precise, the act of jumping refers to setting the vertical velocity to
The unsimplified expression above reflects how it is actually written and calculated in the SDK code. It implies the intention of jumping to the height of 45 units with , though all of the numbers are hardcoded constants independent of any game variables.
The time it takes for the player to fall back onto the ground by entering the 2 units onground layer can be found by solving
Assuming the entity gravity is 1 and the value of
sv_gravity is 800, then we have .
The physics for jumping under water has been described in Sharking.
5.2.1. Bunnyhop cap¶
Denote the value of
sv_maxspeed. Keep in mind that it is not always the case that (where has been defined in Air and ground movements), since is dependent on the duckstate (see Ducking) and the values of , , and .
Consider player velocity vectors in the 3D space, . All, or at least, most, Steam versions of Half-Life have an infamous “cap” on speed which is triggered only when jumping off a ground with speed . When this mechanism is triggered, the new velocity will become . Again, note that this speed “cap” is a not horizontal speed cap, but rather, a cap on the magnitude of the entire 3D vector. This distinguish is very important when performing jumpbugs (see Jumpbug) in the presence of bunnyhop cap.
5.2.2. Jumping up to a slope¶
When there is a sloped plane above the player and the player is able to jump and collide with it, it will result in a respectable horizontal speed boost. This technique works both on the ground and underwater, though the effect is more pronounced and continuous underwater. This technique has been implemented in Half-Life speedruns, especially in the Residue Processing chapter. The horizontal speed tends towards a maximum that depends on the angle of inclination of the plane above. We will consider the underwater case here.
Refer to Fig. 5.6. underwater and ignore water friction for simplicity. Suppose
+jump is held down. In the first frame, the velocity vector shortly before water physics is . By the general collision equation (2.1), the velocity after collision is given by . In the next frame ,
+jump will cause the vertical velocity to be set to the length of again, altering the velocity vector to become . After collision, the new velocity is . Notice that the new horizontal speed is greater than that in the previous frame, that is . As the process repeats, . We therefore trivially obtain the formula for the limit of the horizontal speed as
Observe that is inversely proportional to the angle .
In practice, water inflicts some friction in each frame, causing the actual to be slightly less than what is depicted in Fig. 5.6.. However, by increasing the frame rate, this difference should tend to zero, which justifies the approximations made in this analysis.
The analysis presented here is also roughly applicable to the case of jumping off the ground onto a ceiling. Though, it takes time for the player to land on the ground and make another jump again, so the horizontal acceleration is much lower than that underwater.
TODO: explain what onground, position categorisation means
Fall damage is computed after the player movement functions based on the condition that, within a frame, the player is not onground (i.e. in the air) after the very first position categorisation in
PM_PlayerMove and that the player is onground after the final position categorisation in the same function. It is possible for the player position to change momentarily to something else between the two. For example, the player could be in the air before and after, but onground some point in the middle. This is the loophole that allows jumpbug to work.
Assuming the player is in the air at the first position categorisation and falling towards the ground. The exact vertical velocity does not matter as long as it is negative or below 180. Observe that there is a position categorisation step at the end of
PM_UnDuck, which is only called by
PM_Duck when the player attempts to unduck. Suppose the player duck state is ducked in the air, and crucially, would become onground after unducking due to the position categorisation in
PM_UnDuck. This condition will be met if the player position (i.e. the position of the centre point of the player’s bounding box) is between 36 to 38 units above the ground when the unducking is done, and the vertical velocity is below 180. 1 If these conditions are met, and if the player now unducks, the player will be considered onground at the end of
PM_UnDuck. As a result, the subsequent player physics will be run with that assumption.
As explained in Jumping, a player is allowed to jump only if the player is onground at the moment when
PM_Jump is called. Therefore, if the player is onground after
PM_UnDuck, the player will be allowed to jump, regardless of what happened before unducking! By jumping, the vertical velocity will be set to the positive value given in Jumping. Since this value is larger than the 180 ups limit for being onground, the final position categorisation (occurs after
PM_AirMove) will consider the player to be in the air again. As a result, the game sees the player as being in the air before and after, and thus the fall damage will be completely bypassed.
The criteria for jumpbug is extremely stringent. There is a mere 2 units window for jumpbug to work. Therefore, the frame rate plays a significant role in enabling jumpbug. The higher the frame rate, the smaller the difference between player positions before and after a frame, and therefore more likely to hit the 2 units window. The exact frame rate needed depends on the height and initial falling speed.
18.104.22.168. Effect of bunnyhop cap¶
It is very important to note that, jumpbug may trigger the bunnyhop cap as well (see Bunnyhop cap). In fact, jumpbug is significantly prone to triggering it, because the bunnyhop cap considers the 3D velocity vector and not just in the horizontal directions. For example, suppose the player is moving with velocity with default . After a jumpbug, the new velocity will be cut to . Then, before
PM_Jump returns, the vertical speed is set to that given in Jumping. Notice that, despite the horizontal speed being less than the cap, it is still scaled down due to the very high vertical speed. The conclusion is that jumpbug should be avoided as far as possible when the bunnyhop cap is a significant concern, and alternative methods of avoiding fall damage, such as the edgebug or duckbug (Duckbug) should be used instead.
5.3. Fast stair climbing¶
Stairs are not exactly ubiquitous in Half-Life, though they are still fairly common, and mastering how to climb stairs as fast as possible is an essential to every speedrunner. When walking up to a step, the game checks if the step has height less than or equal to
sv_stepsize, which has a default value of 18. If this is indeed the case, instead of getting blocked by the step, the player would simply “teleport” up by the step size and continue moving forward as though the step was not there. However, this ability to simply “walk up” to a step does not work when the player is in the air. If the player flies into stairs without jumping or ducking, the motion will be stopped entirely.
Although it has roots in the Counter Strike and Adrenaline Gamer jump maps, quadrazid popularised the trick of ducking and unducking repeatedly to climb stairs in Half-Life. This has been referred to by various names, including duckspamming, duckrolling, ducklooping, and doubleducking. We will call it fast stair climbing for less ambiguity. When the ducking frequency is set to the right value, which may be achieved by the rudimentary method of changing frame rates, the player will magically be able to climb stairs fluently as though the they were a simple slope. Fundamentally, the mechanism that allows this trick to work is ducktapping (see Ducktapping). Ducktapping from step to step is fast because it does not add vertical speed, unlike jumping.
However, there still are restrictions on how fast one could travel horizontally when implementing this trick. When ducktapping from a step, it takes time for gravity to pull the player down and land on the next step. If the horizontal speed is too high, the player might crash into a step further ahead and stopping the horizontal motion as a result. This section is devoted to better understand these speed limits.
Referring to Fig. 5.8. and borrowing architectural terminology, we will define a step to be a riser (vertical wall) of rise height followed by a tread (horizontal ground) of tread depth . Most stairs in Half-Life are homogeneous, in that the measurements of every step are the same throughout. This assumption will simplify analyses and cover the vast majority of circumstances a speedrunner would encounter in the field. On that account, we could describe stairs by the 3-tuple which encodes the tread depth, rise height, and number of steps respectively.
As described in ducking physics, tapping the duck key causes the player to teleport 18 units upward. However, if we consider the player position at the feet rather than the bounding box centre, and the player ducks in the air, the player effectively teleports 36 units upward. The height of this teleportation may be denoted as .
Ducktapping yields a parabolic trajectory if the player also moves forward with nonzero speed . The horizontal distance from the point at which ducktapping is initiated to the landing point is . This distance is a function of only , , , , , and the depth offset . The depth offset is the distance from the edge of a step on which the player is standing to the player’s feet, and so . In a given set of stair steps, , , and are fixed, and is also more or less constant. This leaves to be dependent only on and , the former of which takes only two possible values.
The climbing action roughly refers to how the trajectory emerges that is also dependent on all the variables that define . After a ducktap the new satisfies , and therefore
This change in after landing on a new step is used in defining the four possible climbing actions:
- constant action
- leading action
- trailing action
- colliding action
collision with a riser, is undefined
If leading or trailing action is attainable on some values of , then there must exist other values of on which it is not. To see why, suppose . If the player ducktaps at old , then the player may simply collide with the riser of the step ahead of the supposed landing step. Similarly, suppose , and the player ducktaps at old , then the player would simply not be able to reach the desired landing step at all, and would instead collide with the riser or simply lands on the step before the desired one. The range of values that will not degenerate into colliding action may be called the leading window and trailing window associated with leading and trailing actions, the sizes of which may be denoted as and .
To compute the window sizes, we first note that the player trajectory may be given by the following equation of motion, relative to the edge of the current step:
Denote the number of steps advanced, so that if we ducktap at some step , we land on step . To compute the window sizes, we solve
To solve these systems, for each , solve for algebraically. A solution is valid if . The same is repeated for , except that the solution should be computed as an interval and it is valid if it overlaps with . Once and have been found, the distances and can be trivially computed as
5.3.2. Single-action climbing¶
The simplest climbing strategy may be called the single-action climbing. This refers to stair climbing by the same type of climbing action.
The constant action is the simplest climbing action. It preserves , and therefore will never collide with the riser of any step. This action can be used to climb stairs for an indefinite amount of time without collision, as long as all other variables on which depends on remain constant. This type of climbing action serves as a baseline on stair climbing speed, which may be improved upon by use of other climbing strategies. The constant action requires to be a multiple of , which implies, from (), that we must have . Setting in () yields an easy way to compute the required .
The constant action, however, does not yield the highest possible to climb stairs. To increase the speed without collision, we can implement the leading action as the sole action for climbing, because intuitively, increasing requires a greater speed than keeping it constant. However, the speed cannot be too great or collision will ensue. Assuming a finite number of ducktaps over all of the steps and a constant speed , the increase in offset must be evenly distributed among the ducktaps, so that at the final ducktap, barely avoiding collision. 2 To this end, we require
This, of course, assumes . If there is only one ducktap required, then there must only be one step, and there is no speed limit defined for ducktapping away from stairs. If and are known, we can compute , and therefore using () and therefore the required speed using ().
The trailing action can also be chosen as the sole action for climbing if the speed limit is higher than that of the other actions. Similar to using leading action as the action of choice, the decrease in must be evenly distributed among the ducktaps. This implies the existence of a lower speed limit required to avoid collision. We have
The required speed can be computed similarly.
5.3.3. Mixed-action climbing¶
Rather than using the same action throughout the climb, it may be possible to use both leading and trailing actions on different values of , depending on the stair and player movement configurations.
Suppose the cycles between two points. Then , implying or . Now suppose we have , for example, with and . What is the set of solution that satisfy the equations?
The bottom position is half the height of the player’s bounding box below the centre position. The height of the bounding box is 72 units, therefore half the height is 36 units. On the other hand, one condition for being onground is that the bottom of the player’s bounding box lies within 2 units above the ground. It follows that the centre position must be between 36 and 38 units above the ground.
The symbol is required here because the final cannot exactly be equal to , as the position at is not included in the set of all possible positions along the tread of a step.