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