In Descent 3's released source, which includes the unreleased "1.5" patch, the player ship's thrust is the sum of a forward, an up, and a sideways push along the ship's own axes, each the control's value times full_thrust, the whole scaled by the player's movement_scalar and by 1.3 outdoors; the physics then moves a thrusting object against a drag proportional to velocity, solved in closed form over the frame: v = (v₀ − F/d)·e^(−(d/m)·t) + F/d. Computed from that formula, drag acts while the ship thrusts too, and speed levels off at F/d, as in Doom, whose floor friction multiplies momentum by 0.90625 each tic thrust or not; Quake's ground acceleration instead stops at the wish speed. | WinQuake/sv_user.c L195–201 | Code |
A Quake demo (.dem, WinQuake source) opens with a line of text, the CD track to force or -1, and then holds one block for each message the server sent, except one-byte keepalives (svc_nop), which are dropped: its length in 4 bytes, the client's three view angles as 4-byte floats, and the message itself. stop ends the recording with one last block holding a disconnect message. | WinQuake/cl_demo.c L244–255WinQuake/cl_demo.c L66–79WinQuake/cl_demo.c L145–153WinQuake/cl_demo.c L177–180 | Code |
| A Quake demo records what the server told the client, not the player's inputs. Playing one first disconnects from any server, shutting down a local one, and the client then reads its server messages from the file instead of the network and parses them as live ones; the file's view angles set the view. No server runs during playback; the world arrives as the recorded messages. | WinQuake/cl_demo.c L28–34WinQuake/cl_demo.c L116–128WinQuake/cl_demo.c L286WinQuake/cl_main.c L99–124WinQuake/cl_main.c L641–650 | Code |
Playback reads the next block only once the client's clock passes the time stamped in the last message (svc_time), so a demo plays at its recorded speed; timedemo instead reads one block a frame and reports frames, seconds, and frames per second. The file's closing disconnect ends the game, and while the attract loop runs the next demo of the startdemos list, up to 8, starts. | WinQuake/cl_demo.c L98–113WinQuake/cl_parse.c L773–775WinQuake/cl_demo.c L337WinQuake/cl_parse.c L789–790WinQuake/host.c L106–107WinQuake/cl_main.c L228–241WinQuake/client.h L98 | Code |
On the ground, Quake II's Pmove runs PM_Friction, which lowers the speed by step time × 6 (pm_friction) × the larger of the speed and 100 (pm_stopspeed) on any ground that isn't slick, and then PM_Accelerate with pm_accelerate 10: if the speed along the wish direction is under the wish speed, it adds 10 × step time × wish speed, no more than the shortfall. The wish velocity's length is capped at 300 (pm_maxspeed), or 100 ducked. These are the numbers Quake III keeps (friction 6, acceleration 10), where Quake's friction is 4. Computed, not measured: since 10 outpaces 6, a run settles at exactly 300 units/s. The movement constants are C globals, not cvars. | WinQuake/sv_phys.c L42 | Code |
In the air, Quake II's PM_AirMove calls the ground's PM_Accelerate with an acceleration of 1 while pm_airaccelerate is 0: the push is 1 × step time × wish speed (up to 300), and it stops only when the velocity's component along the wish direction reaches the full wish speed. Quake III keeps this rule, with pm_airaccelerate 1 and a PM_Accelerate commented "q2 style". In deathmatch the server copies its latched cvar sv_airaccelerate (default 0) into pm_airaccelerate, and any nonzero value switches to PM_AirAccelerate, which is Quake's air rule: the wish speed capped at 30 for the shortfall, the push from the uncapped wish speed × pm_accelerate (10) × step time. The server's movement code uses the value only as a nonzero test, so it acts as an on-off switch; outside deathmatch it is forced to 0. The server also sends the value to clients as the CS_AIRACCEL config string, for their prediction, in code not read here. | WinQuake/sv_user.c L212–220 | Code |
Quake II's PM_CheckJump, which Pmove runs before friction, does nothing while the landing timer flag is set; otherwise it needs an up move of at least 10 and the jump-held flag clear, which only a command with the key released clears, so holding the key never jumps twice. On ground it adds 270 to the vertical velocity, raising it to at least 270, and clears the ground entity; in water at level 2 or more it sets an upward swim speed instead (100 in water, 80 in slime, 50 in lava). Quake's QuakeC jump and QuakeWorld's movement code also add 270. Gravity is sv_gravity, 800. | QW/client/pmove.c L681 | Code |
Quake II checks the jump before friction: Pmove calls PM_CheckJump, which clears the ground entity, and then PM_Friction, whose ground friction needs a ground entity, so a hop on the first command that starts on the ground loses no speed to friction. QuakeWorld's PlayerMove has the same order: JumpButton clears onground before PM_Friction, whose ground friction needs it. Two conditions apply. The jump key must be released and pressed again (the jump claim). And when the position check first finds ground with the vertical speed below −200, it sets a landing timer of 18 units, or 25 below −400, that blocks the jump until it runs out; each command lowers it by its length in ms divided by 8, rounded down and at least 1, so 18 units last 144 ms at 8 or 16 ms commands and 180 ms at 10 ms (computed). By the atlas's reading the slide move clips the velocity along a floor it runs into, and the stepping move keeps that clipped vertical speed, so a landing step that reaches the floor would not set the timer; how often a hop's landing sets it has not been measured. | QW/client/pmove.c L889–894QW/client/pmove.c L680QW/client/pmove.c L366–369 | Code |
id Software's released Quake engine source numbers its single-player engine, WinQuake, as version 1.09 (VERSION in quakedef.h); the readme calls the release "the complete source code for winquake, glquake, quakeworld, and glquakeworld" and says the original DOS version "should also be buildable from these sources, but we didn't bother trying". | WinQuake/quakedef.h L26readme.txt L2–3readme.txt L13–14 | Code |
Quake's host runs a frame only once at least 1/72 second has passed since the last, so the game steps at most 72 times a second; each frame's time step (host_frametime) is the real time since the last frame, clamped to 0.001–0.1 second, unless host_framerate fixes it, and a time demo skips the 1/72 wait. | WinQuake/host.c L503–519 | Code |
Each Quake server frame reads every client's move and runs SV_ClientThink, which applies friction and acceleration, before SV_Physics, which runs the QuakeC PlayerPreThink (where the jump is), then gravity and the move; SV_ClientThink reads the on-ground flag the last move left, does nothing for a dead player or one with movetype none, and hands a water jump and a player at water level 2 or more (unless noclipping) to the water code. | WinQuake/host.c L611–617WinQuake/sv_user.c L611WinQuake/sv_user.c L625–626WinQuake/sv_user.c L384–429WinQuake/sv_phys.c L1534–1535WinQuake/sv_phys.c L1069WinQuake/sv_phys.c L1086–1095 | Code |
On the ground, Quake's SV_UserFriction takes the horizontal speed and lowers it by frame time × friction × the larger of the speed and sv_stopspeed, to no less than 0, scaling all three velocity axes by the same factor; sv_friction defaults to 4 and sv_stopspeed to 100, and when a trace 16 units ahead along the motion finds no floor within 34 units below the feet, the friction is multiplied by edgefriction (default 2). A standing player is left alone. | WinQuake/sv_phys.c L42–43WinQuake/sv_user.c L27WinQuake/sv_user.c L132–159 | Code |
Quake builds the wish velocity from the forward and side moves along the view's forward and right vectors (zero vertical for a walking player), caps its length at sv_maxspeed (default 320), and on the ground runs friction and then SV_Accelerate: if the speed along the wish direction is under the wish speed, it adds sv_accelerate (default 10) × frame time × wish speed along that direction, no more than the shortfall. | WinQuake/sv_user.c L167–168WinQuake/sv_user.c L341–365WinQuake/sv_user.c L190–204 | Code |
Quake's keyboard moves are cl_forwardspeed and cl_backspeed 200 and cl_sidespeed 350, doubled by the run key (cl_movespeedkey 2.0), and the menu's "always run" sets forward and back to 400. Computed from these and the 320 cap: walking forward wishes 200, while a strafe alone (350), a walk with a strafe (√(200² + 350²) ≈ 403), and any run all wish 320. | WinQuake/cl_input.c L213–217WinQuake/cl_input.c L298–317WinQuake/menu.c L1115–1124WinQuake/sv_user.c L351–354 | Code |
| Computed from the friction and acceleration rules, not measured: because acceleration (10) outpaces friction (4), a Quake player on flat ground settles at exactly the wish speed, 320 units/s at a run; at the 72-frame rate a run from rest reaches it on frame 10 (0.14 s), and letting go from 320 comes to rest after 39 frames (0.54 s). | WinQuake/sv_user.c L150–151WinQuake/sv_user.c L195–201WinQuake/sv_phys.c L42–43WinQuake/sv_user.c L167–168WinQuake/host.c L505 | Code |
In the air, Quake's SV_AirAccelerate takes the wish velocity's direction and length, caps that length at 30, and adds speed along the wish direction only while the velocity's component along it is under 30: the push is sv_accelerate × the uncapped wish speed (up to 320) × frame time, cut to the shortfall below 30. There is no friction in the air. | WinQuake/sv_user.c L207–225WinQuake/sv_user.c L361–368 | Code |
Computed from SV_AirAccelerate, not measured: with a wish speed of 320 the push can reach 10 × 320 × frame time (44.4 at 1/72 s, more at longer frames), so a frame lifts the velocity's component c along the wish direction to 30 whenever c is between −14.4 and 30, and the new speed is √(v² + 900 − c²) for speed v. That is largest with the wish at 90° to the motion: each frame then adds √(v² + 900) − v, 1.40 units/s at 320 and 0.90 at 500, and turns the motion by atan(30/v), 5.4° per frame at 320. At 72 frames a second that is about 101 units/s gained per second at 320. | WinQuake/sv_user.c L212–224WinQuake/sv_user.c L168WinQuake/host.c L505 | Code |
| Computed from the jump and gravity, ignoring frame steps: a Quake jump from flat ground at the default gravity of 800 rises 270² / (2 × 800) ≈ 45.6 units and lands after 2 × 270 / 800 = 0.675 seconds. | WinQuake/sv_phys.c L389 | Code |
Quake's gravity is the cvar sv_gravity, defaulting to 800, and each level's QuakeC worldspawn sets it to 800 again, or to 100 on E1M8; every frame a walking player is not in water (water level 2 or more) or a water jump, SV_AddGravity takes the entity's gravity field (1 when unset) × sv_gravity × frame time off its vertical velocity, on the ground as well as in the air. | WinQuake/sv_phys.c L44WinQuake/sv_phys.c L381–389WinQuake/sv_phys.c L1089–1090WinQuake/sv_phys.c L858 | Code |
Because Quake runs friction in SV_ClientThink before the QuakeC jump in PlayerPreThink, and the on-ground flag is set by the walking move when it ends on a floor, a jump can come no sooner than the frame after landing, and that frame takes ground friction first: computed, a hop loses 4/72 of its horizontal speed (5.6%) at 72 frames a second above 100 units/s, or 8/72 (11.1%) when no floor lies within 34 units below a point 16 units ahead. | WinQuake/host.c L612–617WinQuake/sv_user.c L387WinQuake/sv_user.c L361–364WinQuake/sv_phys.c L1069WinQuake/sv_phys.c L970–971WinQuake/sv_phys.c L284–287WinQuake/sv_phys.c L1033–1037WinQuake/sv_user.c L144–151 | Code |
QuakeWorld's pmove.c checks the jump before friction: PlayerMove calls JumpButton, which clears onground, and then PM_Friction, whose ground friction runs only while onground is set, so a QuakeWorld hop on its first ground frame loses no speed to friction, where WinQuake's takes one frame of it (quake/hop-friction). Both still need the button released between jumps, and both add 270. | QW/client/pmove.c L889–894QW/client/pmove.c L674–681QW/client/pmove.c L366–369QW/server/sv_user.c L1460WinQuake/sv_user.c L361–364WinQuake/sv_phys.c L1069 | Code |
QuakeWorld's PM_AirAccelerate keeps WinQuake's air rule unchanged: the wish speed capped at 30 for the shortfall test, and the push from the uncapped wish speed × acceleration × frame time; it adds only an early return for a dead player or a water jump. | QW/client/pmove.c L412–433WinQuake/sv_user.c L212–222 | Code |
On the ground, Quake III's PM_Friction takes the horizontal speed and lowers it by step time × pm_friction × the larger of the speed and pm_stopspeed, scaling all three velocity axes by the same factor, with pm_friction 6 and pm_stopspeed 100 set in the code; it skips ground friction in water deeper than level 1, on a slick surface, and while the knockback timer runs, and it has no edge friction. Quake's ground friction is the same rule with friction 4 (sv_friction) and the same stop speed, doubled over a ledge (quake/ground-friction). | WinQuake/sv_phys.c L42–43WinQuake/sv_user.c L27WinQuake/sv_user.c L144–151 | Code |
Quake III's PM_WalkMove runs friction and then PM_Accelerate with pm_accelerate 10, which is Quake's rule (Quake III's PM_Accelerate comment says "q2 style"): if the speed along the wish direction is under the wish speed, it adds 10 × step time × wish speed along that direction, no more than the shortfall. The full wish speed is the player's speed, set before each move from g_speed (320, × 1.3 with Haste). Computed, not measured: since 10 outpaces the friction of 6, a run settles at exactly 320, as Quake's does at 10 against 4; at 8 ms steps it reaches 320 from rest in 19 steps (0.15 s) and stops from 320 in 45 (0.36 s). These figures leave out the rounding trap_SnapVector applies to the velocity after each step, engine code the atlas hasn't read. | WinQuake/sv_user.c L195–201WinQuake/sv_user.c L167–168 | Code |
Quake III's keyboard moves are 127 per axis at a run (the speed key or cl_run, one but not both) and 64 at a walk, and PM_CmdScale scales the wish so its length is the player's speed × the largest of the forward, side, and up moves ÷ 127, which a comment says avoids "a sqrt(2) distortion in speed": at a run, forward, a strafe, and both together each wish 320, so a strafe adds no ground speed, where Quake's walk with a strafe wishes 320 against 200 (quake/move-speeds). The up move counts too: computed, holding the jump key (up move 127) in the air with forward and a strafe wishes 320 × √2/√3 ≈ 261. On the ground PM_CheckJump zeroes a held jump's up move, and Pmove lowers it to 20 for the later steps of a command it splits. The figures leave out the rounding trap_SnapVector applies to the velocity after each step, engine code the atlas hasn't read. | WinQuake/cl_input.c L213–215WinQuake/sv_user.c L351–354 | Code |
In the air, Quake III's PM_AirMove runs friction (no ground friction, which needs the walking flag), builds a flat wish velocity, and calls the same PM_Accelerate as the ground with pm_airaccelerate 1: the push is 1 × step time × wish speed (2.56 units/s per 8 ms step at 320), and it stops only when the velocity's component along the wish direction reaches the full wish speed. Quake's air rule is the reverse: a push ten times as strong (10 × 320 × frame time), cut off when that component reaches 30 (quake/air-accelerate). An #else branch left out by #if 1 would push the velocity toward the wish velocity instead, commented "proper way (avoids strafe jump maxspeed bug), but feels bad". The figures leave out the rounding trap_SnapVector applies to the velocity after each step, engine code the atlas hasn't read. | WinQuake/sv_user.c L212–224 | Code |
Quake III checks the jump before friction: each step, PmoveSingle traces for ground before the move, PM_WalkMove calls PM_CheckJump before PM_Friction, and a jump clears the walking flag and hands the step to PM_AirMove, whose friction call skips ground friction without that flag. So a hop on the first step that starts on the ground loses no speed to friction, as in QuakeWorld (quake/qw-hop-friction), where WinQuake's takes one frame of it, 5.6% at 72 frames a second (quake/hop-friction). The hop needs the jump key released and pressed again in the air, as in both. | QW/client/pmove.c L889–894QW/client/pmove.c L674–683QW/client/pmove.c L366–369WinQuake/sv_user.c L361–364WinQuake/sv_phys.c L1069 | Code |
Computed from PM_Accelerate with pm_airaccelerate 1, not measured: at a wish speed W of 320 (forward and a strafe at a run, the jump key up, no Haste) the air push is W × step time, a = 2.56 per 8 ms step, and the cap leaves room for all of it while the velocity's component c along the wish direction is at most W − a; the new speed √(v² + 2ca + a²) is then largest at c = W − a, with the wish 7° off a 320 motion, 37° off 400, and 51° off 500. Each 8 ms step adds 2.54 units/s at 320 and 1.63 at 500, about 317 and 204 units/s each second, close to W²/v at any step length (316 at 1/72 s). Quake's gain is fixed per frame instead, about 101 units/s each second at 320 at 72 frames a second (quake/air-strafe-gain). The figures leave out the rounding trap_SnapVector applies to the velocity after each step, engine code the atlas hasn't read. | WinQuake/sv_user.c L212–222 | Code |