Claim set · data/claims/quake-ii.yaml

Quake II (id Software's GPL source release, 3.19)

Game: Quake II.

id Software's GPL source release of Quake II calls itself "the complete source code for Quake 2, version 3.19", and qcommon.h sets VERSION to 3.19, so the code read here is the 3.19 point release rather than the 1997 retail build. quake-ii/source-version

Value: 3.19

CodeQuake II: the GPL source release (id-Software/Quake-2, 3.19) · readme.txt#L2This is the complete source code for Quake 2, version 3.19, buildable withQuake II: the GPL source release (id-Software/Quake-2, 3.19) · qcommon/qcommon.h#L26#define VERSION 3.19Checked by claude, 2026-10-22 — read the readme's first line and the VERSION define in qcommon/qcommon.h at the pinned commit

Player movement is qcommon/pmove.c, engine code that the server and the client's prediction both run; weapons, damage, monsters, and items are the game library in game/.

Cited by: Quake II

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. quake-ii/ground-move

About: Quake II Running

Value
{
 "pm_friction": 6,
 "pm_accelerate": 10,
 "pm_stopspeed": 100,
 "pm_maxspeed": 300
}
CodeQuake II: the GPL source release (id-Software/Quake-2, 3.19) · qcommon/pmove.c#L53-L59float pm_stopspeed = 100; float pm_maxspeed = 300; float pm_duckspeed = 100; float pm_accelerate = 10; … float pm_friction = 6;Quake II: the GPL source release (id-Software/Quake-2, 3.19) · qcommon/pmove.c#L1335-L1337PM_CheckJump (); … PM_Friction ();Quake II: the GPL source release (id-Software/Quake-2, 3.19) · qcommon/pmove.c#L364-L370// apply ground friction if ((pm->groundentity && pml.groundsurface && !(pml.groundsurface->flags & SURF_SLICK) ) || (pml.ladder) ) { friction = pm_friction; control = speed < pm_stopspeed ? pm_stopspeed : speed; drop += control*friction*pml.frametime;Quake II: the GPL source release (id-Software/Quake-2, 3.19) · qcommon/pmove.c#L397-L411void PM_Accelerate (vec3_t wishdir, float wishspeed, float accel) … currentspeed = DotProduct (pml.velocity, wishdir); addspeed = wishspeed - currentspeed; if (addspeed <= 0) return; accelspeed = accel*pml.frametime*wishspeed; if (accelspeed > addspeed) accelspeed = addspeed;Quake II: the GPL source release (id-Software/Quake-2, 3.19) · qcommon/pmove.c#L607-L613maxspeed = (pm->s.pm_flags & PMF_DUCKED) ? pm_duckspeed : pm_maxspeed; … if (wishspeed > maxspeed) { VectorScale (wishvel, maxspeed/wishspeed, wishvel); wishspeed = maxspeed;Quake II: the GPL source release (id-Software/Quake-2, 3.19) · qcommon/pmove.c#L635-L638else if ( pm->groundentity ) { // walking on ground pml.velocity[2] = 0; //!!! this is before the accel PM_Accelerate (wishdir, wishspeed, pm_accelerate);Quake III Arena: the GPL source release (id-Software/Quake-III-Arena, 1.32b) · code/game/bg_pmove.c#L39float pm_accelerate = 10.0f;Quake III Arena: the GPL source release (id-Software/Quake-III-Arena, 1.32b) · code/game/bg_pmove.c#L44float pm_friction = 6.0f;Quake: the engine source release (id-Software/Quake: WinQuake and QuakeWorld) · WinQuake/sv_phys.c#L42cvar_t sv_friction = {"sv_friction","4",false,true};Checked by claude, 2026-10-22 — read the movement globals, PM_Friction, PM_Accelerate, and PM_AirMove's clamp and ground branch in qcommon/pmove.c, against Quake III's bg_pmove.c globals and WinQuake's sv_friction

At 300 a step's friction removes 1800 × step time and the acceleration could add 3000 × step time, so the cap at the shortfall returns the speed to 300 at any step length. Water friction adds to the same drop. After each command PM_SnapPosition truncates each velocity axis to an eighth of a unit (L1089-L1091), which leaves 300 unchanged.

Cited by: Quake II

Quake II's keyboard moves are cl_forwardspeed, cl_sidespeed, and cl_upspeed 200, all doubled while running, which the speed key toggles: with cl_run 0 holding the key runs, and with cl_run 1 holding it walks. The movement code builds the wish velocity from the view's forward and right vectors, with the pitch first divided by 3, then zeroes its vertical part and caps its length at 300. Computed from these, with the view level (pitch 0): walking forward wishes 200 and walking forward with a strafe about 283, but a run wishes 400 forward and about 566 on the diagonal, both cut to 300, so a strafe adds nothing to a run on the ground. Looking up or down shortens the forward part before the cap. quake-ii/move-speeds

About: Quake II Strafe-running

Value
{
 "walk": 200,
 "walk_diagonal": 283,
 "run": 300
}
CodeQuake II: the GPL source release (id-Software/Quake-2, 3.19) · client/cl_main.c#L1441-L1448cl_upspeed = Cvar_Get ("cl_upspeed", "200", 0); cl_forwardspeed = Cvar_Get ("cl_forwardspeed", "200", 0); cl_sidespeed = Cvar_Get ("cl_sidespeed", "200", 0); … cl_run = Cvar_Get ("cl_run", "0", CVAR_ARCHIVE);Quake II: the GPL source release (id-Software/Quake-2, 3.19) · client/cl_input.c#L289-L309cmd->sidemove += cl_sidespeed->value * CL_KeyState (&in_moveright); … cmd->forwardmove += cl_forwardspeed->value * CL_KeyState (&in_forward); … if ( (in_speed.state & 1) ^ (int)(cl_run->value) ) { cmd->forwardmove *= 2; cmd->sidemove *= 2; cmd->upmove *= 2;Quake II: the GPL source release (id-Software/Quake-2, 3.19) · qcommon/pmove.c#L1344-L1349VectorCopy(pm->viewangles, angles); if (angles[PITCH] > 180) angles[PITCH] = angles[PITCH] - 360; angles[PITCH] /= 3; AngleVectors (angles, pml.forward, pml.right, pml.up);Quake II: the GPL source release (id-Software/Quake-2, 3.19) · qcommon/pmove.c#L595-L613wishvel[i] = pml.forward[i]*fmove + pml.right[i]*smove; wishvel[2] = 0; … wishspeed = VectorNormalize(wishdir); … maxspeed = (pm->s.pm_flags & PMF_DUCKED) ? pm_duckspeed : pm_maxspeed; … if (wishspeed > maxspeed)Checked by claude, 2026-10-22 — read the input cvars in cl_main.c, CL_BaseMove in cl_input.c (the XOR of the speed key with cl_run), Pmove's pitch division, and the wish velocity and its clamp in PM_AirMove

The diagonal figure is √(200² + 200²). The forward vector isn't renormalized after the vertical part is dropped, so at pitch p its horizontal length is cos(p/3). A joystick or mouse move can give other values; this is the keyboard.

Cited by: Quake II, Strafe-running

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. quake-ii/air-accelerate

About: Quake II Strafe-running

Value
{
 "air_accelerate": 1,
 "dm_switch_cap": 30
}
CodeQuake II: the GPL source release (id-Software/Quake-2, 3.19) · qcommon/pmove.c#L57float pm_airaccelerate = 0;Quake II: the GPL source release (id-Software/Quake-2, 3.19) · qcommon/pmove.c#L652-L657{ // not on ground, so little effect on velocity if (pm_airaccelerate) PM_AirAccelerate (wishdir, wishspeed, pm_accelerate); else PM_Accelerate (wishdir, wishspeed, 1);Quake II: the GPL source release (id-Software/Quake-2, 3.19) · qcommon/pmove.c#L414-L427void PM_AirAccelerate (vec3_t wishdir, float wishspeed, float accel) … if (wishspd > 30) wishspd = 30; currentspeed = DotProduct (pml.velocity, wishdir); addspeed = wishspd - currentspeed; if (addspeed <= 0) return; accelspeed = accel * wishspeed * pml.frametime;Quake II: the GPL source release (id-Software/Quake-2, 3.19) · server/sv_main.c#L974sv_airaccelerate = Cvar_Get("sv_airaccelerate", "0", CVAR_LATCH);Quake II: the GPL source release (id-Software/Quake-2, 3.19) · server/sv_init.c#L196-L205if (Cvar_VariableValue ("deathmatch")) { sprintf(sv.configstrings[CS_AIRACCEL], "%g", sv_airaccelerate->value); pm_airaccelerate = sv_airaccelerate->value; } else { strcpy(sv.configstrings[CS_AIRACCEL], "0"); pm_airaccelerate = 0;Quake III Arena: the GPL source release (id-Software/Quake-III-Arena, 1.32b) · code/game/bg_pmove.c#L40float pm_airaccelerate = 1.0f;Quake III Arena: the GPL source release (id-Software/Quake-III-Arena, 1.32b) · code/game/bg_pmove.c#L240-L242static void PM_Accelerate( vec3_t wishdir, float wishspeed, float accel ) { #if 1 // q2 styleQuake III Arena: the GPL source release (id-Software/Quake-III-Arena, 1.32b) · code/game/bg_pmove.c#L637PM_Accelerate (wishdir, wishspeed, pm_airaccelerate);Quake: the engine source release (id-Software/Quake: WinQuake and QuakeWorld) · WinQuake/sv_user.c#L212-L220wishspd = VectorNormalize (wishveloc); if (wishspd > 30) wishspd = 30; currentspeed = DotProduct (velocity, wishveloc); addspeed = wishspd - currentspeed; … accelspeed = sv_accelerate.value*wishspeed * host_frametime;Checked by claude, 2026-10-22 — read PM_AirMove's air branch, PM_AirAccelerate, and the pm_airaccelerate global in pmove.c, the cvar in sv_main.c and its copy in SV_SpawnServer (sv_init.c), against Quake III's PM_Accelerate and air call and WinQuake's SV_AirAccelerate

The client's use of CS_AIRACCEL is in client/cl_pred.c, outside the files pinned here and not read for this claim. Within pmove.c, pm_airaccelerate is used only in the test at L654.

Cited by: Quake II, Strafe-running

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. quake-ii/jump

About: Quake II Jump

Value: 270 units/s

CodeQuake II: the GPL source release (id-Software/Quake-2, 3.19) · qcommon/pmove.c#L778-L822void PM_CheckJump (void) { if (pm->s.pm_flags & PMF_TIME_LAND) { // hasn't been long enough since landing to jump again return; } if (pm->cmd.upmove < 10) { // not holding jump pm->s.pm_flags &= ~PMF_JUMP_HELD; return; } // must wait for jump to be released …Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/g_save.c#L164sv_gravity = gi.cvar ("sv_gravity", "800", 0);Quake: the QuakeC source and compiler (id-Software/Quake-Tools, qcc/v101qc) · qcc/v101qc/client.qc#L811self.velocity_z = self.velocity_z + 270;Quake: the engine source release (id-Software/Quake: WinQuake and QuakeWorld) · QW/client/pmove.c#L681pmove.velocity[2] += 270;Checked by claude, 2026-10-22 — read PM_CheckJump in qcommon/pmove.c and the sv_gravity cvar in game/g_save.c, against the jump lines in Quake's client.qc and QuakeWorld's pmove.c

Because the jump adds to the vertical velocity, a jump made while moving upward (up stairs or a ramp, whose climb the stepping move leaves in the velocity) takes off faster than 270. The swim-up branch also returns early when the player is sinking at 300 units/s or more (L802).

Cited by: Quake II

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. quake-ii/hop-friction

About: Quake II Bunny hopping

CodeQuake II: the GPL source release (id-Software/Quake-2, 3.19) · qcommon/pmove.c#L1333-L1337else { PM_CheckJump (); … PM_Friction ();Quake II: the GPL source release (id-Software/Quake-2, 3.19) · qcommon/pmove.c#L819pm->groundentity = NULL;Quake II: the GPL source release (id-Software/Quake-2, 3.19) · qcommon/pmove.c#L365if ((pm->groundentity && pml.groundsurface && !(pml.groundsurface->flags & SURF_SLICK) ) || (pml.ladder) )Quake II: the GPL source release (id-Software/Quake-2, 3.19) · qcommon/pmove.c#L714-L726if (! (pm->s.pm_flags & PMF_ON_GROUND) ) { // just hit the ground pm->s.pm_flags |= PMF_ON_GROUND; // don't do landing time if we were just going down a slope if (pml.velocity[2] < -200) { pm->s.pm_flags |= PMF_TIME_LAND; // don't allow another jump for a little while if (pml.velocity[2] < -400) pm->s.pm_time = 25; else pm->s.pm_time = 18;Quake II: the GPL source release (id-Software/Quake-2, 3.19) · qcommon/pmove.c#L1302-L1316// drop timing counter if (pm->s.pm_time) { int msec; msec = pm->cmd.msec >> 3; if (!msec) msec = 1; if ( msec >= pm->s.pm_time) { pm->s.pm_flags &= ~(PMF_TIME_WATERJUMP | PMF_TIME_LAND | PMF_TIME_TELEPORT); pm->s.pm_time = 0; } …Quake II: the GPL source release (id-Software/Quake-2, 3.19) · qcommon/pmove.c#L683-L693point[2] = pml.origin[2] - 0.25; … trace = pm->trace (pml.origin, pm->mins, pm->maxs, point);Quake II: the GPL source release (id-Software/Quake-2, 3.19) · qcommon/pmove.c#L219-L221for (i=0 ; i<numplanes ; i++) { PM_ClipVelocity (pml.velocity, planes[i], pml.velocity, 1.01);Quake II: the GPL source release (id-Software/Quake-2, 3.19) · qcommon/pmove.c#L332-L334//!! Special case // if we were walking along a plane, then we need to copy the Z over pml.velocity[2] = down_v[2];Quake: the engine source release (id-Software/Quake: WinQuake and QuakeWorld) · QW/client/pmove.c#L889-L894if (pmove.cmd.buttons & BUTTON_JUMP) JumpButton (); else pmove.oldbuttons &= ~BUTTON_JUMP; … PM_Friction ();Quake: the engine source release (id-Software/Quake: WinQuake and QuakeWorld) · QW/client/pmove.c#L680onground = -1;Quake: the engine source release (id-Software/Quake: WinQuake and QuakeWorld) · QW/client/pmove.c#L366-L369else if (onground != -1) // apply ground friction { control = speed < movevars.stopspeed ? movevars.stopspeed : speed; drop += control*friction*frametime;Checked by claude, 2026-10-22 — read Pmove's order, PM_CheckJump, PM_Friction's ground test, PM_CatagorizePosition's landing branch, the timer countdown, and PM_StepSlideMove(_) in qcommon/pmove.c, against QuakeWorld's PlayerMove, JumpButton, and PM_Friction

The landing timer also restores the unclipped velocity at the end of each slide move while it runs (L259-L262). The reading that clipped landings skip the timer follows the code paths, not a run; a probe that steps Pmove through a hop chain would settle it.

Cited by: Quake II

Quake II's bullets, pellets, and railgun are hitscan: fire_lead, which fire_bullet (the machine gun and chaingun) and fire_shotgun (once per pellet, for both shotguns) call, traces a line 8192 units long in the frame the gun fires, bent by a random spread (crandom × the spread, each way) for each bullet or pellet, and fire_rail traces 8192 units and keeps tracing past every monster or player it hits, damaging each. The blaster, grenades, and rockets launch entities that travel: the player's blaster bolt at 1000 units/s, the launcher's grenade at 600, and the rocket at 650. quake-ii/hitscan-projectiles

About: Quake II Hitscan

CodeQuake II: the GPL source release (id-Software/Quake-2, 3.19) · game/g_weapon.c#L152-L165r = crandom()*hspread; u = crandom()*vspread; VectorMA (start, 8192, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); … tr = gi.trace (start, NULL, NULL, end, self, content_mask);Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/g_weapon.c#L667-L689VectorMA (start, 8192, aimdir, end); … while (ignore) { tr = gi.trace (from, NULL, NULL, end, ignore, mask); … if ((tr.ent->svflags & SVF_MONSTER) || (tr.ent->client)) ignore = tr.ent; else ignore = NULL; if ((tr.ent != self) && (tr.ent->takedamage)) T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, 0, MOD_RAILGUN);Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/g_weapon.c#L277-L295void fire_bullet (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int hspread, int vspread, int mod) { fire_lead (self, start, aimdir, damage, kick, TE_GUNSHOT, hspread, vspread, mod); … Shoots shotgun pellets. Used by shotgun and super shotgun. … for (i = 0; i < count; i++) fire_lead (self, start, aimdir, damage, kick, TE_SHOTGUN, hspread, vspread, mod);Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/p_weapon.c#L1014fire_bullet (ent, start, forward, damage, kick, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MOD_MACHINEGUN);Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/p_weapon.c#L1151fire_bullet (ent, start, forward, damage, kick, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MOD_CHAINGUN);Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/p_weapon.c#L1213fire_shotgun (ent, start, forward, damage, kick, 500, 500, DEFAULT_DEATHMATCH_SHOTGUN_COUNT, MOD_SHOTGUN);Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/p_weapon.c#L1266fire_shotgun (ent, start, forward, damage, kick, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SSHOTGUN_COUNT/2, MOD_SSHOTGUN);Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/p_weapon.c#L728fire_grenade (ent, start, forward, damage, 600, 2.5, radius);Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/g_weapon.c#L620-L629void fire_rocket (edict_t *self, vec3_t start, vec3_t dir, int damage, int speed, float damage_radius, int radius_damage) … VectorScale (dir, speed, rocket->velocity); rocket->movetype = MOVETYPE_FLYMISSILE;Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/p_weapon.c#L783fire_rocket (ent, start, forward, damage, 650, damage_radius, radius_damage);Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/p_weapon.c#L832fire_blaster (ent, start, forward, damage, 1000, effect, hyper);Checked by claude, 2026-10-22 — read fire_lead, fire_bullet, fire_shotgun, fire_rail, and fire_rocket in g_weapon.c and the player's fire calls in p_weapon.c

A bullet that enters water is re-aimed with twice the spread. The shotgun's line at L1213 is its deathmatch pellet count; single player uses the line after it.

Cited by: Quake II

A Quake II rocket's direct hit does 100 + (int)(random() * 20.0) damage, 100 to 119, the roll made when it is fired; the explosion then does radius damage to everything else within 120 units, the rocket's 120 less half the distance from the explosion to the center of the target's box, and the target hit directly takes no splash. A quad damage multiplies both by 4. The railgun's damage is a constant, 150 in single player and 100 in deathmatch. quake-ii/rocket-damage

About: Quake II Damage formula

Value
{
 "direct": "100–119",
 "splash": 120,
 "splash_radius": 120
}
CodeQuake II: the GPL source release (id-Software/Quake-2, 3.19) · game/p_weapon.c#L767-L774damage = 100 + (int)(random() * 20.0); radius_damage = 120; damage_radius = 120; if (is_quad) { damage *= 4; radius_damage *= 4;Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/g_weapon.c#L589-L607if (other->takedamage) { T_Damage (other, ent, ent->owner, ent->velocity, ent->s.origin, plane->normal, ent->dmg, 0, 0, MOD_ROCKET); … T_RadiusDamage(ent, ent->owner, ent->radius_dmg, other, ent->dmg_radius, MOD_R_SPLASH);Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/g_combat.c#L554-L572while ((ent = findradius(ent, inflictor->s.origin, radius)) != NULL) { if (ent == ignore) continue; … VectorAdd (ent->mins, ent->maxs, v); VectorMA (ent->s.origin, 0.5, v, v); VectorSubtract (inflictor->s.origin, v, v); points = damage - 0.5 * VectorLength (v); … T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/g_utils.c#L94-L95eorg[j] = org[j] - (from->s.origin[j] + (from->mins[j] + from->maxs[j])*0.5); if (VectorLength(eorg) > rad)Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/p_weapon.c#L1310-L1318if (deathmatch->value) { // normal damage is too extreme in dm damage = 100; kick = 200; } else { damage = 150;Checked by claude, 2026-10-22 — read Weapon_RocketLauncher_Fire and weapon_railgun_fire in p_weapon.c, rocket_touch in g_weapon.c, T_RadiusDamage in g_combat.c, and findradius in g_utils.c

findradius also skips non-solid entities. A wall between the explosion and the target blocks the splash (CanDamage, L569). Armor and power armor then take their share in T_Damage.

Cited by: Quake II

Quake II's radius damage halves the points for the entity that caused it, so a player takes half splash from their own rocket or grenade, and T_Damage pushes a player who hurt themselves with 1600 × knockback / mass instead of 500 ×, commented "the rocket jump hack...". The player's mass is 200, so a self-inflicted push is 8 units/s per point against 2.5 from anyone else, and the radius damage passes its points as the knockback. Computed: a rocket exploding on the floor directly under a standing player, 28 units below the center of the player's box, does 120 − 14 = 106, halved to 53, which adds 424 units/s upward, against a jump's 270. quake-ii/self-damage

About: Quake II Rocket jumping

Value
{
 "self_scale": 0.5,
 "self_knockback": 1600,
 "other_knockback": 500,
 "mass": 200
}
CodeQuake II: the GPL source release (id-Software/Quake-2, 3.19) · game/g_combat.c#L564-L572points = damage - 0.5 * VectorLength (v); if (ent == attacker) points = points * 0.5; … T_Damage (ent, inflictor, attacker, dir, inflictor->s.origin, vec3_origin, (int)points, (int)points, DAMAGE_RADIUS, mod);Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/g_combat.c#L436-L446if (targ->mass < 50) mass = 50; else mass = targ->mass; if (targ->client && attacker == targ) VectorScale (dir, 1600.0 * (float)knockback / mass, kvel); // the rocket jump hack... else VectorScale (dir, 500.0 * (float)knockback / mass, kvel); VectorAdd (targ->velocity, kvel, targ->velocity);Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/p_client.c#L1099-L1100vec3_t mins = {-16, -16, -24}; vec3_t maxs = {16, 16, 32};Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/p_client.c#L1170ent->mass = 200;Checked by claude, 2026-10-22 — read T_RadiusDamage and T_Damage's knockback in g_combat.c and the player's box and mass in PutClientInServer (p_client.c)

The box runs from 24 below the origin to 32 above, so its center is 4 above the origin and the floor 28 below the center. The push direction is from the explosion to the player's origin. The worked case ignores armor, which lowers the damage taken but not the push, and the rocket's own offset from the eye.

Cited by: Quake II, Rocket jumping

On Easy (skill 0) outside deathmatch, Quake II's T_Damage halves the damage a player takes, to no less than 1; the knockback is worked out separately and isn't halved. Levels also pick their entities by skill: an entity flagged not-easy, not-medium, or not-hard is removed at load on that skill, with Nightmare (3) counted as hard. quake-ii/easy-damage

About: Quake II Difficulty level

Value: 0.5 ×

CodeQuake II: the GPL source release (id-Software/Quake-2, 3.19) · game/g_combat.c#L404-L410// easy mode takes half damage if (skill->value == 0 && deathmatch->value == 0 && targ->client) { damage *= 0.5; if (!damage) damage = 1;Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/g_combat.c#L428-L446// figure momentum add … VectorScale (dir, 500.0 * (float)knockback / mass, kvel);Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/g_spawn.c#L585-L593else { if ( /* ((coop->value) && (ent->spawnflags & SPAWNFLAG_NOT_COOP)) || */ ((skill->value == 0) && (ent->spawnflags & SPAWNFLAG_NOT_EASY)) || ((skill->value == 1) && (ent->spawnflags & SPAWNFLAG_NOT_MEDIUM)) || (((skill->value == 2) || (skill->value == 3)) && (ent->spawnflags & SPAWNFLAG_NOT_HARD)) ) { G_FreeEdict (ent);Checked by claude, 2026-10-22 — read T_Damage's easy-mode and knockback lines in g_combat.c and SpawnEntities' skill filter in g_spawn.c

The damage is an int, so an odd amount rounds down. The skill filter is the non-deathmatch branch; deathmatch removes entities flagged not-deathmatch instead (L578).

Cited by: Difficulty level, Quake II

Quake II's T_Damage doubles the damage a player does to a living monster that has no enemy yet, commented "bonus damage for suprising a monster", unless the damage is radius damage; the doubling comes after Easy's halving, which applies only to players. quake-ii/surprise-damage

About: Quake II Damage formula

Value: 2 ×

CodeQuake II: the GPL source release (id-Software/Quake-2, 3.19) · game/g_combat.c#L421-L423// bonus damage for suprising a monster if (!(dflags & DAMAGE_RADIUS) && (targ->svflags & SVF_MONSTER) && (attacker->client) && (!targ->enemy) && (targ->health > 0)) damage *= 2;Checked by claude, 2026-10-22 — read T_Damage in g_combat.c

A rocket's direct hit is not radius damage, so it doubles on a monster that hasn't noticed anyone; its splash does not.

Cited by: Quake II

In the four Quake II monsters read, a flinch is a cooldown rather than a roll: the soldier's, infantry's, and gunner's pain functions play a pain animation unless one started within the last 3 seconds (pain_debounce_time), choosing which at random (soldier, infantry) or by the damage (gunner), and on Nightmare (skill 3) they return before the animation. The soldier has an exception both ways: knocked upward faster than 100, it switches to a fourth pain animation even within the cooldown (if already in one of the other three) and even on Nightmare. On Nightmare the cooldown is 5 seconds, not 3: T_Damage resets it to 5 after each hit that calls the pain function. The tank adds a roll: it ignores hits of 10 or less, and flinches from 11 to 30 only when random() is at most 0.2; on hard and Nightmare it doesn't flinch during two of its attacks. quake-ii/pain-cooldown

About: Quake II Pain chance

CodeQuake II: the GPL source release (id-Software/Quake-2, 3.19) · game/m_soldier.c#L417-L450if (level.time < self->pain_debounce_time) { if ((self->velocity[2] > 100) && ( (self->monsterinfo.currentmove == &soldier_move_pain1) || (self->monsterinfo.currentmove == &soldier_move_pain2) || (self->monsterinfo.currentmove == &soldier_move_pain3))) self->monsterinfo.currentmove = &soldier_move_pain4; return; } self->pain_debounce_time = level.time + 3; … if (self->velocity[2] > 100) { self->monsterinfo.currentmove = &soldier_move_pain4; return; …Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/m_infantry.c#L222-L230if (level.time < self->pain_debounce_time) return; self->pain_debounce_time = level.time + 3; if (skill->value == 3) return; // no pain anims in nightmare n = rand() % 2;Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/m_gunner.c#L288-L302if (level.time < self->pain_debounce_time) return; self->pain_debounce_time = level.time + 3; … if (skill->value == 3) return; // no pain anims in nightmare if (damage <= 10) self->monsterinfo.currentmove = &gunner_move_pain3;Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/m_tank.c#L293-L312if (damage <= 10) return; if (level.time < self->pain_debounce_time) return; if (damage <= 30) if (random() > 0.2) return; // If hard or nightmare, don't go into pain while attacking if ( skill->value >= 2) … self->pain_debounce_time = level.time + 3;Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/g_combat.c#L506-L514if (targ->svflags & SVF_MONSTER) { M_ReactToDamage (targ, attacker); if (!(targ->monsterinfo.aiflags & AI_DUCKED) && (take)) { targ->pain (targ, attacker, knockback, take); // nightmare mode monsters don't go into pain frames often if (skill->value == 3) targ->pain_debounce_time = level.time + 5;Checked by claude, 2026-10-22 — read soldier_pain, infantry_pain, gunner_pain, and tank_pain in their m_*.c files and the monster branch at the end of T_Damage in g_combat.c

Only four of the monster files were read; the others' pain functions may differ. A ducked monster skips its pain function altogether (g_combat.c L509).

Cited by: Pain chance, Quake II

When a monster survives damage from another monster, Quake II's M_ReactToDamage makes the attacker its enemy if both move the same way (walking, flying, or swimming), their class names differ, and the attacker isn't a tank, super tank, Makron, or Jorg; failing that, it fights back only if the attacker was targeting it, and otherwise takes on the attacker's enemy, unless that enemy is itself. Nothing happens when it hurt itself or the attacker is already its enemy. A player it was chasing is kept as the old enemy. Quake's rule, in the version 1.01 QuakeC of id's Quake-Tools release, is simpler: a monster turns on any attacker of a different class, and soldiers turn on each other too. quake-ii/infighting

About: Quake II Monster infighting

CodeQuake II: the GPL source release (id-Software/Quake-2, 3.19) · game/g_combat.c#L295-L301void M_ReactToDamage (edict_t *targ, edict_t *attacker) { if (!(attacker->client) && !(attacker->svflags & SVF_MONSTER)) return; if (attacker == targ || attacker == targ->enemy) return;Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/g_combat.c#L335-L367// it's the same base (walk/swim/fly) type and a different classname and it's not a tank // (they spray too much), get mad at them if (((targ->flags & (FL_FLY|FL_SWIM)) == (attacker->flags & (FL_FLY|FL_SWIM))) && (strcmp (targ->classname, attacker->classname) != 0) && (strcmp(attacker->classname, "monster_tank") != 0) && (strcmp(attacker->classname, "monster_supertank") != 0) && (strcmp(attacker->classname, "monster_makron") != 0) && (strcmp(attacker->classname, "monster_jorg") != 0) ) { if (targ->enemy && targ->enemy->client) targ->oldenemy = targ->enemy; targ->enemy = attacker; …Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/g_combat.c#L506-L508if (targ->svflags & SVF_MONSTER) { M_ReactToDamage (targ, attacker);Quake: the QuakeC source and compiler (id-Software/Quake-Tools, qcc/v101qc) · qcc/v101qc/combat.qc#L198-L210if ( (self.flags & FL_MONSTER) && attacker != world) { // get mad unless of the same class (except for soldiers) if (self != attacker && attacker != self.enemy) { if ( (self.classname != attacker.classname) || (self.classname == "monster_army" ) ) { if (self.enemy.classname == "player") self.oldenemy = self.enemy; self.enemy = attacker;Checked by claude, 2026-10-22 — read M_ReactToDamage and its call at the end of T_Damage in g_combat.c, against T_Damage in Quake 1.01's combat.qc

T_Damage calls it only when the monster survives (a kill returns at L497-L502). Two monsters of the same class skip the first branch, but one still fights back if the other was targeting it, and otherwise joins the other's target. Damage from a player always makes the player the enemy, except that a good-guy monster ignores players, and in coop a monster chasing a visible player keeps it (L303-L332).

Cited by: Monster infighting, Quake II

A Quake II weapon pickup adds the weapon to the player's inventory, which keeps every weapon taken, and gives that weapon's ammo with it unless the item carries the DROPPED_ITEM flag. The player switches to it only when it is the first of its kind carried, and in deathmatch only while the blaster is in hand. In coop, or in deathmatch with weapons-stay set, a player who already has the weapon can't take a placed one. A placed weapon picked up in deathmatch stays where it is with weapons-stay set and otherwise respawns after 30 seconds, and in coop it stays; a weapon flagged DROPPED_PLAYER_ITEM, which a dying player's weapon is, gives its ammo but neither stays nor respawns. quake-ii/weapon-pickup

About: Quake II Weapon pickup

CodeQuake II: the GPL source release (id-Software/Quake-2, 3.19) · game/p_weapon.c#L125-L160if ( ( ((int)(dmflags->value) & DF_WEAPONS_STAY) || coop->value) && other->client->pers.inventory[index]) { if (!(ent->spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM) ) ) return false; // leave the weapon for others to pickup } other->client->pers.inventory[index]++; if (!(ent->spawnflags & DROPPED_ITEM) ) { // give them some ammo with it ammo = FindItem (ent->item->ammo); … …Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/g_items.c#L814-L819if (!((coop->value) && (ent->item->flags & IT_STAY_COOP)) || (ent->spawnflags & (DROPPED_ITEM | DROPPED_PLAYER_ITEM))) { if (ent->flags & FL_RESPAWN) ent->flags &= ~FL_RESPAWN; else G_FreeEdict (ent);Quake II: the GPL source release (id-Software/Quake-2, 3.19) · game/p_client.c#L439-L441drop = Drop_Item (self, item); … drop->spawnflags = DROPPED_PLAYER_ITEM;Checked by claude, 2026-10-22 — read Pickup_Weapon in game/p_weapon.c, the end of Touch_Item in g_items.c, and the weapon a dying player drops in TossClientWeapon (p_client.c)

With infinite ammo set, the pickup gives 1000 rounds instead of the item's quantity (L138-L139). Drop_Item (g_items.c) flags what it drops DROPPED_ITEM; a dying player's weapon then has its flags replaced by DROPPED_PLAYER_ITEM alone, so it passes the ammo test at L134. After a pickup, Touch_Item clears a set FL_RESPAWN and leaves the item, and frees it otherwise; SetRespawn (g_items.c) is the code that hides it for 30 seconds.

Cited by: Quake II, Weapon pickup