sm64

A Super Mario 64 decompilation
Log | Files | Refs | README | LICENSE

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 }