Ladders are ubiquitous in Half-Life maps, most of which are perfectly vertical.
The climbing speed along a ladder can be increased beyond what the developers
intended, and the method is so straightforward that even a beginner speedrunner
could pull it off. A sloped ladder is much rarer, and the viewangles to optimise
the climbing speed is less obvious. In this chapter, we derive these viewangles
for the general case of a sloped ladder.
Fig. 9.1. A sloped ladder section in the Blast Pit map of c1a4e, connected to a
perfectly vertical section above. What would the optimal climbing viewangles
be for this ladder?ยถ
Whether a player is on a ladder or not is checked by the PM_Ladder function
near the beginning of PM_PlayerMove. If the player is on a ladder, the
PM_LadderMove function will be called soon after, which handles the physics
of moving on a ladder and set the movetype to MOVETYPE_FLY. This means
that the gravity, friction, and other player movement physics described in
Player movement basics are completely skipped. Nevertheless, the basevelocity and collision (see Collision) still apply and
PM_FlyMove is called to update the player position. The fact that no gravity
is exerted under MOVETYPE_FLY is significant, as will be explained in
Ladder exit.
We first introduce F and S, which are
analogues of ๐น and ๐ from the standard movement physics. Issuing
+forward adds 200 to F, and issuing +back subtracts
200 from it. Thus, when both +forward and +back are issued we have
F=0. Similarly, executing +moveright adds 200 to
S and +moveleft subtracts 200 from it. Note that the value
of 200 cannot be modified without recompilation. For ladder physics, it does not
matter what ๐น and ๐ are. If the duckstate is 2, then
Fโฆ0.333F and Sโฆ0.333S in newer Half-Life versions. This is not true for earlier
versions such as the one bundled in NGHL. Regardless of viewangles, jumping off
ladder always sets
๐ฏโฒ=270ห๐ง
where ห๐ง is the unit normal vector of the ladderโs
climbable plane.
If ๐ฎ=Fห๐+Sห๐ฌ and ห๐งโ โจ0,0,ยฑ1โฉ then
where ๐ผ is the angle between ๐ฎ and
ห๐ง. โจโ๐๐ง,0,๐๐ฅโฉ is actually
ห๐ง rotated by ๐/2 anticlockwise when viewing
into the positive direction of ๐ฆ-axis. Expanding
โ๐ฏโฒโ=โ๐ฏโฒโ ๐ฏโฒ,
We conclude that ๐ผ=3๐/4 maximises
โ๐ฏโฒโ. If |F|=|S|=200, we have โ๐ฏโฒโ=400.
Knowing the optimal angle ๐ผ is useful for theoretical
understanding, but in practice we must be able to calculate the playerโs yaw
and pitch angles that maximises vertical climbing speed. For ladders that are
perfectly vertical the optimal viewangles are trivial to find, but we need
explicit formulae for slanted ladders.
Immediately we observe that ๐๐งโฅ0 is required for this equation to
have real solutions. We will deal with this in a later section. At this point we
are required to take square roots. This is a critical step and we must carefully
choose the signs for the numerator and the denominator, as they will determine
the quadrant in which (๐โ๐) resides.
We define three free variables:
The sign of S. Positive if rightward and negative if
leftward.
The sign of F. Positive if forward and negative if backward.
The sign of ๐ฃโฒ๐ง. Positive if upward and negative if downward.
The motivation is that we want to be able to automatically determine the correct
signs for the numerator and the denominator given our choices of the signs of
the free variables. This is useful in practice because we often make conscious
decisions regarding the directions in which we want to strafe when climbing
ladders. For example, we may choose to invoke +forward and +moveleft, or
+back and +moveright. In both cases the resulting velocity is
identically optimal, and yet the viewangles are different. By declaring the
signs of S and F as free variables, we can
choose the strafing directions mathematically by simply setting the correct
signs.
Optimal ladder climbing can go in two possible directions, that is upward or
downward. Again, the maximum climbing speed does not depend on the direction,
though the viewangles do. Hence we declare the sign of ๐ฃโฒ๐ง as a free
variable.
We will now attempt to formulate the final viewangles in terms of these free
variables. To begin, we examine Equation (9.1) more closely. We make
three observations:
We have 1โ๐๐ง๐๐งโฅ0 when 0โค๐๐งโค1/โ2 and
1โ๐๐ง๐๐ง<0 when 1/โ2<๐๐งโค1.
We have ๐๐ง>0.
We have cosโก๐โฅ0 for โ๐/2โค๐โค๐/2.
We start by considering the sign of ๐ฃโฒ๐ง. Obviously, the right hand side
of Equation (9.1) must have the same sign as the ๐ฃโฒ๐ง. But
observe that there are two terms in the right hand side. Therefore, both terms
should also be as large as possible in the direction indicated by the sign of
๐ฃโฒ๐ง. For example, if we choose ๐ฃโฒ๐ง<0, then the terms on the
right hand side should be as negative as possible, and vice versa.
We will deal with the angle (๐โ๐) first, which appears
only in the second term, so we will assume that the first term has been dealt
with (that is, conforming to the sign of ๐ฃโฒ๐ง while being as large as
possible in magnitude). Now, we want
By one of the observations we made, we have ๐๐ง>0 and
cosโก๐โฅ0. Also, โ๐2๐ฅ+๐2๐ฆ is always
positive. Hence, equivalently we need
Observe that the required signs of sinโก(๐โ๐) and
cosโก(๐โ๐) depends on the chosen signs of
F and S respectively, in addition to the
sign of ๐ฃโฒ๐ง. If we look at Equation (9.4) again, notice
that the signs of sinโก(๐โ๐) and cosโก(๐โ๐) determine the signs of the numerator and denominator respectively after
removing the squares, because tanโก(๐ฅ)=sinโก(๐ฅ)/cosโก(๐ฅ) for all
๐ฅ.
Deriving from Equation (9.4), the formula for the optimal yaw is
thus, in all its glory,
Here, we only need to determine the sign of the right hand side as a whole,
rather than considering the numerator and the denominator separately. The sign
of ๐ will indicate whether the player should look upward or
downward when climbing. Going back to Equation (9.1) again, we assume
the second term has been dealt with, in the same way we assumed the first term
to have been dealt with when deducing the signs for the optimal yaw. Now we must
have
Notice that the sign of (1โ๐๐ง๐๐ง) plays a role here. In practice,
however, 1โ๐๐ง๐๐ง is less efficient to compute. Using one of the
observations, we see that sgnโก(1โ๐๐ง๐๐ง)=sgnโก(1/โ2โ๐๐ง). So we are done and we can
write out the complete formula for the optimal pitch as follows:
The equations (9.5) and (9.6) can be trivially
implemented in code to compute the best ladder climbing viewangles. Note that,
since the ladder normal is a unit vector, the ๐2๐ฅ+๐2๐ฆ that appears
in both of these equations can alternatively be written as 1โ๐2๐ง.
Fig. 9.2. Plot of the relationship between yaw and pitch by varying ladder slope angle
0<๐ผโค๐/2 such that ห๐ง=โจcosโก๐ผ,0,sinโก๐ผโฉ. The free variables have values ๐=1, ๐น=1, and ๐ฃ๐ง=1.ยถ
When ๐๐ง<0, the derivatives will never be zero. However, we can
observe that |๐| increases when ๐๐ง decreases.
We also note we constrain the range of ๐ to [โ๐/2,๐/2] while the value of ๐ is unrestricted. Hence we can
substitute the maximum value |๐|=๐/2 into
๐๐ฃโฒ๐ง/๐๐=0 and solve for ๐. It is
found to be
๐=๐ยฑ๐2
We need to determine what the sign of ๐/2 means. Substituting
๐=ยฑ๐/2 and ๐โ๐=ยฑ๐/2 into the
original vertical velocity equation gives
Note that ๐๐ง<0 when ๐๐ง<โ1/โ2. Now we can use the
similar technique to deduce the required signs of ๐ and
(๐โ๐), which results in
Up to this point we have been assuming the normal vector not being vertical.
If ห๐ง=โจ0,0,ยฑ1โฉ, then the second term in
the bracket vanishes (since VectorNormalize in pm_shared/pm_math.c
returns a zero vector if the input, which is โจ0,0,1โฉรห๐ง, is also a zero vector) instead of being indeterminate,
leaving only
๐ฏโฒ=๐ฎโโ๐ฎโcosโก๐ผโจ0,0,ยฑ1โฉ
thus
โ๐ฏโฒโ=โF2+S2โ1โcos2โก๐ผ
which is maximised when ๐ผ=๐/2. This can be achieved by setting
๐=0. If |F|=|S|โ 0 then the yaw should be 45 or 135 degrees away from the intended
direction, depending on the signs.
We call โexiting a ladderโ to mean moving out of a ladder so that the player is
no longer on the ladder (as determined by PM_Ladder). This is different from
ladder jumping, where the player jumps off a ladder, which has been described in
Preliminaries. In some speedrunning context, ladder exit may be
referred to as ladder jumping, though for the purpose of this documentation we
do not adopt this meaning.
Fig. 9.3. A common and old trick in the c1a0e test chamber map, where the player
jumps onto the lamp above by exiting the ladder at full speed at a lower
frame rate.ยถ
Fig. 9.3. illustrates a common use of ladder exit strategy in
speedrunning. In the test chamber map, it is desirable to avoid getting
teleported to Xen, and one way to avoid this is to jump onto the lamp above to
avoid a big trigger_transition below. Interestingly, the lamp is unreachable
at higher frame rates, but easily accessible at lower frame rates. This runs
counter to the intuition of jumping in Half-Life where the normal jumping height
is frame rate independent as explained in Gravity.
To understand this trick, first recall that the movetype is assigned to be
MOVETYPE_FLY while on the ladder, which prevents the gravity to act on the
player. Suppose in a frame, the player starts off on the ladder with vertical
position ๐ง0 and is moving away to exit the ladder at some vertical
climbing speed ๐ฃ0. The player position will be updated as per usual by
PM_FlyMove.
๐ฃ1=๐ฃ0๐ง1=๐ง0+๐ฃ1๐=๐ง0+๐ฃ0๐
Suppose the new position ๐ง1 is no longer on the ladder and is in the
air. Despite this, the new vertical velocity is the same as before and no
gravity will be applied until the next frame!
Now consider the next frame. Since the movetype is no longer
MOVETYPE_FLY, gravity will act on the player like normal. The game thus
computes
Observe that at any time ๐ก, the vertical velocity is always higher than
expected by normal jumping physics by a constant ๐๐. In addition, the
vertical position higher than expected by ๐ฃ0๐. This is why the
ladder exit strategy in the test chamber works. By lowering the frame rate, the
jump height can be increased. For example, at 1000 fps and ๐ฃ0=400,
the extra height is only 400โ 0.001=0.4. At 20 fps, however, the
extra height is 400โ 0.05=20. The extra 20 units can make a
noticeable difference.
In general, if it is desired to attain as much height as possible by exiting a
ladder, possibly with damage boosting immediately afterwards, it is always more
optimal to exit the ladder at a lower frame rate.
It may be possible for the ladder normal ๐ง to not be normalised. This can happen if the player manages to โenterโ the ladder entity. This may be achieved by ducking and unducking underneath the ladder entity.