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

Metal Gear Solid is Konami’s 1998 PlayStation stealth game. The code read here is FoxdieTeam’s mgs_reversing, a matching decompilation of Metal Gear Solid Integral, the Japanese re-release, so each claim names that release. The guard code is in its enemy folder.

A guard sees Snake inside a cone 45° either side of its facing, out to an eye length of 4,000 units by default, or anywhere within 500 units, unless a wall is in the way. Each frame of full sight raises its alert level1. The alert has no timer of its own. It holds until every guard’s alert level has fallen back, then evasion runs for 300 frames, about 10 seconds2. During both phases the radar shows a status panel instead of the map3. A noise’s power decides who hears it. A knock on a wall reaches only the nearest guard, and an explosion is heard at any distance4. Guards that a stage script marks for it follow footprints inside the same cone5.

Mechanics

MechanicKindHow
Vision coneConea 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
Alert phaseAlert then evasionalert while any guard's alert level holds the commander's value up, then evasion for 300 frames, then infiltration again
Noise detectionBy powera noise's power sets who hears it: a radius, every guard, or the one nearest guard, which is how a knock on a wall draws a single guard
Every claim about this game, by concept (5)
Alert phaseIn Metal Gear Solid Integral (SLPM-86247), the enemy commander keeps one alert value and a mode: 0 (TOP_COMM_TRAVEL), 1 (TOP_COMM_ALERT), and 2, which sets GM_AlertMode to ALERT_EVASION; the atlas reads 0 as infiltration. Each update, s00a_command_800CF6A0 sets the value to the highest alert_level among its active guards, or lowers it by 4 when it is more than 4 above that, and raises it to TOPCOMMAND.alert, which the security camera and searchlight code set to 255 (ENE_SetTopCommAL). A patrolling guard (think1 0, think2 0) whose alert_level passes 2, after three frames of full sight from 0 (computed), goes to a reaction state: on its first frame it puts a mark over its head (ENE_PutMark, mark 0), and after 20 updates it switches to its alert routines (think1 1) and sets alert_level to 255. In mode 0, a value of 255 calls GM_AlertModeSet(3) (ALERT_ACTIVE), which plays SE_ALERT_SIREN when the mode was ALERT_OFF, and enters mode 1; it adds 1 to GM_EnemyWatchCount unless COM_GameStatus bit 2 is set. Once the commander leaves mode 0, every guard still in its patrol routines switches to its alert routines. Mode 1 has no timer: it lasts until the value falls to 0, which starts mode 2 with a counter of 300 that drops by 1 each update. A value of 255 during mode 2 returns to mode 1; when the counter runs out, the commander returns to mode 0 and ALERT_OFF. The game waits DG_FrameRate = 2 vertical blanks per frame, 30 frames a second on a 60 Hz display, so the evasion phase lasts 10 seconds (computed). The stage script's l option sets COM_EYE_LENGTH separately for modes 0, 1, and 2, 4,000 in each by default.Code
In Metal Gear Solid Integral (SLPM-86247), the radar (draw_radar) draws the map only in ALERT_OFF. In ALERT_ACTIVE and ALERT_EVASION, and in ALERT_OFF while GM_GameStatus has STATE_CHAFF or STATE_JAMMING set (shown as ALERT_JAMMING), it draws a status panel (menu_radar_draw_state) in its place, playing SE_RADAR_JAMMED when jamming begins from ALERT_OFF; for alert and evasion the panel draws GM_AlertLevel as a gauge. When the panel ends, the map returns behind a sweep while a counter set to 93 falls by 2 per frame, about 47 frames (computed), with SE_RADAR_CHIME, which the decompilers' comment says plays when evasion or jamming ends. In ALERT_OFF, with no override function set, the radar isn't drawn in a duct (PLAYER_INTRUDE) or with the radar turned off in the options. On the map, each guard's cone comes from its sight: its facing, vision.length, and twice vision.angle as the width (s00a_command_800C9878).Code
Noise detectionIn Metal Gear Solid Integral (SLPM-86247), a noise is a power, a length, and a position (GM_SetNoise), and a noise replaces the current one only if its power is higher, or equal with a length at least as long. A guard whose field_BA3 bit 1 is set (its setup sets it) hears the current noise by its power (s00a_command_800C9930): 5 within 1,500 units, 200 within COM_NOISEMODE_DIS (8,000 by default; a stage script's y option sets it), 255 at any distance, and 100 only if it is the guard the commander picked; for every power but 255 the guard must be on Snake's map, and 255 has its own test on GM_ClaymoreMap. Other powers aren't heard, such as the 4 of Snake's kick. For a 100, the commander (s00a_command_800CF9A0), outside mode 1, picks the guard within COM_NOISEMODE_DIS of the noise whose HZD_ZoneDistance to it is under 300 and least; that guard hears it and clears it, so one guard answers. Knocking on a wall makes a 100: sna_knock_80054D68 probes the wall beside Snake and, when the surface's attribute code is under 4, starts NewAfterSe, which 6 frames later plays the knock and calls GM_SetNoise(100, 16) at Snake's position. A footstep makes a 100 when Snake isn't in a duct (PLAYER_INTRUDE) and the stage script's n list gives the floor under him a nonzero noise flag. Explosions (blast.c) and stun grenades (stngrnd.c) make a 255. A patrolling guard (think2 0, s00a_command_800CD210) answers a heard noise (field_BA1 bit 1) after bits 4 and 2 and before footprints (bit 16) and sight; for a 255 it goes to the same reaction state as a sighting (think2 7), which after 21 updates (computed) sets its alert_level to 255.Code
Vision coneIn 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. ^In 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), the enemy commander keeps one alert value and a mode: 0 (TOP_COMM_TRAVEL), 1 (TOP_COMM_ALERT), and 2, which sets GM_AlertMode to ALERT_EVASION; the atlas reads 0 as infiltration. Each update, s00a_command_800CF6A0 sets the value to the highest alert_level among its active guards, or lowers it by 4 when it is more than 4 above that, and raises it to TOPCOMMAND.alert, which the security camera and searchlight code set to 255 (ENE_SetTopCommAL). A patrolling guard (think1 0, think2 0) whose alert_level passes 2, after three frames of full sight from 0 (computed), goes to a reaction state: on its first frame it puts a mark over its head (ENE_PutMark, mark 0), and after 20 updates it switches to its alert routines (think1 1) and sets alert_level to 255. In mode 0, a value of 255 calls GM_AlertModeSet(3) (ALERT_ACTIVE), which plays SE_ALERT_SIREN when the mode was ALERT_OFF, and enters mode 1; it adds 1 to GM_EnemyWatchCount unless COM_GameStatus bit 2 is set. Once the commander leaves mode 0, every guard still in its patrol routines switches to its alert routines. Mode 1 has no timer: it lasts until the value falls to 0, which starts mode 2 with a counter of 300 that drops by 1 each update. A value of 255 during mode 2 returns to mode 1; when the counter runs out, the commander returns to mode 0 and ALERT_OFF. The game waits DG_FrameRate = 2 vertical blanks per frame, 30 frames a second on a 60 Hz display, so the evasion phase lasts 10 seconds (computed). The stage script's l option sets COM_EYE_LENGTH separately for modes 0, 1, and 2, 4,000 in each by default.CodeFoxdieTeam/mgs_reversing
    How we know
    598void s00a_command_800CF6A0( int val, ENEMY_COMMAND* command )
    ⋮
    600    if ( command->alert - val > 4 )
    ⋮
    602        command->alert -= 4;
    ⋮
    604    else
    ⋮
    606        command->alert = val;
    ⋮
    609    if ( command->alert < TOPCOMMAND.alert )
    ⋮
    611        command->alert = TOPCOMMAND.alert;
    ⋮
    614    ENE_SetTopCommAL( 0 );
    
    source/enemy/command.c lines 598–615 at bf9189e · upstream ↗
    617void s00a_command_800CF704( ENEMY_COMMAND *command )
    ⋮
    621    switch ( command->mode )
    ⋮
    626        case 0:
    627            COM_EYE_LENGTH = EnemyCommand.field_0x88.vx;
    628            COM_SHOOTRANGE = EnemyCommand.field_0x88.vx + 500;
    629            if ( command->alert >= 255 )
    ⋮
    631                command->alert = 255;
    632                GM_AlertModeSet(3);
    633                command->mode = 1;
    634                EnemyCommand.reset_enemy_num = 0;
    635                COM_GameStatus |= 0x1;
    ⋮
    637                if ( !( COM_GameStatus & 2 ) )
    ⋮
    
    source/enemy/command.c lines 617–694 at bf9189e · upstream ↗
    760    alert = 0;
    ⋮
    766    for ( i = 0; i < EnemyCommand.field_0x08; i++ )
    ⋮
    768        if ( EnemyCommand.field_0xC8[ i ].field_04 == 2 )
    ⋮
    770            watcher = EnemyCommand.field_0xC8[ i ].watcher;
    771            alert =  s00a_command_800CF688( alert, watcher->alert_level );
    ⋮
    785    s00a_command_800CF6A0( alert, &EnemyCommand );
    786    s00a_command_800CF704( &EnemyCommand );
    
    source/enemy/command.c lines 760–786 at bf9189e · upstream ↗
    588int s00a_command_800CF688( int a0, int a1 )
    ⋮
    590    if ( a0 < a1 )
    ⋮
    592        a0 = a1;
    ⋮
    595    return a0;
    
    source/enemy/command.c lines 588–596 at bf9189e · upstream ↗
    536#define TOP_COMM_TRAVEL 0
    537#define TOP_COMM_ALERT  1
    
    source/enemy/enemy.h lines 536–537 at bf9189e · upstream ↗
    177enum {
    178    ALERT_OFF = 0,
    179    ALERT_JAMMING = 1,
    180    ALERT_EVASION = 2,
    181    ALERT_ACTIVE = 3
    
    source/game/g_define.h lines 177–182 at bf9189e · upstream ↗
    260    if (field_1EC == 35)
    ⋮
    262        ENE_SetTopCommAL(255);
    
    source/enemy/camera.c lines 260–262 at bf9189e · upstream ↗
    448    if (s01a_searchli_800D73D8(work))
    ⋮
    450        ENE_SetTopCommAL(255);
    
    source/enemy/searchli.c lines 448–450 at bf9189e · upstream ↗
    1955    else if ( work->alert_level > 2 )
    ⋮
    1957        ENE_SetGopointLast();
    1958        s00a_command_800CB240( work );
    
    source/enemy/think.c lines 1955–1959 at bf9189e · upstream ↗
    385void s00a_command_800CB240( WatcherWork* work )
    ⋮
    387    work->think2 = 7;
    388    work->think3 = 14;
    389    work->count3 = 0;
    
    source/enemy/think.c lines 385–390 at bf9189e · upstream ↗
    2324void s00a_command_800CDD80( WatcherWork *work )
    ⋮
    2326    switch( work->think3 )
    ⋮
    2328        case 14:
    2329        if ( s00a_command_800CBDFC( work ) )
    ⋮
    2331            s00a_command_800CB2C8( work );
    ⋮
    2340            work->alert_level = 255;
    
    source/enemy/think.c lines 2324–2343 at bf9189e · upstream ↗
    929int s00a_command_800CBDFC( WatcherWork* work )
    ⋮
    935    if ( count == 0 )
    ⋮
    937        ENE_PutMark( work, 0 );
    ⋮
    942    if ( count < 20 )
    ⋮
    944        work->pad.press |= 0x20;
    945        work->count3++;
    946        return 0;
    ⋮
    949    return 1;
    
    source/enemy/think.c lines 929–951 at bf9189e · upstream ↗
    411void s00a_command_800CB2C8( WatcherWork *work )
    ⋮
    414    work->think1 = 1;
    415    work->think2 = 9;
    
    source/enemy/think.c lines 411–418 at bf9189e · upstream ↗
    2688void s00a_command_800CE634( WatcherWork *work )
    ⋮
    2690    switch( work->think2 )
    ⋮
    2694        break;
    2695        case 0:
    2696        s00a_command_800CD210( work );
    2697        break;
    ⋮
    2701        break;
    ⋮
    2705        break;
    ⋮
    2709        break;
    ⋮
    2713        break;
    ⋮
    2717        break;
    ⋮
    2721        break;
    ⋮
    
    source/enemy/think.c lines 2688–2736 at bf9189e · upstream ↗
    2832    switch ( work->think1 )
    ⋮
    2834    case 0:
    2835        work->unknown.field_1E = 1;
    2836        s00a_command_800CE634( work );
    2837        break;
    2838    case 1:
    2839        work->unknown.field_1E = 1;
    2840        s00a_command_800CE778( work );
    
    source/enemy/think.c lines 2832–2840 at bf9189e · upstream ↗
    193    if ( new_alert_mode >= 0 && new_alert_mode != GM_AlertMode )
    ⋮
    195        if ( song_status == 0 )
    ⋮
    197            switch ( new_alert_mode )
    ⋮
    199            case ALERT_ACTIVE:
    200                if ( GM_AlertMode == ALERT_OFF )
    ⋮
    202                    GM_SeSet2( 0, 0x3F, SE_ALERT_SIREN );
    
    source/game/alert.c lines 193–204 at bf9189e · upstream ↗
    1039    s00a_command_800D018C( work );
    ⋮
    1043    s00a_command_800CFA94( work );
    ⋮
    1046    EnemyCommand.field_0x0C++;
    
    source/enemy/command.c lines 1039–1046 at bf9189e · upstream ↗
    24int DG_FrameRate = 2;
    
    source/libdg/dgd.c line 24 at bf9189e · upstream ↗
    77    else
    ⋮
    79        mts_wait_vbl( DG_FrameRate );
    
    source/libdg/dgd.c lines 77–79 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

  3. ^In Metal Gear Solid Integral (SLPM-86247), the radar (draw_radar) draws the map only in ALERT_OFF. In ALERT_ACTIVE and ALERT_EVASION, and in ALERT_OFF while GM_GameStatus has STATE_CHAFF or STATE_JAMMING set (shown as ALERT_JAMMING), it draws a status panel (menu_radar_draw_state) in its place, playing SE_RADAR_JAMMED when jamming begins from ALERT_OFF; for alert and evasion the panel draws GM_AlertLevel as a gauge. When the panel ends, the map returns behind a sweep while a counter set to 93 falls by 2 per frame, about 47 frames (computed), with SE_RADAR_CHIME, which the decompilers' comment says plays when evasion or jamming ends. In ALERT_OFF, with no override function set, the radar isn't drawn in a duct (PLAYER_INTRUDE) or with the radar turned off in the options. On the map, each guard's cone comes from its sight: its facing, vision.length, and twice vision.angle as the width (s00a_command_800C9878).CodeFoxdieTeam/mgs_reversing
    How we know
    1038void draw_radar(MenuWork *work, u_long *ot)
    ⋮
    1046    alertLevel = GM_AlertLevel;
    1047    alertMode = GM_AlertMode;
    ⋮
    1054    if (alertMode == ALERT_OFF && gFn_radar_800AB48C == NULL)
    ⋮
    1056        if (GM_PlayerStatus & PLAYER_INTRUDE)
    ⋮
    1058            return;
    ⋮
    1061        if (GM_Configuration & GM_CONFIG_RADAR_OFF)
    ⋮
    1063            return;
    ⋮
    1074    else
    ⋮
    1076        if (alertMode == ALERT_OFF)
    ⋮
    1078            if (GM_GameStatus & (STATE_CHAFF | STATE_JAMMING))
    ⋮
    1080                alertMode = ALERT_JAMMING;
    ⋮
    
    source/menu/radar.c lines 1038–1170 at bf9189e · upstream ↗
    921void menu_radar_draw_state(MenuWork *work, u_long *ot, int radarMode, int alertLevel)
    ⋮
    930    switch (radarMode)
    ⋮
    932    case ALERT_JAMMING:
    ⋮
    943    default:
    944        draw_seg(work->prim, alertLevel, 3 - radarMode);
    
    source/menu/radar.c lines 921–946 at bf9189e · upstream ↗
    24void s00a_command_800C9878( WatcherWork* work )
    ⋮
    26    VISION     *vision = &work->vision;
    27    RADAR_SIGHT_PARAM *r_param   = &work->control.radar_param;
    ⋮
    29    r_param->dir = vision->facedir;
    30    r_param->dis = vision->length;
    31    r_param->range = vision->angle * 2;
    
    source/enemy/enemy.c lines 24–32 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

  4. ^In Metal Gear Solid Integral (SLPM-86247), a noise is a power, a length, and a position (GM_SetNoise), and a noise replaces the current one only if its power is higher, or equal with a length at least as long. A guard whose field_BA3 bit 1 is set (its setup sets it) hears the current noise by its power (s00a_command_800C9930): 5 within 1,500 units, 200 within COM_NOISEMODE_DIS (8,000 by default; a stage script's y option sets it), 255 at any distance, and 100 only if it is the guard the commander picked; for every power but 255 the guard must be on Snake's map, and 255 has its own test on GM_ClaymoreMap. Other powers aren't heard, such as the 4 of Snake's kick. For a 100, the commander (s00a_command_800CF9A0), outside mode 1, picks the guard within COM_NOISEMODE_DIS of the noise whose HZD_ZoneDistance to it is under 300 and least; that guard hears it and clears it, so one guard answers. Knocking on a wall makes a 100: sna_knock_80054D68 probes the wall beside Snake and, when the surface's attribute code is under 4, starts NewAfterSe, which 6 frames later plays the knock and calls GM_SetNoise(100, 16) at Snake's position. A footstep makes a 100 when Snake isn't in a duct (PLAYER_INTRUDE) and the stage script's n list gives the floor under him a nonzero noise flag. Explosions (blast.c) and stun grenades (stngrnd.c) make a 255. A patrolling guard (think2 0, s00a_command_800CD210) answers a heard noise (field_BA1 bit 1) after bits 4 and 2 and before footprints (bit 16) and sight; for a 255 it goes to the same reaction state as a sighting (think2 7), which after 21 updates (computed) sets its alert_level to 255.CodeFoxdieTeam/mgs_reversingrevised
    How we know
    74static inline void GM_SetNoise( int power, int length, SVECTOR *pos )
    ⋮
    76    int old ;
    ⋮
    78    old = GM_NoisePower ;
    79    if ( power < old ) return ;
    80    if ( power == old && length < GM_NoiseLength ) return ;
    81    GM_NoisePower = power ;
    82    GM_NoiseLength = length ;
    83    GM_NoisePosition = *pos ;
    
    source/game/g_macro.h lines 74–84 at bf9189e · upstream ↗
    62void s00a_command_800C9930( WatcherWork* work )
    ⋮
    64    CONTROL *ctrl;
    65    if ( !( work->field_BA3 & 1 ) )
    ⋮
    67        return;
    ⋮
    72        return;
    ⋮
    78        return;
    ⋮
    83    if ( GM_NoisePower == 0xFF )
    ⋮
    86        if ( !( ctrl->map->index & GM_ClaymoreMap ) &&
    ⋮
    90            return;
    ⋮
    93    else
    ⋮
    95        if ( !( ctrl->map->index & GM_PlayerMap ) )
    ⋮
    97            return;
    ⋮
    
    source/enemy/enemy.c lines 62–130 at bf9189e · upstream ↗
    677    work->field_BA3 =  7;
    
    source/enemy/watcher.c line 677 at bf9189e · upstream ↗
    1190    COM_NOISEMODE_DIS = 0x1F40;
    ⋮
    1192    ops = GCL_GetOption( 'y' );
    1193    if ( ops )
    ⋮
    1195        COM_NOISEMODE_DIS = GCL_StrToInt( ops );
    
    source/enemy/command.c lines 1190–1196 at bf9189e · upstream ↗
    714int s00a_command_800CF9A0( WatcherWork *work, int dis, int idx )
    ⋮
    721    if ( TOPCOMMAND.mode == 1 )
    ⋮
    723        goto exit;
    ⋮
    726    if ( GM_NoisePower != 0x64 )
    ⋮
    728        goto exit;
    ⋮
    733       goto exit;
    ⋮
    736    if ( GV_DiffVec3( &GM_NoisePosition, &ctrl->mov ) >= COM_NOISEMODE_DIS )
    ⋮
    738        goto exit;
    ⋮
    741    x = s00a_command_800CF940( ctrl->map->hzd, &ctrl->mov, &GM_NoisePosition );
    ⋮
    743    if ( x < 300 && x < dis )
    ⋮
    745        COM_NoiseMinDisID = idx;
    746        dis = x;
    
    source/enemy/command.c lines 714–750 at bf9189e · upstream ↗
    697int s00a_command_800CF940( HZD_HDL *hzd, SVECTOR *pos, SVECTOR *pos2 )
    ⋮
    702    from = HZD_GetAddress( hzd, pos, -1 );
    ⋮
    707        to = HZD_GetAddress( hzd, pos2, -1 );
    708        do {
    709            return HZD_ZoneDistance( hzd, from & 0xFF, to & 0xFF );
    
    source/enemy/command.c lines 697–711 at bf9189e · upstream ↗
    4128        if (sna_8004F628(work, &vec, -250, HZD_CHK_D_SEGMENT | HZD_CHK_F_SEGMENT, SEGMENT_ATR, var_t0))
    ⋮
    4130            temp_v0 = HZD_GetOnlineHazardAtr();
    4131            code = (temp_v0 >> 8) & 7;
    ⋮
    4133            if (code < 4)
    ⋮
    4135                temp_v0 = GM_GetNoiseSound(temp_v0, 0);
    4136                noise = temp_v0;
    4137                NewAfterSe(noise, 6);
    
    source/chara/snake/sna_init.c lines 4128–4137 at bf9189e · upstream ↗
    14static void Act(Work *work)
    ⋮
    16    if ( --work->time == 0 )
    ⋮
    18        // Play the sound
    19        GM_SeSetMode(&GM_PlayerPosition, work->sound, GM_SEMODE_BOMB);
    ⋮
    21        // Allow soliders/chavs/whatever to hear it
    22        GM_SetNoise(100, 16, &GM_PlayerPosition);
    
    source/chara/snake/afterse.c lines 14–22 at bf9189e · upstream ↗
    104        else
    ⋮
    106            index = asioto_800C396C(work);
    ⋮
    112            bank2 = asioto_800C392C(work);
    113            if (bank2 >= 0)
    ⋮
    115                GM_SeSet(&GM_PlayerPosition, work->se[bank2][index]);
    ⋮
    117                if (work->noise[bank2] != 0)
    ⋮
    119                    GM_SetNoise(100, 4, &GM_PlayerPosition);
    
    source/enemy/asioto.c lines 104–120 at bf9189e · upstream ↗
    90        if (GM_PlayerStatus & PLAYER_INTRUDE)
    ⋮
    92            if (GM_PlayerStatus & PLAYER_MOVE)
    
    source/enemy/asioto.c lines 90–92 at bf9189e · upstream ↗
    193    if (GCL_GetOption('n'))
    ⋮
    202            work->noise[i] = GCL_StrToInt(GCL_NextStr());
    
    source/enemy/asioto.c lines 193–202 at bf9189e · upstream ↗
    6847        sound = SE_KICK_SWING;
    ⋮
    6854        GM_SetNoise(4, 2, &work->control.mov);
    
    source/chara/snake/sna_init.c lines 6847–6854 at bf9189e · upstream ↗
    170        GM_SetNoise(255, 32, &work->pos);
    
    source/bullet/blast.c line 170 at bf9189e · upstream ↗
    224        GM_SetNoise(255, 32, &work->field_E0);
    
    source/okajima/stngrnd.c line 224 at bf9189e · upstream ↗
    1939    if ( work->field_BA1 & 4 )
    ⋮
    1941        s00a_command_800CB3F0( work );
    ⋮
    1943    else if ( work->field_BA1 & 2)
    ⋮
    1945        s00a_command_800CB610( work );
    ⋮
    1947    else if ( work->field_BA1 & 1)
    ⋮
    1949        s00a_command_800CB504( work );
    ⋮
    1951    else if ( work->field_BA1 & 16 )
    ⋮
    1953        s00a_command_800CB628( work );
    
    source/enemy/think.c lines 1939–1954 at bf9189e · upstream ↗
    558    case 255:
    559        s00a_command_800CAB74( work );
    560        s00a_command_800CEB54();
    561        work->think2 = 7;
    562        work->think3 = 16;
    
    source/enemy/think.c lines 558–562 at bf9189e · upstream ↗
    2344        case 16:
    2345        if ( s00a_command_800CBE90( work ) )
    ⋮
    2356            work->alert_level = 255;
    
    source/enemy/think.c lines 2344–2356 at bf9189e · upstream ↗
    953int s00a_command_800CBE90( WatcherWork* work )
    ⋮
    962    if ( work->count3 > 20 )
    ⋮
    964        return 1;
    ⋮
    967    work->count3++;
    
    source/enemy/think.c lines 953–969 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

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