287 lines
10 KiB
C
287 lines
10 KiB
C
#include <ultra64.h>
|
|
|
|
#include "sm64.h"
|
|
#include "game.h"
|
|
#include "memory.h"
|
|
#include "area.h"
|
|
#include "save_file.h"
|
|
#include "object_helpers.h"
|
|
#include "engine/behavior_script.h"
|
|
#include "ingame_menu.h"
|
|
#include "text_strings.h"
|
|
#include "audio/external.h"
|
|
#include "level_update.h"
|
|
#include "segment2.h"
|
|
#include "segment7.h"
|
|
#include "star_select.h"
|
|
#include "model_ids.h"
|
|
#include "object_list_processor.h"
|
|
|
|
static struct Object *sStarSelectIcons[8];
|
|
static s8 sCurrentMission; // The mission the course is loaded as, affects whether some objects spawn.
|
|
|
|
static u8 sObtainedStars; // Number of obtained stars, excluding the coin star.
|
|
static s8 sVisibleStars; // Total number of stars that appear in the act selector menu
|
|
static u8 sDefaultSelectedAct; // Act selected when the menu is first opened.
|
|
|
|
extern u8 bhvStarInActSelector[];
|
|
|
|
static s8 sSelectedAct = 0;
|
|
static s8 sSelectedStarIndex = 0;
|
|
static s32 sActSelectorMenuTimer = 0; // Delays the time until you can select an act.
|
|
|
|
void BehStarActSelectorLoop(void) {
|
|
switch (gCurrentObject->oStarSelectorType) {
|
|
case STAR_SELECTOR_NOT_SELECTED:
|
|
gCurrentObject->oStarSelectorSize -= 0.1;
|
|
if (gCurrentObject->oStarSelectorSize < 1.0) {
|
|
gCurrentObject->oStarSelectorSize = 1.0;
|
|
}
|
|
gCurrentObject->oFaceAngleYaw = 0;
|
|
break;
|
|
case STAR_SELECTOR_SELECTED:
|
|
gCurrentObject->oStarSelectorSize += 0.1;
|
|
if (gCurrentObject->oStarSelectorSize > 1.3) {
|
|
gCurrentObject->oStarSelectorSize = 1.3;
|
|
}
|
|
gCurrentObject->oFaceAngleYaw += 0x800;
|
|
break;
|
|
case STAR_SELECTOR_100_COINS:
|
|
gCurrentObject->oFaceAngleYaw += 0x800;
|
|
break;
|
|
}
|
|
|
|
obj_scale(gCurrentObject->oStarSelectorSize);
|
|
gCurrentObject->oStarSelectorTimer++; // unused timer field? only ever referenced here
|
|
}
|
|
|
|
void Show100CoinStar(u8 stars) {
|
|
if (stars & (1 << 6)) {
|
|
// If the 100 coin star has been collected, create a new star selector next to the coin score.
|
|
sStarSelectIcons[6] = spawn_object_abs_with_rot(gCurrentObject, 0, MODEL_STAR,
|
|
bhvStarInActSelector, 370, 24, -300, 0, 0, 0);
|
|
sStarSelectIcons[6]->oStarSelectorSize = 0.8;
|
|
sStarSelectIcons[6]->oStarSelectorType = STAR_SELECTOR_100_COINS;
|
|
}
|
|
}
|
|
|
|
void BehActSelectorInit(void) {
|
|
s16 i = 0;
|
|
s32 selectorModelIDs[10];
|
|
u8 stars = save_file_get_star_flags(gCurrSaveFileNum - 1, gCurrCourseNum - 1);
|
|
|
|
sVisibleStars = 0;
|
|
while (i != sObtainedStars) {
|
|
if (stars & (1 << sVisibleStars)) { // Star has been collected
|
|
selectorModelIDs[sVisibleStars] = MODEL_STAR;
|
|
i++;
|
|
} else { // Star has not been collected
|
|
selectorModelIDs[sVisibleStars] = MODEL_TRANSPARENT_STAR;
|
|
// If this is the first star that has not been collected, set
|
|
// the default selection to this star.
|
|
if (sDefaultSelectedAct == 0) {
|
|
sDefaultSelectedAct = sVisibleStars + 1;
|
|
sSelectedStarIndex = sVisibleStars;
|
|
}
|
|
}
|
|
sVisibleStars++;
|
|
}
|
|
|
|
// If the stars have been collected in order so far, show the next star.
|
|
if (sVisibleStars == sObtainedStars && sVisibleStars != 6) {
|
|
selectorModelIDs[sVisibleStars] = MODEL_TRANSPARENT_STAR;
|
|
sDefaultSelectedAct = sVisibleStars + 1;
|
|
sSelectedStarIndex = sVisibleStars;
|
|
sVisibleStars++;
|
|
}
|
|
|
|
// If all stars have been collected, set the default selection to the last star.
|
|
if (sObtainedStars == 6) {
|
|
sDefaultSelectedAct = sVisibleStars;
|
|
}
|
|
|
|
//! Useless, since sDefaultSelectedAct has already been set in this
|
|
//! scenario by the code that shows the next uncollected star.
|
|
if (sObtainedStars == 0) {
|
|
sDefaultSelectedAct = 1;
|
|
}
|
|
|
|
for (i = 0; i < sVisibleStars; i++) { // Spawn star selector objects
|
|
sStarSelectIcons[i] =
|
|
spawn_object_abs_with_rot(gCurrentObject, 0, selectorModelIDs[i], bhvStarInActSelector,
|
|
75 + sVisibleStars * -75 + i * 152, 248, -300, 0, 0, 0);
|
|
sStarSelectIcons[i]->oStarSelectorSize = 1.0f;
|
|
}
|
|
|
|
Show100CoinStar(stars);
|
|
}
|
|
|
|
void BehActSelectorLoop(void) {
|
|
s8 i;
|
|
u8 starIndexCounter;
|
|
u8 stars = save_file_get_star_flags(gCurrSaveFileNum - 1, gCurrCourseNum - 1);
|
|
|
|
if (sObtainedStars != 6) {
|
|
// Sometimes, stars are not selectable even if they appear on the screen.
|
|
// This code filters selectable and non-selectable stars.
|
|
sSelectedAct = 0;
|
|
handleMenuScrolling(MENU_SCROLL_HORIZONTAL, &sSelectedStarIndex, 0, sObtainedStars);
|
|
starIndexCounter = sSelectedStarIndex;
|
|
for (i = 0; i < sVisibleStars; i++) {
|
|
// Can the star be selected (is it either already completed or the first non-completed mission)
|
|
if ((stars & (1 << i)) || i + 1 == sDefaultSelectedAct) {
|
|
if (starIndexCounter == 0) { // We have reached the sSelectedStarIndex-th selectable star.
|
|
sSelectedAct = i;
|
|
break;
|
|
}
|
|
starIndexCounter--;
|
|
}
|
|
}
|
|
} else {
|
|
// If all stars are collected then they are all selectable.
|
|
handleMenuScrolling(MENU_SCROLL_HORIZONTAL, &sSelectedStarIndex, 0, sVisibleStars - 1);
|
|
sSelectedAct = sSelectedStarIndex;
|
|
}
|
|
|
|
for (i = 0; i < sVisibleStars; i++) {
|
|
if (sSelectedAct == i) {
|
|
sStarSelectIcons[i]->oStarSelectorType = STAR_SELECTOR_SELECTED;
|
|
} else {
|
|
sStarSelectIcons[i]->oStarSelectorType = STAR_SELECTOR_NOT_SELECTED;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ShowCourseNumber(void) {
|
|
u8 courseNum[4];
|
|
|
|
dl_add_new_translation_matrix(1, 158.0f, 81.0f, 0.0f);
|
|
|
|
gSPDisplayList(gDisplayListHead++, main_menu_seg7_dl_0700F228);
|
|
gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW);
|
|
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin);
|
|
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
|
|
|
|
Int2Str(gCurrCourseNum, courseNum);
|
|
if (gCurrCourseNum < 10) {
|
|
PutString(2, 152, 158, courseNum);
|
|
} else {
|
|
PutString(2, 143, 158, courseNum);
|
|
}
|
|
|
|
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end);
|
|
}
|
|
|
|
static void ShowActSelectorMenu(void) {
|
|
// TODO: EU relocates level and act name tables to translation segment 0x19
|
|
#ifndef VERSION_EU
|
|
#ifdef VERSION_JP
|
|
unsigned char myScore[] = { TEXT_MY_SCORE };
|
|
#else
|
|
unsigned char myScore[] = { TEXT_MYSCORE };
|
|
#endif
|
|
unsigned char starNumbers[] = { TEXT_0 };
|
|
u32 *levelNameTbl = (u32 *) segmented_to_virtual(seg2_level_name_table);
|
|
u8 *currLevelName = (u8 *) segmented_to_virtual((void *) levelNameTbl[gCurrCourseNum - 1]);
|
|
u32 *actNameTbl = (u32 *) segmented_to_virtual(seg2_act_name_table);
|
|
u8 *selectedActName;
|
|
s16 x;
|
|
s16 x2;
|
|
s8 i;
|
|
|
|
dl_add_new_ortho_matrix();
|
|
|
|
// Display the coin highscore.
|
|
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin);
|
|
gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255);
|
|
|
|
ShowCoins(1, gCurrSaveFileNum - 1, gCurrCourseNum - 1, 155, 106);
|
|
|
|
gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end);
|
|
gSPDisplayList(gDisplayListHead++, dl_ia8_text_begin);
|
|
gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255);
|
|
|
|
// Display the "MY SCORE" text
|
|
if (save_file_get_course_coin_score(gCurrSaveFileNum - 1, gCurrCourseNum - 1) != 0) {
|
|
PrintGenericText(102, 118, myScore);
|
|
}
|
|
|
|
// Display the level name; add 3 to skip the number and spacing to get to the actual string to center.
|
|
x = get_str_x_pos_from_center(160, currLevelName + 3, 10.0f);
|
|
PrintGenericText(x, 33, currLevelName + 3);
|
|
|
|
// Display the course number.
|
|
gSPDisplayList(gDisplayListHead++, dl_ia8_text_end);
|
|
|
|
ShowCourseNumber();
|
|
|
|
gSPDisplayList(gDisplayListHead++, main_menu_seg7_dl_0700D108);
|
|
gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255);
|
|
|
|
// Display the name of the selected act.
|
|
if (sVisibleStars != 0) {
|
|
selectedActName = (u8 *) segmented_to_virtual((void *) (actNameTbl[(gCurrCourseNum - 1) * 6 + sSelectedAct]));
|
|
#ifdef VERSION_JP
|
|
x2 = get_str_x_pos_from_center(158, selectedActName, 8.0f);
|
|
#else
|
|
x2 = get_str_x_pos_from_center(163, selectedActName, 8.0f);
|
|
#endif
|
|
PrintRegularText(x2, 81, selectedActName);
|
|
}
|
|
|
|
// Display the numbers above each star.
|
|
for (i = 1; i <= sVisibleStars; i++) {
|
|
starNumbers[0] = i;
|
|
PrintRegularText(i * 34 - sVisibleStars * 17 + 0x8B, 38, starNumbers);
|
|
}
|
|
|
|
gSPDisplayList(gDisplayListHead++, main_menu_seg7_dl_0700D160);
|
|
#endif // !VERSION_EU
|
|
}
|
|
|
|
//! @bug Another geo function missing the third param. Harmless in practice due to o32 convention.
|
|
Gfx *Geo18_80177518(s16 run, UNUSED struct GraphNode *node) {
|
|
if (run == TRUE) {
|
|
ShowActSelectorMenu();
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void LevelProc_80177560(UNUSED s32 a, UNUSED s32 b) {
|
|
u8 stars = save_file_get_star_flags(gCurrSaveFileNum - 1, gCurrCourseNum - 1);
|
|
|
|
sCurrentMission = 0;
|
|
sDefaultSelectedAct = 0;
|
|
sVisibleStars = 0;
|
|
sActSelectorMenuTimer = 0;
|
|
sObtainedStars = save_file_get_course_star_count(gCurrSaveFileNum - 1, gCurrCourseNum - 1);
|
|
|
|
// Don't count 100 coin star
|
|
if (stars & (1 << 6)) {
|
|
sObtainedStars--;
|
|
}
|
|
}
|
|
|
|
int LevelProc_80177610(UNUSED s32 a, UNUSED s32 b) {
|
|
if (sActSelectorMenuTimer >= 11) {
|
|
if ((gPlayer3Controller->buttonPressed & A_BUTTON)
|
|
|| (gPlayer3Controller->buttonPressed & START_BUTTON)
|
|
|| (gPlayer3Controller->buttonPressed & B_BUTTON)) {
|
|
#ifdef VERSION_JP
|
|
play_sound(SOUND_MENU_STARSOUND, gDefaultSoundArgs);
|
|
#else
|
|
play_sound(SOUND_MENU_STARSOUNDLETSAGO, gDefaultSoundArgs);
|
|
#endif
|
|
if (sDefaultSelectedAct > sSelectedAct) {
|
|
sCurrentMission = sSelectedAct + 1;
|
|
} else {
|
|
sCurrentMission = sDefaultSelectedAct;
|
|
}
|
|
D_80330534 = sSelectedAct + 1;
|
|
}
|
|
}
|
|
area_update_objects();
|
|
sActSelectorMenuTimer++;
|
|
return sCurrentMission;
|
|
}
|