sm64

A Super Mario 64 decompilation
Log | Files | Refs | README | LICENSE

title_screen.c (7935B)


      1 #include <PR/ultratypes.h>
      2 
      3 #include "audio/external.h"
      4 #include "engine/math_util.h"
      5 #include "game/area.h"
      6 #include "game/game_init.h"
      7 #include "game/level_update.h"
      8 #include "game/main.h"
      9 #include "game/memory.h"
     10 #include "game/print.h"
     11 #include "game/save_file.h"
     12 #include "game/sound_init.h"
     13 #include "game/rumble_init.h"
     14 #include "level_table.h"
     15 #include "seq_ids.h"
     16 #include "sm64.h"
     17 #include "title_screen.h"
     18 
     19 /**
     20  * @file title_screen.c
     21  * This file implements how title screen functions.
     22  * That includes playing demo sequences, introduction screens
     23  * and a level select used for testing purposes.
     24  */
     25 
     26 #define STUB_LEVEL(textname, _1, _2, _3, _4, _5, _6, _7, _8) textname,
     27 #define DEFINE_LEVEL(textname, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) textname,
     28 
     29 static char sLevelSelectStageNames[64][16] = {
     30     #include "levels/level_defines.h"
     31 };
     32 #undef STUB_LEVEL
     33 #undef DEFINE_LEVEL
     34 
     35 static u16 sDemoCountdown = 0;
     36 #ifndef VERSION_JP
     37 static s16 sPlayMarioGreeting = TRUE;
     38 static s16 sPlayMarioGameOver = TRUE;
     39 #endif
     40 
     41 #define PRESS_START_DEMO_TIMER 800
     42 
     43 /**
     44  * Run the demo timer on the PRESS START screen after a number of frames.
     45  * This function returns the level ID from the first byte of a demo file.
     46  * It also returns the level ID from intro_regular (file select or level select menu)
     47  */
     48 s32 run_level_id_or_demo(s32 level) {
     49     gCurrDemoInput = NULL;
     50 
     51     if (level == LEVEL_NONE) {
     52         if (!gPlayer1Controller->buttonDown && !gPlayer1Controller->stickMag) {
     53             // start the demo. 800 frames has passed while
     54             // player is idle on PRESS START screen.
     55             if ((++sDemoCountdown) == PRESS_START_DEMO_TIMER) {
     56 
     57                 // start the Mario demo animation for the demo list.
     58                 load_patchable_table(&gDemoInputsBuf, gDemoInputListID);
     59 
     60                 // if the next demo sequence ID is the count limit, reset it back to
     61                 // the first sequence.
     62                 if (++gDemoInputListID == gDemoInputsBuf.dmaTable->count) {
     63                     gDemoInputListID = 0;
     64                 }
     65 
     66                 // add 1 (+4) to the pointer to skip the first 4 bytes
     67                 // Use the first 4 bytes to store level ID,
     68                 // then use the rest of the values for inputs
     69                 gCurrDemoInput = ((struct DemoInput *) gDemoInputsBuf.bufTarget) + 1;
     70                 level = (s8)((struct DemoInput *) gDemoInputsBuf.bufTarget)->timer;
     71                 gCurrSaveFileNum = 1;
     72                 gCurrActNum = 1;
     73             }
     74         } else { // activity was detected, so reset the demo countdown.
     75             sDemoCountdown = 0;
     76         }
     77     }
     78     return level;
     79 }
     80 
     81 /**
     82  * Level select intro function, updates the selected stage
     83  * count if an input was received. signals the stage to be started
     84  * or the level select to be exited if start or the quit combo is pressed.
     85  */
     86 s16 intro_level_select(void) {
     87     s32 stageChanged = FALSE;
     88 
     89     // perform the ID updates per each button press.
     90     // runs into a loop so after a button is pressed
     91     // stageChanged goes back to FALSE
     92     if (gPlayer1Controller->buttonPressed & A_BUTTON) {
     93         ++gCurrLevelNum, stageChanged = TRUE;
     94     }
     95     if (gPlayer1Controller->buttonPressed & B_BUTTON) {
     96         --gCurrLevelNum, stageChanged = TRUE;
     97     }
     98     if (gPlayer1Controller->buttonPressed & U_JPAD) {
     99         --gCurrLevelNum, stageChanged = TRUE;
    100     }
    101     if (gPlayer1Controller->buttonPressed & D_JPAD) {
    102         ++gCurrLevelNum, stageChanged = TRUE;
    103     }
    104     if (gPlayer1Controller->buttonPressed & L_JPAD) {
    105         gCurrLevelNum -= 10, stageChanged = TRUE;
    106     }
    107     if (gPlayer1Controller->buttonPressed & R_JPAD) {
    108         gCurrLevelNum += 10, stageChanged = TRUE;
    109     }
    110 
    111     // if the stage was changed, play the sound for changing a stage.
    112     if (stageChanged) {
    113         play_sound(SOUND_GENERAL_LEVEL_SELECT_CHANGE, gGlobalSoundSource);
    114     }
    115 
    116     if (gCurrLevelNum > LEVEL_MAX) {
    117         gCurrLevelNum = LEVEL_MIN; // exceeded max. set to min.
    118     }
    119 
    120     if (gCurrLevelNum < LEVEL_MIN) {
    121         gCurrLevelNum = LEVEL_MAX; // exceeded min. set to max.
    122     }
    123 
    124     // Use file 4 and last act as a test
    125     gCurrSaveFileNum = 4;
    126     gCurrActNum = 6;
    127 
    128     print_text_centered(160, 80, "SELECT STAGE");
    129     print_text_centered(160, 30, "PRESS START BUTTON");
    130     print_text_fmt_int(40, 60, "%2d", gCurrLevelNum);
    131     print_text(80, 60, sLevelSelectStageNames[gCurrLevelNum - 1]); // print stage name
    132 
    133 #define QUIT_LEVEL_SELECT_COMBO (Z_TRIG | START_BUTTON | L_CBUTTONS | R_CBUTTONS)
    134 
    135     // start being pressed signals the stage to be started. that is, unless...
    136     if (gPlayer1Controller->buttonPressed & START_BUTTON) {
    137         // ... the level select quit combo is being pressed, which uses START. If this
    138         // is the case, quit the menu instead.
    139         if (gPlayer1Controller->buttonDown == QUIT_LEVEL_SELECT_COMBO) {
    140             gDebugLevelSelect = FALSE;
    141             return -1;
    142         }
    143         play_sound(SOUND_MENU_STAR_SOUND, gGlobalSoundSource);
    144         return gCurrLevelNum;
    145     }
    146     return 0;
    147 }
    148 
    149 /**
    150  * Regular intro function that handles Mario's greeting voice and game start.
    151  */
    152 s32 intro_regular(void) {
    153     s32 level = LEVEL_NONE;
    154 
    155 #ifndef VERSION_JP
    156     // When the game stars, gGlobalTimer is less than 129 frames,
    157     // so Mario greets the player. After that, he will always say
    158     // "press start to play" when it goes back to the title screen
    159     // (using SAVE AND QUIT)
    160     if (sPlayMarioGreeting == TRUE) {
    161         if (gGlobalTimer < 129) {
    162             play_sound(SOUND_MARIO_HELLO, gGlobalSoundSource);
    163         } else {
    164             play_sound(SOUND_MARIO_PRESS_START_TO_PLAY, gGlobalSoundSource);
    165         }
    166         sPlayMarioGreeting = FALSE;
    167     }
    168 #endif
    169     print_intro_text();
    170 
    171     if (gPlayer1Controller->buttonPressed & START_BUTTON) {
    172         play_sound(SOUND_MENU_STAR_SOUND, gGlobalSoundSource);
    173 #if ENABLE_RUMBLE
    174         queue_rumble_data(60, 70);
    175         func_sh_8024C89C(1);
    176 #endif
    177         // calls level ID 100 (or 101 adding level select bool value)
    178         // defined in level_intro_mario_head_regular JUMP_IF commands
    179         // 100 is File Select - 101 is Level Select
    180         level = 100 + gDebugLevelSelect;
    181 #ifndef VERSION_JP
    182         sPlayMarioGreeting = TRUE;
    183 #endif
    184     }
    185     return run_level_id_or_demo(level);
    186 }
    187 
    188 /**
    189  * Game over intro function that handles Mario's game over voice and game start.
    190  */
    191 s32 intro_game_over(void) {
    192     s32 level = LEVEL_NONE;
    193 
    194 #ifndef VERSION_JP
    195     if (sPlayMarioGameOver == TRUE) {
    196         play_sound(SOUND_MARIO_GAME_OVER, gGlobalSoundSource);
    197         sPlayMarioGameOver = FALSE;
    198     }
    199 #endif
    200 
    201     print_intro_text();
    202 
    203     if (gPlayer1Controller->buttonPressed & START_BUTTON) {
    204         play_sound(SOUND_MENU_STAR_SOUND, gGlobalSoundSource);
    205 #if ENABLE_RUMBLE
    206         queue_rumble_data(60, 70);
    207         func_sh_8024C89C(1);
    208 #endif
    209         // same criteria as intro_regular
    210         level = 100 + gDebugLevelSelect;
    211 #ifndef VERSION_JP
    212         sPlayMarioGameOver = TRUE;
    213 #endif
    214     }
    215     return run_level_id_or_demo(level);
    216 }
    217 
    218 /**
    219  * Plays the casual "It's a me mario" when the game stars.
    220  */
    221 s32 intro_play_its_a_me_mario(void) {
    222     set_background_music(0, SEQ_SOUND_PLAYER, 0);
    223     play_sound(SOUND_MENU_COIN_ITS_A_ME_MARIO, gGlobalSoundSource);
    224     return 1;
    225 }
    226 
    227 /**
    228  * Update intro functions to handle title screen actions.
    229  * Returns a level ID after their criteria is met.
    230  */
    231 s32 lvl_intro_update(s16 arg, UNUSED s32 unusedArg) {
    232     s32 retVar;
    233 
    234     switch (arg) {
    235         case LVL_INTRO_PLAY_ITS_A_ME_MARIO:
    236             retVar = intro_play_its_a_me_mario();
    237             break;
    238         case LVL_INTRO_REGULAR:
    239             retVar = intro_regular();
    240             break;
    241         case LVL_INTRO_GAME_OVER:
    242             retVar = intro_game_over();
    243             break;
    244         case LVL_INTRO_LEVEL_SELECT:
    245             retVar = intro_level_select();
    246             break;
    247     }
    248     return retVar;
    249 }