Metal Gear Solid
Konami's 1998 PlayStation stealth game, read through FoxdieTeam's mgs_reversing, a matching decompilation of the Japanese Integral re-release.
On this page
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
| Mechanic | Kind | How |
|---|---|---|
| Vision cone | Cone | 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 |
| Alert phase | Alert then evasion | alert while any guard's alert level holds the commander's value up, then evasion for 300 frames, then infiltration again |
| Noise detection | By power | a 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)
References
- ^In Metal Gear Solid Integral (SLPM-86247, the Japanese re-release the decompilation builds), a guard's sight check
s00a_command_800C9E68updates its resultvision.field_B92for one guard per commander update, the guard whosefield_B78equals the commander's update countfield_0x0Cmodulofield_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 thanvision.anglefrom 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 setsvision.angleto 512 of the 4,096 units in a turn, a cone of 45° either side of the facing, 90° across (computed), andvision.lengthto COM_EYE_LENGTH, 4,000 units unless a stage script'sloption sets it; some guard actions set the length to 3,000 or 0. The result is 1 for a sighting beyondvision.lengthand 2 within it, and 0 whenever Snake has the Stealth item equipped, unless the guard'sparam_bloodis 83. Every frame,s00a_command_800C9D7Cmoves the guard'salert_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'stoption 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'svision.lengthisn't 0.CodeFoxdieTeam/mgs_reversingrevisedHow 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 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_800CF6A0sets the value to the highestalert_levelamong 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) whosealert_levelpasses 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 setsalert_levelto 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'sloption sets COM_EYE_LENGTH separately for modes 0, 1, and 2, 4,000 in each by default.CodeFoxdieTeam/mgs_reversingHow 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 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 twicevision.angleas the width (s00a_command_800C9878).CodeFoxdieTeam/mgs_reversingHow 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 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_BA3bit 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'syoption 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_80054D68probes the wall beside Snake and, when the surface's attribute code is under 4, startsNewAfterSe, 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'snlist 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_BA1bit 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 itsalert_levelto 255.CodeFoxdieTeam/mgs_reversingrevisedHow 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 Metal Gear Solid Integral (SLPM-86247), a guard looks for footprints only if its stage script gives it the
coption, which setsfield_BA3bit 0x10, and not in the commander's mode 1, TOP_COMM_ALERT (s00a_command_800C9D28skips 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) whosepadis 1 that lies withinvision.length(a horizontal distance) and less thanvision.anglefrom 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 offield_BA1bits 4, 2, and 1 (the last a heard noise) ranks above it.CodeFoxdieTeam/mgs_reversingHow 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.
Sources
Code
- FoxdieTeam and mgs_reversing contributors. 2026. “mgs_reversing: a matching decompilation of Metal Gear Solid Integral for the PlayStation (FoxdieTeam/mgs_reversing).” Commit bf9189e. FoxdieTeam/mgs_reversing @ bf9189e · licence, all uses