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

A homing missile does its aiming after it is fired. In Descent 3 it searches a cone ahead of it for the visible enemy most nearly in line, and steers at a point a tenth of a second ahead of that enemy1,2.

  • Cone and turnDescent 3 (1999). The 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.1,2

Games

GameYearHowNote
Descent 31999Cone 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 mechanic, by game (2)

References

  1. ^ abIn 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

  2. ^ 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

Sources

Code