sm64pc/src/menu/level_select_menu.c

212 lines
6.4 KiB
C
Raw Normal View History

2019-08-25 04:46:40 +00:00
#include <ultra64.h>
#include "sm64.h"
#include "audio/external.h"
2020-04-03 18:57:26 +00:00
#include "game/game_init.h"
2019-10-05 19:08:05 +00:00
#include "game/main.h"
#include "game/memory.h"
#include "game/area.h"
#include "game/save_file.h"
#include "game/level_update.h"
#include "game/sound_init.h"
#include "game/print.h"
2019-08-25 04:46:40 +00:00
#include "seq_ids.h"
2019-10-05 19:08:05 +00:00
#include "engine/math_util.h"
2019-12-02 02:52:53 +00:00
#include "level_table.h"
2019-08-25 04:46:40 +00:00
#define PRESS_START_DEMO_TIMER 800
2019-12-02 02:52:53 +00:00
#define STUB_LEVEL(textname, _1, _2, _3, _4, _5, _6, _7, _8) textname,
#define DEFINE_LEVEL(textname, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) textname,
2019-10-05 19:08:05 +00:00
static char gLevelSelect_StageNamesText[64][16] = {
2019-12-02 02:52:53 +00:00
#include "levels/level_defines.h"
2019-10-05 19:08:05 +00:00
};
2019-12-02 02:52:53 +00:00
#undef STUB_LEVEL
#undef DEFINE_LEVEL
2019-08-25 04:46:40 +00:00
static u16 gDemoCountdown = 0;
#ifndef VERSION_JP
static s16 D_U_801A7C34 = 1;
static s16 gameOverNotPlayed = 1;
#endif
// run the demo timer on the PRESS START screen.
// this function will return a non-0 timer once
// the demo starts, signaling to the subsystem that
// the demo needs to be ran.
// don't shift this function from being the first function in the segment.
// the level scripts assume this function is the first, so it cant be moved.
int run_press_start_demo_timer(s32 timer) {
gCurrDemoInput = NULL;
if (timer == 0) {
if (!gPlayer1Controller->buttonDown && !gPlayer1Controller->stickMag) {
if ((++gDemoCountdown) == PRESS_START_DEMO_TIMER) {
// start the demo. 800 frames has passed while
// player is idle on PRESS START screen.
// start the mario demo animation for the demo list.
2020-03-02 03:42:52 +00:00
load_patchable_table(&gDemo, gDemoInputListID);
2019-08-25 04:46:40 +00:00
// if the next demo sequence ID is the count limit, reset it back to
// the first sequence.
2019-10-05 19:08:05 +00:00
if (++gDemoInputListID == gDemo.animDmaTable->count) {
2019-08-25 04:46:40 +00:00
gDemoInputListID = 0;
2019-09-01 19:50:50 +00:00
}
2019-08-25 04:46:40 +00:00
2019-10-05 19:08:05 +00:00
// add 1 (+4) to the pointer to skip the demoID.
gCurrDemoInput = ((struct DemoInput *) gDemo.targetAnim) + 1;
timer = (s8)((struct DemoInput *) gDemo.targetAnim)->timer;
2019-08-25 04:46:40 +00:00
gCurrSaveFileNum = 1;
gCurrActNum = 1;
}
2019-09-01 19:50:50 +00:00
} else { // activity was detected, so reset the demo countdown.
2019-08-25 04:46:40 +00:00
gDemoCountdown = 0;
2019-09-01 19:50:50 +00:00
}
2019-08-25 04:46:40 +00:00
}
return timer;
}
// input loop for the level select menu. updates the selected stage
// count if an input was received. signals the stage to be started
// or the level select to be exited if start or the quit combo is
// pressed.
s16 level_select_input_loop(void) {
s32 stageChanged = FALSE;
// perform the ID updates per each button press.
2019-09-01 19:50:50 +00:00
if (gPlayer1Controller->buttonPressed & A_BUTTON) {
2019-08-25 04:46:40 +00:00
++gCurrLevelNum, stageChanged = TRUE;
2019-09-01 19:50:50 +00:00
}
if (gPlayer1Controller->buttonPressed & B_BUTTON) {
2019-08-25 04:46:40 +00:00
--gCurrLevelNum, stageChanged = TRUE;
2019-09-01 19:50:50 +00:00
}
if (gPlayer1Controller->buttonPressed & U_JPAD) {
2019-08-25 04:46:40 +00:00
--gCurrLevelNum, stageChanged = TRUE;
2019-09-01 19:50:50 +00:00
}
if (gPlayer1Controller->buttonPressed & D_JPAD) {
2019-08-25 04:46:40 +00:00
++gCurrLevelNum, stageChanged = TRUE;
2019-09-01 19:50:50 +00:00
}
if (gPlayer1Controller->buttonPressed & L_JPAD) {
2019-08-25 04:46:40 +00:00
gCurrLevelNum -= 10, stageChanged = TRUE;
2019-09-01 19:50:50 +00:00
}
if (gPlayer1Controller->buttonPressed & R_JPAD) {
2019-08-25 04:46:40 +00:00
gCurrLevelNum += 10, stageChanged = TRUE;
2019-09-01 19:50:50 +00:00
}
2019-08-25 04:46:40 +00:00
// if the stage was changed, play the sound for changing a stage.
2019-09-01 19:50:50 +00:00
if (stageChanged) {
2019-10-05 19:08:05 +00:00
play_sound(SOUND_GENERAL_LEVEL_SELECT_CHANGE, gDefaultSoundArgs);
2019-09-01 19:50:50 +00:00
}
2019-08-25 04:46:40 +00:00
// TODO: enum counts for the stage lists
2019-09-01 19:50:50 +00:00
if (gCurrLevelNum > LEVEL_MAX) {
2019-08-25 04:46:40 +00:00
gCurrLevelNum = LEVEL_MIN; // exceeded max. set to min.
2019-09-01 19:50:50 +00:00
}
2019-08-25 04:46:40 +00:00
2019-09-01 19:50:50 +00:00
if (gCurrLevelNum < LEVEL_MIN) {
2019-08-25 04:46:40 +00:00
gCurrLevelNum = LEVEL_MAX; // exceeded min. set to max.
2019-09-01 19:50:50 +00:00
}
2019-08-25 04:46:40 +00:00
gCurrSaveFileNum = 4; // file 4 is used for level select tests
gCurrActNum = 6;
print_text_centered(160, 80, "SELECT STAGE");
print_text_centered(160, 30, "PRESS START BUTTON");
print_text_fmt_int(40, 60, "%2d", gCurrLevelNum);
print_text(80, 60, gLevelSelect_StageNamesText[gCurrLevelNum - 1]); // print stage name
#define QUIT_LEVEL_SELECT_COMBO (Z_TRIG | START_BUTTON | L_CBUTTONS | R_CBUTTONS)
// start being pressed signals the stage to be started. that is, unless...
if (gPlayer1Controller->buttonPressed & START_BUTTON) {
// ... the level select quit combo is being pressed, which uses START. If this
// is the case, quit the menu instead.
if (gPlayer1Controller->buttonDown == QUIT_LEVEL_SELECT_COMBO) {
gDebugLevelSelect = 0;
return -1;
}
2019-10-05 19:08:05 +00:00
play_sound(SOUND_MENU_STAR_SOUND, gDefaultSoundArgs);
2019-08-25 04:46:40 +00:00
return gCurrLevelNum;
}
return 0;
}
2020-04-03 18:57:26 +00:00
int intro_default(void) {
2019-08-25 04:46:40 +00:00
s32 sp1C = 0;
#ifndef VERSION_JP
if (D_U_801A7C34 == 1) {
if (gGlobalTimer < 0x81) {
play_sound(SOUND_MARIO_HELLO, gDefaultSoundArgs);
} else {
2019-10-05 19:08:05 +00:00
play_sound(SOUND_MARIO_PRESS_START_TO_PLAY, gDefaultSoundArgs);
2019-08-25 04:46:40 +00:00
}
D_U_801A7C34 = 0;
}
#endif
print_intro_text();
if (gPlayer1Controller->buttonPressed & START_BUTTON) {
#ifdef VERSION_JP
2019-10-05 19:08:05 +00:00
play_sound(SOUND_MENU_STAR_SOUND, gDefaultSoundArgs);
2019-08-25 04:46:40 +00:00
sp1C = 100 + gDebugLevelSelect;
#else
2019-10-05 19:08:05 +00:00
play_sound(SOUND_MENU_STAR_SOUND, gDefaultSoundArgs);
2019-08-25 04:46:40 +00:00
sp1C = 100 + gDebugLevelSelect;
D_U_801A7C34 = 1;
#endif
}
return run_press_start_demo_timer(sp1C);
}
2020-04-03 18:57:26 +00:00
int intro_game_over(void) {
2019-08-25 04:46:40 +00:00
s32 sp1C = 0;
#ifndef VERSION_JP
if (gameOverNotPlayed == 1) {
2019-10-05 19:08:05 +00:00
play_sound(SOUND_MARIO_GAME_OVER, gDefaultSoundArgs);
2019-08-25 04:46:40 +00:00
gameOverNotPlayed = 0;
}
#endif
print_intro_text();
if (gPlayer1Controller->buttonPressed & START_BUTTON) {
2019-10-05 19:08:05 +00:00
play_sound(SOUND_MENU_STAR_SOUND, gDefaultSoundArgs);
2019-08-25 04:46:40 +00:00
sp1C = 100 + gDebugLevelSelect;
#ifndef VERSION_JP
gameOverNotPlayed = 1;
#endif
}
return run_press_start_demo_timer(sp1C);
}
2020-04-03 18:57:26 +00:00
int intro_play_its_a_me_mario(void) {
2019-08-25 04:46:40 +00:00
set_background_music(0, SEQ_SOUND_PLAYER, 0);
2019-10-05 19:08:05 +00:00
play_sound(SOUND_MENU_COIN_ITS_A_ME_MARIO, gDefaultSoundArgs);
2019-08-25 04:46:40 +00:00
return 1;
}
2020-04-03 18:57:26 +00:00
s32 lvl_intro_update(s16 arg1, UNUSED s32 arg2) {
2019-08-25 04:46:40 +00:00
s32 retVar;
switch (arg1) {
case 0:
2020-04-03 18:57:26 +00:00
retVar = intro_play_its_a_me_mario();
2019-08-25 04:46:40 +00:00
break;
case 1:
2020-04-03 18:57:26 +00:00
retVar = intro_default();
2019-08-25 04:46:40 +00:00
break;
case 2:
2020-04-03 18:57:26 +00:00
retVar = intro_game_over();
2019-08-25 04:46:40 +00:00
break;
case 3:
retVar = level_select_input_loop();
break; // useless break needed to match
}
return retVar;
}