sm64pc/src/menu/level_select_menu.c

230 lines
6.9 KiB
C

#include <ultra64.h>
#include "sm64.h"
#include "audio/external.h"
#include "game/game.h"
#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"
#include "game/display.h"
#include "seq_ids.h"
#include "engine/math_util.h"
#include "level_table.h"
#define PRESS_START_DEMO_TIMER 800
#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,
static char gLevelSelect_StageNamesText[64][16] = {
#include "levels/level_defines.h"
};
#undef STUB_LEVEL
#undef DEFINE_LEVEL
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.
func_80278AD4(&gDemo, gDemoInputListID);
// if the next demo sequence ID is the count limit, reset it back to
// the first sequence.
if (++gDemoInputListID == gDemo.animDmaTable->count) {
gDemoInputListID = 0;
}
// add 1 (+4) to the pointer to skip the demoID.
gCurrDemoInput = ((struct DemoInput *) gDemo.targetAnim) + 1;
timer = (s8)((struct DemoInput *) gDemo.targetAnim)->timer;
gCurrSaveFileNum = 1;
gCurrActNum = 1;
}
} else { // activity was detected, so reset the demo countdown.
gDemoCountdown = 0;
}
}
return timer;
}
extern int gDemoInputListID_2;
extern int gPressedStart;
int start_demo(int timer)
{
gCurrDemoInput = NULL;
gPressedStart = 0;
// start the mario demo animation for the demo list.
func_80278AD4(&gDemo, gDemoInputListID_2);
// if the next demo sequence ID is the count limit, reset it back to
// the first sequence.
if((++gDemoInputListID_2) == gDemo.animDmaTable->count)
gDemoInputListID_2 = 0;
gCurrDemoInput = ((struct DemoInput *) gDemo.targetAnim) + 1; // add 1 (+4) to the pointer to skip the demoID.
timer = (s8)((struct DemoInput *) gDemo.targetAnim)->timer; // TODO: see if making timer s8 matches
gCurrSaveFileNum = 1;
gCurrActNum = 6;
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.
if (gPlayer1Controller->buttonPressed & A_BUTTON) {
++gCurrLevelNum, stageChanged = TRUE;
}
if (gPlayer1Controller->buttonPressed & B_BUTTON) {
--gCurrLevelNum, stageChanged = TRUE;
}
if (gPlayer1Controller->buttonPressed & U_JPAD) {
--gCurrLevelNum, stageChanged = TRUE;
}
if (gPlayer1Controller->buttonPressed & D_JPAD) {
++gCurrLevelNum, stageChanged = TRUE;
}
if (gPlayer1Controller->buttonPressed & L_JPAD) {
gCurrLevelNum -= 10, stageChanged = TRUE;
}
if (gPlayer1Controller->buttonPressed & R_JPAD) {
gCurrLevelNum += 10, stageChanged = TRUE;
}
// if the stage was changed, play the sound for changing a stage.
if (stageChanged) {
play_sound(SOUND_GENERAL_LEVEL_SELECT_CHANGE, gDefaultSoundArgs);
}
// TODO: enum counts for the stage lists
if (gCurrLevelNum > LEVEL_MAX) {
gCurrLevelNum = LEVEL_MIN; // exceeded max. set to min.
}
if (gCurrLevelNum < LEVEL_MIN) {
gCurrLevelNum = LEVEL_MAX; // exceeded min. set to max.
}
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;
}
play_sound(SOUND_MENU_STAR_SOUND, gDefaultSoundArgs);
return gCurrLevelNum;
}
return 0;
}
int func_8016F3CC(void) {
s32 sp1C = 0;
#ifndef VERSION_JP
if (D_U_801A7C34 == 1) {
play_sound(SOUND_MARIO_HELLO, gDefaultSoundArgs);
D_U_801A7C34 = 0;
}
#endif
print_intro_text();
if (gPlayer1Controller->buttonPressed & START_BUTTON) {
play_sound(SOUND_MENU_STAR_SOUND, gDefaultSoundArgs);
sp1C = 100 + gDebugLevelSelect;
#ifndef VERSION_JP
D_U_801A7C34 = 1;
#endif
}
return run_press_start_demo_timer(sp1C);
}
int func_8016F444(void) {
s32 sp1C = 0;
#ifndef VERSION_JP
if (gameOverNotPlayed == 1) {
play_sound(SOUND_MARIO_GAME_OVER, gDefaultSoundArgs);
gameOverNotPlayed = 0;
}
#endif
print_intro_text();
if (gPlayer1Controller->buttonPressed & START_BUTTON) {
play_sound(SOUND_MENU_STAR_SOUND, gDefaultSoundArgs);
sp1C = 100 + gDebugLevelSelect;
#ifndef VERSION_JP
gameOverNotPlayed = 1;
#endif
}
return run_press_start_demo_timer(sp1C);
}
int func_8016F4BC(void) {
set_background_music(0, SEQ_SOUND_PLAYER, 0);
play_sound(SOUND_MENU_COIN_ITS_A_ME_MARIO, gDefaultSoundArgs);
return 1;
}
s32 LevelProc_8016F508(s16 arg1, UNUSED s32 arg2) {
s32 retVar;
switch (arg1) {
case 0:
retVar = func_8016F4BC();
break;
case 1:
retVar = func_8016F3CC();
break;
case 2:
retVar = func_8016F444();
break;
case 3:
retVar = level_select_input_loop();
break; // useless break needed to match
}
return retVar;
}