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 }