Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/src/menu/star_select.c
7857 views
1
#include <PR/ultratypes.h>
2
3
#include "audio/external.h"
4
#include "behavior_data.h"
5
#include "engine/behavior_script.h"
6
#include "engine/graph_node.h"
7
#include "eu_translation.h"
8
#include "game/area.h"
9
#include "game/game_init.h"
10
#include "game/ingame_menu.h"
11
#include "game/level_update.h"
12
#include "game/memory.h"
13
#include "game/object_helpers.h"
14
#include "game/object_list_processor.h"
15
#include "game/save_file.h"
16
#include "game/segment2.h"
17
#include "game/segment7.h"
18
#include "game/rumble_init.h"
19
#include "sm64.h"
20
#include "star_select.h"
21
#include "text_strings.h"
22
#include "prevent_bss_reordering.h"
23
24
#include "game/settings.h"
25
26
/**
27
* @file star_select.c
28
* This file implements how the star select screen (act selector) function.
29
* That includes handles what stars can be selected, star selector types,
30
* strings, act values, and star selector model rendering if a star is collected or not.
31
*/
32
33
// Star Selector count models printed in the act selector menu.
34
static struct Object *sStarSelectorModels[8];
35
36
// The act the course is loaded as, affects whether some objects spawn.
37
static s8 sLoadedActNum;
38
39
// Number of obtained stars, excluding the coin star.
40
static u8 sObtainedStars;
41
42
// Total number of stars that appear in the act selector menu.
43
static s8 sVisibleStars;
44
45
// Act selected when the act menu is first opened.
46
static u8 sInitSelectedActNum;
47
48
// Index value of the act selected in the act menu.
49
static s8 sSelectedActIndex = 0;
50
51
// Index value of the star that is selectable in the act menu.
52
// Excluding the next star, it doesn't count other transparent stars.
53
static s8 sSelectableStarIndex = 0;
54
55
// Act Selector menu timer that keeps counting until you choose an act.
56
static s32 sActSelectorMenuTimer = 0;
57
58
/**
59
* Act Selector Star Type Loop Action
60
* Defines a select type for a star in the act selector.
61
*/
62
void bhv_act_selector_star_type_loop(void) {
63
switch (gCurrentObject->oStarSelectorType) {
64
// If a star is not selected, don't rotate or change size
65
case STAR_SELECTOR_NOT_SELECTED:
66
gCurrentObject->oStarSelectorSize -= 0.1;
67
if (gCurrentObject->oStarSelectorSize < 1.0) {
68
gCurrentObject->oStarSelectorSize = 1.0;
69
}
70
gCurrentObject->oFaceAngleYaw = 0;
71
break;
72
// If a star is selected, rotate and slightly increase size
73
case STAR_SELECTOR_SELECTED:
74
gCurrentObject->oStarSelectorSize += 0.1;
75
if (gCurrentObject->oStarSelectorSize > 1.3) {
76
gCurrentObject->oStarSelectorSize = 1.3;
77
}
78
gCurrentObject->oFaceAngleYaw += 0x800;
79
break;
80
// If the 100 coin star is selected, rotate
81
case STAR_SELECTOR_100_COINS:
82
gCurrentObject->oFaceAngleYaw += 0x800;
83
break;
84
}
85
// Scale act selector stars depending of the type selected
86
cur_obj_scale(gCurrentObject->oStarSelectorSize);
87
// Unused timer, only referenced here. Probably replaced by sActSelectorMenuTimer
88
gCurrentObject->oStarSelectorTimer++;
89
}
90
91
/**
92
* Renders the 100 coin star with an special star selector type.
93
*/
94
void render_100_coin_star(u8 stars) {
95
if (stars & (1 << 6)) {
96
// If the 100 coin star has been collected, create a new star selector next to the coin score.
97
sStarSelectorModels[6] = spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_STAR,
98
bhvActSelectorStarType, 370, 24, -300, 0, 0, 0);
99
sStarSelectorModels[6]->oStarSelectorSize = 0.8;
100
sStarSelectorModels[6]->oStarSelectorType = STAR_SELECTOR_100_COINS;
101
}
102
else if (configShow100CoinStar) {
103
sStarSelectorModels[6] = spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_TRANSPARENT_STAR,
104
bhvActSelectorStarType, 370, 24, -300, 0, 0, 0);
105
sStarSelectorModels[6]->oStarSelectorSize = 0.8;
106
sStarSelectorModels[6]->oStarSelectorType = STAR_SELECTOR_100_COINS;
107
108
}
109
}
110
111
/**
112
* Act Selector Init Action
113
* Checks how many stars has been obtained in a course, to render
114
* the correct star models, the 100 coin star and also handles
115
* checks of what star should be next in sInitSelectedActNum.
116
*/
117
void bhv_act_selector_init(void) {
118
s16 i = 0;
119
s32 selectorModelIDs[10];
120
u8 stars = save_file_get_star_flags(gCurrSaveFileNum - 1, gCurrCourseNum - 1);
121
122
sVisibleStars = 0;
123
while (i != sObtainedStars) {
124
if (stars & (1 << sVisibleStars)) { // Star has been collected
125
selectorModelIDs[sVisibleStars] = MODEL_STAR;
126
i++;
127
} else { // Star has not been collected
128
selectorModelIDs[sVisibleStars] = MODEL_TRANSPARENT_STAR;
129
// If this is the first star that has not been collected, set
130
// the default selection to this star.
131
if (sInitSelectedActNum == 0) {
132
sInitSelectedActNum = sVisibleStars + 1;
133
sSelectableStarIndex = sVisibleStars;
134
}
135
}
136
sVisibleStars++;
137
}
138
139
// If the stars have been collected in order so far, show the next star.
140
if (sVisibleStars == sObtainedStars && sVisibleStars != 6) {
141
selectorModelIDs[sVisibleStars] = MODEL_TRANSPARENT_STAR;
142
sInitSelectedActNum = sVisibleStars + 1;
143
sSelectableStarIndex = sVisibleStars;
144
sVisibleStars++;
145
}
146
147
// If all stars have been collected, set the default selection to the last star.
148
if (sObtainedStars == 6) {
149
sInitSelectedActNum = sVisibleStars;
150
}
151
152
//! Useless, since sInitSelectedActNum has already been set in this
153
//! scenario by the code that shows the next uncollected star.
154
if (sObtainedStars == 0) {
155
sInitSelectedActNum = 1;
156
}
157
158
// Render star selector objects
159
for (i = 0; i < sVisibleStars; i++) {
160
sStarSelectorModels[i] =
161
spawn_object_abs_with_rot(gCurrentObject, 0, selectorModelIDs[i], bhvActSelectorStarType,
162
75 + sVisibleStars * -75 + i * 152, 248, -300, 0, 0, 0);
163
sStarSelectorModels[i]->oStarSelectorSize = 1.0f;
164
}
165
166
render_100_coin_star(stars);
167
}
168
169
/**
170
* Act Selector Loop Action
171
* Handles star selector scrolling depending of what stars are
172
* selectable, whenever all 6 stars are obtained or not.
173
* Also handles 2 star selector types whenever the star is selected
174
* or not, the types are defined in bhv_act_selector_star_type_loop.
175
*/
176
void bhv_act_selector_loop(void) {
177
s8 i;
178
u8 starIndexCounter;
179
u8 stars = save_file_get_star_flags(gCurrSaveFileNum - 1, gCurrCourseNum - 1);
180
181
if (sObtainedStars != 6) {
182
// Sometimes, stars are not selectable even if they appear on the screen.
183
// This code filters selectable and non-selectable stars.
184
sSelectedActIndex = 0;
185
handle_menu_scrolling(MENU_SCROLL_HORIZONTAL, &sSelectableStarIndex, 0, sObtainedStars);
186
starIndexCounter = sSelectableStarIndex;
187
for (i = 0; i < sVisibleStars; i++) {
188
// Can the star be selected (is it either already completed or the first non-completed mission)
189
if ((stars & (1 << i)) || i + 1 == sInitSelectedActNum) {
190
if (starIndexCounter == 0) { // We have reached the sSelectableStarIndex-th selectable star.
191
sSelectedActIndex = i;
192
break;
193
}
194
starIndexCounter--;
195
}
196
}
197
} else {
198
// If all stars are collected then they are all selectable.
199
handle_menu_scrolling(MENU_SCROLL_HORIZONTAL, &sSelectableStarIndex, 0, sVisibleStars - 1);
200
sSelectedActIndex = sSelectableStarIndex;
201
}
202
203
// Star selector type handler
204
for (i = 0; i < sVisibleStars; i++) {
205
if (sSelectedActIndex == i) {
206
sStarSelectorModels[i]->oStarSelectorType = STAR_SELECTOR_SELECTED;
207
} else {
208
sStarSelectorModels[i]->oStarSelectorType = STAR_SELECTOR_NOT_SELECTED;
209
}
210
}
211
}
212
213
/**
214
* Print the course number selected with the wood rgba16 course texture.
215
*/
216
#ifdef VERSION_EU
217
void print_course_number(s16 language) {
218
#else
219
void print_course_number(void) {
220
#endif
221
u8 courseNum[4];
222
223
create_dl_translation_matrix(MENU_MTX_PUSH, 158.0f, 81.0f, 0.0f);
224
225
// Full wood texture in JP & US, lower part of it on EU
226
gSPDisplayList(gDisplayListHead++, dl_menu_rgba16_wood_course);
227
228
#ifdef VERSION_EU
229
// Change upper part of the wood texture depending of the language defined
230
switch (language) {
231
case LANGUAGE_ENGLISH:
232
gSPDisplayList(gDisplayListHead++, dl_menu_texture_course_upper);
233
break;
234
case LANGUAGE_FRENCH:
235
gSPDisplayList(gDisplayListHead++, dl_menu_texture_niveau_upper);
236
break;
237
case LANGUAGE_GERMAN:
238
gSPDisplayList(gDisplayListHead++, dl_menu_texture_kurs_upper);
239
break;
240
}
241
242
gSPDisplayList(gDisplayListHead++, dl_menu_rgba16_wood_course_end);
243
#endif
244
245
gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
246
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin);
247
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
248
249
int_to_str(gCurrCourseNum, courseNum);
250
251
if (gCurrCourseNum < 10) { // 1 digit number
252
print_hud_lut_string(HUD_LUT_GLOBAL, 152, 158, courseNum);
253
} else { // 2 digit number
254
print_hud_lut_string(HUD_LUT_GLOBAL, 143, 158, courseNum);
255
}
256
257
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end);
258
}
259
260
#ifdef VERSION_JP
261
#define ACT_NAME_X 158
262
#else
263
#define ACT_NAME_X 163
264
#endif
265
266
/**
267
* Print act selector strings, some with special checks.
268
*/
269
void print_act_selector_strings(void) {
270
#ifdef VERSION_EU
271
unsigned char myScore[][10] = { {TEXT_MYSCORE}, {TEXT_MY_SCORE_FR}, {TEXT_MY_SCORE_DE} };
272
#else
273
unsigned char myScore[] = { TEXT_MYSCORE };
274
#endif
275
unsigned char starNumbers[] = { TEXT_ZERO };
276
277
#ifdef VERSION_EU
278
u8 **levelNameTbl;
279
u8 *currLevelName;
280
u8 **actNameTbl;
281
#else
282
u8 **levelNameTbl = segmented_to_virtual(seg2_course_name_table);
283
u8 *currLevelName = segmented_to_virtual(levelNameTbl[gCurrCourseNum - 1]);
284
u8 **actNameTbl = segmented_to_virtual(seg2_act_name_table);
285
#endif
286
u8 *selectedActName;
287
#ifndef VERSION_EU
288
s16 lvlNameX;
289
s16 actNameX;
290
#endif
291
s8 i;
292
#ifdef VERSION_EU
293
s16 language = eu_get_language();
294
#endif
295
296
create_dl_ortho_matrix();
297
298
#ifdef VERSION_EU
299
switch (language) {
300
case LANGUAGE_ENGLISH:
301
actNameTbl = segmented_to_virtual(act_name_table_eu_en);
302
levelNameTbl = segmented_to_virtual(course_name_table_eu_en);
303
break;
304
case LANGUAGE_FRENCH:
305
actNameTbl = segmented_to_virtual(act_name_table_eu_fr);
306
levelNameTbl = segmented_to_virtual(course_name_table_eu_fr);
307
break;
308
case LANGUAGE_GERMAN:
309
actNameTbl = segmented_to_virtual(act_name_table_eu_de);
310
levelNameTbl = segmented_to_virtual(course_name_table_eu_de);
311
break;
312
}
313
currLevelName = segmented_to_virtual(levelNameTbl[gCurrCourseNum - 1]);
314
#endif
315
316
// Print the coin highscore.
317
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin);
318
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
319
print_hud_my_score_coins(1, gCurrSaveFileNum - 1, gCurrCourseNum - 1, 155, 106);
320
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end);
321
322
gSPDisplayList(gDisplayListHead++, dl_ia_text_begin);
323
gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255);
324
// Print the "MY SCORE" text if the coin score is more than 0
325
if (save_file_get_course_coin_score(gCurrSaveFileNum - 1, gCurrCourseNum - 1) != 0) {
326
#ifdef VERSION_EU
327
print_generic_string(95, 118, myScore[language]);
328
#else
329
print_generic_string(102, 118, myScore);
330
#endif
331
}
332
333
#ifdef VERSION_EU
334
print_generic_string(get_str_x_pos_from_center(160, currLevelName + 3, 10.0f), 33, currLevelName + 3);
335
#else
336
lvlNameX = get_str_x_pos_from_center(160, currLevelName + 3, 10.0f);
337
print_generic_string(lvlNameX, 33, currLevelName + 3);
338
#endif
339
340
gSPDisplayList(gDisplayListHead++, dl_ia_text_end);
341
342
#ifdef VERSION_EU
343
print_course_number(language);
344
#else
345
print_course_number();
346
#endif
347
348
gSPDisplayList(gDisplayListHead++, dl_menu_ia8_text_begin);
349
gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255);
350
// Print the name of the selected act.
351
if (sVisibleStars != 0) {
352
selectedActName = segmented_to_virtual(actNameTbl[(gCurrCourseNum - 1) * 6 + sSelectedActIndex]);
353
354
#ifdef VERSION_EU
355
print_menu_generic_string(get_str_x_pos_from_center(ACT_NAME_X, selectedActName, 8.0f), 81, selectedActName);
356
#else
357
actNameX = get_str_x_pos_from_center(ACT_NAME_X, selectedActName, 8.0f);
358
print_menu_generic_string(actNameX, 81, selectedActName);
359
#endif
360
}
361
362
// Print the numbers above each star.
363
for (i = 1; i <= sVisibleStars; i++) {
364
starNumbers[0] = i;
365
#ifdef VERSION_EU
366
print_menu_generic_string(143 - sVisibleStars * 15 + i * 30, 38, starNumbers);
367
#else
368
print_menu_generic_string(139 - sVisibleStars * 17 + i * 34, 38, starNumbers);
369
#endif
370
}
371
372
gSPDisplayList(gDisplayListHead++, dl_menu_ia8_text_end);
373
}
374
375
/**
376
* Geo function that Print act selector strings.
377
*!@bug: This geo function is missing the third param. Harmless in practice due to o32 convention.
378
*/
379
#ifdef AVOID_UB
380
Gfx *geo_act_selector_strings(s16 callContext, UNUSED struct GraphNode *node, UNUSED void *context) {
381
#else
382
Gfx *geo_act_selector_strings(s16 callContext, UNUSED struct GraphNode *node) {
383
#endif
384
if (callContext == GEO_CONTEXT_RENDER) {
385
print_act_selector_strings();
386
}
387
return NULL;
388
}
389
390
/**
391
* Initiates act selector values before entering a main course.
392
* Also load how much stars a course has, without counting the 100 coin star.
393
*/
394
s32 lvl_init_act_selector_values_and_stars(UNUSED s32 arg, UNUSED s32 unused) {
395
u8 stars = save_file_get_star_flags(gCurrSaveFileNum - 1, gCurrCourseNum - 1);
396
397
gCanMirror = 0;
398
399
sLoadedActNum = 0;
400
sInitSelectedActNum = 0;
401
sVisibleStars = 0;
402
sActSelectorMenuTimer = 0;
403
#ifdef NO_SEGMENTED_MEMORY
404
sSelectedActIndex = 0;
405
sSelectableStarIndex = 0;
406
#endif
407
sObtainedStars = save_file_get_course_star_count(gCurrSaveFileNum - 1, gCurrCourseNum - 1);
408
409
// Don't count 100 coin star
410
if (stars & (1 << 6)) {
411
sObtainedStars--;
412
}
413
414
//! no return value
415
#ifdef AVOID_UB
416
return 0;
417
#endif
418
}
419
420
/**
421
* Loads act selector button actions with selected act value checks.
422
* Also updates objects and returns act number selected after is chosen.
423
*/
424
s32 lvl_update_obj_and_load_act_button_actions(UNUSED s32 arg, UNUSED s32 unused) {
425
if (sActSelectorMenuTimer >= 11) {
426
// If any of these buttons are pressed, play sound and go to course act
427
#ifndef VERSION_EU
428
if ((gPlayer3Controller->buttonPressed & A_BUTTON)
429
|| (gPlayer3Controller->buttonPressed & START_BUTTON)
430
|| (gPlayer3Controller->buttonPressed & B_BUTTON)) {
431
#else
432
if ((gPlayer3Controller->buttonPressed & (A_BUTTON | START_BUTTON | B_BUTTON | Z_TRIG))) {
433
#endif
434
#if defined(VERSION_JP)
435
play_sound(SOUND_MENU_STAR_SOUND, gGlobalSoundSource);
436
#else
437
play_sound(SOUND_MENU_STAR_SOUND_LETS_A_GO, gGlobalSoundSource);
438
#endif
439
#if ENABLE_RUMBLE
440
queue_rumble_data(60, 70);
441
func_sh_8024C89C(1);
442
#endif
443
if (sInitSelectedActNum >= sSelectedActIndex + 1) {
444
sLoadedActNum = sSelectedActIndex + 1;
445
} else {
446
sLoadedActNum = sInitSelectedActNum;
447
}
448
gDialogCourseActNum = sSelectedActIndex + 1;
449
}
450
}
451
452
area_update_objects();
453
sActSelectorMenuTimer++;
454
return sLoadedActNum;
455
}
456
457