On this page
  1. Related
  2. Mechanics
  3. References
  4. Sources

The atlas reads Descent 3 through the source code Outrage’s Kevin Bentley released in 2024, at its initial import. That code includes the unreleased “1.5” patch, and its files carry revisions as late as 20011, so every claim here describes the released code rather than the 1999 retail build. The game’s data tables aren’t in the release, so the ships’ and weapons’ own numbers are not known here.

The ship has inertia on all three axes. Its thrust is a forward, an up, and a sideways push, and a drag proportional to speed works against it every frame, thrusting or not, so the ship levels off at thrust over drag, as Doom’s player does against floor friction2. Turning works the same way about three axes, for six degrees of freedom in all3. Each push is capped on its own, so holding all three moves the ship 1.73 ×4 as fast as forward alone, the three-axis form of Doom’s strafe-running4.

The afterburner is a boost meter: a 5-second tank that multiplies forward thrust by 1.6 and refills when let go, taking energy as it does5.

A homing missile looks for the enemy most nearly ahead of it inside its weapon’s cone, within 500 units and with a clear line to it, and steers at a point a tenth of a second ahead of it6,7. Robots’ missiles turn at 0.2 times their rate on Trainee and 1.4 times on Insane7.

Robots can lose their nerve. Every few seconds a robot weighs its shields, the friends near it, and a per-robot life_preservation value, and a frightened robot flees for 10 × life_preservation + 5 seconds8.

Mechanics

MechanicKindHow
InertiaDrag alwaysthrust on three axes against a drag proportional to speed, solved exactly each frame, so the ship settles at thrust over drag
Six degrees of freedomDampedpitch, heading, and bank take thrust like movement does and fade under their own drag when let go
Strafe-runningSeparately capped axesforward, up, and sideways are capped apart and summed, so all three at once push 1.73 times as hard as forward alone
Boost meterFree refilla 5-second afterburner tank at 1.6 times forward thrust, refilled while let go at a cost in energy
Homing missileCone and turnthe target most nearly ahead in the weapon's cone within 500 units, led by a tenth of a second, with robots' missiles turning slower or faster by difficulty
Every claim about this game, by concept (8)
generalThe README of the Descent 3 source release, at its initial import, calls it "the latest version of the Descent 3 source code" and says it "includes the '1.5' patch that Jeff Slutter and Kevin Bentley wrote several years ago"; the SourceSafe header comment of WeaponFire.cpp gives its last revision as 279, dated 9/20/01.Code
In Descent 3's released source, every 3 to 5 seconds an AI object that is more than barely aware and has its fourth goal slot free can take fright, by one of three tests on its own life_preservation (lp), the first two only for generic objects (robots, buildings, clutter, and powerups): its shields are under lp of its hit points, it dodged an enemy shot since the last check, it has fewer than 2 friends near, and a random number below lp; or it lost more than 25% × (1 − lp) of the shields it had some checks ago, and a random number below lp; or lp is over 0.5 and the friends it had are gone while enemies remain. Frightened, it wanders at its flee speed, facing the way it moves, for 10 × lp + 5 seconds.Code
Boost meterDescent 3's released source gives the afterburner a 5-second tank (AFTERBURN_TIME, 5000 in an E3 demo build): while it is held and time is left, the forward control is replaced by the afterburner control times 1.6, and times a punch factor of 1.8 while over 90% of the tank is left, the factor easing to 1 by 80%, and the tank drains by the frame time. Let go, the tank refills by the frame time, or twice that with the "Afterburner" item in the inventory, but only while energy is above 5, and energy drops by the same amount, so a full refill costs 5 energy (computed) whichever rate applies.Code
Homing missileIn Descent 3's released source a homing weapon with thrust left starts homing 0.1 s after it is created, unless it is guided. Without a target, it scans every object in a room the missile's room can see: robots, players, and buildings with AI, not its own shooter, not cloaked, and only enemies of the shooter, inside a cone where the dot product of the direction to it with the missile's facing exceeds the weapon's homing_fov. In object order, a chaff chunk ends the scan (by a 1-in-4 roll in single player) and so does anything within 15 units (the distance between centers less both objects' sizes); otherwise the missile takes the object within 500 units most nearly straight ahead with a clear line to it. A target it has is kept while it stays inside the cone and either within 15 units, or within 500 units, in a visible room, with a clear line.Code
Descent 3's released source steers a homing weapon at the target's position plus a tenth of the target's velocity when the target moves under physics or walks. A weapon with rotational drag fires full full_rotthrust left or right and up or down, by which side of the missile the aim point is on, with no roll; one without it turns straight toward the aim point by at most full_rotthrust × the frame time and has its velocity set along its new facing at full_thrust/drag. A weapon whose shooter is not a player has its turning scaled by difficulty: 0.20, 0.70, 1.00, 1.20, and 1.40 from Trainee to Insane.Code
InertiaIn 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.Code
Six degrees of freedomDescent 3's released source turns the player ship on three axes the same way it moves it on three: pitch, heading, and bank each take the control's value times full_rotthrust times the player's turn_scalar, and an object flagged PF_USES_THRUST with a rotational drag above 0 has its turning speed solved against that drag with the same closed form, ω = (ω₀ − R/d)·e^(−(d/m)·t) + R/d. Computed from it, a held turn levels off at R/d and a released one fades out at rate d/m. The input code clamps pitch to 0.75 and heading and bank to 1, except in a Macintosh build, which skips those three clamps; before the drag step, PhysicsDoSimRot can add auto-leveling thrust to pitch and bank, which this claim doesn't cover.Code
Strafe-runningDescent 3's input code clamps the forward, up, and sideways thrust controls to ±1 each, separately, and the released source adds the three pushes along the ship's perpendicular axes, so all three held at once push √3 ≈ 1.73 times as hard as forward alone, and, computed from the linear drag, the ship settles at 1.73 times the speed; Doom does the same with two axes, clamping its forward and side moves apart and thrusting them at 90°.Code

References

  1. ^The README of the Descent 3 source release, at its initial import, calls it "the latest version of the Descent 3 source code" and says it "includes the '1.5' patch that Jeff Slutter and Kevin Bentley wrote several years ago"; the SourceSafe header comment of WeaponFire.cpp gives its last revision as 279, dated 9/20/01.CodeDescentDevelopers/Descent3
    How we know
    This is the latest version of the Descent 3 source code. This includes the '1.5' patch that Jeff Slutter and Kevin Bentley wrote several years ago.
    README.md#L3 · upstream ↗
    2 * $Logfile: /DescentIII/Main/WeaponFire.cpp $
    3 * $Revision: 279 $
    4 * $Date: 9/20/01 5:33p $
    
    Descent3/WeaponFire.cpp lines 2–4 at 61bb9a3 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Checked by a second AI model, 2026-10-22.

    In the claim ledger

  2. ^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.CodeDescentDevelopers/Descent3 · id-Software/DOOM · id-Software/Quake
    How we know
    2580		if (OBJECT_OUTSIDE (objp))
    2581			speed_scalar*=1.3f;
    ⋮
    2583		objp->mtype.phys_info.thrust = speed_scalar*Players[objp->id].movement_scalar * ((objp->orient.fvec * controls.forward_thrust  * objp->mtype.phys_info.full_thrust) + 
    2584											 (objp->orient.uvec * controls.vertical_thrust * objp->mtype.phys_info.full_thrust) + 
    2585											 (objp->orient.rvec * controls.sideways_thrust * objp->mtype.phys_info.full_thrust));
    
    Descent3/object.cpp lines 2580–2585 at 61bb9a3 · upstream ↗
    994	if (obj->mtype.phys_info.drag != 0.0) 
    ⋮
    996		if (obj->mtype.phys_info.flags & PF_USES_THRUST) 
    ⋮
    998			total_force += obj->mtype.phys_info.thrust;
    
    physics/physics.cpp lines 994–998 at 61bb9a3 · upstream ↗
    279	// Standard motion with a linear air drag (drag is proportional to velocity)
    ⋮
    281	const double oneOverDrag      = 1.0 / drag;
    282	const double massOverDrag     = mass / drag;
    283	const double forceOverDrag[3] = { force.x * oneOverDrag, force.y * oneOverDrag, force.z * oneOverDrag };
    ⋮
    285	const double expDoMDt         = exp( (-1.0 / massOverDrag) * dt );
    ⋮
    291	newVel.x = static_cast<float>( (objVel[0] - forceOverDrag[0]) * expDoMDt + forceOverDrag[0] );
    
    physics/physics.cpp lines 279–293 at 61bb9a3 · upstream ↗
    238	mo->momx = FixedMul (mo->momx, FRICTION);
    239	mo->momy = FixedMul (mo->momy, FRICTION);
    
    linuxdoom-1.10/p_mobj.c lines 238–239 at a77dfb9 · upstream ↗
    112#define FRICTION		0xe800
    
    linuxdoom-1.10/p_mobj.c line 112 at a77dfb9 · upstream ↗
    195	currentspeed = DotProduct (velocity, wishdir);
    196	addspeed = wishspeed - currentspeed;
    ⋮
    199	accelspeed = sv_accelerate.value*host_frametime*wishspeed;
    200	if (accelspeed > addspeed)
    201		accelspeed = addspeed;
    
    WinQuake/sv_user.c lines 195–201 at bf4ac42 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Checked by a second AI model, 2026-10-22.

    In the claim ledger

  3. ^Descent 3's released source turns the player ship on three axes the same way it moves it on three: pitch, heading, and bank each take the control's value times full_rotthrust times the player's turn_scalar, and an object flagged PF_USES_THRUST with a rotational drag above 0 has its turning speed solved against that drag with the same closed form, ω = (ω₀ − R/d)·e^(−(d/m)·t) + R/d. Computed from it, a held turn levels off at R/d and a released one fades out at rate d/m. The input code clamps pitch to 0.75 and heading and bank to 1, except in a Macintosh build, which skips those three clamps; before the drag step, PhysicsDoSimRot can add auto-leveling thrust to pitch and bank, which this claim doesn't cover.CodeDescentDevelopers/Descent3
    How we know
    2566			objp->mtype.phys_info.rotthrust.x = controls.pitch_thrust * objp->mtype.phys_info.full_rotthrust * playp->turn_scalar;
    2567			objp->mtype.phys_info.rotthrust.z = controls.bank_thrust * objp->mtype.phys_info.full_rotthrust * playp->turn_scalar;
    2568			objp->mtype.phys_info.rotthrust.y = controls.heading_thrust * objp->mtype.phys_info.full_rotthrust * playp->turn_scalar;
    
    Descent3/object.cpp lines 2566–2568 at 61bb9a3 · upstream ↗
    430#define LIMIT_PITCH			0.75f
    431#define LIMIT_HEADING		1.0f
    432#define LIMIT_BANK			1.0f
    
    Descent3/Controls.cpp lines 430–432 at 61bb9a3 · upstream ↗
    887#ifndef MACINTOSH
    888//	clip controller values
    889	if (controls->pitch_thrust > LIMIT_PITCH) controls->pitch_thrust = LIMIT_PITCH;
    ⋮
    894	if (controls->bank_thrust < -LIMIT_BANK) controls->bank_thrust = -LIMIT_BANK;
    895#endif
    
    Descent3/Controls.cpp lines 887–895 at 61bb9a3 · upstream ↗
    714	if (pi->rotdrag > 0.0f)
    ⋮
    716		if(!(pi->flags & PF_USES_THRUST)) 
    ⋮
    718			*rotthrust = Zero_vector;	
    ⋮
    721		PhysicsApplyConstRotForce(*obj, *rotthrust, *rotvel, frame_time);	
    
    physics/physics.cpp lines 714–721 at 61bb9a3 · upstream ↗
    245	// Standard angular motion with a linear air drag (drag is proportional to angular velocity)
    246	const double oneOverDrag         = 1.0 / drag;
    247	const double rotForceOverDrag[3] = { double(rotforce.x) * oneOverDrag, double(rotforce.y) * oneOverDrag, double(rotforce.z) * oneOverDrag };
    248	const double dragOverMass        = drag / mass;
    249	const double expDoMDt            = exp( -dragOverMass * double(deltaTime) );
    ⋮
    252	newRotVel[0] = ( double(rotvel.x) - rotForceOverDrag[0] ) * expDoMDt + rotForceOverDrag[0];
    
    physics/physics.cpp lines 245–252 at 61bb9a3 · upstream ↗
    684							rotthrust->z -= scale * BANK_AUTOLEVEL_SPEED_SCALAR * obj->mtype.phys_info.full_rotthrust;
    
    physics/physics.cpp line 684 at 61bb9a3 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Checked by a second AI model, 2026-10-22.

    In the claim ledger

  4. ^ abDescent 3's input code clamps the forward, up, and sideways thrust controls to ±1 each, separately, and the released source adds the three pushes along the ship's perpendicular axes, so all three held at once push √3 ≈ 1.73 times as hard as forward alone, and, computed from the linear drag, the ship settles at 1.73 times the speed; Doom does the same with two axes, clamping its forward and side moves apart and thrusting them at 90°.CodeDescentDevelopers/Descent3 · id-Software/DOOM
    How we know
    896	if (controls->sideways_thrust > LIMIT_HORIZ) controls->sideways_thrust = LIMIT_HORIZ;
    ⋮
    898	if (controls->vertical_thrust > LIMIT_VERT) controls->vertical_thrust = LIMIT_VERT;
    ⋮
    901	if (controls->forward_thrust > LIMIT_FORWARD) controls->forward_thrust = LIMIT_FORWARD;
    
    Descent3/Controls.cpp lines 896–902 at 61bb9a3 · upstream ↗
    433#define LIMIT_HORIZ			1.0f
    434#define LIMIT_VERT			1.0f
    435#define LIMIT_FORWARD		1.0f
    
    Descent3/Controls.cpp lines 433–435 at 61bb9a3 · upstream ↗
    2583		objp->mtype.phys_info.thrust = speed_scalar*Players[objp->id].movement_scalar * ((objp->orient.fvec * controls.forward_thrust  * objp->mtype.phys_info.full_thrust) + 
    2584											 (objp->orient.uvec * controls.vertical_thrust * objp->mtype.phys_info.full_thrust) + 
    2585											 (objp->orient.rvec * controls.sideways_thrust * objp->mtype.phys_info.full_thrust));
    
    Descent3/object.cpp lines 2583–2585 at 61bb9a3 · upstream ↗
    160    if (cmd->forwardmove && onground)
    161	P_Thrust (player, player->mo->angle, cmd->forwardmove*2048);
    ⋮
    163    if (cmd->sidemove && onground)
    164	P_Thrust (player, player->mo->angle-ANG90, cmd->sidemove*2048);
    
    linuxdoom-1.10/p_user.c lines 160–164 at a77dfb9 · upstream ↗
    413    if (forward > MAXPLMOVE) 
    414	forward = MAXPLMOVE; 
    ⋮
    417    if (side > MAXPLMOVE) 
    418	side = MAXPLMOVE; 
    
    linuxdoom-1.10/g_game.c lines 413–420 at a77dfb9 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Checked by a second AI model, 2026-10-22.

    In the claim ledger

  5. ^Descent 3's released source gives the afterburner a 5-second tank (AFTERBURN_TIME, 5000 in an E3 demo build): while it is held and time is left, the forward control is replaced by the afterburner control times 1.6, and times a punch factor of 1.8 while over 90% of the tank is left, the factor easing to 1 by 80%, and the tank drains by the frame time. Let go, the tank refills by the frame time, or twice that with the "Afterburner" item in the inventory, but only while energy is above 5, and energy drops by the same amount, so a full refill costs 5 energy (computed) whichever rate applies.CodeDescentDevelopers/Descent3revised
    How we know
    372// How long afterburner lasts before it has to be recharged
    373#ifdef E3_DEMO
    374#define AFTERBURN_TIME	5000.0
    375#else
    376#define AFTERBURN_TIME	5.0
    377#endif
    
    Descent3/player.h lines 372–377 at 61bb9a3 · upstream ↗
    2289	if (controls->afterburn_thrust>0)
    ⋮
    2293		if (Players[slot].afterburn_time_left>0)
    ⋮
    2295			float punch_scalar=1.0;
    ⋮
    2300			if (Players[slot].afterburn_time_left>(AFTERBURN_TIME*.90))
    2301				punch_scalar=1.8f;
    2302			else if (Players[slot].afterburn_time_left>(AFTERBURN_TIME*.80) && Players[slot].afterburn_time_left<(AFTERBURN_TIME*.90))
    ⋮
    2304				float norm=Players[slot].afterburn_time_left-(AFTERBURN_TIME*.80);
    2305				norm/=(AFTERBURN_TIME*.1);
    2306				punch_scalar=1.0+(norm*.8);
    ⋮
    2310				controls->forward_thrust=controls->afterburn_thrust*1.6*punch_scalar;
    2311			else
    2312				controls->forward_thrust=controls->afterburn_thrust*1.6*punch_scalar;
    ⋮
    
    Descent3/object.cpp lines 2289–2317 at 61bb9a3 · upstream ↗
    2325	else
    ⋮
    2329		if (Players[slot].afterburn_time_left<AFTERBURN_TIME)
    ⋮
    2331			if (Players[slot].energy>5.0)
    ⋮
    2337				if( afterburner_id!=-1 && Players[slot].inventory.CheckItem(OBJ_POWERUP,afterburner_id))
    2338					useage = Frametime*2;
    2339				else
    2340					useage = Frametime;
    ⋮
    2342				Players[slot].afterburn_time_left+=useage;
    2343				Players[slot].afterburn_time_left=min (AFTERBURN_TIME,Players[slot].afterburn_time_left);
    ⋮
    2345				Players[slot].energy-=(useage);
    
    Descent3/object.cpp lines 2325–2345 at 61bb9a3 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Revised after a check by a second AI model, 2026-10-22.

    In the claim ledger

  6. ^In Descent 3's released source a homing weapon with thrust left starts homing 0.1 s after it is created, unless it is guided. Without a target, it scans every object in a room the missile's room can see: robots, players, and buildings with AI, not its own shooter, not cloaked, and only enemies of the shooter, inside a cone where the dot product of the direction to it with the missile's facing exceeds the weapon's homing_fov. In object order, a chaff chunk ends the scan (by a 1-in-4 roll in single player) and so does anything within 15 units (the distance between centers less both objects' sizes); otherwise the missile takes the object within 500 units most nearly straight ahead with a clear line to it. A target it has is kept while it stays inside the cone and either within 15 units, or within 500 units, in a visible room, with a clear line.CodeDescentDevelopers/Descent3
    How we know
    1719	if((obj->mtype.phys_info.flags & PF_USES_THRUST) && (obj->ctype.laser_info.thrust_left >= 0.0))
    ⋮
    1721		obj->ctype.laser_info.thrust_left -= Frametime;
    ⋮
    1723		if((obj->creation_time + 0.1f < Gametime) && (obj->mtype.phys_info.flags & PF_HOMING) &&	!(obj->mtype.phys_info.flags & PF_GUIDED))
    ⋮
    1725			obj->ctype.laser_info.last_track_time = Gametime - HOMING_CHECK_VIS_INTERVAL;
    1726			HomingDoFrame(obj);
    
    Descent3/WeaponFire.cpp lines 1719–1726 at 61bb9a3 · upstream ↗
    1461#define MAX_HOMING_DIST	500.0f
    1462#define CLOSE_HOMING_DIST 15.0
    
    Descent3/WeaponFire.cpp lines 1461–1462 at 61bb9a3 · upstream ↗
    1604			for(i = 0; i <= Highest_object_index; i++)
    ⋮
    1606				if(BOA_IsVisible(obj->roomnum, Objects[i].roomnum))
    ⋮
    1608					if((i != OBJNUM(weapon_parent))  && ((Objects[i].type == OBJ_BUILDING && Objects[i].ai_info) || (Objects[i].type == OBJ_ROBOT) || (Objects[i].type == OBJ_PLAYER)))
    ⋮
    1612						if(Objects[i].effect_info && Objects[i].effect_info->type_flags & EF_CLOAKED)
    1613							continue;
    ⋮
    1615						if(weapon_parent && !AIObjEnemy(ObjGetUltimateParent(weapon_parent), ObjGetUltimateParent(&Objects[i])))
    1616							continue;
    ⋮
    1619						float cur_dot = to_target * obj->orient.fvec;
    ⋮
    1621						if(cur_dot > Weapons[obj->id].homing_fov)
    
    Descent3/WeaponFire.cpp lines 1604–1621 at 61bb9a3 · upstream ↗
    1623							//Pick chaff over other objects
    1624							if ((Objects[i].type == OBJ_ROBOT) && (Objects[i].id == GENOBJ_CHAFFCHUNK)) 
    ⋮
    1634											break;
    ⋮
    1637									else
    ⋮
    1642											break;
    ⋮
    1646								else
    ⋮
    1648									if (ps_rand() < RAND_MAX/4)
    1649									{		// 1/4 chance of picking chaff
    1650										best_index = i;
    1651										break;
    ⋮
    1655							else if(dist_to_target < CLOSE_HOMING_DIST)
    ⋮
    1657								best_index = i;
    ⋮
    
    Descent3/WeaponFire.cpp lines 1623–1659 at 61bb9a3 · upstream ↗
    1660							else if(dist_to_target < MAX_HOMING_DIST)
    ⋮
    1662								if(cur_dot > best_dot)
    ⋮
    1677									if(hit_info.hit_type[0] == HIT_NONE)
    ⋮
    1679										best_index = i;
    1680										best_dist = dist_to_target;
    1681										best_dot = cur_dot;
    
    Descent3/WeaponFire.cpp lines 1660–1681 at 61bb9a3 · upstream ↗
    1497		else if(to_target * obj->orient.fvec > Weapons[obj->id].homing_fov)
    ⋮
    1509				else
    ⋮
    1539					if(hit_info.hit_type[0] == HIT_NONE)
    ⋮
    1549			if(dist_to_target < CLOSE_HOMING_DIST)
    ⋮
    1551				f_locked = true;
    ⋮
    1553			else if(dist_to_target < MAX_HOMING_DIST)
    ⋮
    1555				if(BOA_IsVisible(obj->roomnum, track_goal->roomnum))
    ⋮
    1572						if(hit_info.hit_type[0] == HIT_NONE)
    ⋮
    1574							f_locked = true;
    ⋮
    1577					else
    ⋮
    1579						f_locked = true;
    
    Descent3/WeaponFire.cpp lines 1497–1580 at 61bb9a3 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Checked by a second AI model, 2026-10-22.

    In the claim ledger

  7. ^ abDescent 3's released source steers a homing weapon at the target's position plus a tenth of the target's velocity when the target moves under physics or walks. A weapon with rotational drag fires full full_rotthrust left or right and up or down, by which side of the missile the aim point is on, with no roll; one without it turns straight toward the aim point by at most full_rotthrust × the frame time and has its velocity set along its new facing at full_thrust/drag. A weapon whose shooter is not a player has its turning scaled by difficulty: 0.20, 0.70, 1.00, 1.20, and 1.40 from Trainee to Insane.CodeDescentDevelopers/Descent3
    How we know
    1413	if(target->movement_type == MT_PHYSICS || target->movement_type == MT_WALKING)
    ⋮
    1415		movement = 0.1f * (target->mtype.phys_info.velocity);
    ⋮
    1418	dir_to_target = target->pos + movement - weapon->pos;
    
    Descent3/WeaponFire.cpp lines 1413–1418 at 61bb9a3 · upstream ↗
    1420	if(weapon->mtype.phys_info.rotdrag > 0.0f)
    ⋮
    1422		if(dir_to_target * weapon->orient.rvec > 0.0)
    ⋮
    1424			weapon->mtype.phys_info.rotthrust.y = weapon->mtype.phys_info.full_rotthrust;
    ⋮
    1431		if(dir_to_target * weapon->orient.uvec > 0.0)
    ⋮
    1433			weapon->mtype.phys_info.rotthrust.x = -weapon->mtype.phys_info.full_rotthrust;
    ⋮
    1440		weapon->mtype.phys_info.rotthrust.z = 0.0;
    ⋮
    1442		if(!ObjGet(weapon->parent_handle) || ObjGet(weapon->parent_handle)->type != OBJ_PLAYER)
    1443				weapon->mtype.phys_info.rotthrust *= Diff_homing_strength[DIFF_LEVEL];
    
    Descent3/WeaponFire.cpp lines 1420–1443 at 61bb9a3 · upstream ↗
    1445	else
    ⋮
    1450		if(!ObjGet(weapon->parent_handle) || ObjGet(weapon->parent_handle)->type != OBJ_PLAYER)
    1451			scalar = Diff_homing_strength[DIFF_LEVEL];
    1452		else 
    1453			scalar = 1.0f;
    ⋮
    1455		AITurnTowardsDir(weapon, &dir_to_target, weapon->mtype.phys_info.full_rotthrust * scalar);
    1456		weapon->mtype.phys_info.velocity = weapon->orient.fvec * weapon->mtype.phys_info.full_thrust/weapon->mtype.phys_info.drag;
    
    Descent3/WeaponFire.cpp lines 1445–1457 at 61bb9a3 · upstream ↗
    2206	max_angle = turn_rate * Frametime;
    ⋮
    2208	if((float)goal_angle > max_angle)
    
    Descent3/AImain.cpp lines 2206–2208 at 61bb9a3 · upstream ↗
    16float Diff_homing_strength[5]  = {0.20f, 0.70f, 1.00f, 1.20f, 1.40f};
    
    Descent3/difficulty.cpp line 16 at 61bb9a3 · upstream ↗
    7#define DIFFICULTY_TRAINEE		0
    8#define DIFFICULTY_ROOKIE		1
    9#define DIFFICULTY_HOTSHOT		2
    10#define DIFFICULTY_ACE			3
    11#define DIFFICULTY_INSANE		4
    
    Descent3/difficulty_external.h lines 7–11 at 61bb9a3 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Checked by a second AI model, 2026-10-22.

    In the claim ledger

  8. ^In Descent 3's released source, every 3 to 5 seconds an AI object that is more than barely aware and has its fourth goal slot free can take fright, by one of three tests on its own life_preservation (lp), the first two only for generic objects (robots, buildings, clutter, and powerups): its shields are under lp of its hit points, it dodged an enemy shot since the last check, it has fewer than 2 friends near, and a random number below lp; or it lost more than 25% × (1 − lp) of the shields it had some checks ago, and a random number below lp; or lp is over 0.5 and the friends it had are gone while enemies remain. Frightened, it wanders at its flee speed, facing the way it moves, for 10 × lp + 5 seconds.CodeDescentDevelopers/Descent3
    How we know
    6704	if(ai_info->mem_time_till_next_update <= 0.0f)
    ⋮
    6708		// Compute next analyze time
    6709		ai_info->mem_time_till_next_update = 3.0f + (float)ps_rand()/(float)RAND_MAX * 2.0f;
    
    Descent3/AImain.cpp lines 6704–6709 at 61bb9a3 · upstream ↗
    6640	int fear_depth = (ai_info->life_preservation) * (AI_MEM_DEPTH - 1) + 1;
    ⋮
    6644	if(fear_depth >= AI_MEM_DEPTH)
    6645		fear_depth = AI_MEM_DEPTH - 1;
    
    Descent3/AImain.cpp lines 6640–6645 at 61bb9a3 · upstream ↗
    6651	if(ai_info->goals[3].used == 0 && ai_info->awareness > AWARE_BARELY)
    ⋮
    6653		if(IS_GENERIC(obj->type) && 
    6654			mem[0].shields/Object_info[obj->id].hit_points < ai_info->life_preservation &&
    6655			mem[0].num_enemy_shots_dodged > 0 &&
    6656			mem[0].num_friends < 2 &&
    6657			(float)ps_rand()/(float)RAND_MAX < ai_info->life_preservation)
    ⋮
    6659			float time = 10.0f * ai_info->life_preservation + 5.0f;
    ⋮
    6661			GoalAddGoal(obj, AIG_WANDER_AROUND, NULL, 3, 2.0f, GF_SPEED_FLEE | GF_ORIENT_VELOCITY | GF_NONFLUSHABLE);
    6662			GoalAddEnabler(obj, 3, AIE_CLEAR_TIME, (void *)&time, 1.0, 0.0);
    ⋮
    6669		if(IS_GENERIC(obj->type) && 
    6670			(mem[fear_depth].shields - mem[0].shields)/mem[fear_depth].shields > 0.25f * (1.0f - ai_info->life_preservation) && 
    6671			(float)ps_rand()/(float)RAND_MAX < ai_info->life_preservation)
    ⋮
    
    Descent3/AImain.cpp lines 6651–6686 at 61bb9a3 · upstream ↗
    4995		case GF_SPEED_FLEE:
    4996			n_speed = obj->ai_info->flee_vel_percent;
    
    Descent3/AImain.cpp lines 4995–4996 at 61bb9a3 · upstream ↗
    1700			case(AIE_CLEAR_TIME):
    ⋮
    1702					f_enabled = e_info->time >= Gametime - cur_goal->creation_time;
    1703					if(!f_enabled)
    ⋮
    1705						GoalClearGoal(obj, cur_goal);
    
    Descent3/AIGoal.cpp lines 1700–1706 at 61bb9a3 · upstream ↗
    172#define IS_GENERIC(type)  ((type == OBJ_CLUTTER) || (type == OBJ_BUILDING) || (type == OBJ_ROBOT) || (type == OBJ_POWERUP))
    
    Descent3/object_external.h line 172 at 61bb9a3 · upstream ↗

    Checked against the evidence by its author, an AI model, 2026-10-22.

    Checked by a second AI model, 2026-10-22.

    In the claim ledger

Sources

Code