For a player unfamiliar with the world of kreedz, movement in the water is often viewed from the position of "jump into the water, look where you want to swim, hold down the button and go." For a more experienced player, this is supplemented by the skill of staying on the surface of the water by holding down the jump button, as well as the ability to gain speed with the help of strafes. There are, however, strange moments when jumping out of the water doesn't work the first time, and even during acceleration with strafes on the surface, something seems to slow us down. In this article we will try to understand both the basic movement in water and more subtle aspects.
In fact, a number of functions in
PM_PlayerMove are responsible for the physics of movement in water:
Here we can immediately highlight several key points:
- the main function of movement in water is PM_WaterMove
- the degree of immersion in water is determined by the variable
waterlevel
, which is set in the function PM_CheckWater
- when the jump key is held down in water, PM_Jump works somehow
- PM_WaterJump is responsible for a waterjump, the operation of which is regulated by the counter
waterjumptime
Let's start with
PM_CheckWater, which is called both inside
PM_CategorizePosition and separately during a waterjump:
Here, by default, we assume that we are not in the water, so
waterlevel
is zero. Next, we take a point 1 unit higher than the foot level (for a standing model, 35 units below the player's center of origin, for a sitting model, 17 units below), and if it is in the water, we set
waterlevel
to 1. If the center of the model is also submerged in water, we change
waterlevel
to 2. And finally if a point above
origin
at eye level is in the water (17 units above the center of a standing model or 12 units above a sitting model), then
waterlevel
becomes equal to 3.
Now from
PM_PlayerMove it becomes clear that movement in water begins from the moment the center of the model is submerged in water, i.e. conditionally when we are in water at least up to our waist. This movement can be divided into several stages. First, the desired direction of speed
wishvel
and its value
wishspeed
are calculated:
The direction of movement is directly obtained from the pressed keys
WASD and the direction of the view, and upward movement can also be added to it using the command
+moveup (if the player can find the corresponding key). However, this will not change anything basically, since the speed is then cut to the maximum speed with the selected weapon and loses 20%. From this we immediately get that the speed of movement with usp in water: 250 * 0.8 = 200 units/s. Here you can also notice that if the movement keys are not pressed, the player will drown at a speed of 60 * 0.8 = 48 units/s. Moreover, the speed of movement with the keys depends on both holding down
+duck (250 * 0.333 * 0.8 = 66.6 units/s) and using
+speed (250 * 0.52 * 0.8 = 104 units/s), at that the speed at which the player drowns does not depend on these conditions and is always equal to 48.
The following part is reminiscent of the logic of movement on land, when on one hand we are slowed down, on the other hand we are trying to speed up:
This logic does not allow us to instantly gain speed at the beginning of the movement, and imitates inertia when stopping. When slowdown and acceleration balance each other, we get uniform motion. Let's consider, for example, moving forward at a speed of 200 units/s. The desired speed is directed forward,
wishspeed
= 200,
speed
= 200,
newspeed
= 200 - 4 * 1 * 0.01 * 200 = 0.96 * 200. Then the velocity vector is scaled, falling by 4%. Now we calculate the increase from the acceleration part:
addspeed
= 200 - 200 * 0.96 = 0.04 * 200,
accelspeed
= 5 * 1 * 0.01 * 200 = 0.05 * 200. Since
accelspeed > addspeed
, then
accelspeed
is cut off to
addspeed
, which is added to the speed. Thus, the speed increases by exactly the same amount that it lost during slowdown, which means the movement is truly uniform.
Furher the last part allows you to climb underwater on steps and inclined surfaces:
The logic here is much simpler than in a similar check in
PM_WalkMove, but it also has its own peculiarities. For example, the height of the steps that can be climbed is 1 unit higher in water, making it 19 units. However, to prevent the trace from touching the step, you need to look at the horizon or higher while holding down
W, since this affects the end point of the trace
dest
. In fact, you can even lower your gaze a little, if you remember that from your feet to the ground we have an additional 0.03125 units, and the trace does not take them into account.
Now let's figure out what role the
PM_Jump function plays in water. It is called in
PM_PlayerMove when the jump key is pressed and the player is submerged in water at least up to the waist:
If a waterjump is already in progress,
PM_Jump only decreases its timer and exits. Otherwise, the player's vertical speed is set to 100 units/s in the upward direction (in a swamp, the ascent speed will be 80 units/s, in other liquids such as lava, 50 units/s). Again, this speed here does not depend on the
+duck or
+speed commands. Then, once per second, one of four water sounds is played.
After
PM_Jump,
PM_WaterMove is called, in which, if the movement keys are not pressed, 4% is subtracted from the speed. If emersion occurs, its final speed is equal to 96 units/s. If we run up and jump into the water from some height, releasing all keys except the jump, then we will jump out of the water with a vertical speed of 96 units/s as soon as we find ourselves waist-deep in it, and thus perform movements similar to the oscillations of a float with a period of 26 frames. At the same time, each touch of the water will subtract 4% from the horizontal speed, therefore, at high speed, slowdown will be felt to a greater extent. For example, at a speed of 250 units/s after 10 touches (2.6 seconds) there will be 166 units/s left, and at an initial speed of 350 after 10 touches there will be 232 units/s left. Therefore to maintain speed it is necessary to use strafes, which outside of water work according to the algorithms already known to us for movement in the air.
The only thing left to figure out is the waterjump. It is only possible when
waterlevel
is 2, that is, when we are submerged to the waist, but the eye level is above the surface of the water. The following checks occur inside
PM_CheckWaterJump:
If a waterjump is already in progress, then a new one will not start. Also, a waterjump cannot be started when falling at a speed exceeding 180 units/s. Further, if the speed is non-zero, but at the same time the angle between the direction of the horizontal velocity
flatvelocity
and the direction of the gaze
flatforward
is more than 90°, then jumping out of the water will also not occur. Next we make two traces for the point hull:
The beginning of the first trace is 8 units above the center of the model, and its end is shifted relative to the beginning by 24 units in the direction of the view. If the trace hits a plane close to vertical (the permissible deviation is no more than 5.74°), then the next trace occurs, also 24 units in the direction of the view, but already at a height equal to the height of the model:
If this trace in turn did not hit anything, then the
FL_WATERJUMP
flag is set, the
waterjumptime
counter is set to 2000 frames (2 seconds), and we are thrown up at a speed of 225 units/s (which immediately after this in
PM_WaterMove will lose 4% and become equal to 216 units/s). From this point on, instead of the
PM_CheckWaterJump,
PM_Jump and
PM_WaterMove we have considered, the
PM_WaterJump function works:
In addition to decreasing the
waterjumptime
counter, it sets the direction of the horizontal velocity along the horizontal component of the
movedir
vector. In turn,
movedir
was obtained in
PM_CheckWaterJump as a vector opposite to the normal to the plane with which the interaction occurred. This means that if jumping out of the water occurs near a wall with a slight slope, then during the throw up we will also be pressed against the wall.
The speed of 216 units/s will be maintained until we leave the water, at which point the waterlevel will be reset to zero,
PM_WaterJump will reset
waterjumptime
, after which we will rise to a height of 30.24 units in 28 frames. Thus, we will be able to jump onto a wall rising 30 units above the water. Adding a duck will only add 5 units to the possible height of the wall, since with a wall height of 36 units above the water in the
PM_CheckWaterJump function the second trace will hit the wall, and no waterjump will occur.
Finally, let's pay attention to the fact that for a waterjump we did not need to use the jump key. That is, to jump out, it is enough to swim up to the wall and look in its direction (if the wall is parallel to the base axis, then a trace of 24 units with half the width of the model of 16 units allows you to turn away from the wall by
arccos(16/24) = 48.19°
when jumping out). The required degree of immersion in water depends on the height of the wall in relation to the water. For example, if they are at the same level, then to initiate a waterjump it is necessary to submerge the center of the model at least 8 units into the water. Not knowing this nuance often prevents you from jumping out of the water the first time and causes confusion.