Mechanic · enemies & ai · stealth
Alert phase
A level-wide state that enemies enter when the player is spotted, with its own rules for how it starts and ends.
On this page
An alert phase is a state the whole level enters when enemies spot the player. In Metal Gear Solid the alert lasts as long as some guard’s alert level holds the commander’s value up, and then a 300-frame evasion phase must run out before the level returns to infiltration1. The radar shows a status panel instead of the map in both phases2.
- Alert then evasionMetal Gear Solid (1998). Alert while any guard's alert level holds the commander's value up, then evasion for 300 frames, then infiltration again.1,2
Games
| Game | Year | How | Note |
|---|---|---|---|
| Metal Gear Solid | 1998 | Alert then evasion | alert while any guard's alert level holds the commander's value up, then evasion for 300 frames, then infiltration again |
Every claim about this mechanic, by game (2)
References
- ^ abIn 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.
- ^ abIn 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.
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