3. Player fundamentals

The player refers to the self. Specifically it is not necessarily you, but rather the self in the Half-Life universe.

3.1. Input

All player movements can be controlled through commands. In the default game setup, pressing down the “W” key usually results in the +forward command being issued. Releasing the same key will cause -forward to be issued. This is because the “W” key is bound to the +forward command with the bind command, usually issued from config.cfg. The -forward command need not be explicitly bound.

There are many similar commands available. It is beyond the scope of this documentation to provide a detailed description for all commands and indeed all cvars. The reader is invited to generate a list of all commands with the cmdlist command and study the SDK code for each of them.

There are, however, a few points to note about command issuing that are of concern to speedrunning. One of them is the impulse down phenomena. This affects primarily the viewangles (see Viewangles) and the FSU (see Forwardmove, sidemove, and upmove) computations. For example, the viewangles are typically changed by one of the viewangles commands such as +left for yawing left. This is done by adding to subtracting the viewangles by the value

𝜏×𝚌𝚕_𝚢𝚊𝚠𝚜𝚙𝚎𝚎𝚍/𝚌𝚕_𝚙𝚒𝚝𝚌𝚑𝚜𝚙𝚎𝚎𝚍×keystate

The “key state” is the state of the command being issued (+left for example). The key state is typically 1, but in the first frame in which the command is being issued the value is 0.5. In other words, the change in viewangles is half of what it normally is in the first frame of the active command.

This is not limited to the viewangles. The FSU values (which is crucial to player movement as will be described in Forwardmove, sidemove, and upmove) are also affected by the impulse down. For example, by issuing +forward, the following value will be added to 𝐹:

𝚌𝚕_𝚏𝚘𝚛𝚠𝚊𝚛𝚍𝚜𝚙𝚎𝚎𝚍×keystate

Again, the key state here is typically 1, except the first frame of the +forward command. This can result in a noticeably drop in player acceleration.

Tip

The reader is advised to perform a detailed study of cl_dlls/input.cpp to understand the processes and computations involved to greater depths.

3.2. Viewangles

The term viewangles is usually associated with the player entity. The viewangles refer to a group of three angles which describe the player’s view orientation. We call these angles yaw, pitch and roll. Mathematically, we denote the yaw by

𝜗

and the pitch by

𝜑

Note that these are different from 𝜃 and 𝜙. We do not have a mathematical symbol for roll as it is rarely used. In mathematical discussions, the viewangles are assumed to be in radians unless stated otherwise. However, do keep in mind that they are stored in degrees in the game.

_images/viewangles.svg

Fig. 3.1. Illustration of the geometric meaning of 𝜗 and 𝜑, with the camera’s view represented by OV and OF is the projection of OV on the horizontal plane. Note that since the sign convention of in-game 𝜑 differs from that of standard trigonometry, a negative sign is needed.

One way to change the yaw and pitch is by moving the mouse. This is not useful for tool-assisted speedrunning, however. A better method for precise control of the yaw and pitch angles is by issuing the commands +left, +right, +up, or +down. When these commands are active, the game increments or decrements the yaw or pitch by a certain controllable amount per frame. The amounts can be controlled by adjusting the variables cl_yawspeed and cl_pitchspeed. For instance, when +right is active, the game multiplies the value of cl_yawspeed by the frame time, then subtracts the result from the yaw angle.

3.2.1. Anglemod

When the viewangles are sent to the server, their values in degrees are rounded slightly using the anglemod function, which will be denoted 𝔄. We’ll define the function precisely as follows.

Definition 3.1 (Integer truncation)

For all 𝑥 , define int : 𝕀𝑛 the integer part or integer truncation function as

int(𝑥)={𝑥𝑥0𝑥𝑥<0,

where 𝕀𝑛 is an 𝑛-bit integer in two’s complement. We will assume in this documentation that 𝑛 >16.

Definition 3.2 (Degrees-anglemod)

The degrees-anglemod function 𝔄𝑑 : may be written as

𝔄𝑑(𝑥)=36065536(int(𝑥65536360)𝙰𝙽𝙳65535)

where AND is the bitwise AND binary operator.

Definition 3.3 (Radians-anglemod)

The radians-anglemod function 𝔄𝑟 : may be written as

𝔄𝑟(𝑥)=2𝜋65536(int(𝑥655362𝜋)𝙰𝙽𝙳65535).

To illustrate, we have the following examples of the output of degrees-anglemod.

𝑥

𝔄𝑑(𝑥)

𝑥mod360

0

0

0

1

0.99975586

1

20

19.995117

20

45

45

45

89

88.99475

89

400

39.995728

40

0.005

0

359.995

1

359.00024

359

20

340.00488

340

45

315

315

400

320.00427

320

The philosophy behind the anglemod function is to “wrap” the input angle into the range of [0,360) (for the degrees version). Except, rather than implementing the function in the most straightforward way using conditional branches and floating point divisions, the game approximates the result with a combination of integer bitwise operations and floating point multiplications, presumably to improve performance on 1990s hardware. On modern hardware, one could simply call the fmod standard library function in C. Incidentally, the CryEngine 1 also contains small uses of anglemod, though it’s not used for view computation.

Definition 3.4 (Real version of modulo)

A version of the modulo binary operator 𝑥mod𝑦 may be defined for 𝑥 and 𝑦 + with 𝑦 >0 such that 𝑥 =𝑦𝑞 +𝑟 where 𝑞 and 𝑟 with 0 𝑟 <𝑦.

Anglemod, then, is an approximation of 𝑥mod360 with 𝑥 for the version in degrees.

Lemma 3.1 is useful for converting the bitwise AND operation into the mathematically more well understood and convenient mod operator. Since we assume the int operator produces 𝕀𝑛 where 𝑛 >16, this lemma is applicable to the anglemod function as it computes an integer of more than 16 bits modulo 65536 =216 with 𝑚 =16. This will be useful in the subsequent proofs.

Lemma 3.1 (Equivalence of bitwise AND and modulo)

Let 𝑥 be an 𝑛-bit integer in two’s complement and 𝑚 <𝑛 an integer. Then 𝑥𝙰𝙽𝙳(2𝑚1) =𝑥mod2𝑚 =𝑟, such that 𝑥 =2𝑚𝑞 +𝑟 with 0 𝑟 <2𝑚.

Proof. Assume 𝑥 0 with 𝑛 bits. We may write 𝑥 =𝑛𝑘=0𝑏𝑘2𝑘. Then 𝑥𝙰𝙽𝙳(2𝑚1) =𝑚1𝑘=0𝑏𝑘2𝑘 as this is equivalent to “masking out” the least significant 𝑚 bits. Separately, note that 𝑥mod2𝑚 removes higher order terms 𝑛𝑘=𝑚𝑏𝑘2𝑘 because 2𝑚 divides the sum, hence 𝑥mod2𝑚 =𝑚1𝑘=0𝑏𝑘2𝑘 =𝑥𝙰𝙽𝙳(2𝑚1), as required.

Now assume 𝑥 <0. Since 𝑥 is stored in two’s complement, if we reinterpret the bits as an unsigned integer, we obtain ˜𝑥 =2𝑛 +𝑥 >0. Now since 𝑚 <𝑛, we have

˜𝑥𝙰𝙽𝙳(2𝑚1)=(2𝑛+𝑥)𝙰𝙽𝙳(2𝑚1)=(2𝑛+𝑥2𝑛)𝙰𝙽𝙳(2𝑚1)=𝑥𝙰𝙽𝙳(2𝑚1).

Namely, the most significant sign bit will be cleared as a result of masking out the least significant 𝑛 1 bits at most. On the other hand,

˜𝑥𝙰𝙽𝙳(2𝑚1)=(2𝑛+𝑥)𝙰𝙽𝙳(2𝑚1)=(2𝑛+𝑥)mod2𝑚=𝑥mod2𝑚

as required.

Lemma 3.2 (Partial periodicity of anglemod)

The degrees-anglemod 𝔄𝑑 is “partially” periodic with a period of 𝑝 =360 in the sense that

𝔄𝑑(𝑥)=𝔄𝑑(𝑥+𝑝)𝑥0𝔄𝑑(𝑥)=𝔄𝑑(𝑥𝑝)𝑥<0.

Proof. Assume 𝑥 0. By Lemma 3.1 and Definition 3.1, we have

𝔄𝑑(𝑥+𝑝)=36065536(𝑥65536360+65536mod65536)=36065536((𝑥65536360+65536)mod65536)=36065536(𝑥65536360mod65536)=𝔄𝑑(𝑥).

Now assume 𝑥 <0. We similarly have

𝔄𝑑(𝑥𝑝)=36065536(𝑥6553636065536mod65536)=36065536((𝑥6553636065536)mod65536)=36065536(𝑥65536360mod65536)=𝔄𝑑(𝑥).

Theorem 3.1 (Error bounds of anglemod)

Let 𝑥 . Assume 𝑥mod360 to carry the meaning defined in Definition 3.4. The error bounds on degrees-anglemod are given as follows.

0(𝑥mod360)𝔄𝑑(𝑥)<36065536for 𝑥00<360(𝑥mod360)𝔄𝑑(𝑥)<36065536for 36065536<𝑥<00𝔄𝑑(𝑥)(𝑥mod360)<36065536for 𝑥36065536.

Proof. Let 𝑓(𝑥) =(𝑥mod360) 𝔄𝑑(𝑥).

Suppose 𝑥 0. By inspection and Lemma 3.2, we only need to consider 0 𝑥 <360, as any 𝑥 360 can be reduced to these bounds by subtracting a multiple of 360. This allows us to simplify and write 𝑓 =𝑥 𝔄𝑑(𝑥). By Definition 3.1, we can also replace the integer truncation function int with the simpler floor function in 𝔄𝑑. Now

𝑓=36065536(𝑥65536360(𝑥65536360mod65536))=36065536(𝑦(𝑦mod65536))

where we have set 𝑦 =𝑥 65536/360. The assumption 0 𝑥 <360 implies 0 𝑦 <65536 and 𝑦mod65536 =𝑦, so

0𝑓=36065536(𝑦𝑦)<36065536.

Suppose 360/65536 <𝑥 <0. Observe that 𝔄𝑑(𝑥) =0 but 𝑥mod360 =360 𝑥. So 360 (𝑥mod360) =𝑥, as required.

Finally, suppose 360 <𝑥 360/65536. Again with Lemma 3.2, any 𝑥 360 can be reduced to these bounds or the case above by adding a multiple of 360. So 𝑓 =360 +𝑥 𝔄𝑑(𝑥). We can replace the int function with in 𝔄𝑑 by Definition 3.1. So similarly,

𝑓=36065536(65536+𝑥65536360(𝑥65536360mod65536))=36065536(65536+𝑦(𝑦mod65536))=36065536(65536+𝑦(|𝑦|mod65536)).

Note that the assumption 360 <𝑥 360/65536 implies 65536 <𝑦 1. This allows us to reduce |𝑦|mod65536 =65536 |𝑦|. Hence,

𝑓=36065536(65536+𝑦(65536|𝑦|))=36065536(|𝑦||𝑦|).

This gives us the bounds 0 𝑓 <360/65536.

As stated by Theorem 3.1, anglemod introduces a loss of precision in setting angles. This can result in a loss of optimality in strafing. There are two ways to reduce the effects of anglemod, namely by the simple anglemod compensation and the more advanced vectorial compensation. These techniques will be described in Vectorial compensation.

3.3. View vectors

There are two vectors associated with the player’s viewangles. These are called the view vectors. For discussions in 3D space, they are defined to be

ˆ𝐟:=cos𝜗cos𝜑,sin𝜗cos𝜑,sin𝜑ˆ𝐬:=sin𝜗,cos𝜗,0

We will refer to the former as the unit forward vector and the latter as the unit right vector. The negative sign for 𝑓𝑧 is an idiosyncrasy of the GoldSrc engine inherited from Quake. This is the consequence of the fact that looking up gives negative pitch angles and looking down gives positive pitch angles.

We sometimes restrict our discussions to the horizontal plane, such as in the description of strafing. In this case we assume 𝜑 =0 and define

ˆ𝐟:=cos𝜗,sin𝜗ˆ𝐬:=sin𝜗,cos𝜗

Such restriction is equivalent to projecting the ˆ𝐟 vector onto the 𝑥𝑦 plane, provided the original vector is not vertical.

The above definitions are not valid if the roll is nonzero. Nevertheless, the roll is very rarely nonzero in practice, and so it rarely affects the physics described in this document, if at all.

3.4. Punchangles

The punchangles can refer to the client side or the server side values. The client side punchangles are usually affected by weapon recoil and are cosmetic in nature. Namely, they do not affect the aiming viewangles of the player. The player may be aiming with zero pitch while the camera appears to point elsewhere. The server side punchangles, on the other hand, affects the viewangles and therefore the aiming. The server side punchangles are affected by certain types of damage (see Health and damage) and punches from monsters (which are different from the purely damage itself).

The punchangles may be denoted as 𝐏, consisting of punch pitch, punch yaw, and punch roll. When the punchangles are nonzero, the game will smoothly decrease the angles until all of them become zero. In each frame, the game calculates

𝐏=max(0,𝐏(112𝜏)10𝜏)ˆ𝐏

The punchangles are rarely big issues except when the punch yaw and punch roll are nonzero. In these cases, strafing (Strafing) can be affected. Though this very rarely happens.

When a saveload is performed, the punchangles will be added to the viewangles permanently, while the punchangles will be set to zero. When this happens, the viewangles will not be reduced gradually like the case when punchangles are nonzero.

3.5. Forwardmove, sidemove, and upmove

When the movement keys are held, there exists three values, 𝐹, 𝑆, and 𝑈, that are set. These values are called the forwardmove, sidemove, and upmove respectively, or FSU for short, and are used in player movement physics (see Player movement basics). In the beginning of player movement physics, the FSU values are computed in the following way. First, define client side analogues of ˜𝐹, ˜𝑆, and ˜𝑈. Then,

+forward and +back

Assigns the positive or negative cl_forwardspeed to ˜𝐹

+moveright and +moveleft

Assigns the positive or negative cl_sidespeed to ˜𝑆

+moveup and +movedown

Assigns the positive or negative cl_upspeed to ˜𝑈

This is done at client side. Before sending these values to the server, however, they will be truncated to integers and clamped to [ 2047,2047]. Let 𝑀𝑚 the value of sv_maxspeed. Then, PM_CheckParamters [sic] computes the final server side FSU values such that, assuming not all of FSU are zero,

𝐹=˜𝐹𝑀𝑚˜𝐹2+˜𝑆2+˜𝑈2𝑆=˜𝑆𝑀𝑚˜𝐹2+˜𝑆2+˜𝑈2𝑈=˜𝑈𝑀𝑚˜𝐹2+˜𝑆2+˜𝑈2

If all of FSU are zero, then nothing is done and they remain zero.