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

A vision cone is the space in which an enemy can see the player. In Metal Gear Solid it reaches 45° either side of a guard’s facing and out to its eye length, and a guard also sees anything within 500 units. Walls block the line of sight, and each frame of full sight raises the guard’s alert level1.

  • ConeMetal Gear Solid (1998). A 90° cone out to an eye length of 4,000 units by default, a 500-unit ring all round, and walls block it; a sighting raises the guard's alert level.1,2

Games

GameYearHowNote
Metal Gear Solid1998Conea 90° cone out to an eye length of 4,000 units by default, a 500-unit ring all round, and walls block it; a sighting raises the guard's alert level
Every claim about this mechanic, by game (2)
Metal Gear Solid 1998In Metal Gear Solid Integral (SLPM-86247, the Japanese re-release the decompilation builds), a guard's sight check s00a_command_800C9E68 updates its result vision.field_B92 for one guard per commander update, the guard whose field_B78 equals the commander's update count field_0x0C modulo field_0x08, the number of guard slots it loops over; between turns the result stands. Snake can't be seen off the guard's map or with the player status PLAYER_INTRUDE or PLAYER_HIDDEN set. Otherwise he is seen when he is within COM_EYE_LENGTH + 2,000 units (a 3D distance) and within 2,000 units of the guard's height, and either closer than 500 units or less than vision.angle from the guard's facing, with no wall or floor (HZD_OnlineHazardCheck, HZD_CHK_ALL) and no target flagged TARGET_SEEK (GM_OnlineTargetCheckAny) on the line between them. The guard's setup sets vision.angle to 512 of the 4,096 units in a turn, a cone of 45° either side of the facing, 90° across (computed), and vision.length to COM_EYE_LENGTH, 4,000 units unless a stage script's l option sets it; some guard actions set the length to 3,000 or 0. The result is 1 for a sighting beyond vision.length and 2 within it, and 0 whenever Snake has the Stealth item equipped, unless the guard's param_blood is 83. Every frame, s00a_command_800C9D7C moves the guard's alert_level (0 to 255): up 1 at a result of 2, down 1 at 1, and down COM_ALERT_DECREMENT at 0 (4 by default; a stage script's t option of 1 or 2 makes it 3 or 2). On the result-0 branch one exception holds the level where it is instead of lowering it: when the guard's last sighting was a 2 and Snake has PLAYER_INTRUDE or PLAYER_NOHIDDEN set, isn't hidden, and the guard's vision.length isn't 0.Code
In Metal Gear Solid Integral (SLPM-86247), a guard looks for footprints only if its stage script gives it the c option, which sets field_BA3 bit 0x10, and not in the commander's mode 1, TOP_COMM_ALERT (s00a_command_800C9D28 skips the check there, though not in evasion). The check (s00a_command_800C9C7C) runs when AsiatoCheck passes and the guard isn't already in think2 5, and takes the nearest of the 48 footprint slots (AsiatoPositions) whose pad is 1 that lies within vision.length (a horizontal distance) and less than vision.angle from the guard's facing, with no wall between (SearchNearAsiato): the sight cone, without its 500-unit ring or its outer band. A patrolling guard that finds one goes to think2 5 if none of field_BA1 bits 4, 2, and 1 (the last a heard noise) ranks above it.Code

References

  1. ^ abIn Metal Gear Solid Integral (SLPM-86247, the Japanese re-release the decompilation builds), a guard's sight check s00a_command_800C9E68 updates its result vision.field_B92 for one guard per commander update, the guard whose field_B78 equals the commander's update count field_0x0C modulo field_0x08, the number of guard slots it loops over; between turns the result stands. Snake can't be seen off the guard's map or with the player status PLAYER_INTRUDE or PLAYER_HIDDEN set. Otherwise he is seen when he is within COM_EYE_LENGTH + 2,000 units (a 3D distance) and within 2,000 units of the guard's height, and either closer than 500 units or less than vision.angle from the guard's facing, with no wall or floor (HZD_OnlineHazardCheck, HZD_CHK_ALL) and no target flagged TARGET_SEEK (GM_OnlineTargetCheckAny) on the line between them. The guard's setup sets vision.angle to 512 of the 4,096 units in a turn, a cone of 45° either side of the facing, 90° across (computed), and vision.length to COM_EYE_LENGTH, 4,000 units unless a stage script's l option sets it; some guard actions set the length to 3,000 or 0. The result is 1 for a sighting beyond vision.length and 2 within it, and 0 whenever Snake has the Stealth item equipped, unless the guard's param_blood is 83. Every frame, s00a_command_800C9D7C moves the guard's alert_level (0 to 255): up 1 at a result of 2, down 1 at 1, and down COM_ALERT_DECREMENT at 0 (4 by default; a stage script's t option of 1 or 2 makes it 3 or 2). On the result-0 branch one exception holds the level where it is instead of lowering it: when the guard's last sighting was a 2 and Snake has PLAYER_INTRUDE or PLAYER_NOHIDDEN set, isn't hidden, and the guard's vision.length isn't 0.CodeFoxdieTeam/mgs_reversingrevised
    How we know
    3This project aims to completely reverse engineer *Metal Gear Solid Integral* for PlayStation back to C source code which when compiled produces the same assembly code.
    
    README.md lines 3–5 at bf9189e · upstream ↗
    189            <td><b>s00a</b></td>
    190            <td>120 KB</td>
    191            <td>Loading Dock</td>
    192            <td><b>✔️ 100% decompiled!</b></td>
    
    README.md lines 189–192 at bf9189e · upstream ↗
    454    include "{{OBJ_DIR}}\enemy\watcher.obj", overlay
    455    include "{{OBJ_DIR}}\enemy\check.obj", overlay
    456    include "{{OBJ_DIR}}\enemy\action.obj", overlay
    457    include "{{OBJ_DIR}}\enemy\enemy.obj", overlay
    
    build/linker_command_file.txt lines 454–457 at bf9189e · upstream ↗
    269    flag = &work->vision.field_B92;
    ⋮
    274    dir = GV_VecDir2( &svec );
    ⋮
    276    dis = GV_VecLen3( &svec );
    ⋮
    284    diff = pos->vy - work->control.mov.vy;
    ⋮
    290    if ( !( work->control.map->index & GM_PlayerMap ) || GM_PlayerStatus & ( PLAYER_INTRUDE | PLAYER_HIDDEN ) )
    ⋮
    292        work->vision.field_B92 = 0;
    293        return;
    ⋮
    296    if ( EnemyCommand.field_0x0C % EnemyCommand.field_0x08 == work->field_B78 )
    ⋮
    298        x = 2000;
    ⋮
    300        if ( COM_EYE_LENGTH + x >= dis )
    ⋮
    302            if ( x >= diff )
    ⋮
    304                if ( dis < 500 || GV_DiffDirAbs( work->vision.facedir, dir ) < work->vision.angle )
    ⋮
    
    source/enemy/enemy.c lines 269–349 at bf9189e · upstream ↗
    172int GV_DiffDirAbs( int from, int to )
    ⋮
    174    int diff;
    ⋮
    176    diff = 4095 & ( to - from );
    177    return ( diff <= 2048 ) ? diff : 4096 - diff;
    
    source/libgv/util.c lines 172–178 at bf9189e · upstream ↗
    667        if ((iter->flag & 0x1) && (iter->class & TARGET_SEEK))
    ⋮
    669            if (TargetConflict(iter, &tmp) && OnlineTarget(from, to, iter, hit))
    
    source/game/target.c lines 667–669 at bf9189e · upstream ↗
    235    work->vision.angle = 512;
    236    work->vision.length    = COM_EYE_LENGTH;
    
    source/enemy/check.c lines 235–236 at bf9189e · upstream ↗
    1179    EnemyCommand.field_0x88.vx = 0xFA0;
    1180    EnemyCommand.field_0x88.vy = 0xFA0;
    1181    EnemyCommand.field_0x88.vz = 0xFA0;
    ⋮
    1183    ops = GCL_GetOption( 'l' );
    1184    if ( ops )
    ⋮
    1186        GCL_StrToSV( ops, (short *)&EnemyCommand.field_0x88 );
    
    source/enemy/command.c lines 1179–1187 at bf9189e · upstream ↗
    1201    ops = GCL_GetOption( 't' );
    1202    if ( ops ) {
    1203        EnemyCommand.field_0x88.pad = GCL_StrToInt( ops );
    ⋮
    1206    switch ( EnemyCommand.field_0x88.pad )
    ⋮
    1208        case 0:
    1209            COM_ALERT_DECREMENT = 4;
    1210            break;
    1211        case 1:
    1212            COM_ALERT_DECREMENT = 3;
    1213            break;
    1214        case 2:
    1215            COM_ALERT_DECREMENT = EnemyCommand.field_0x88.pad;
    ⋮
    
    source/enemy/command.c lines 1201–1219 at bf9189e · upstream ↗
    1046    EnemyCommand.field_0x0C++;
    
    source/enemy/command.c line 1046 at bf9189e · upstream ↗
    766    for ( i = 0; i < EnemyCommand.field_0x08; i++ )
    
    source/enemy/command.c line 766 at bf9189e · upstream ↗
    1455void ActOverScoutD_800C85DC( WatcherWork* work, int time )
    ⋮
    1457    work->vision.length = 3000;
    
    source/enemy/action.c lines 1455–1457 at bf9189e · upstream ↗
    610void s00a_command_800C6B24( WatcherWork* work, int time )
    ⋮
    612    work->vision.length = 0;
    
    source/enemy/action.c lines 610–612 at bf9189e · upstream ↗
    224void s00a_command_800C9D7C( WatcherWork* work )
    ⋮
    226    int val;
    227    val = work->vision.field_B92;
    228    switch ( val )
    ⋮
    230        case 0:
    231            if ( work->field_B94 != 2 || GM_PlayerStatus & PLAYER_HIDDEN || !( GM_PlayerStatus & ( PLAYER_INTRUDE | PLAYER_NOHIDDEN ) ) || work->vision.length == 0 )
    ⋮
    233                work->field_B94 = 0;
    234                work->alert_level -= COM_ALERT_DECREMENT;
    ⋮
    236            break;
    237        case 1:
    238            work->field_B94 = val;
    239            work->alert_level -= 1;
    ⋮
    
    source/enemy/enemy.c lines 224–255 at bf9189e · upstream ↗
    352void EnemyActionMain( WatcherWork *work )
    ⋮
    356        s00a_command_800C9878( work );
    357        s00a_command_800C9E68(work);
    358        s00a_command_800C9D28(work);
    359        s00a_command_800C9D7C(work);
    360        Enemy_Think(work);
    
    source/enemy/enemy.c lines 352–360 at bf9189e · 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

  2. ^In Metal Gear Solid Integral (SLPM-86247), a guard looks for footprints only if its stage script gives it the c option, which sets field_BA3 bit 0x10, and not in the commander's mode 1, TOP_COMM_ALERT (s00a_command_800C9D28 skips the check there, though not in evasion). The check (s00a_command_800C9C7C) runs when AsiatoCheck passes and the guard isn't already in think2 5, and takes the nearest of the 48 footprint slots (AsiatoPositions) whose pad is 1 that lies within vision.length (a horizontal distance) and less than vision.angle from the guard's facing, with no wall between (SearchNearAsiato): the sight cone, without its 500-unit ring or its outer band. A patrolling guard that finds one goes to think2 5 if none of field_BA1 bits 4, 2, and 1 (the last a heard noise) ranks above it.CodeFoxdieTeam/mgs_reversing
    How we know
    681    opt = GCL_GetOption( 'c' );
    682    if ( opt )
    ⋮
    684        work->field_BA3 |= 0x10;
    
    source/enemy/watcher.c lines 681–685 at bf9189e · upstream ↗
    179void s00a_command_800C9C7C( WatcherWork* work )
    ⋮
    185    if ( !( work->field_BA3 & 16 ) )
    ⋮
    187        return;
    ⋮
    191    if ( work->think2 == 5 )
    ⋮
    193        return;
    ⋮
    198    if ( !( AsiatoCheck( hzd, &ctrl->mov ) ) )
    ⋮
    200        return;
    ⋮
    203    if ( SearchNearAsiato( hzd, &ctrl->mov, vision->facedir, vision->angle, vision->length ) < 0 )
    ⋮
    205        return;
    ⋮
    208    work->field_BA1 |= 16;
    ⋮
    211void s00a_command_800C9D28( WatcherWork* work )
    ⋮
    213    work->field_BA1 = 0;
    ⋮
    
    source/enemy/enemy.c lines 179–222 at bf9189e · upstream ↗
    205int SearchNearAsiato( HZD_HDL *hzd, SVECTOR *pos, int facedir, int range, int length )
    ⋮
    216    for ( i = 0; i < 48 ; i++ )
    ⋮
    218        if (AsiatoPositions[i].pad == 1 )
    ⋮
    220            GV_SubVec3( &AsiatoPositions[i], pos, &svec );
    221            svec.vy = 0;
    222            len = GV_VecLen3( &svec );
    ⋮
    224            if ( len < max_len && len < length && GV_DiffDirAbs( facedir, GV_VecDir2(&svec) ) < range &&
    225                !HZD_OnlineHazardCheck(hzd, pos, &AsiatoPositions[i], HZD_CHK_ALL, SEGMENT_ATR) )
    
    source/enemy/asiato.c lines 205–231 at bf9189e · upstream ↗
    1951    else if ( work->field_BA1 & 16 )
    ⋮
    1953        s00a_command_800CB628( work );
    
    source/enemy/think.c lines 1951–1954 at bf9189e · upstream ↗
    590void s00a_command_800CB628( WatcherWork *work )
    ⋮
    592    work->think2 = 5;
    
    source/enemy/think.c lines 590–593 at bf9189e · 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