# Game Loop The control flow of _Contra_ is dictated by the CPU memory address `GAME_ROUTINE_INDEX` ($18). You can think of the `game_routine_xx` methods like canal locks. As the game progresses, the flow moves to the next `game_routine`. * `game_routine_00` and `game_routine_01` are for setting up the game intro and player select UI. * `game_routine_02` is for showing the demo. * `game_routine_03` loads and auto-plays the demo level * `game_routine_04` clears level memory and player-specific memory * `game_routine_05` is where majority of level logic is executed * `game_routine_06` runs at the end of the game after defeating the final boss alien ## game_routine_00 This label is only run once to initialize the NES's graphics and CPU memory that is used for the introduction animation and player select. * Set nametable tiles all to #$00 - `zero_out_nametables` (ROM: $1c9a2, MEM: Bank 7 $c9a2) * Load intro pattern table, nametable, and palette (`load_intro_graphics`) * Reset Komani code number of correct sequence inputs (`KONAMI_CODE_NUM_CORRECT`) to back to #$00 * Initialize the horizontal scroll to #$01 for the sliding intro animation * Initialize the high byte of the delay timer that is used for waiting before showing the demo ## game_routine_01 This label is executed once per frame repeatedly while the _Contra_ logo is scrolled across the screen (intro animation). This label also checks for the Konami code is checked. Once the logo is finished scrolling from right to left, the label will load the sprites needed to show Bill and Lance as well as the player select UI and cursor. Then the intro theme music will be played. Finally, this label waits for both timers to complete before either starting the game (if the player has made a selection), or show a demo level. * Check for Konami code (`konami_input_check`) * Scroll intro graphic * This is executed repeatedly until scrolling stops, the the logic below is executed * Load assets after scrolling is complete (`game_routine_01_scroll_complete`) * Load player select menu * Play intro explosion sound * Load sprite for yellow falcon cursor and sprites for Bill and Lance * Wait for timer to complete before moving on to `game_routine_02` (`dec_theme_delay_check_user_input`) * The timer resets if select is pressed While outside of the `game_routine_01` method, the `exe_game_routine` method will call `dec_theme_delay_check_user_input` for game routine `game_routine_00`, `game_routine_01`, and `game_routine_02`. For `game_routine_01` this method will * decrement timer used to wait for intro theme to play * check whether the player has pressed the start button and if so stop the intro scrolling animation and show player select UI ## game_routine_02 * Load demo level and plays the level * Stop level when demo timer elapsed and loads next level to demo (only levels 0-2) * Reset `GAME_ROUTINE_INDEX` to #$0 between demo levels to reshow intro scroll and player select ## game_routine_03 This label is executed once the player has pressed start to begin a game while in `game_routine_02`. It simply ensures the intro theme is finished playing and then flashes the player selection until all timers are elapsed * Wait for intro theme to finish playing * Flash "1 PLAYER" or "2 PLAYER" for a bit ## game_routine_04 This label clears level memory and player-specific memory like number of lives, number of continues, as well as level header data. * `init_score_player_lives` - clears memory addresses $0028 to $00f0 then `CPU_SPRITE_BUFFER` ($300) up to but not including `CPU_GRAPHICS_BUFFER` ($700) ## game_routine_05 This is the where the majority of the game logic is executed from. `game_routine_05` maintains its own separate set of routines for managing the level state. See Level Routines. There are #$0a level routines. ## game_routine_06 This routine runs at the end of the game after defeating the final boss alien. Begins the `game_end_routine_XX` routine execution flow and runs through each routine * `game_end_routine_00` * `game_end_routine_01` * `game_end_routine_02` * `game_end_routine_03` * `end_game_sequence_00` * `end_game_sequence_01` * `end_game_sequence_02` * `game_end_routine_04` * `game_end_routine_05` # Level Routines `game_routine_05` maintains a `LEVEL_ROUTINE_INDEX` ($2c). This is an offset into the `level_routine_ptr_tbl` (ROM: $1ce35, MEM: Bank 7 $ce35). These routines manage the state of the level as the player progresses. These routines are not necessarily executed in order like a waterfall. There are #$0a level routines in total. These routines manage * level initialization * showing score before starting a level * handle player input, including pause * level completion, advancing level * game over screen - continue / end * game over with no more continues * showing game ending sequence Below is a mermaid diagram of the logic that dictates the state changes among the level routines. ```mermaid graph TD level_routine_00 --> level_routine_01 level_routine_01 --> level_routine_02 level_routine_02 --> level_routine_03 level_routine_03 --> level_routine_04 level_routine_04 --> |Level Complete|level_routine_08 level_routine_04 --> |Game Over|level_routine_0a level_routine_05 --> |Level Complete|level_routine_00 level_routine_05 --> |Complete Last Level/Game Over|level_routine_06 level_routine_05 --> |Game Over No Continues|level_routine_07 level_routine_06 --> |Continue/End Selected|level_routine_00 level_routine_08 --> |Game Over|level_routine_0a level_routine_08 --> |Level Complete|level_routine_09 level_routine_0a --> |Game Over Timer Expired|level_routine_05 level_routine_09 --> |Level Complete|level_routine_05 ``` ## level_routine_00 This routine is responsible for loading the level header data into memory. This specifies things like what type of level (indoor/base, outdoor), where graphics data for the level are (super-tile pattern tiles, which super-tiles are on each screen, and palette data). Then the routine initializes many PPU write addresses, and loads the data which specifies the super-tiles on the first screen. Note that the actual super-tile pattern tiles aren't loaded into memory until `level_routine_03`. * Load the level header data * Load palette for screen that displays number of lives remaining and level/ stage name. * Initialize `BOSS_DEFEATED_FLAG` and `LEVEL_END_PLAYERS_ALIVE` to #$00 * Call `init_ppu_write_screen_supertiles` to initialize PPU scroll offset, PPU write offsets and call `load_current_supertiles_screen_indexes` to decompress super-tiles indexes for level's screen to load into CPU memory at `LEVEL_SCREEN_SUPERTILES` * Loads bank 0 where the enemy routines are and initializes the memory address $80 to point to where the enemy routines are for the current level ## level_routine_01 Responsible for displaying the number of lives remaining for players before the level loads. This routine does not execute when demoing (in demo mode). It is only executed once, whereas `level_routine_02` is executed repeatedly to flash the score. The number of lives does not flash. ## level_routine_02 * Flash the score text until timer elapses * Load the theme music for the level `level_vert_scroll_and_song` when not in demo mode * Sets the vertical scroll ## level_routine_03 Responsible for rendering the nametable super-tiles. ## level_routine_04 * Sees if the player is pausing or un-pausing by reading input. * If the player is pausing, the pause sound is played and the game state is updated * If the player is paused, then the routine ends * Check if BOSS_DEFEATED_FLAG is set, the the current level routine is updated to `level_routine_08` and the current routine ends * Check if player(s) have entered game over state, and if so, updates the current level routine to `level_routine_0a` and the current routine ends * Run all enemy logic * Run soldier generation * Update palette for flashing palettes, see palette cycling documentation in `Graphics Documentation.md` * Load alternate graphics if necessary ## level_routine_05 Handles when the the level is complete or ended due to game over. If it's not the last level, then the sets things up so the next level is loaded. If it's the last level, then configures things for the end of game sequences. During game over, after GAME_OVER_DELAY_TIMER elapses, level routine #$0a sets the next routine to be level_routine_05 so that the game over high score screen can be shown * Clear memory $40 to $f0, then $300 to $600 (exclusively) * If game over * Load game over screen pattern table and show game over screen `show_game_over_screen` * Increment `CURRENT_LEVEL` * If the last level * Start game ending sequence `inc_routine_index_set_timer` * Increment `GAME_COMPLETION_COUNT` so next play through is more challenging * Reset `LEVEL_ROUTINE_INDEX` back to level_routine_00 * If not the last level * Loads graphics for current score screen and level intro screen in `load_level_intro` * Increments `LEVEL_ROUTINE_INDEX` to go to level_routine_06 `inc_routine_index_set_timer` ## level_routine_06 This level routine is the game over screen routine. It is executed after the player has died. This routine shows the player scores, the high score. It also shows the text "GAME OVER" and gives the player the option to either "CONTINUE" or "END". * Display score and "CONTINUE"/"END" * Handle player input to change cursor between "CONTINUE" and "END" (select button) * Handle player input to select either "CONTINUE" and "END" (start button) ## level_routine_07 This routine is executed when there are no continues remaining. It shows the score and the text "GAME OVER" while waiting for the player to press start. Once the player presses start, the level routine is set to #$00 `level_routine_07` is executed after checking the number of remaining continues in `level_routine_05` (`@no_continues_remaining`). ## level_routine_08 Boss destroy animation. Plays level end music * Check if player(s) have entered game over state, and if so, updates the current level routine to `level_routine_0a` and the current routine ends. This seems unlikely as this check happens in `level_routine_04` as well. This same code is part of `level_routine_07`, and seems equally unlikely to happen for that level routine as well. ## level_routine_09 This routine runs the appropriate level routine from the `end_level_sequence_ptr_tbl` table. After each sequence is finished executing, control is sent back to `level_routine_05`. * `end_level_sequence_00` * Wait for the players to land if they were jumping * Set a small delay before `end_level_sequence_01` does its logic * `end_level_sequence_01` * Wait for timer to elapse * Run level-specific level routine ending animation from `end_of_lvl_lvl_routine_ptr_tbl` * `end_level_sequence_02` ## level_routine_0a This is the last level routine, but is executed as part of a game over sequence. After the player(s) get a game over, this is the routine that waits until `GAME_OVER_DELAY_TIMER` has elapsed before setting the level routine to #$05 (`level_routine_05`) to reinitialize the level. The `GAME_OVER_DELAY_TIMER` delay timer starts at #60 and is used to wait a bit before showing the player scores.