Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/src/menu/title_screen.c
7857 views
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(0);
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(0);
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
}
250