ingame_menu.c (116174B)
1 #include <ultra64.h> 2 3 #include "actors/common1.h" 4 #include "area.h" 5 #include "audio/external.h" 6 #include "camera.h" 7 #include "course_table.h" 8 #include "dialog_ids.h" 9 #include "engine/math_util.h" 10 #include "eu_translation.h" 11 #include "game_init.h" 12 #include "gfx_dimensions.h" 13 #include "ingame_menu.h" 14 #include "level_update.h" 15 #include "levels/castle_grounds/header.h" 16 #include "memory.h" 17 #include "print.h" 18 #include "save_file.h" 19 #include "segment2.h" 20 #include "segment7.h" 21 #include "seq_ids.h" 22 #include "sm64.h" 23 #include "text_strings.h" 24 #include "types.h" 25 26 #ifdef VERSION_EU 27 #undef LANGUAGE_FUNCTION 28 #define LANGUAGE_FUNCTION gInGameLanguage 29 #endif 30 31 FORCE_BSS u16 gMenuTextColorTransTimer; 32 FORCE_BSS s8 gLastDialogLineNum; 33 FORCE_BSS s32 gDialogVariable; 34 FORCE_BSS u16 gMenuTextAlpha; 35 #ifdef VERSION_EU 36 s16 gDialogX; 37 s16 gDialogY; 38 #endif 39 FORCE_BSS s16 gCutsceneMsgXOffset; 40 FORCE_BSS s16 gCutsceneMsgYOffset; 41 s8 gRedCoinsCollected; 42 43 extern u8 gLastCompletedCourseNum; 44 extern u8 gLastCompletedStarNum; 45 46 enum MenuState { 47 MENU_STATE_0, 48 MENU_STATE_1, 49 MENU_STATE_2, 50 MENU_STATE_3, 51 MENU_STATE_DEFAULT = MENU_STATE_0, 52 53 // Dialog 54 MENU_STATE_DIALOG_OPENING = MENU_STATE_0, 55 MENU_STATE_DIALOG_OPEN = MENU_STATE_1, 56 MENU_STATE_DIALOG_SCROLLING = MENU_STATE_2, 57 MENU_STATE_DIALOG_CLOSING = MENU_STATE_3, 58 59 // Pause Screen 60 MENU_STATE_PAUSE_SCREEN_OPENING = MENU_STATE_0, 61 MENU_STATE_PAUSE_SCREEN_COURSE = MENU_STATE_1, 62 MENU_STATE_PAUSE_SCREEN_CASTLE = MENU_STATE_2, 63 64 // Course Complete Screen 65 MENU_STATE_COURSE_COMPLETE_SCREEN_OPENING = MENU_STATE_0, 66 MENU_STATE_COURSE_COMPLETE_SCREEN_OPEN = MENU_STATE_1 67 }; 68 69 enum DialogBoxPageState { 70 DIALOG_PAGE_STATE_NONE, 71 DIALOG_PAGE_STATE_SCROLL, 72 DIALOG_PAGE_STATE_END 73 }; 74 75 enum DialogBoxType { 76 DIALOG_TYPE_ROTATE, // used in NPCs and level messages 77 DIALOG_TYPE_ZOOM // used in signposts and wall signs and etc 78 }; 79 80 #define DIALOG_BOX_ANGLE_DEFAULT 90.0f 81 #define DIALOG_BOX_SCALE_DEFAULT 19.0f 82 83 #if defined(VERSION_US) || defined(VERSION_EU) || defined(VERSION_CN) 84 u8 gDialogCharWidths[256] = { // TODO: Is there a way to auto generate this? 85 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, 86 6, 6, 5, 6, 6, 5, 8, 8, 6, 6, 6, 6, 6, 5, 6, 6, 87 8, 7, 6, 6, 6, 5, 5, 6, 5, 5, 6, 5, 4, 5, 5, 3, 88 7, 5, 5, 5, 6, 5, 5, 5, 5, 5, 7, 7, 5, 5, 4, 4, 89 8, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 90 8, 8, 8, 8, 7, 7, 6, 7, 7, 0, 0, 0, 0, 0, 0, 0, 91 #ifdef VERSION_EU 92 6, 6, 6, 0, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 4, 93 5, 5, 5, 5, 6, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 94 5, 5, 5, 0, 6, 6, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 95 0, 5, 5, 0, 0, 6, 6, 0, 0, 0, 0, 0, 0, 0, 5, 6, 96 0, 4, 4, 0, 0, 5, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 97 #else 98 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 99 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 100 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 6, 102 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103 #endif 104 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107 #ifdef VERSION_EU 108 7, 5, 10, 5, 9, 8, 4, 0, 0, 0, 0, 5, 5, 6, 5, 0, 109 #else 110 7, 5, 10, 5, 9, 8, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111 #endif 112 0, 0, 5, 7, 7, 6, 6, 8, 0, 8, 10, 6, 4, 10, 0, 0 113 }; 114 #endif 115 116 s8 gMenuState = MENU_STATE_DEFAULT; 117 f32 gDialogBoxAngle = DIALOG_BOX_ANGLE_DEFAULT; 118 f32 gDialogBoxScale = DIALOG_BOX_SCALE_DEFAULT; 119 s16 gDialogScrollOffsetY = 0; 120 s8 gDialogBoxType = DIALOG_TYPE_ROTATE; 121 s16 gDialogID = DIALOG_NONE; 122 s16 gNextDialogPageStartStrIndex = 0; 123 s16 gDialogPageStartStrIndex = 0; 124 #ifdef VERSION_EU 125 s32 gInGameLanguage = LANGUAGE_ENGLISH; 126 #endif 127 s8 gMenuLineNum = 1; 128 s8 gDialogWithChoice = FALSE; 129 u8 gMenuHoldKeyIndex = 0; 130 u8 gMenuHoldKeyTimer = 0; 131 s32 gDialogResponse = DIALOG_RESPONSE_NONE; 132 133 void create_dl_identity_matrix(void) { 134 Mtx *matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); 135 136 if (matrix == NULL) { 137 return; 138 } 139 140 #ifndef GBI_FLOATS 141 matrix->m[0][0] = 0x00010000; matrix->m[1][0] = 0x00000000; matrix->m[2][0] = 0x00000000; matrix->m[3][0] = 0x00000000; 142 matrix->m[0][1] = 0x00000000; matrix->m[1][1] = 0x00010000; matrix->m[2][1] = 0x00000000; matrix->m[3][1] = 0x00000000; 143 matrix->m[0][2] = 0x00000001; matrix->m[1][2] = 0x00000000; matrix->m[2][2] = 0x00000000; matrix->m[3][2] = 0x00000000; 144 matrix->m[0][3] = 0x00000000; matrix->m[1][3] = 0x00000001; matrix->m[2][3] = 0x00000000; matrix->m[3][3] = 0x00000000; 145 #else 146 guMtxIdent(matrix); 147 #endif 148 149 gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_LOAD | G_MTX_NOPUSH); 150 gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_PROJECTION | G_MTX_LOAD | G_MTX_NOPUSH); 151 } 152 153 void create_dl_translation_matrix(s8 pushOp, f32 x, f32 y, f32 z) { 154 Mtx *matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); 155 156 if (matrix == NULL) { 157 return; 158 } 159 160 guTranslate(matrix, x, y, z); 161 162 if (pushOp == MENU_MTX_PUSH) { 163 gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH); 164 } 165 166 if (pushOp == MENU_MTX_NOPUSH) { 167 gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH); 168 } 169 } 170 171 void create_dl_rotation_matrix(s8 pushOp, f32 a, f32 x, f32 y, f32 z) { 172 Mtx *matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); 173 174 if (matrix == NULL) { 175 return; 176 } 177 178 guRotate(matrix, a, x, y, z); 179 180 if (pushOp == MENU_MTX_PUSH) { 181 gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH); 182 } 183 184 if (pushOp == MENU_MTX_NOPUSH) { 185 gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH); 186 } 187 } 188 189 void create_dl_scale_matrix(s8 pushOp, f32 x, f32 y, f32 z) { 190 Mtx *matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); 191 192 if (matrix == NULL) { 193 return; 194 } 195 196 guScale(matrix, x, y, z); 197 198 if (pushOp == MENU_MTX_PUSH) { 199 gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_PUSH); 200 } 201 202 if (pushOp == MENU_MTX_NOPUSH) { 203 gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_MODELVIEW | G_MTX_MUL | G_MTX_NOPUSH); 204 } 205 } 206 207 void create_dl_ortho_matrix(void) { 208 Mtx *matrix = (Mtx *) alloc_display_list(sizeof(Mtx)); 209 210 if (matrix == NULL) { 211 return; 212 } 213 214 create_dl_identity_matrix(); 215 216 guOrtho(matrix, 0.0f, SCREEN_WIDTH, 0.0f, SCREEN_HEIGHT, -10.0f, 10.0f, 1.0f); 217 218 // Should produce G_RDPHALF_1 in Fast3D 219 gSPPerspNormalize(gDisplayListHead++, 0xFFFF); 220 221 gSPMatrix(gDisplayListHead++, VIRTUAL_TO_PHYSICAL(matrix), G_MTX_PROJECTION | G_MTX_MUL | G_MTX_NOPUSH) 222 } 223 224 #if defined(VERSION_US) || defined(VERSION_EU) 225 UNUSED 226 #endif 227 #ifdef VERSION_CN 228 static u8 *alloc_ia8_text_from_i1(u8 *in) { 229 s32 i, j; 230 u8 l, r; 231 u8 bitMask = 0x80; 232 u8 *out = alloc_display_list(8 * 8); 233 234 if (out == NULL) { 235 return NULL; 236 } 237 238 for (i = 0; i < 16; i++) { 239 for (j = 0; j < 8;) { 240 r = 0; 241 l = ((in[i] & (bitMask >> j)) >> (7 - j)) != 0 ? 0xF0 : 0x00; 242 j++; 243 r = ((in[i] & (bitMask >> j)) >> (7 - j)) != 0 ? 0x0F : 0x00; 244 out[i * 4 + j / 2] = l | r; 245 CN_DEBUG_PRINTF(("%x, ", out[i * 4 + j / 2])); 246 j++; 247 } 248 CN_DEBUG_PRINTF(("\n")); 249 } 250 251 return out; 252 } 253 #else 254 static u8 *alloc_ia8_text_from_i1(u16 *in, s16 width, s16 height) { 255 s32 inPos; 256 u16 bitMask; 257 u8 *out; 258 s16 outPos = 0; 259 260 out = (u8 *) alloc_display_list((u32) width * (u32) height); 261 262 if (out == NULL) { 263 return NULL; 264 } 265 266 for (inPos = 0; inPos < (width * height) / 16; inPos++) { 267 bitMask = 0x8000; 268 269 while (bitMask != 0) { 270 if (in[inPos] & bitMask) { 271 out[outPos] = 0xFF; 272 } else { 273 out[outPos] = 0x00; 274 } 275 276 bitMask /= 2; 277 outPos++; 278 } 279 } 280 281 return out; 282 } 283 #endif 284 285 #ifdef VERSION_CN 286 void render_generic_char(u16 c) 287 #else 288 void render_generic_char(u8 c) 289 #endif 290 { 291 void **fontLUT = segmented_to_virtual(main_font_lut); 292 void *packedTexture = segmented_to_virtual(fontLUT[c]); 293 #if defined(VERSION_JP) || defined(VERSION_SH) 294 void *unpackedTexture = alloc_ia8_text_from_i1(packedTexture, 8, 16); 295 #endif 296 297 #ifndef VERSION_EU 298 gDPPipeSync(gDisplayListHead++); 299 #endif 300 #if defined(VERSION_JP) || defined(VERSION_SH) 301 gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_8b, 1, VIRTUAL_TO_PHYSICAL(unpackedTexture)); 302 #else 303 gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_16b, 1, VIRTUAL_TO_PHYSICAL(packedTexture)); 304 #endif 305 gSPDisplayList(gDisplayListHead++, dl_ia_text_tex_settings); 306 #ifdef VERSION_EU 307 gSPTextureRectangleFlip(gDisplayListHead++, gDialogX << 2, (gDialogY - 16) << 2, 308 (gDialogX + 8) << 2, gDialogY << 2, G_TX_RENDERTILE, 8 << 6, 4 << 6, 1 << 10, 1 << 10); 309 #endif 310 } 311 312 #ifdef VERSION_EU 313 u8 *alloc_ia4_tex_from_i1(u8 *in, s16 width, s16 height) { 314 u32 size = (u32) width * (u32) height; 315 u8 *out; 316 s32 inPos; 317 s16 outPos = 0; 318 u8 bitMask; 319 320 out = (u8 *) alloc_display_list(size); 321 322 if (out == NULL) { 323 return NULL; 324 } 325 326 for (inPos = 0; inPos < (width * height) / 4; inPos++) { 327 bitMask = 0x80; 328 329 while (bitMask != 0) { 330 out[outPos] = (in[inPos] & bitMask) ? 0xF0 : 0x00; 331 bitMask /= 2; 332 out[outPos] = (in[inPos] & bitMask) ? out[outPos] + 0x0F : out[outPos]; 333 bitMask /= 2; 334 outPos++; 335 } 336 } 337 338 return out; 339 } 340 341 void render_generic_char_at_pos(s16 xPos, s16 yPos, u8 c) { 342 void **fontLUT = segmented_to_virtual(main_font_lut); 343 void *packedTexture = segmented_to_virtual(fontLUT[c]); 344 void *unpackedTexture = alloc_ia4_tex_from_i1(packedTexture, 8, 8); 345 346 gDPPipeSync(gDisplayListHead++); 347 gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_16b, 1, VIRTUAL_TO_PHYSICAL(unpackedTexture)); 348 gSPDisplayList(gDisplayListHead++, dl_ia_text_tex_settings); 349 gSPTextureRectangleFlip(gDisplayListHead++, xPos << 2, (yPos - 16) << 2, (xPos + 8) << 2, yPos << 2, 350 G_TX_RENDERTILE, 8 << 6, 4 << 6, 1 << 10, 1 << 10); 351 } 352 353 void render_lowercase_diacritic(s16 *xPos, s16 *yPos, u8 letter, u8 diacritic) { 354 render_generic_char_at_pos(*xPos, *yPos, letter); 355 render_generic_char_at_pos(*xPos, *yPos, diacritic + 0xE7); 356 *xPos += gDialogCharWidths[letter]; 357 } 358 359 void render_uppercase_diacritic(s16 *xPos, s16 *yPos, u8 letter, u8 diacritic) { 360 render_generic_char_at_pos(*xPos, *yPos, letter); 361 render_generic_char_at_pos(*xPos, *yPos - 4, diacritic + 0xE3); 362 *xPos += gDialogCharWidths[letter]; 363 } 364 #endif // VERSION_EU 365 366 #ifdef VERSION_CN 367 void render_generic_char_cn(u16 c) { 368 void **fontLUT = segmented_to_virtual(main_font_lut); 369 void *packedTexture = segmented_to_virtual(fontLUT[c]); 370 void *unpackedTexture = alloc_ia8_text_from_i1(packedTexture); 371 372 gDPPipeSync(gDisplayListHead++); 373 gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_16b, 1, VIRTUAL_TO_PHYSICAL(unpackedTexture)); 374 gSPDisplayList(gDisplayListHead++, dl_ia_text_tex_settings); 375 } 376 #endif 377 378 #if defined(VERSION_US) || defined(VERSION_EU) || defined(VERSION_CN) 379 // First byte is length, rest is string 380 struct MultiTextEntry { 381 u8 str[5]; 382 }; 383 384 #define TEXT_THE_RAW ASCII_TO_DIALOG('t'), ASCII_TO_DIALOG('h'), ASCII_TO_DIALOG('e'), 0x00 385 #define TEXT_YOU_RAW ASCII_TO_DIALOG('y'), ASCII_TO_DIALOG('o'), ASCII_TO_DIALOG('u'), 0x00 386 387 enum MultiStringIDs { STRING_THE, STRING_YOU }; 388 389 /* 390 * Place the multi-text string according to the ID passed. (US, EU) 391 * 0: 'the' 392 * 1: 'you' 393 */ 394 #if defined(VERSION_US) || defined(VERSION_CN) 395 void render_multi_text_string(s8 multiTextID) 396 #elif defined(VERSION_EU) 397 void render_multi_text_string(s16 *xPos, s16 *yPos, s8 multiTextID) 398 #endif 399 { 400 s8 i; 401 struct MultiTextEntry textLengths[2] = { 402 { { 3, TEXT_THE_RAW } }, 403 { { 3, TEXT_YOU_RAW } }, 404 }; 405 406 for (i = 0; i < textLengths[multiTextID].str[0]; i++) { 407 #if defined(VERSION_US) || defined(VERSION_CN) 408 render_generic_char(textLengths[multiTextID].str[1 + i]); 409 create_dl_translation_matrix( 410 MENU_MTX_NOPUSH, (f32)(gDialogCharWidths[textLengths[multiTextID].str[1 + i]]), 0.0f, 0.0f); 411 #elif defined(VERSION_EU) 412 render_generic_char_at_pos(*xPos, *yPos, textLengths[multiTextID].str[1 + i]); 413 *xPos += gDialogCharWidths[textLengths[multiTextID].str[1 + i]]; 414 #endif 415 } 416 } 417 #endif 418 419 #if defined(VERSION_JP) || defined(VERSION_SH) 420 #define CUR_CHAR str[strPos] 421 #define MAX_STRING_WIDTH 18 422 #define CHAR_WIDTH_SPACE 5.0f 423 #define CHAR_WIDTH_DEFAULT 10.0f 424 #elif defined(VERSION_CN) 425 #define CUR_CHAR strChar 426 #define MAX_STRING_WIDTH 18 427 #define CHAR_WIDTH_SPACE gDialogCharWidths[DIALOG_CHAR_SPACE] 428 #define CHAR_WIDTH_DEFAULT gDialogCharWidths[CUR_CHAR] 429 #else 430 #define CUR_CHAR str[strPos] 431 #define MAX_STRING_WIDTH 16 432 #define CHAR_WIDTH_SPACE gDialogCharWidths[DIALOG_CHAR_SPACE] 433 #define CHAR_WIDTH_DEFAULT gDialogCharWidths[CUR_CHAR] 434 #endif 435 436 /** 437 * Prints a generic white string. 438 * In JP/EU a IA1 texture is used but in US a IA4 texture is used. 439 */ 440 void print_generic_string(s16 x, s16 y, const u8 *str) { 441 UNUSED s8 mark = DIALOG_MARK_NONE; // unused in EU 442 s32 strPos = 0; 443 u8 lineNum = 1; 444 #ifdef VERSION_EU 445 s16 xCoord = x; 446 s16 yCoord = 240 - y; 447 #endif 448 #ifdef VERSION_CN 449 s32 strChar; 450 #endif 451 452 #ifndef VERSION_EU 453 create_dl_translation_matrix(MENU_MTX_PUSH, x, y, 0.0f); 454 #endif 455 456 #ifdef VERSION_CN 457 while (str[strPos] != DIALOG_CHAR_SPECIAL_MODIFIER || str[strPos + 1] != DIALOG_CHAR_TERMINATOR) { 458 strChar = str[strPos] << 8 | str[strPos + 1]; 459 #else 460 while (str[strPos] != SPECIAL_CHAR(DIALOG_CHAR_TERMINATOR)) { 461 #endif 462 switch (CUR_CHAR) { 463 #ifdef VERSION_CN 464 case SPECIAL_CHAR(DIALOG_CHAR_NEWLINE): 465 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 466 create_dl_translation_matrix(MENU_MTX_PUSH, x, y - (lineNum * MAX_STRING_WIDTH), 0.0f); 467 lineNum++; 468 break; 469 case SPECIAL_CHAR(DIALOG_CHAR_STAR_OPEN): // ??? 470 create_dl_translation_matrix(MENU_MTX_NOPUSH, 32.0f, 0.0f, 0.0f); 471 break; 472 #endif 473 #ifdef VERSION_EU 474 case SPECIAL_CHAR(DIALOG_CHAR_SPACE): 475 xCoord += 5; 476 break; 477 case SPECIAL_CHAR(DIALOG_CHAR_NEWLINE): 478 yCoord += 16; 479 xCoord = x; 480 lineNum++; 481 break; 482 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_A_GRAVE): 483 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_A_CIRCUMFLEX): 484 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_A_UMLAUT): 485 render_lowercase_diacritic(&xCoord, &yCoord, ASCII_TO_DIALOG('a'), CUR_CHAR & 0xF); 486 break; 487 case SPECIAL_CHAR(DIALOG_CHAR_UPPER_A_UMLAUT): // @bug grave and circumflex (0x64-0x65) are absent here 488 render_uppercase_diacritic(&xCoord, &yCoord, ASCII_TO_DIALOG('A'), CUR_CHAR & 0xF); 489 break; 490 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_E_GRAVE): 491 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_E_CIRCUMFLEX): 492 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_E_UMLAUT): 493 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_E_ACUTE): 494 render_lowercase_diacritic(&xCoord, &yCoord, ASCII_TO_DIALOG('e'), CUR_CHAR & 0xF); 495 break; 496 case SPECIAL_CHAR(DIALOG_CHAR_UPPER_E_GRAVE): 497 case SPECIAL_CHAR(DIALOG_CHAR_UPPER_E_CIRCUMFLEX): 498 case SPECIAL_CHAR(DIALOG_CHAR_UPPER_E_UMLAUT): 499 case SPECIAL_CHAR(DIALOG_CHAR_UPPER_E_ACUTE): 500 render_uppercase_diacritic(&xCoord, &yCoord, ASCII_TO_DIALOG('E'), CUR_CHAR & 0xF); 501 break; 502 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_U_GRAVE): 503 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_U_CIRCUMFLEX): 504 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_U_UMLAUT): 505 render_lowercase_diacritic(&xCoord, &yCoord, ASCII_TO_DIALOG('u'), CUR_CHAR & 0xF); 506 break; 507 case SPECIAL_CHAR(DIALOG_CHAR_UPPER_U_UMLAUT): // @bug grave and circumflex (0x84-0x85) are absent here 508 render_uppercase_diacritic(&xCoord, &yCoord, ASCII_TO_DIALOG('U'), CUR_CHAR & 0xF); 509 break; 510 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_O_CIRCUMFLEX): 511 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_O_UMLAUT): 512 render_lowercase_diacritic(&xCoord, &yCoord, ASCII_TO_DIALOG('o'), CUR_CHAR & 0xF); 513 break; 514 case SPECIAL_CHAR(DIALOG_CHAR_UPPER_O_UMLAUT): // @bug circumflex (0x95) is absent here 515 render_uppercase_diacritic(&xCoord, &yCoord, ASCII_TO_DIALOG('O'), CUR_CHAR & 0xF); 516 break; 517 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_I_CIRCUMFLEX): 518 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_I_UMLAUT): 519 render_lowercase_diacritic(&xCoord, &yCoord, DIALOG_CHAR_I_NO_DIA, CUR_CHAR & 0xF); 520 break; 521 #else // i.e. not EU 522 case SPECIAL_CHAR(DIALOG_CHAR_DAKUTEN): 523 mark = DIALOG_MARK_DAKUTEN; 524 break; 525 case SPECIAL_CHAR(DIALOG_CHAR_PERIOD_OR_HANDAKUTEN): 526 mark = DIALOG_MARK_HANDAKUTEN; 527 break; 528 #ifndef VERSION_CN 529 case SPECIAL_CHAR(DIALOG_CHAR_NEWLINE): 530 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 531 create_dl_translation_matrix(MENU_MTX_PUSH, x, y - (lineNum * MAX_STRING_WIDTH), 0.0f); 532 lineNum++; 533 break; 534 #endif 535 case SPECIAL_CHAR(DIALOG_CHAR_PERIOD): 536 create_dl_translation_matrix(MENU_MTX_PUSH, -2.0f, -5.0f, 0.0f); 537 render_generic_char(DIALOG_CHAR_PERIOD_OR_HANDAKUTEN); 538 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 539 break; 540 #endif 541 542 #if defined(VERSION_US) || defined(VERSION_EU) || defined(VERSION_CN) 543 case SPECIAL_CHAR(DIALOG_CHAR_SLASH): 544 #ifdef VERSION_EU 545 xCoord += gDialogCharWidths[DIALOG_CHAR_SPACE] * 2; 546 #else 547 create_dl_translation_matrix( 548 MENU_MTX_NOPUSH, (f32)(gDialogCharWidths[DIALOG_CHAR_SPACE] * 2), 0.0f, 0.0f); 549 #endif 550 break; 551 case SPECIAL_CHAR(DIALOG_CHAR_MULTI_THE): 552 #ifdef VERSION_EU 553 render_multi_text_string(&xCoord, &yCoord, STRING_THE); 554 #else 555 render_multi_text_string(STRING_THE); 556 #endif 557 break; 558 case SPECIAL_CHAR(DIALOG_CHAR_MULTI_YOU): 559 #ifdef VERSION_EU 560 render_multi_text_string(&xCoord, &yCoord, STRING_YOU); 561 #else 562 render_multi_text_string(STRING_YOU); 563 #endif 564 break; 565 #endif 566 567 #ifndef VERSION_EU 568 case SPECIAL_CHAR(DIALOG_CHAR_SPACE): 569 create_dl_translation_matrix(MENU_MTX_NOPUSH, CHAR_WIDTH_SPACE, 0.0f, 0.0f); 570 break; 571 #ifdef VERSION_JP 572 break; // ? needed to match 573 #endif 574 #endif 575 576 default: 577 #ifdef VERSION_EU 578 render_generic_char_at_pos(xCoord, yCoord, CUR_CHAR); 579 xCoord += gDialogCharWidths[CUR_CHAR]; 580 #else 581 #ifdef VERSION_CN 582 if (strChar >= 0x0100) { 583 strChar = (strChar - 0xFF) * 2 + 0xFE; 584 render_generic_char_cn(strChar); 585 create_dl_translation_matrix(MENU_MTX_NOPUSH, 8.0f, 0.0f, 0.0f); 586 render_generic_char_cn(strChar + 1); 587 create_dl_translation_matrix(MENU_MTX_NOPUSH, 8.0f, 0.0f, 0.0f); 588 break; 589 } 590 #endif 591 render_generic_char(CUR_CHAR); 592 if (mark != DIALOG_MARK_NONE) { 593 create_dl_translation_matrix(MENU_MTX_PUSH, 5.0f, 5.0f, 0.0f); 594 render_generic_char(DIALOG_CHAR_MARK_START + mark); 595 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 596 mark = DIALOG_MARK_NONE; 597 } 598 599 create_dl_translation_matrix(MENU_MTX_NOPUSH, CHAR_WIDTH_DEFAULT, 0.0f, 0.0f); 600 #endif 601 #ifndef VERSION_JP 602 break; // what an odd difference. US (and probably later) versions added a useless break here. 603 #endif 604 } 605 606 #ifdef VERSION_CN 607 strPos += 2; 608 #else 609 strPos++; 610 #endif 611 } 612 613 #ifndef VERSION_EU 614 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 615 #endif 616 } 617 618 #ifdef VERSION_EU 619 void print_hud_char_umlaut(s16 x, s16 y, u8 chr) { 620 void **fontLUT = segmented_to_virtual(main_hud_lut); 621 622 gDPPipeSync(gDisplayListHead++); 623 gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, fontLUT[chr]); 624 gSPDisplayList(gDisplayListHead++, dl_rgba16_load_tex_block); 625 gSPTextureRectangle(gDisplayListHead++, x << 2, y << 2, (x + 16) << 2, (y + 16) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); 626 627 gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, fontLUT[GLYPH_UMLAUT]); 628 gSPDisplayList(gDisplayListHead++, dl_rgba16_load_tex_block); 629 gSPTextureRectangle(gDisplayListHead++, x << 2, (y - 4) << 2, (x + 16) << 2, (y + 12) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); 630 } 631 #endif 632 633 /** 634 * Prints a hud string depending of the hud table list defined. 635 */ 636 void print_hud_lut_string(s8 hudLUT, s16 x, s16 y, const u8 *str) { 637 s32 strPos = 0; 638 void **hudLUT1 = segmented_to_virtual(menu_hud_lut); // Japanese Menu HUD Color font 639 void **hudLUT2 = segmented_to_virtual(main_hud_lut); // 0-9 A-Z HUD Color Font 640 u32 curX = x; 641 u32 curY = y; 642 643 #ifdef VERSION_CN 644 u32 xOffset = 0; 645 #else 646 u32 xStride; // X separation 647 648 if (hudLUT == HUD_LUT_JPMENU) { 649 xStride = 16; 650 } else { // HUD_LUT_GLOBAL 651 #ifdef VERSION_JP 652 xStride = 14; 653 #else 654 xStride = 12; //? Shindou uses this. 655 #endif 656 } 657 #endif 658 659 while (str[strPos] != GLOBAL_CHAR_TERMINATOR) { 660 #ifndef VERSION_JP 661 switch (str[strPos]) { 662 #ifdef VERSION_CN 663 case GLOBAL_CHAR_NEWLINE: 664 curY += 16; 665 curX -= xOffset; 666 break; 667 #elif defined(VERSION_EU) 668 case GLOBAL_CHAR_SPACE: 669 curX += xStride / 2; 670 break; 671 case HUD_CHAR_A_UMLAUT: 672 print_hud_char_umlaut(curX, curY, ASCII_TO_DIALOG('A')); 673 curX += xStride; 674 break; 675 case HUD_CHAR_O_UMLAUT: 676 print_hud_char_umlaut(curX, curY, ASCII_TO_DIALOG('O')); 677 curX += xStride; 678 break; 679 case HUD_CHAR_U_UMLAUT: 680 print_hud_char_umlaut(curX, curY, ASCII_TO_DIALOG('U')); 681 curX += xStride; 682 break; 683 #else 684 case GLOBAL_CHAR_SPACE: 685 curX += 8; 686 break; 687 #endif 688 default: 689 #endif 690 gDPPipeSync(gDisplayListHead++); 691 692 #ifndef VERSION_CN 693 if (hudLUT == HUD_LUT_JPMENU) { 694 gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, hudLUT1[str[strPos]]); 695 } 696 697 if (hudLUT == HUD_LUT_GLOBAL) { 698 #endif 699 gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, hudLUT2[str[strPos]]); 700 #ifndef VERSION_CN 701 } 702 #endif 703 704 gSPDisplayList(gDisplayListHead++, dl_rgba16_load_tex_block); 705 gSPTextureRectangle(gDisplayListHead++, curX << 2, curY << 2, (curX + 16) << 2, 706 (curY + 16) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); 707 708 #ifdef VERSION_CN 709 if (str[strPos] < 0x3A) { 710 curX += 12; 711 xOffset += 12; 712 } else { 713 curX += 16; 714 xOffset += 16; 715 } 716 #else 717 curX += xStride; 718 #endif 719 #ifndef VERSION_JP 720 } 721 #endif 722 strPos++; 723 } 724 } 725 726 #ifdef VERSION_EU 727 void print_menu_char_umlaut(s16 x, s16 y, u8 chr) { 728 void **fontLUT = segmented_to_virtual(menu_font_lut); 729 730 gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_8b, 1, fontLUT[chr]); 731 gDPLoadSync(gDisplayListHead++); 732 gDPLoadBlock(gDisplayListHead++, G_TX_LOADTILE, 0, 0, 8 * 8 - 1, CALC_DXT(8, G_IM_SIZ_8b_BYTES)); 733 gSPTextureRectangle(gDisplayListHead++, x << 2, y << 2, (x + 8) << 2, (y + 8) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); 734 735 gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_8b, 1, fontLUT[DIALOG_CHAR_UMLAUT]); 736 gDPLoadSync(gDisplayListHead++); 737 gDPLoadBlock(gDisplayListHead++, G_TX_LOADTILE, 0, 0, 8 * 8 - 1, CALC_DXT(8, G_IM_SIZ_8b_BYTES)); 738 gSPTextureRectangle(gDisplayListHead++, x << 2, (y - 4) << 2, (x + 8) << 2, (y + 4) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); 739 } 740 #endif 741 742 void print_menu_generic_string(s16 x, s16 y, const u8 *str) { 743 UNUSED s8 mark = DIALOG_MARK_NONE; // unused in EU 744 s32 strPos = 0; 745 u32 curX = x; 746 u32 curY = y; 747 void **fontLUT = segmented_to_virtual(menu_font_lut); 748 749 while (str[strPos] != DIALOG_CHAR_TERMINATOR) { 750 switch (str[strPos]) { 751 #ifdef VERSION_EU 752 case DIALOG_CHAR_UPPER_A_UMLAUT: 753 print_menu_char_umlaut(curX, curY, ASCII_TO_DIALOG('A')); 754 curX += gDialogCharWidths[str[strPos]]; 755 break; 756 case DIALOG_CHAR_UPPER_U_UMLAUT: 757 print_menu_char_umlaut(curX, curY, ASCII_TO_DIALOG('U')); 758 curX += gDialogCharWidths[str[strPos]]; 759 break; 760 case DIALOG_CHAR_UPPER_O_UMLAUT: 761 print_menu_char_umlaut(curX, curY, ASCII_TO_DIALOG('O')); 762 curX += gDialogCharWidths[str[strPos]]; 763 break; 764 #else 765 case DIALOG_CHAR_DAKUTEN: 766 mark = DIALOG_MARK_DAKUTEN; 767 break; 768 case DIALOG_CHAR_PERIOD_OR_HANDAKUTEN: 769 mark = DIALOG_MARK_HANDAKUTEN; 770 break; 771 #endif 772 case DIALOG_CHAR_SPACE: 773 curX += 4; 774 break; 775 default: 776 gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_8b, 1, fontLUT[str[strPos]]); 777 gDPLoadSync(gDisplayListHead++); 778 gDPLoadBlock(gDisplayListHead++, G_TX_LOADTILE, 0, 0, 8 * 8 - 1, CALC_DXT(8, G_IM_SIZ_8b_BYTES)); 779 gSPTextureRectangle(gDisplayListHead++, curX << 2, curY << 2, (curX + 8) << 2, 780 (curY + 8) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); 781 782 #ifndef VERSION_EU 783 if (mark != DIALOG_MARK_NONE) { 784 gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_8b, 1, fontLUT[DIALOG_CHAR_MARK_START + mark]); 785 gDPLoadSync(gDisplayListHead++); 786 gDPLoadBlock(gDisplayListHead++, G_TX_LOADTILE, 0, 0, 8 * 8 - 1, CALC_DXT(8, G_IM_SIZ_8b_BYTES)); 787 gSPTextureRectangle(gDisplayListHead++, (curX + 6) << 2, (curY - 7) << 2, 788 (curX + 6 + 8) << 2, (curY - 7 + 8) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); 789 790 mark = DIALOG_MARK_NONE; 791 } 792 #endif 793 #if defined(VERSION_JP) || defined(VERSION_SH) || defined(VERSION_CN) 794 curX += 9; 795 #else 796 curX += gDialogCharWidths[str[strPos]]; 797 #endif 798 } 799 strPos++; 800 } 801 } 802 803 void print_credits_string(s16 x, s16 y, const u8 *str) { 804 s32 strPos = 0; 805 void **fontLUT = segmented_to_virtual(main_credits_font_lut); 806 u32 curX = x; 807 u32 curY = y; 808 809 gDPSetTile(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 0, 0, G_TX_LOADTILE, 0, 810 G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOLOD, G_TX_WRAP | G_TX_NOMIRROR, G_TX_NOMASK, G_TX_NOLOD); 811 gDPTileSync(gDisplayListHead++); 812 gDPSetTile(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 2, 0, G_TX_RENDERTILE, 0, 813 G_TX_CLAMP, 3, G_TX_NOLOD, G_TX_CLAMP, 3, G_TX_NOLOD); 814 gDPSetTileSize(gDisplayListHead++, G_TX_RENDERTILE, 0, 0, (8 - 1) << G_TEXTURE_IMAGE_FRAC, (8 - 1) << G_TEXTURE_IMAGE_FRAC); 815 816 while (str[strPos] != GLOBAL_CHAR_TERMINATOR) { 817 switch (str[strPos]) { 818 case GLOBAL_CHAR_SPACE: 819 curX += 4; 820 break; 821 default: 822 gDPPipeSync(gDisplayListHead++); 823 gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_RGBA, G_IM_SIZ_16b, 1, fontLUT[str[strPos]]); 824 gDPLoadSync(gDisplayListHead++); 825 gDPLoadBlock(gDisplayListHead++, G_TX_LOADTILE, 0, 0, 8 * 8 - 1, CALC_DXT(8, G_IM_SIZ_16b_BYTES)); 826 gSPTextureRectangle(gDisplayListHead++, curX << 2, curY << 2, (curX + 8) << 2, 827 (curY + 8) << 2, G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); 828 curX += 7; 829 break; 830 } 831 strPos++; 832 } 833 } 834 835 void handle_menu_scrolling(s8 scrollDirection, s8 *currentIndex, s8 minIndex, s8 maxIndex) { 836 u8 index = 0; 837 838 if (scrollDirection == MENU_SCROLL_VERTICAL) { 839 if (gPlayer3Controller->rawStickY > 60) { 840 index++; 841 } 842 843 if (gPlayer3Controller->rawStickY < -60) { 844 index += 2; 845 } 846 } else if (scrollDirection == MENU_SCROLL_HORIZONTAL) { 847 if (gPlayer3Controller->rawStickX > 60) { 848 index += 2; 849 } 850 851 if (gPlayer3Controller->rawStickX < -60) { 852 index++; 853 } 854 } 855 856 if (((index ^ gMenuHoldKeyIndex) & index) == 2) { 857 if (*currentIndex == maxIndex) { 858 //! Probably originally a >=, but later replaced with an == and an else statement. 859 *currentIndex = maxIndex; 860 } else { 861 play_sound(SOUND_MENU_CHANGE_SELECT, gGlobalSoundSource); 862 (*currentIndex)++; 863 } 864 } 865 866 if (((index ^ gMenuHoldKeyIndex) & index) == 1) { 867 if (*currentIndex == minIndex) { 868 // Same applies to here as above 869 } else { 870 play_sound(SOUND_MENU_CHANGE_SELECT, gGlobalSoundSource); 871 (*currentIndex)--; 872 } 873 } 874 875 if (gMenuHoldKeyTimer == 10) { 876 gMenuHoldKeyTimer = 8; 877 gMenuHoldKeyIndex = 0; 878 } else { 879 gMenuHoldKeyTimer++; 880 gMenuHoldKeyIndex = index; 881 } 882 883 if ((index & 3) == 0) { 884 gMenuHoldKeyTimer = 0; 885 } 886 } 887 888 // EU has both get_str_x_pos_from_center and get_str_x_pos_from_center_scale 889 // JP, US, Shindou and iQue only implement one or the other 890 #if defined(VERSION_US) || defined(VERSION_EU) || defined(VERSION_CN) 891 s16 get_str_x_pos_from_center(s16 centerPos, u8 *str, UNUSED f32 scale) { 892 s16 strPos = 0; 893 f32 spacesWidth = 0.0f; 894 895 #ifdef VERSION_CN 896 while (!(str[strPos] == DIALOG_CHAR_SPECIAL_MODIFIER && str[strPos + 1] == DIALOG_CHAR_TERMINATOR)) { 897 u16 character = (str[strPos] << 8) | str[strPos + 1]; 898 if (character <= 0xFF) { 899 spacesWidth += gDialogCharWidths[character]; 900 } else { 901 spacesWidth += 16.0; 902 } 903 strPos += 2; 904 } 905 #else 906 while (str[strPos] != DIALOG_CHAR_TERMINATOR) { 907 spacesWidth += gDialogCharWidths[str[strPos]]; 908 strPos++; 909 } 910 #endif 911 // return the x position of where the string starts as half the string's 912 // length from the position of the provided center. 913 return (s16)(centerPos - (s16)(spacesWidth / 2.0)); 914 } 915 #endif 916 917 #if defined(VERSION_JP) || defined(VERSION_EU) || defined(VERSION_SH) 918 s16 get_str_x_pos_from_center_scale(s16 centerPos, u8 *str, f32 scale) { 919 s16 strPos = 0; 920 f32 charsWidth = 0.0f; 921 f32 spacesWidth = 0.0f; 922 923 while (str[strPos] != DIALOG_CHAR_TERMINATOR) { 924 //! EU checks for dakuten and handakuten despite dialog code unable to handle it 925 if (str[strPos] == DIALOG_CHAR_SPACE) { 926 spacesWidth += 1.0; 927 } else if (str[strPos] != DIALOG_CHAR_DAKUTEN 928 && str[strPos] != DIALOG_CHAR_PERIOD_OR_HANDAKUTEN) { 929 charsWidth += 1.0; 930 } 931 strPos++; 932 } 933 // return the x position of where the string starts as half the string's 934 // length from the position of the provided center. 935 return (f32) centerPos - (scale * (charsWidth / 2.0)) - ((scale / 2.0) * (spacesWidth / 2.0)); 936 } 937 #endif 938 939 #if defined(VERSION_US) || defined(VERSION_EU) || defined(VERSION_CN) 940 s16 get_string_width(u8 *str) { 941 s16 strPos = 0; 942 s16 width = 0; 943 944 #ifdef VERSION_CN 945 while (!(str[strPos] == DIALOG_CHAR_SPECIAL_MODIFIER && str[strPos + 1] == DIALOG_CHAR_TERMINATOR)) { 946 u16 character = (str[strPos] << 8) | str[strPos + 1]; 947 if (character <= 0xFF) { 948 width += gDialogCharWidths[character]; 949 } else { 950 width += 16; 951 } 952 strPos += 2; 953 } 954 #else 955 while (str[strPos] != DIALOG_CHAR_TERMINATOR) { 956 width += gDialogCharWidths[str[strPos]]; 957 strPos++; 958 } 959 #endif 960 return width; 961 } 962 #endif 963 964 u8 gHudSymCoin[] = { GLYPH_COIN, GLYPH_SPACE }; 965 u8 gHudSymX[] = { GLYPH_MULTIPLY, GLYPH_SPACE }; 966 967 void print_hud_my_score_coins(s32 useCourseCoinScore, s8 fileIndex, s8 courseIndex, s16 x, s16 y) { 968 u8 strNumCoins[4]; 969 s16 numCoins; 970 971 if (!useCourseCoinScore) { 972 numCoins = (u16)(save_file_get_max_coin_score(courseIndex) & 0xFFFF); 973 } else { 974 numCoins = save_file_get_course_coin_score(fileIndex, courseIndex); 975 } 976 977 if (numCoins != 0) { 978 print_hud_lut_string(HUD_LUT_GLOBAL, x, y, gHudSymCoin); 979 print_hud_lut_string(HUD_LUT_GLOBAL, x + 16, y, gHudSymX); 980 int_to_str(numCoins, strNumCoins); 981 print_hud_lut_string(HUD_LUT_GLOBAL, x + 32, y, strNumCoins); 982 } 983 } 984 985 void print_hud_my_score_stars(s8 fileIndex, s8 courseIndex, s16 x, s16 y) { 986 u8 strStarCount[4]; 987 s16 starCount; 988 u8 textSymStar[] = { GLYPH_STAR, GLYPH_SPACE }; 989 UNUSED u16 unused; 990 u8 textSymX[] = { GLYPH_MULTIPLY, GLYPH_SPACE }; 991 992 starCount = save_file_get_course_star_count(fileIndex, courseIndex); 993 994 if (starCount != 0) { 995 print_hud_lut_string(HUD_LUT_GLOBAL, x, y, textSymStar); 996 print_hud_lut_string(HUD_LUT_GLOBAL, x + 16, y, textSymX); 997 int_to_str(starCount, strStarCount); 998 print_hud_lut_string(HUD_LUT_GLOBAL, x + 32, y, strStarCount); 999 } 1000 } 1001 1002 void int_to_str(s32 num, u8 *dst) { 1003 s32 digit1; 1004 s32 digit2; 1005 s32 digit3; 1006 1007 s8 pos = 0; 1008 1009 if (num > 999) { 1010 dst[0] = 0x00; dst[1] = DIALOG_CHAR_TERMINATOR; 1011 return; 1012 } 1013 1014 digit1 = num / 100; 1015 digit2 = (num - digit1 * 100) / 10; 1016 digit3 = (num - digit1 * 100) - (digit2 * 10); 1017 1018 if (digit1 != 0) { 1019 dst[pos] = digit1; pos++; 1020 } 1021 1022 if (digit2 != 0 || digit1 != 0) { 1023 dst[pos] = digit2; pos++; 1024 } 1025 1026 dst[pos] = digit3; 1027 pos++; 1028 dst[pos] = DIALOG_CHAR_TERMINATOR; 1029 } 1030 1031 #ifdef VERSION_CN 1032 void int_to_str_2(s32 num, u8 *dst) { 1033 s32 digit1; 1034 s32 digit2; 1035 s32 digit3; 1036 1037 s8 pos = 0; 1038 1039 if (num > 999) { 1040 dst[0] = 0x00; 1041 dst[1] = 0x00; 1042 dst[2] = DIALOG_CHAR_SPECIAL_MODIFIER; 1043 dst[3] = DIALOG_CHAR_TERMINATOR; 1044 return; 1045 } 1046 1047 digit1 = num / 100; 1048 digit2 = (num - digit1 * 100) / 10; 1049 digit3 = (num - digit1 * 100) - (digit2 * 10); 1050 1051 if (digit1 != 0) { 1052 dst[pos] = 0x00; 1053 pos++; 1054 dst[pos] = digit1; 1055 pos++; 1056 } 1057 1058 if (digit2 != 0 || digit1 != 0) { 1059 dst[pos] = 0x00; 1060 pos++; 1061 dst[pos] = digit2; 1062 pos++; 1063 } 1064 1065 dst[pos] = 0x00; 1066 pos++; 1067 dst[pos] = digit3; 1068 pos++; 1069 dst[pos] = DIALOG_CHAR_SPECIAL_MODIFIER; 1070 pos++; 1071 dst[pos] = DIALOG_CHAR_TERMINATOR; 1072 } 1073 #endif 1074 1075 s16 get_dialog_id(void) { 1076 return gDialogID; 1077 } 1078 1079 void create_dialog_box(s16 dialog) { 1080 if (gDialogID == DIALOG_NONE) { 1081 gDialogID = dialog; 1082 gDialogBoxType = DIALOG_TYPE_ROTATE; 1083 } 1084 } 1085 1086 void create_dialog_box_with_var(s16 dialog, s32 dialogVar) { 1087 if (gDialogID == DIALOG_NONE) { 1088 gDialogID = dialog; 1089 gDialogVariable = dialogVar; 1090 gDialogBoxType = DIALOG_TYPE_ROTATE; 1091 } 1092 } 1093 1094 void create_dialog_inverted_box(s16 dialog) { 1095 if (gDialogID == DIALOG_NONE) { 1096 gDialogID = dialog; 1097 gDialogBoxType = DIALOG_TYPE_ZOOM; 1098 } 1099 } 1100 1101 void create_dialog_box_with_response(s16 dialog) { 1102 if (gDialogID == DIALOG_NONE) { 1103 gDialogID = dialog; 1104 gDialogBoxType = DIALOG_TYPE_ROTATE; 1105 gDialogWithChoice = TRUE; 1106 } 1107 } 1108 1109 void reset_dialog_render_state(void) { 1110 level_set_transition(0, NULL); 1111 1112 if (gDialogBoxType == DIALOG_TYPE_ZOOM) { 1113 trigger_cutscene_dialog(2); 1114 } 1115 1116 gDialogBoxScale = DIALOG_BOX_SCALE_DEFAULT; 1117 gDialogBoxAngle = DIALOG_BOX_ANGLE_DEFAULT; 1118 gMenuState = MENU_STATE_DEFAULT; 1119 gDialogID = DIALOG_NONE; 1120 gDialogPageStartStrIndex = 0; 1121 gDialogWithChoice = FALSE; 1122 gNextDialogPageStartStrIndex = 0; 1123 gDialogResponse = DIALOG_RESPONSE_NONE; 1124 } 1125 1126 #if defined(VERSION_JP) || defined(VERSION_SH) 1127 #define X_VAL1 -5.0f 1128 #define Y_VAL1 2.0 1129 #define Y_VAL2 4 1130 #else 1131 #define X_VAL1 -7.0f 1132 #define Y_VAL1 5.0 1133 #define Y_VAL2 5.0f 1134 #endif 1135 1136 void render_dialog_box_type(struct DialogEntry *dialog, s8 linesPerBox) { 1137 #ifndef VERSION_CN 1138 UNUSED u8 filler[4]; 1139 #endif 1140 1141 create_dl_translation_matrix(MENU_MTX_NOPUSH, dialog->leftOffset, dialog->width, 0); 1142 1143 switch (gDialogBoxType) { 1144 case DIALOG_TYPE_ROTATE: // Renders a dialog black box with zoom and rotation 1145 if (gMenuState == MENU_STATE_DIALOG_OPENING || gMenuState == MENU_STATE_DIALOG_CLOSING) { 1146 create_dl_scale_matrix(MENU_MTX_NOPUSH, 1.0 / gDialogBoxScale, 1.0 / gDialogBoxScale, 1.0f); 1147 // convert the speed into angle 1148 create_dl_rotation_matrix(MENU_MTX_NOPUSH, gDialogBoxAngle * 4.0f, 0, 0, 1.0f); 1149 } 1150 gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 150); 1151 break; 1152 case DIALOG_TYPE_ZOOM: // Renders a dialog white box with zoom 1153 if (gMenuState == MENU_STATE_DIALOG_OPENING || gMenuState == MENU_STATE_DIALOG_CLOSING) { 1154 create_dl_translation_matrix(MENU_MTX_NOPUSH, 65.0 - (65.0 / gDialogBoxScale), 1155 (40.0 / gDialogBoxScale) - 40, 0); 1156 create_dl_scale_matrix(MENU_MTX_NOPUSH, 1.0 / gDialogBoxScale, 1.0 / gDialogBoxScale, 1.0f); 1157 } 1158 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 150); 1159 break; 1160 } 1161 1162 create_dl_translation_matrix(MENU_MTX_PUSH, X_VAL1, Y_VAL1, 0); 1163 #ifdef VERSION_CN 1164 create_dl_scale_matrix(MENU_MTX_NOPUSH, 1.1f, ((f32)(linesPerBox + 1) / Y_VAL2) + 0.1, 1.0f); 1165 #else 1166 create_dl_scale_matrix(MENU_MTX_NOPUSH, 1.1f, ((f32) linesPerBox / Y_VAL2) + 0.1, 1.0f); 1167 #endif 1168 1169 gSPDisplayList(gDisplayListHead++, dl_draw_text_bg_box); 1170 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 1171 } 1172 1173 void change_and_flash_dialog_text_color_lines(s8 colorMode, s8 lineNum) { 1174 u8 color; 1175 1176 if (colorMode == 1) { // unused 1177 if (lineNum == 1) { 1178 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); 1179 } else { 1180 if (lineNum == gMenuLineNum) { 1181 color = sins(gMenuTextColorTransTimer) * 50.0f + 200.0f; 1182 gDPSetEnvColor(gDisplayListHead++, color, color, color, 255); 1183 } else { 1184 gDPSetEnvColor(gDisplayListHead++, 200, 200, 200, 255); 1185 } 1186 } 1187 } else { 1188 switch (gDialogBoxType) { 1189 case DIALOG_TYPE_ROTATE: 1190 break; 1191 case DIALOG_TYPE_ZOOM: 1192 gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255); 1193 break; 1194 } 1195 } 1196 } 1197 1198 #ifdef VERSION_EU 1199 void render_generic_dialog_char_at_pos(struct DialogEntry *dialog, s16 x, s16 y, u8 c) { 1200 s16 width = (8.0 - (gDialogBoxScale * 0.8)); 1201 s16 height = (16.0 - (gDialogBoxScale * 0.8)); 1202 s16 tmpX = (dialog->leftOffset + (65.0 - (65.0 / gDialogBoxScale))); 1203 s16 tmpY = ((240 - dialog->width) - ((40.0 / gDialogBoxScale) - 40)); 1204 s16 xCoord = (tmpX + (x / gDialogBoxScale)); 1205 s16 yCoord = (tmpY + (y / gDialogBoxScale)); 1206 1207 void **fontLUT = segmented_to_virtual(main_font_lut); 1208 void *packedTexture = segmented_to_virtual(fontLUT[c]); 1209 void *unpackedTexture = alloc_ia4_tex_from_i1(packedTexture, 8, 8); 1210 1211 gDPSetTextureImage(gDisplayListHead++, G_IM_FMT_IA, G_IM_SIZ_16b, 1, VIRTUAL_TO_PHYSICAL(unpackedTexture)); 1212 gSPDisplayList(gDisplayListHead++, dl_ia_text_tex_settings); 1213 gSPTextureRectangleFlip(gDisplayListHead++, xCoord << 2, (yCoord - height) << 2, 1214 (xCoord + width) << 2, yCoord << 2, G_TX_RENDERTILE, 8 << 6, 4 << 6, 1 << 10, 1 << 10); 1215 } 1216 #endif 1217 1218 #if defined(VERSION_JP) || defined(VERSION_SH) 1219 #define X_VAL3 5.0f 1220 #define Y_VAL3 20 1221 #elif defined(VERSION_CN) 1222 #define X_VAL3 0.0f 1223 #define Y_VAL3 20 1224 #else 1225 #define X_VAL3 0.0f 1226 #define Y_VAL3 16 1227 #endif 1228 1229 #ifdef VERSION_EU 1230 void handle_dialog_scroll_page_state(s8 lineNum, s8 totalLines, s8 *pageState, s8 *xMatrix) 1231 #else 1232 void handle_dialog_scroll_page_state(s8 lineNum, s8 totalLines, s8 *pageState, s8 *xMatrix, s16 *linePos) 1233 #endif 1234 { 1235 #ifndef VERSION_EU 1236 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 1237 #endif 1238 1239 if (lineNum == totalLines) { 1240 *pageState = DIALOG_PAGE_STATE_SCROLL; 1241 return; 1242 } 1243 #ifdef VERSION_EU 1244 gDialogY += 16; 1245 #else 1246 create_dl_translation_matrix(MENU_MTX_PUSH, X_VAL3, 2 - (lineNum * Y_VAL3), 0); 1247 1248 *linePos = 0; 1249 #endif 1250 *xMatrix = 1; 1251 } 1252 1253 #if defined(VERSION_JP) || defined(VERSION_SH) 1254 void adjust_pos_and_print_period_char(s8 *xMatrix, s16 *linePos) { 1255 if (*linePos != 0) { 1256 create_dl_translation_matrix(MENU_MTX_NOPUSH, 10 * *xMatrix, 0, 0); 1257 } 1258 1259 create_dl_translation_matrix(MENU_MTX_PUSH, -2.0f, -5.0f, 0); 1260 render_generic_char(DIALOG_CHAR_PERIOD_OR_HANDAKUTEN); 1261 1262 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 1263 1264 (*linePos)++; 1265 *xMatrix = 1; 1266 } 1267 #endif 1268 1269 #ifdef VERSION_EU 1270 void render_star_count_dialog_text(struct DialogEntry *dialog, s8 *linePos) 1271 #else 1272 void render_star_count_dialog_text(s8 *xMatrix, s16 *linePos) 1273 #endif 1274 { 1275 s8 tensDigit = gDialogVariable / 10; 1276 s8 onesDigit = gDialogVariable - (tensDigit * 10); // remainder 1277 1278 if (tensDigit != 0) { 1279 #if defined(VERSION_JP) || defined(VERSION_SH) 1280 create_dl_translation_matrix(MENU_MTX_NOPUSH, 10 * *xMatrix, 0, 0); 1281 render_generic_char(tensDigit); 1282 #elif defined(VERSION_US) || defined(VERSION_CN) 1283 if (*xMatrix != 1) { 1284 create_dl_translation_matrix( 1285 MENU_MTX_NOPUSH, (f32)(gDialogCharWidths[DIALOG_CHAR_SPACE] * *xMatrix), 0, 0); 1286 } 1287 1288 render_generic_char(tensDigit); 1289 create_dl_translation_matrix(MENU_MTX_NOPUSH, (f32) gDialogCharWidths[tensDigit], 0, 0); 1290 *xMatrix = 1; 1291 (*linePos)++; 1292 #elif defined(VERSION_EU) 1293 render_generic_dialog_char_at_pos(dialog, gDialogX, gDialogY, tensDigit); 1294 gDialogX += gDialogCharWidths[tensDigit]; 1295 *linePos = 1; 1296 #endif 1297 } 1298 #ifndef VERSION_EU 1299 else { 1300 #if defined(VERSION_JP) || defined(VERSION_SH) 1301 (*xMatrix)++; 1302 #endif 1303 } 1304 #endif 1305 1306 #ifndef VERSION_EU 1307 #if defined(VERSION_JP) || defined(VERSION_SH) 1308 create_dl_translation_matrix(MENU_MTX_NOPUSH, 10 * *xMatrix, 0, 0); 1309 render_generic_char(onesDigit); 1310 #elif defined(VERSION_US) || defined(VERSION_CN) 1311 if (*xMatrix != 1) { 1312 create_dl_translation_matrix( 1313 MENU_MTX_NOPUSH, (f32)(gDialogCharWidths[DIALOG_CHAR_SPACE] * (*xMatrix - 1)), 0, 0); 1314 } 1315 1316 render_generic_char(onesDigit); 1317 create_dl_translation_matrix(MENU_MTX_NOPUSH, (f32) gDialogCharWidths[onesDigit], 0, 0); 1318 #endif 1319 (*linePos)++; 1320 *xMatrix = 1; 1321 #else // VERSION_EU 1322 render_generic_dialog_char_at_pos(dialog, gDialogX, gDialogY, onesDigit); 1323 gDialogX += gDialogCharWidths[onesDigit]; 1324 *linePos = 1; 1325 #endif 1326 } 1327 1328 #if defined(VERSION_US) || defined(VERSION_EU) || defined(VERSION_CN) 1329 #ifdef VERSION_EU 1330 void render_multi_text_string_lines(s8 multiTextId, s8 lineNum, s8 linesPerBox, UNUSED s8 xMatrix, s8 lowerBound, struct DialogEntry *dialog) 1331 #else 1332 void render_multi_text_string_lines(s8 multiTextId, s8 lineNum, s16 *linePos, s8 linesPerBox, s8 xMatrix, s8 lowerBound) 1333 #endif 1334 { 1335 s8 i; 1336 struct MultiTextEntry textLengths[2] = { 1337 { { 3, TEXT_THE_RAW } }, 1338 { { 3, TEXT_YOU_RAW } }, 1339 }; 1340 1341 if (lineNum >= lowerBound && lineNum <= (lowerBound + linesPerBox)) { 1342 #ifndef VERSION_EU 1343 if (*linePos != 0 || xMatrix != 1) { 1344 create_dl_translation_matrix( 1345 MENU_MTX_NOPUSH, (gDialogCharWidths[DIALOG_CHAR_SPACE] * (xMatrix - 1)), 0, 0); 1346 } 1347 #endif 1348 for (i = 0; i < textLengths[multiTextId].str[0]; i++) { 1349 #ifdef VERSION_EU 1350 render_generic_dialog_char_at_pos(dialog, gDialogX, gDialogY, textLengths[multiTextId].str[1 + i]); 1351 gDialogX += gDialogCharWidths[textLengths[multiTextId].str[1 + i]]; 1352 #else 1353 render_generic_char(textLengths[multiTextId].str[1 + i]); 1354 create_dl_translation_matrix( 1355 MENU_MTX_NOPUSH, (gDialogCharWidths[textLengths[multiTextId].str[1 + i]]), 0, 0); 1356 #endif 1357 } 1358 } 1359 1360 #ifndef VERSION_EU 1361 linePos += textLengths[multiTextId].str[0]; 1362 #endif 1363 } 1364 #endif 1365 1366 #ifdef VERSION_EU 1367 void render_dialog_lowercase_diacritic(struct DialogEntry *dialog, u8 chr, u8 diacritic) { 1368 render_generic_dialog_char_at_pos(dialog, gDialogX, gDialogY, chr); 1369 render_generic_dialog_char_at_pos(dialog, gDialogX, gDialogY, diacritic + 0xE7); 1370 gDialogX += gDialogCharWidths[chr]; 1371 } 1372 1373 void render_dialog_uppercase_diacritic(struct DialogEntry *dialog, u8 chr, u8 diacritic) { 1374 render_generic_dialog_char_at_pos(dialog, gDialogX, gDialogY, chr); 1375 render_generic_dialog_char_at_pos(dialog, gDialogX, gDialogY - 4, diacritic + 0xE3); 1376 gDialogX += gDialogCharWidths[chr]; 1377 } 1378 #endif 1379 1380 u32 ensure_nonnegative(s16 value) { 1381 if (value < 0) { 1382 value = 0; 1383 } 1384 1385 return value; 1386 } 1387 1388 #ifdef VERSION_JP 1389 void handle_dialog_text_and_pages(s8 colorMode, struct DialogEntry *dialog) 1390 #else 1391 void handle_dialog_text_and_pages(s8 colorMode, struct DialogEntry *dialog, s8 lowerBound) 1392 #endif 1393 { 1394 UNUSED u64 filler; 1395 #ifdef VERSION_EU 1396 s16 startY = 14; 1397 #endif 1398 #ifdef VERSION_CN 1399 s32 strChar; 1400 #else 1401 u8 strChar; 1402 #endif 1403 u8 *str = segmented_to_virtual(dialog->str); 1404 s8 lineNum = 1; 1405 s8 totalLines; 1406 s8 pageState = DIALOG_PAGE_STATE_NONE; 1407 UNUSED s8 mark = DIALOG_MARK_NONE; // only used in JP and SH 1408 s8 xMatrix = 1; 1409 s8 linesPerBox = dialog->linesPerBox; 1410 s16 strIndex; 1411 #ifndef VERSION_EU 1412 s16 linePos = 0; 1413 #endif 1414 1415 if (gMenuState == MENU_STATE_DIALOG_SCROLLING) { 1416 // If scrolling, consider the number of lines for both 1417 // the current page and the page being scrolled to. 1418 #ifdef VERSION_CN 1419 totalLines = linesPerBox * 2 - 1; 1420 #else 1421 totalLines = linesPerBox * 2 + 1; 1422 #endif 1423 } else { 1424 #ifdef VERSION_CN 1425 totalLines = linesPerBox; 1426 #else 1427 totalLines = linesPerBox + 1; 1428 #endif 1429 } 1430 1431 gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); 1432 1433 strIndex = gDialogPageStartStrIndex; 1434 1435 #ifdef VERSION_EU 1436 gDialogX = 0; 1437 gDialogY = startY; 1438 #endif 1439 1440 if (gMenuState == MENU_STATE_DIALOG_SCROLLING) { 1441 #ifdef VERSION_EU 1442 gDialogY -= gDialogScrollOffsetY; 1443 #else 1444 create_dl_translation_matrix(MENU_MTX_NOPUSH, 0, (f32) gDialogScrollOffsetY, 0); 1445 #endif 1446 } 1447 1448 #ifndef VERSION_EU 1449 create_dl_translation_matrix(MENU_MTX_PUSH, X_VAL3, 2 - lineNum * Y_VAL3, 0); 1450 #endif 1451 1452 while (pageState == DIALOG_PAGE_STATE_NONE) { 1453 change_and_flash_dialog_text_color_lines(colorMode, lineNum); 1454 1455 #ifdef VERSION_CN 1456 strChar = str[strIndex] << 8 | str[strIndex + 1]; 1457 #else 1458 strChar = str[strIndex]; 1459 #endif 1460 1461 switch (strChar) { 1462 case SPECIAL_CHAR(DIALOG_CHAR_TERMINATOR): 1463 pageState = DIALOG_PAGE_STATE_END; 1464 #ifndef VERSION_EU 1465 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 1466 #endif 1467 break; 1468 case SPECIAL_CHAR(DIALOG_CHAR_NEWLINE): 1469 lineNum++; 1470 #ifdef VERSION_EU 1471 handle_dialog_scroll_page_state(lineNum, totalLines, &pageState, &xMatrix); 1472 gDialogX = 0; 1473 #else 1474 handle_dialog_scroll_page_state(lineNum, totalLines, &pageState, &xMatrix, &linePos); 1475 #ifdef VERSION_SH 1476 mark = DIALOG_MARK_NONE; 1477 #endif 1478 #endif 1479 break; 1480 1481 #ifdef VERSION_EU 1482 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_A_GRAVE): 1483 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_A_CIRCUMFLEX): 1484 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_A_UMLAUT): 1485 render_dialog_lowercase_diacritic(dialog, ASCII_TO_DIALOG('a'), strChar & 0xF); 1486 break; 1487 case SPECIAL_CHAR(DIALOG_CHAR_UPPER_A_GRAVE): 1488 case SPECIAL_CHAR(DIALOG_CHAR_UPPER_A_CIRCUMFLEX): 1489 case SPECIAL_CHAR(DIALOG_CHAR_UPPER_A_UMLAUT): 1490 render_dialog_uppercase_diacritic(dialog, ASCII_TO_DIALOG('A'), strChar & 0xF); 1491 break; 1492 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_E_GRAVE): 1493 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_E_CIRCUMFLEX): 1494 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_E_UMLAUT): 1495 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_E_ACUTE): 1496 render_dialog_lowercase_diacritic(dialog, ASCII_TO_DIALOG('e'), strChar & 0xF); 1497 break; 1498 case SPECIAL_CHAR(DIALOG_CHAR_UPPER_E_GRAVE): 1499 case SPECIAL_CHAR(DIALOG_CHAR_UPPER_E_CIRCUMFLEX): 1500 case SPECIAL_CHAR(DIALOG_CHAR_UPPER_E_UMLAUT): 1501 case SPECIAL_CHAR(DIALOG_CHAR_UPPER_E_ACUTE): 1502 render_dialog_uppercase_diacritic(dialog, ASCII_TO_DIALOG('E'), strChar & 0xF); 1503 break; 1504 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_U_GRAVE): 1505 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_U_CIRCUMFLEX): 1506 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_U_UMLAUT): 1507 render_dialog_lowercase_diacritic(dialog, ASCII_TO_DIALOG('u'), strChar & 0xF); 1508 break; 1509 case SPECIAL_CHAR(DIALOG_CHAR_UPPER_U_GRAVE): 1510 case SPECIAL_CHAR(DIALOG_CHAR_UPPER_U_CIRCUMFLEX): 1511 case SPECIAL_CHAR(DIALOG_CHAR_UPPER_U_UMLAUT): 1512 render_dialog_uppercase_diacritic(dialog, ASCII_TO_DIALOG('U'), strChar & 0xF); 1513 break; 1514 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_O_CIRCUMFLEX): 1515 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_O_UMLAUT): 1516 render_dialog_lowercase_diacritic(dialog, ASCII_TO_DIALOG('o'), strChar & 0xF); 1517 break; 1518 case SPECIAL_CHAR(DIALOG_CHAR_UPPER_O_CIRCUMFLEX): 1519 case SPECIAL_CHAR(DIALOG_CHAR_UPPER_O_UMLAUT): 1520 render_dialog_uppercase_diacritic(dialog, ASCII_TO_DIALOG('O'), strChar & 0xF); 1521 break; 1522 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_I_CIRCUMFLEX): 1523 case SPECIAL_CHAR(DIALOG_CHAR_LOWER_I_UMLAUT): 1524 render_dialog_lowercase_diacritic(dialog, DIALOG_CHAR_I_NO_DIA, strChar & 0xF); 1525 break; 1526 #else 1527 case SPECIAL_CHAR(DIALOG_CHAR_DAKUTEN): 1528 #ifndef VERSION_CN 1529 mark = DIALOG_MARK_DAKUTEN; 1530 #endif 1531 break; 1532 case SPECIAL_CHAR(DIALOG_CHAR_PERIOD_OR_HANDAKUTEN): 1533 #ifndef VERSION_CN 1534 mark = DIALOG_MARK_HANDAKUTEN; 1535 #endif 1536 break; 1537 #endif 1538 1539 #ifdef VERSION_CN 1540 case SPECIAL_CHAR(DIALOG_CHAR_STAR_OPEN): // ??? 1541 xMatrix += 2; 1542 linePos += 2; 1543 break; 1544 #endif 1545 1546 case SPECIAL_CHAR(DIALOG_CHAR_SPACE): 1547 #ifdef VERSION_EU 1548 gDialogX += gDialogCharWidths[DIALOG_CHAR_SPACE]; 1549 #else 1550 #if defined(VERSION_JP) || defined(VERSION_SH) 1551 if (linePos != 0) { 1552 #endif 1553 xMatrix++; 1554 #if defined(VERSION_JP) || defined(VERSION_SH) 1555 } 1556 #endif 1557 linePos++; 1558 #endif 1559 break; 1560 1561 #if defined(VERSION_JP) || defined(VERSION_SH) 1562 case SPECIAL_CHAR(DIALOG_CHAR_PERIOD): 1563 adjust_pos_and_print_period_char(&xMatrix, &linePos); 1564 break; 1565 #else 1566 case SPECIAL_CHAR(DIALOG_CHAR_SLASH): 1567 #ifdef VERSION_EU 1568 gDialogX += gDialogCharWidths[DIALOG_CHAR_SPACE] * 2; 1569 #else 1570 xMatrix += 2; 1571 linePos += 2; 1572 #endif 1573 break; 1574 case SPECIAL_CHAR(DIALOG_CHAR_MULTI_THE): 1575 #ifdef VERSION_EU 1576 render_multi_text_string_lines(STRING_THE, lineNum, linesPerBox, xMatrix, lowerBound, dialog); 1577 #else 1578 render_multi_text_string_lines(STRING_THE, lineNum, &linePos, linesPerBox, xMatrix, lowerBound); 1579 #endif 1580 xMatrix = 1; 1581 break; 1582 case SPECIAL_CHAR(DIALOG_CHAR_MULTI_YOU): 1583 #ifdef VERSION_EU 1584 render_multi_text_string_lines(STRING_YOU, lineNum, linesPerBox, xMatrix, lowerBound, dialog); 1585 #else 1586 render_multi_text_string_lines(STRING_YOU, lineNum, &linePos, linesPerBox, xMatrix, lowerBound); 1587 #endif 1588 xMatrix = 1; 1589 break; 1590 #endif 1591 1592 case SPECIAL_CHAR(DIALOG_CHAR_STAR_COUNT): 1593 #ifdef VERSION_EU 1594 render_star_count_dialog_text(dialog, &xMatrix); 1595 #else 1596 render_star_count_dialog_text(&xMatrix, &linePos); 1597 #endif 1598 break; 1599 1600 #ifdef VERSION_EU 1601 case SPECIAL_CHAR(DIALOG_CHAR_DOUBLE_LOW_QUOTE): 1602 render_generic_dialog_char_at_pos(dialog, gDialogX, gDialogY + 8, 0xF6); 1603 gDialogX += gDialogCharWidths[0xF6]; 1604 break; 1605 #endif 1606 1607 default: // any other character 1608 #ifdef VERSION_CN 1609 if (strChar >= 0x0100) { 1610 strChar = strChar * 2 - 0x0100; 1611 1612 if (lineNum < lowerBound || lineNum > (lowerBound + linesPerBox)) { 1613 break; 1614 } 1615 1616 if (linePos != 0 || xMatrix != 1) { 1617 create_dl_translation_matrix(MENU_MTX_NOPUSH, gDialogCharWidths[DIALOG_CHAR_SPACE] * (xMatrix - 1), 0.0f, 0.0f); 1618 } 1619 1620 render_generic_char_cn(strChar); 1621 create_dl_translation_matrix(MENU_MTX_NOPUSH, 8.0f, 0.0f, 0.0f); 1622 render_generic_char_cn(strChar + 1); 1623 create_dl_translation_matrix(MENU_MTX_NOPUSH, 8.0f, 0.0f, 0.0f); 1624 } else { 1625 if (lineNum < lowerBound || lineNum > (lowerBound + linesPerBox)) { 1626 break; 1627 } 1628 1629 if (linePos != 0 || xMatrix != 1) { 1630 create_dl_translation_matrix(MENU_MTX_NOPUSH, gDialogCharWidths[DIALOG_CHAR_SPACE] * (xMatrix - 1), 0.0f, 0.0f); 1631 } 1632 1633 render_generic_char(strChar); 1634 create_dl_translation_matrix(MENU_MTX_NOPUSH, gDialogCharWidths[strChar], 0.0f, 0.0f); 1635 } 1636 1637 xMatrix = 1; 1638 linePos++; 1639 #elif defined(VERSION_JP) || defined(VERSION_SH) 1640 #ifdef VERSION_SH 1641 if (lineNum >= lowerBound && lineNum <= (lowerBound + linesPerBox)) { 1642 #endif 1643 if (linePos != 0) { 1644 create_dl_translation_matrix(MENU_MTX_NOPUSH, 10 * xMatrix, 0, 0); 1645 } 1646 1647 render_generic_char(strChar); 1648 xMatrix = 1; 1649 linePos++; 1650 1651 if (mark != DIALOG_MARK_NONE) { 1652 create_dl_translation_matrix(MENU_MTX_PUSH, 5.0f, 7.0f, 0); 1653 render_generic_char(DIALOG_CHAR_MARK_START + mark); 1654 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 1655 mark = DIALOG_MARK_NONE; 1656 } 1657 #ifdef VERSION_SH 1658 } 1659 #endif 1660 #elif defined(VERSION_US) 1661 if (lineNum >= lowerBound && lineNum <= (lowerBound + linesPerBox)) { 1662 if (linePos != 0 || xMatrix != 1) { 1663 create_dl_translation_matrix( 1664 MENU_MTX_NOPUSH, (f32)(gDialogCharWidths[DIALOG_CHAR_SPACE] * (xMatrix - 1)), 0, 0); 1665 } 1666 1667 render_generic_char(strChar); 1668 create_dl_translation_matrix(MENU_MTX_NOPUSH, (f32)(gDialogCharWidths[strChar]), 0, 0); 1669 xMatrix = 1; 1670 linePos++; 1671 } 1672 #else // VERSION_EU 1673 if (lineNum >= lowerBound && lineNum <= (lowerBound + linesPerBox)) { 1674 render_generic_dialog_char_at_pos(dialog, gDialogX, gDialogY, strChar); 1675 } 1676 gDialogX += gDialogCharWidths[strChar]; 1677 #endif 1678 } 1679 1680 #ifdef VERSION_JP 1681 if (linePos == 12) { 1682 if (str[strIndex + 1] == DIALOG_CHAR_PERIOD) { 1683 adjust_pos_and_print_period_char(&xMatrix, &linePos); 1684 strIndex++; 1685 } 1686 1687 if (str[strIndex + 1] == DIALOG_CHAR_COMMA) { 1688 create_dl_translation_matrix(MENU_MTX_NOPUSH, 10 * xMatrix, 0, 0); 1689 render_generic_char(DIALOG_CHAR_COMMA); 1690 strIndex++; 1691 } 1692 1693 if (str[strIndex + 1] == DIALOG_CHAR_NEWLINE) { 1694 strIndex++; 1695 } 1696 1697 if (str[strIndex + 1] == DIALOG_CHAR_TERMINATOR) { 1698 pageState = DIALOG_PAGE_STATE_END; 1699 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 1700 break; // exit loop 1701 } else { 1702 lineNum++; 1703 handle_dialog_scroll_page_state(lineNum, totalLines, &pageState, &xMatrix, &linePos); 1704 } 1705 } 1706 #endif 1707 1708 #ifdef VERSION_CN 1709 strIndex += 2; 1710 #else 1711 strIndex++; 1712 #endif 1713 } 1714 1715 gSPDisplayList(gDisplayListHead++, dl_ia_text_end); 1716 1717 if (gMenuState == MENU_STATE_DIALOG_OPEN) { 1718 if (pageState == DIALOG_PAGE_STATE_END) { 1719 gNextDialogPageStartStrIndex = -1; 1720 } else { 1721 gNextDialogPageStartStrIndex = strIndex; 1722 } 1723 } 1724 1725 gLastDialogLineNum = lineNum; 1726 } 1727 1728 #if defined(VERSION_JP) || defined(VERSION_SH) 1729 #define X_VAL4_1 50 1730 #define X_VAL4_2 25 1731 #define Y_VAL4_1 1 1732 #define Y_VAL4_2 20 1733 #elif defined(VERSION_CN) 1734 #define X_VAL4_1 58 1735 #define X_VAL4_2 0 1736 #define Y_VAL4_1 2 1737 #define Y_VAL4_2 20 1738 #else 1739 #define X_VAL4_1 56 1740 #define X_VAL4_2 9 1741 #define Y_VAL4_1 2 1742 #define Y_VAL4_2 16 1743 #endif 1744 1745 void render_dialog_triangle_choice(void) { 1746 if (gMenuState == MENU_STATE_DIALOG_OPEN) { 1747 handle_menu_scrolling(MENU_SCROLL_HORIZONTAL, &gMenuLineNum, 1, 2); 1748 } 1749 1750 create_dl_translation_matrix(MENU_MTX_NOPUSH, ((gMenuLineNum - 1) * X_VAL4_1) + X_VAL4_2, Y_VAL4_1 - (gLastDialogLineNum * Y_VAL4_2), 0); 1751 1752 if (gDialogBoxType == DIALOG_TYPE_ROTATE) { // White Text 1753 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); 1754 } else { // Black Text 1755 gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255); 1756 } 1757 1758 gSPDisplayList(gDisplayListHead++, dl_draw_triangle); 1759 } 1760 1761 #if defined(VERSION_JP) || defined(VERSION_SH) 1762 #define X_VAL5 123.0f 1763 #define Y_VAL5_1 -20 1764 #define Y_VAL5_2 2 1765 #define X_Y_VAL6 0.8f 1766 #elif defined(VERSION_US) 1767 #define X_VAL5 118.0f 1768 #define Y_VAL5_1 -16 1769 #define Y_VAL5_2 5 1770 #define X_Y_VAL6 0.8f 1771 #elif defined(VERSION_EU) 1772 #define X_VAL5 122.0f 1773 #define Y_VAL5_1 -16 1774 #define Y_VAL5_2 3 1775 #define X_Y_VAL6 0.5f 1776 #elif defined(VERSION_CN) 1777 #define X_VAL5 118.0f 1778 #define Y_VAL5_1 -18 1779 #define Y_VAL5_2 5 1780 #define X_Y_VAL6 0.8f 1781 #endif 1782 1783 void render_dialog_triangle_next(s8 linesPerBox) { 1784 s32 globalTimer = gGlobalTimer; 1785 1786 if (globalTimer & 8) { 1787 return; 1788 } 1789 1790 create_dl_translation_matrix(MENU_MTX_PUSH, X_VAL5, (linesPerBox * Y_VAL5_1) + Y_VAL5_2, 0); 1791 create_dl_scale_matrix(MENU_MTX_NOPUSH, X_Y_VAL6, X_Y_VAL6, 1.0f); 1792 create_dl_rotation_matrix(MENU_MTX_NOPUSH, -90.0f, 0, 0, 1.0f); 1793 1794 if (gDialogBoxType == DIALOG_TYPE_ROTATE) { // White Text 1795 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); 1796 } else { // Black Text 1797 gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 255); 1798 } 1799 1800 gSPDisplayList(gDisplayListHead++, dl_draw_triangle); 1801 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 1802 } 1803 1804 void handle_special_dialog_text(s16 dialogID) { // dialog ID tables, in order 1805 // King Bob-omb (Start), Whomp (Start), King Bob-omb (throw him out), Eyerock (Start), Wiggler (Start) 1806 s16 dialogBossStart[] = { DIALOG_017, DIALOG_114, DIALOG_128, DIALOG_117, DIALOG_150 }; 1807 // Koopa the Quick (BoB), Koopa the Quick (THI), Penguin Race, Fat Penguin Race (120 stars) 1808 s16 dialogRaceSound[] = { DIALOG_005, DIALOG_009, DIALOG_055, DIALOG_164 }; 1809 // Red Switch, Green Switch, Blue Switch, 100 coins star, Bowser Red Coin Star 1810 s16 dialogStarSound[] = { DIALOG_010, DIALOG_011, DIALOG_012, DIALOG_013, DIALOG_014 }; 1811 // King Bob-omb (Start), Whomp (Defeated), King Bob-omb (Defeated, missing in JP), Eyerock (Defeated), Wiggler (Defeated) 1812 #if BUGFIX_KING_BOB_OMB_FADE_MUSIC 1813 s16 dialogBossStop[] = { DIALOG_017, DIALOG_115, DIALOG_116, DIALOG_118, DIALOG_152 }; 1814 #else 1815 //! @bug JP misses King Bob-omb defeated DIALOG_116, meaning that the boss music will still 1816 //! play after King Bob-omb is defeated until BoB loads it's music after the star cutscene 1817 s16 dialogBossStop[] = { DIALOG_017, DIALOG_115, DIALOG_118, DIALOG_152 }; 1818 #endif 1819 s16 i; 1820 1821 for (i = 0; i < (s16) ARRAY_COUNT(dialogBossStart); i++) { 1822 if (dialogBossStart[i] == dialogID) { 1823 seq_player_unlower_volume(SEQ_PLAYER_LEVEL, 60); 1824 play_music(SEQ_PLAYER_LEVEL, SEQUENCE_ARGS(4, SEQ_EVENT_BOSS), 0); 1825 return; 1826 } 1827 } 1828 1829 for (i = 0; i < (s16) ARRAY_COUNT(dialogRaceSound); i++) { 1830 if (dialogRaceSound[i] == dialogID && gMenuLineNum == DIALOG_RESPONSE_YES) { 1831 play_race_fanfare(); 1832 return; 1833 } 1834 } 1835 1836 for (i = 0; i < (s16) ARRAY_COUNT(dialogStarSound); i++) { 1837 if (dialogStarSound[i] == dialogID && gMenuLineNum == DIALOG_RESPONSE_YES) { 1838 play_sound(SOUND_MENU_STAR_SOUND, gGlobalSoundSource); 1839 return; 1840 } 1841 } 1842 1843 for (i = 0; i < (s16) ARRAY_COUNT(dialogBossStop); i++) { 1844 if (dialogBossStop[i] == dialogID) { 1845 seq_player_fade_out(SEQ_PLAYER_LEVEL, 1); 1846 return; 1847 } 1848 } 1849 } 1850 1851 s16 gMenuMode = MENU_MODE_NONE; 1852 1853 #ifdef VERSION_CN 1854 u8 gEndCutsceneStrEn0[] = { TEXT_EMPTY_STRING }; 1855 u8 gEndCutsceneStrEn1[] = { TEXT_EMPTY_STRING }; 1856 u8 gEndCutsceneStrEn2[] = { TEXT_EMPTY_STRING }; 1857 u8 gEndCutsceneStrEn3[] = { TEXT_EMPTY_STRING }; 1858 u8 gEndCutsceneStrEn4[] = { TEXT_EMPTY_STRING }; 1859 u8 gEndCutsceneStrEn5[] = { TEXT_EMPTY_STRING }; 1860 u8 gEndCutsceneStrEn6[] = { TEXT_EMPTY_STRING }; 1861 u8 gEndCutsceneStrEn7[] = { TEXT_EMPTY_STRING }; 1862 u8 gEndCutsceneStrEn8[] = { TEXT_EMPTY_STRING }; 1863 #else 1864 u8 gEndCutsceneStrEn0[] = { TEXT_FILE_MARIO_EXCLAMATION }; 1865 u8 gEndCutsceneStrEn1[] = { TEXT_POWER_STARS_RESTORED }; 1866 u8 gEndCutsceneStrEn2[] = { TEXT_THANKS_TO_YOU }; 1867 u8 gEndCutsceneStrEn3[] = { TEXT_THANK_YOU_MARIO }; 1868 u8 gEndCutsceneStrEn4[] = { TEXT_SOMETHING_SPECIAL }; 1869 u8 gEndCutsceneStrEn5[] = { TEXT_LISTEN_EVERYBODY }; 1870 u8 gEndCutsceneStrEn6[] = { TEXT_LETS_HAVE_CAKE }; 1871 u8 gEndCutsceneStrEn7[] = { TEXT_FOR_MARIO }; 1872 u8 gEndCutsceneStrEn8[] = { TEXT_FILE_MARIO_QUESTION }; 1873 #endif 1874 1875 u8 *gEndCutsceneStringsEn[] = { 1876 gEndCutsceneStrEn0, 1877 gEndCutsceneStrEn1, 1878 gEndCutsceneStrEn2, 1879 gEndCutsceneStrEn3, 1880 gEndCutsceneStrEn4, 1881 gEndCutsceneStrEn5, 1882 gEndCutsceneStrEn6, 1883 gEndCutsceneStrEn7, 1884 // This [8] string is actually unused. In the cutscene handler, the developers do not 1885 // set the 8th one, but use the first string again at the very end, so Peach ends up 1886 // saying "Mario!" twice. It is likely that she was originally meant to say "Mario?" at 1887 // the end but the developers changed their mind, possibly because the line recorded 1888 // sounded more like an exclamation than a question. 1889 gEndCutsceneStrEn8, 1890 NULL 1891 }; 1892 1893 #ifdef VERSION_EU 1894 u8 gEndCutsceneStrFr0[] = { TEXT_FILE_MARIO_EXCLAMATION }; 1895 u8 gEndCutsceneStrFr1[] = { TEXT_POWER_STARS_RESTORED_FR }; 1896 u8 gEndCutsceneStrFr2[] = { TEXT_THANKS_TO_YOU_FR }; 1897 u8 gEndCutsceneStrFr3[] = { TEXT_THANK_YOU_MARIO_FR }; 1898 u8 gEndCutsceneStrFr4[] = { TEXT_SOMETHING_SPECIAL_FR }; 1899 u8 gEndCutsceneStrFr5[] = { TEXT_COME_ON_EVERYBODY_FR }; 1900 u8 gEndCutsceneStrFr6[] = { TEXT_LETS_HAVE_CAKE_FR }; 1901 u8 gEndCutsceneStrFr7[] = { TEXT_FOR_MARIO_FR }; 1902 u8 gEndCutsceneStrFr8[] = { TEXT_FILE_MARIO_QUESTION }; 1903 1904 u8 *gEndCutsceneStringsFr[] = { 1905 gEndCutsceneStrFr0, 1906 gEndCutsceneStrFr1, 1907 gEndCutsceneStrFr2, 1908 gEndCutsceneStrFr3, 1909 gEndCutsceneStrFr4, 1910 gEndCutsceneStrFr5, 1911 gEndCutsceneStrFr6, 1912 gEndCutsceneStrFr7, 1913 gEndCutsceneStrFr8, 1914 NULL 1915 }; 1916 1917 u8 gEndCutsceneStrDe0[] = { TEXT_FILE_MARIO_EXCLAMATION }; 1918 u8 gEndCutsceneStrDe1[] = { TEXT_POWER_STARS_RESTORED_DE }; 1919 u8 gEndCutsceneStrDe2[] = { TEXT_THANKS_TO_YOU_DE }; 1920 u8 gEndCutsceneStrDe3[] = { TEXT_THANK_YOU_MARIO_DE }; 1921 u8 gEndCutsceneStrDe4[] = { TEXT_SOMETHING_SPECIAL_DE }; 1922 u8 gEndCutsceneStrDe5[] = { TEXT_COME_ON_EVERYBODY_DE }; 1923 u8 gEndCutsceneStrDe6[] = { TEXT_LETS_HAVE_CAKE_DE }; 1924 u8 gEndCutsceneStrDe7[] = { TEXT_FOR_MARIO_DE }; 1925 u8 gEndCutsceneStrDe8[] = { TEXT_FILE_MARIO_QUESTION }; 1926 1927 u8 *gEndCutsceneStringsDe[] = { 1928 gEndCutsceneStrDe0, 1929 gEndCutsceneStrDe1, 1930 gEndCutsceneStrDe2, 1931 gEndCutsceneStrDe3, 1932 gEndCutsceneStrDe4, 1933 gEndCutsceneStrDe5, 1934 gEndCutsceneStrDe6, 1935 gEndCutsceneStrDe7, 1936 gEndCutsceneStrDe8, 1937 NULL 1938 }; 1939 #endif 1940 1941 u16 gCutsceneMsgFade = 0; 1942 s16 gCutsceneMsgIndex = -1; 1943 s16 gCutsceneMsgDuration = -1; 1944 s16 gCutsceneMsgTimer = 0; 1945 s8 gDialogCameraAngleIndex = CAM_SELECTION_MARIO; 1946 s8 gDialogCourseActNum = 1; 1947 1948 #if defined(VERSION_JP) || defined(VERSION_SH) 1949 #define DIAG_VAL1 20 1950 #define DIAG_VAL2 240 1951 #define DIAG_VAL3 130 1952 #define DIAG_VAL4 4 1953 #else 1954 #define DIAG_VAL1 16 1955 #if defined(VERSION_US) || defined(VERSION_CN) 1956 #define DIAG_VAL2 240 1957 #else 1958 #define DIAG_VAL2 238 1959 #endif 1960 #define DIAG_VAL3 132 1961 #define DIAG_VAL4 5 1962 #endif 1963 1964 void render_dialog_entries(void) { 1965 #ifdef VERSION_EU 1966 s8 lowerBound; 1967 #endif 1968 void **dialogTable; 1969 struct DialogEntry *dialog; 1970 #if defined(VERSION_US) || defined(VERSION_SH) || defined(VERSION_CN) 1971 s8 lowerBound; 1972 #endif 1973 1974 #ifdef VERSION_EU 1975 gInGameLanguage = eu_get_language(); 1976 switch (gInGameLanguage) { 1977 case LANGUAGE_ENGLISH: 1978 dialogTable = segmented_to_virtual(dialog_table_eu_en); 1979 break; 1980 case LANGUAGE_FRENCH: 1981 dialogTable = segmented_to_virtual(dialog_table_eu_fr); 1982 break; 1983 case LANGUAGE_GERMAN: 1984 dialogTable = segmented_to_virtual(dialog_table_eu_de); 1985 break; 1986 } 1987 #else 1988 dialogTable = segmented_to_virtual(seg2_dialog_table); 1989 #endif 1990 dialog = segmented_to_virtual(dialogTable[gDialogID]); 1991 1992 // if the dialog entry is invalid, set the ID to DIALOG_NONE. 1993 if (dialog == segmented_to_virtual(NULL)) { 1994 gDialogID = DIALOG_NONE; 1995 return; 1996 } 1997 1998 #ifdef VERSION_EU 1999 gDialogX = 0; 2000 gDialogY = 0; 2001 #endif 2002 2003 switch (gMenuState) { 2004 case MENU_STATE_DIALOG_OPENING: 2005 if (gDialogBoxAngle == DIALOG_BOX_ANGLE_DEFAULT) { 2006 play_dialog_sound(gDialogID); 2007 play_sound(SOUND_MENU_MESSAGE_APPEAR, gGlobalSoundSource); 2008 } 2009 2010 if (gDialogBoxType == DIALOG_TYPE_ROTATE) { 2011 gDialogBoxAngle -= 7.5; 2012 gDialogBoxScale -= 1.5; 2013 } else { 2014 gDialogBoxAngle -= 10.0; 2015 gDialogBoxScale -= 2.0; 2016 } 2017 2018 if (gDialogBoxAngle == 0.0f) { 2019 gMenuState = MENU_STATE_DIALOG_OPEN; 2020 gMenuLineNum = 1; 2021 } 2022 #ifndef VERSION_JP 2023 lowerBound = 1; 2024 #endif 2025 break; 2026 2027 case MENU_STATE_DIALOG_OPEN: 2028 gDialogBoxAngle = 0.0f; 2029 2030 if ((gPlayer3Controller->buttonPressed & A_BUTTON) 2031 || (gPlayer3Controller->buttonPressed & B_BUTTON)) { 2032 if (gNextDialogPageStartStrIndex == -1) { 2033 handle_special_dialog_text(gDialogID); 2034 gMenuState = MENU_STATE_DIALOG_CLOSING; 2035 } else { 2036 gMenuState = MENU_STATE_DIALOG_SCROLLING; 2037 play_sound(SOUND_MENU_MESSAGE_NEXT_PAGE, gGlobalSoundSource); 2038 } 2039 } 2040 #ifndef VERSION_JP 2041 lowerBound = 1; 2042 #endif 2043 break; 2044 2045 case MENU_STATE_DIALOG_SCROLLING: 2046 gDialogScrollOffsetY += dialog->linesPerBox * 2; 2047 2048 if (gDialogScrollOffsetY >= dialog->linesPerBox * DIAG_VAL1) { 2049 gDialogPageStartStrIndex = gNextDialogPageStartStrIndex; 2050 gMenuState = MENU_STATE_DIALOG_OPEN; 2051 gDialogScrollOffsetY = 0; 2052 } 2053 #ifndef VERSION_JP 2054 lowerBound = (gDialogScrollOffsetY / DIAG_VAL1) + 1; 2055 #endif 2056 break; 2057 2058 case MENU_STATE_DIALOG_CLOSING: 2059 if (gDialogBoxAngle == 20.0f) { 2060 level_set_transition(0, NULL); 2061 play_sound(SOUND_MENU_MESSAGE_DISAPPEAR, gGlobalSoundSource); 2062 2063 if (gDialogBoxType == DIALOG_TYPE_ZOOM) { 2064 trigger_cutscene_dialog(2); 2065 } 2066 2067 gDialogResponse = gMenuLineNum; 2068 } 2069 2070 gDialogBoxAngle += 10.0f; 2071 gDialogBoxScale += 2.0f; 2072 2073 if (gDialogBoxAngle == DIALOG_BOX_ANGLE_DEFAULT) { 2074 gMenuState = MENU_STATE_DEFAULT; 2075 gDialogID = DIALOG_NONE; 2076 gDialogPageStartStrIndex = 0; 2077 gDialogWithChoice = FALSE; 2078 gNextDialogPageStartStrIndex = 0; 2079 gDialogResponse = DIALOG_RESPONSE_NONE; 2080 } 2081 #ifndef VERSION_JP 2082 lowerBound = 1; 2083 #endif 2084 break; 2085 } 2086 2087 render_dialog_box_type(dialog, dialog->linesPerBox); 2088 2089 #ifdef VERSION_CN 2090 // This isn't really a diff. The iQue compiler doesn't allow the use of ifdefs inside a macro for some reason, so those were eliminated. 2091 #ifdef WIDESCREEN 2092 #define ulx 0 2093 #define lrx SCREEN_WIDTH 2094 #else 2095 #define ulx ensure_nonnegative(dialog->leftOffset) 2096 #define lrx ensure_nonnegative(dialog->leftOffset + DIAG_VAL3) 2097 #endif 2098 gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, ulx, ensure_nonnegative(DIAG_VAL2 - dialog->width), 2099 lrx, ensure_nonnegative((256 - dialog->width) + (dialog->linesPerBox * 80 / DIAG_VAL4))); 2100 #undef ulx 2101 #undef lrx 2102 #else 2103 gDPSetScissor( 2104 gDisplayListHead++, G_SC_NON_INTERLACE, 2105 // Horizontal scissoring isn't really required and can potentially mess up widescreen enhancements. 2106 #ifdef WIDESCREEN 2107 0, 2108 #else 2109 ensure_nonnegative(dialog->leftOffset), 2110 #endif 2111 ensure_nonnegative(DIAG_VAL2 - dialog->width), 2112 #ifdef VERSION_EU 2113 #ifdef WIDESCREEN 2114 SCREEN_WIDTH, 2115 #else 2116 ensure_nonnegative(dialog->leftOffset + (DIAG_VAL3 / gDialogBoxScale)), 2117 #endif 2118 ensure_nonnegative((240 - dialog->width) + (dialog->linesPerBox * 80 / DIAG_VAL4 / gDialogBoxScale)) 2119 #else 2120 #ifdef WIDESCREEN 2121 SCREEN_WIDTH, 2122 #else 2123 ensure_nonnegative(dialog->leftOffset + DIAG_VAL3), 2124 #endif 2125 ensure_nonnegative((240 - dialog->width) + (dialog->linesPerBox * 80 / DIAG_VAL4)) 2126 #endif 2127 ); 2128 #endif 2129 2130 #ifdef VERSION_JP 2131 handle_dialog_text_and_pages(0, dialog); 2132 #else 2133 handle_dialog_text_and_pages(0, dialog, lowerBound); 2134 #endif 2135 2136 if (gNextDialogPageStartStrIndex == -1 && gDialogWithChoice == TRUE) { 2137 render_dialog_triangle_choice(); 2138 } 2139 2140 #ifdef VERSION_EU 2141 #undef BORDER_HEIGHT 2142 #define BORDER_HEIGHT 8 2143 #endif 2144 gDPSetScissor(gDisplayListHead++, G_SC_NON_INTERLACE, 2, 2, SCREEN_WIDTH - BORDER_HEIGHT / 2, SCREEN_HEIGHT - BORDER_HEIGHT / 2); 2145 #ifdef VERSION_EU 2146 #undef BORDER_HEIGHT 2147 #define BORDER_HEIGHT 1 2148 #endif 2149 2150 if (gNextDialogPageStartStrIndex != -1 && gMenuState == MENU_STATE_DIALOG_OPEN) { 2151 render_dialog_triangle_next(dialog->linesPerBox); 2152 } 2153 } 2154 2155 // Calls a gMenuMode value defined by render_menus_and_dialogs cases 2156 void set_menu_mode(s16 mode) { 2157 if (gMenuMode == MENU_MODE_NONE) { 2158 gMenuMode = mode; 2159 } 2160 } 2161 2162 void reset_cutscene_msg_fade(void) { 2163 gCutsceneMsgFade = 0; 2164 } 2165 2166 void dl_rgba16_begin_cutscene_msg_fade(void) { 2167 gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin); 2168 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gCutsceneMsgFade); 2169 } 2170 2171 void dl_rgba16_stop_cutscene_msg_fade(void) { 2172 gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end); 2173 2174 if (gCutsceneMsgFade < 250) { 2175 gCutsceneMsgFade += 25; 2176 } else { 2177 gCutsceneMsgFade = 255; 2178 } 2179 } 2180 2181 u8 ascii_to_credits_char(u8 c) { 2182 if (c >= 'A' && c <= 'Z') { 2183 return (c - ('A' - 0xA)); 2184 } 2185 2186 if (c >= 'a' && c <= 'z') { // remap lower to upper case 2187 return (c - ('a' - 0xA)); 2188 } 2189 2190 if (c == ' ') { 2191 return GLOBAL_CHAR_SPACE; 2192 } 2193 2194 if (c == '.') { 2195 return 0x24; 2196 } 2197 2198 if (c == '3') { 2199 return ASCII_TO_DIALOG('3'); 2200 } 2201 2202 if (c == '4') { 2203 return ASCII_TO_DIALOG('4'); 2204 } 2205 2206 if (c == '6') { 2207 return ASCII_TO_DIALOG('6'); 2208 } 2209 2210 return GLOBAL_CHAR_SPACE; 2211 } 2212 2213 void print_credits_str_ascii(s16 x, s16 y, const char *str) { 2214 s32 pos = 0; 2215 u8 c = str[pos]; 2216 u8 creditStr[100]; 2217 2218 while (c != '\0') { 2219 creditStr[pos++] = ascii_to_credits_char(c); 2220 c = str[pos]; 2221 } 2222 2223 creditStr[pos] = GLOBAL_CHAR_TERMINATOR; 2224 2225 print_credits_string(x, y, creditStr); 2226 } 2227 2228 void set_cutscene_message(s16 xOffset, s16 yOffset, s16 msgIndex, s16 msgDuration) { 2229 // is message done printing? 2230 if (gCutsceneMsgIndex == -1) { 2231 gCutsceneMsgIndex = msgIndex; 2232 gCutsceneMsgDuration = msgDuration; 2233 gCutsceneMsgTimer = 0; 2234 gCutsceneMsgXOffset = xOffset; 2235 gCutsceneMsgYOffset = yOffset; 2236 gCutsceneMsgFade = 0; 2237 } 2238 } 2239 2240 void do_cutscene_handler(void) { 2241 s16 x; 2242 2243 // is a cutscene playing? do not perform this handler's actions if so. 2244 if (gCutsceneMsgIndex == -1) { 2245 return; 2246 } 2247 2248 create_dl_ortho_matrix(); 2249 2250 gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); 2251 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gCutsceneMsgFade); 2252 2253 #ifdef VERSION_EU 2254 switch (eu_get_language()) { 2255 case LANGUAGE_ENGLISH: 2256 x = get_str_x_pos_from_center(gCutsceneMsgXOffset, gEndCutsceneStringsEn[gCutsceneMsgIndex], 10.0f); 2257 print_generic_string(x, 240 - gCutsceneMsgYOffset, gEndCutsceneStringsEn[gCutsceneMsgIndex]); 2258 break; 2259 case LANGUAGE_FRENCH: 2260 x = get_str_x_pos_from_center(gCutsceneMsgXOffset, gEndCutsceneStringsFr[gCutsceneMsgIndex], 10.0f); 2261 print_generic_string(x, 240 - gCutsceneMsgYOffset, gEndCutsceneStringsFr[gCutsceneMsgIndex]); 2262 break; 2263 case LANGUAGE_GERMAN: 2264 x = get_str_x_pos_from_center(gCutsceneMsgXOffset, gEndCutsceneStringsDe[gCutsceneMsgIndex], 10.0f); 2265 print_generic_string(x, 240 - gCutsceneMsgYOffset, gEndCutsceneStringsDe[gCutsceneMsgIndex]); 2266 break; 2267 } 2268 #else 2269 // get the x coordinate of where the cutscene string starts. 2270 x = get_str_x_pos_from_center(gCutsceneMsgXOffset, gEndCutsceneStringsEn[gCutsceneMsgIndex], 10.0f); 2271 print_generic_string(x, 240 - gCutsceneMsgYOffset, gEndCutsceneStringsEn[gCutsceneMsgIndex]); 2272 #endif 2273 2274 gSPDisplayList(gDisplayListHead++, dl_ia_text_end); 2275 2276 // if the timing variable is less than 5, increment 2277 // the fade until we are at full opacity. 2278 if (gCutsceneMsgTimer < 5) { 2279 gCutsceneMsgFade += 50; 2280 } 2281 2282 // if the cutscene frame length + the fade-in counter is 2283 // less than the timer, it means we have exceeded the 2284 // time that the message is supposed to remain on 2285 // screen. if (message_duration = 50) and (msg_timer = 55) 2286 // then after the first 5 frames, the message will remain 2287 // on screen for another 50 frames until it starts fading. 2288 if (gCutsceneMsgDuration + 5 < gCutsceneMsgTimer) { 2289 gCutsceneMsgFade -= 50; 2290 } 2291 2292 // like the first check, it takes 5 frames to fade out, so 2293 // perform a + 10 to account for the earlier check (10-5=5). 2294 if (gCutsceneMsgDuration + 10 < gCutsceneMsgTimer) { 2295 gCutsceneMsgIndex = -1; 2296 gCutsceneMsgFade = 0; 2297 gCutsceneMsgTimer = 0; 2298 return; 2299 } 2300 2301 gCutsceneMsgTimer++; 2302 } 2303 2304 #ifdef VERSION_JP 2305 #define PEACH_MESSAGE_TIMER 170 2306 #else 2307 #define PEACH_MESSAGE_TIMER 250 2308 #endif 2309 2310 #if defined(VERSION_JP) || defined(VERSION_SH) 2311 #define STR_X 53 2312 #define STR_Y 136 2313 #else 2314 #define STR_X 38 2315 #define STR_Y 142 2316 #endif 2317 2318 // "Dear Mario" message handler 2319 void print_peach_letter_message(void) { 2320 void **dialogTable; 2321 struct DialogEntry *dialog; 2322 u8 *str; 2323 2324 #ifdef VERSION_EU 2325 gInGameLanguage = eu_get_language(); 2326 switch (gInGameLanguage) { 2327 case LANGUAGE_ENGLISH: 2328 dialogTable = segmented_to_virtual(dialog_table_eu_en); 2329 break; 2330 case LANGUAGE_FRENCH: 2331 dialogTable = segmented_to_virtual(dialog_table_eu_fr); 2332 break; 2333 case LANGUAGE_GERMAN: 2334 dialogTable = segmented_to_virtual(dialog_table_eu_de); 2335 break; 2336 } 2337 #else 2338 dialogTable = segmented_to_virtual(seg2_dialog_table); 2339 #endif 2340 dialog = segmented_to_virtual(dialogTable[gDialogID]); 2341 str = segmented_to_virtual(dialog->str); 2342 2343 create_dl_translation_matrix(MENU_MTX_PUSH, 97.0f, 118.0f, 0); 2344 2345 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gCutsceneMsgFade); 2346 gSPDisplayList(gDisplayListHead++, castle_grounds_seg7_dl_0700EA58); 2347 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 2348 gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); 2349 gDPSetEnvColor(gDisplayListHead++, 20, 20, 20, gCutsceneMsgFade); 2350 2351 print_generic_string(STR_X, STR_Y, str); 2352 #if defined(VERSION_JP) 2353 gSPDisplayList(gDisplayListHead++, dl_ia_text_end); 2354 #endif 2355 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); 2356 #ifndef VERSION_JP 2357 gSPDisplayList(gDisplayListHead++, dl_ia_text_end); 2358 gDPSetEnvColor(gDisplayListHead++, 200, 80, 120, gCutsceneMsgFade); 2359 gSPDisplayList(gDisplayListHead++, castle_grounds_seg7_us_dl_0700F2E8); 2360 #endif 2361 2362 // at the start/end of message, reset the fade. 2363 if (gCutsceneMsgTimer == 0) { 2364 gCutsceneMsgFade = 0; 2365 } 2366 2367 // we're less than 20 increments, so increase the fade. 2368 if (gCutsceneMsgTimer < 20) { 2369 gCutsceneMsgFade += 10; 2370 } 2371 2372 // we're after PEACH_MESSAGE_TIMER increments, so decrease the fade. 2373 if (gCutsceneMsgTimer > PEACH_MESSAGE_TIMER) { 2374 gCutsceneMsgFade -= 10; 2375 } 2376 2377 // 20 increments after the start of the decrease, we're 2378 // back where we are, so reset everything at the end. 2379 if (gCutsceneMsgTimer > (PEACH_MESSAGE_TIMER + 20)) { 2380 gCutsceneMsgIndex = -1; 2381 gCutsceneMsgFade = 0; //! uselessly reset since the next execution will just set it to 0 again. 2382 gDialogID = DIALOG_NONE; 2383 gCutsceneMsgTimer = 0; 2384 return; // return to avoid incrementing the timer 2385 } 2386 2387 gCutsceneMsgTimer++; 2388 } 2389 2390 /** 2391 * Renders the cannon reticle when Mario is inside a cannon. 2392 * Formed by four triangles. 2393 */ 2394 void render_hud_cannon_reticle(void) { 2395 create_dl_translation_matrix(MENU_MTX_PUSH, 160.0f, 120.0f, 0); 2396 2397 gDPSetEnvColor(gDisplayListHead++, 50, 50, 50, 180); 2398 create_dl_translation_matrix(MENU_MTX_PUSH, -20.0f, -8.0f, 0); 2399 gSPDisplayList(gDisplayListHead++, dl_draw_triangle); 2400 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 2401 2402 create_dl_translation_matrix(MENU_MTX_PUSH, 20.0f, 8.0f, 0); 2403 create_dl_rotation_matrix(MENU_MTX_NOPUSH, 180.0f, 0, 0, 1.0f); 2404 gSPDisplayList(gDisplayListHead++, dl_draw_triangle); 2405 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 2406 2407 create_dl_translation_matrix(MENU_MTX_PUSH, 8.0f, -20.0f, 0); 2408 create_dl_rotation_matrix(MENU_MTX_NOPUSH, 90.0f, 0, 0, 1.0f); 2409 gSPDisplayList(gDisplayListHead++, dl_draw_triangle); 2410 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 2411 2412 create_dl_translation_matrix(MENU_MTX_PUSH, -8.0f, 20.0f, 0); 2413 create_dl_rotation_matrix(MENU_MTX_NOPUSH, -90.0f, 0, 0, 1.0f); 2414 gSPDisplayList(gDisplayListHead++, dl_draw_triangle); 2415 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 2416 2417 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 2418 } 2419 2420 void reset_red_coins_collected(void) { 2421 gRedCoinsCollected = 0; 2422 } 2423 2424 void change_dialog_camera_angle(void) { 2425 if (cam_select_alt_mode(0) == CAM_SELECTION_MARIO) { 2426 gDialogCameraAngleIndex = CAM_SELECTION_MARIO; 2427 } else { 2428 gDialogCameraAngleIndex = CAM_SELECTION_FIXED; 2429 } 2430 } 2431 2432 void shade_screen(void) { 2433 create_dl_translation_matrix(MENU_MTX_PUSH, GFX_DIMENSIONS_FROM_LEFT_EDGE(0), SCREEN_HEIGHT, 0); 2434 2435 // This is a bit weird. It reuses the dialog text box (width 130, height -80), 2436 // so scale to at least fit the screen. 2437 #ifdef WIDESCREEN 2438 create_dl_scale_matrix(MENU_MTX_NOPUSH, 2439 GFX_DIMENSIONS_ASPECT_RATIO * SCREEN_HEIGHT / 130.0f, 3.0f, 1.0f); 2440 #else 2441 create_dl_scale_matrix(MENU_MTX_NOPUSH, 2.6f, 3.4f, 1.0f); 2442 #endif 2443 2444 gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 110); 2445 gSPDisplayList(gDisplayListHead++, dl_draw_text_bg_box); 2446 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 2447 } 2448 2449 void print_animated_red_coin(s16 x, s16 y) { 2450 u32 globalTimer = gGlobalTimer; 2451 2452 create_dl_translation_matrix(MENU_MTX_PUSH, x, y, 0); 2453 create_dl_scale_matrix(MENU_MTX_NOPUSH, 0.2f, 0.2f, 1.0f); 2454 gDPSetRenderMode(gDisplayListHead++, G_RM_TEX_EDGE, G_RM_TEX_EDGE2); 2455 2456 switch (globalTimer & 6) { 2457 case 0: 2458 gSPDisplayList(gDisplayListHead++, coin_seg3_dl_03007940); 2459 break; 2460 case 2: 2461 gSPDisplayList(gDisplayListHead++, coin_seg3_dl_03007968); 2462 break; 2463 case 4: 2464 gSPDisplayList(gDisplayListHead++, coin_seg3_dl_03007990); 2465 break; 2466 case 6: 2467 gSPDisplayList(gDisplayListHead++, coin_seg3_dl_030079B8); 2468 break; 2469 } 2470 2471 gDPSetRenderMode(gDisplayListHead++, G_RM_AA_ZB_OPA_SURF, G_RM_AA_ZB_OPA_SURF2); 2472 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 2473 } 2474 2475 void render_pause_red_coins(void) { 2476 s8 x; 2477 2478 for (x = 0; x < gRedCoinsCollected; x++) { 2479 print_animated_red_coin(GFX_DIMENSIONS_FROM_RIGHT_EDGE(30) - x * 20, 16); 2480 } 2481 } 2482 2483 #ifdef VERSION_EU 2484 u8 gTextCourse[][7] = { 2485 { TEXT_COURSE }, 2486 { TEXT_COURSE_FR }, 2487 { TEXT_COURSE_DE } 2488 }; 2489 #define textCourse gTextCourse 2490 #endif 2491 2492 #if defined(VERSION_JP) || defined(VERSION_SH) 2493 #define CRS_NUM_X1 93 2494 #elif defined(VERSION_US) || defined(VERSION_CN) 2495 #define CRS_NUM_X1 100 2496 #elif defined(VERSION_EU) 2497 #define CRS_NUM_X1 get_string_width(LANGUAGE_ARRAY(textCourse)) + 51 2498 #endif 2499 2500 #if defined(VERSION_EU) 2501 #define TXT_COURSE_X 48 2502 #define TXT_STAR_X 89 2503 #define ACT_NAME_X 107 2504 #define CRS_NAME_START 3 2505 #define CRS_NAME_X 108 2506 #define SECRET_CRS_NAME_X get_str_x_pos_from_center(159, &courseName[CRS_NAME_START], 10.0f) 2507 #define MYSCORE_X 48 2508 #elif defined(VERSION_CN) 2509 #define TXT_COURSE_X 63 2510 #define TXT_STAR_X 98 2511 #define ACT_NAME_X 112 2512 #define CRS_NAME_START 6 2513 #define CRS_NAME_X 117 2514 #define SECRET_CRS_NAME_X 94 2515 #define MYSCORE_X 42 2516 #else 2517 #define TXT_COURSE_X 63 2518 #define TXT_STAR_X 98 2519 #define ACT_NAME_X 116 2520 #define CRS_NAME_START 3 2521 #define CRS_NAME_X 117 2522 #define SECRET_CRS_NAME_X 94 2523 #define MYSCORE_X 62 2524 #endif 2525 2526 void render_pause_my_score_coins(void) { 2527 #ifdef VERSION_EU 2528 u8 textMyScore[][10] = { 2529 { TEXT_MY_SCORE }, 2530 { TEXT_MY_SCORE_FR }, 2531 { TEXT_MY_SCORE_DE } 2532 }; 2533 #else 2534 u8 textCourse[] = { TEXT_COURSE }; 2535 u8 textMyScore[] = { TEXT_MY_SCORE }; 2536 #endif 2537 u8 textStar[] = { TEXT_STAR_DIFF }; 2538 u8 textUnfilledStar[] = { TEXT_UNFILLED_STAR }; 2539 2540 u8 strCourseNum[4]; 2541 void **courseNameTbl; 2542 u8 *courseName; 2543 void **actNameTbl; 2544 u8 *actName; 2545 u8 courseIndex; 2546 u8 starFlags; 2547 2548 #ifndef VERSION_EU 2549 courseNameTbl = segmented_to_virtual(seg2_course_name_table); 2550 actNameTbl = segmented_to_virtual(seg2_act_name_table); 2551 #endif 2552 2553 courseIndex = COURSE_NUM_TO_INDEX(gCurrCourseNum); 2554 starFlags = save_file_get_star_flags(gCurrSaveFileNum - 1, COURSE_NUM_TO_INDEX(gCurrCourseNum)); 2555 2556 #ifdef VERSION_EU 2557 switch (gInGameLanguage) { 2558 case LANGUAGE_ENGLISH: 2559 actNameTbl = segmented_to_virtual(act_name_table_eu_en); 2560 courseNameTbl = segmented_to_virtual(course_name_table_eu_en); 2561 break; 2562 case LANGUAGE_FRENCH: 2563 actNameTbl = segmented_to_virtual(act_name_table_eu_fr); 2564 courseNameTbl = segmented_to_virtual(course_name_table_eu_fr); 2565 break; 2566 case LANGUAGE_GERMAN: 2567 actNameTbl = segmented_to_virtual(act_name_table_eu_de); 2568 courseNameTbl = segmented_to_virtual(course_name_table_eu_de); 2569 break; 2570 } 2571 #endif 2572 2573 gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin); 2574 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha); 2575 2576 if (courseIndex <= COURSE_NUM_TO_INDEX(COURSE_STAGES_MAX)) { 2577 print_hud_my_score_coins(1, gCurrSaveFileNum - 1, courseIndex, 178, 103); 2578 print_hud_my_score_stars(gCurrSaveFileNum - 1, courseIndex, 118, 103); 2579 } 2580 2581 gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end); 2582 gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); 2583 2584 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha); 2585 2586 if (courseIndex <= COURSE_NUM_TO_INDEX(COURSE_STAGES_MAX) 2587 && save_file_get_course_star_count(gCurrSaveFileNum - 1, courseIndex) != 0) { 2588 print_generic_string(MYSCORE_X, 121, LANGUAGE_ARRAY(textMyScore)); 2589 } 2590 2591 courseName = segmented_to_virtual(courseNameTbl[courseIndex]); 2592 2593 if (courseIndex <= COURSE_NUM_TO_INDEX(COURSE_STAGES_MAX)) { 2594 print_generic_string(TXT_COURSE_X, 157, LANGUAGE_ARRAY(textCourse)); 2595 INT_TO_STR_DIFF(gCurrCourseNum, strCourseNum); 2596 print_generic_string(CRS_NUM_X1, 157, strCourseNum); 2597 2598 actName = segmented_to_virtual(actNameTbl[COURSE_NUM_TO_INDEX(gCurrCourseNum) * 6 + gDialogCourseActNum - 1]); 2599 2600 if (starFlags & (1 << (gDialogCourseActNum - 1))) { 2601 print_generic_string(TXT_STAR_X, 140, textStar); 2602 } else { 2603 print_generic_string(TXT_STAR_X, 140, textUnfilledStar); 2604 } 2605 2606 print_generic_string(ACT_NAME_X, 140, actName); 2607 #ifndef VERSION_JP 2608 print_generic_string(CRS_NAME_X, 157, &courseName[CRS_NAME_START]); 2609 } else { 2610 print_generic_string(SECRET_CRS_NAME_X, 157, &courseName[CRS_NAME_START]); 2611 #endif 2612 } 2613 2614 #ifdef VERSION_JP 2615 print_generic_string(CRS_NAME_X, 157, &courseName[CRS_NAME_START]); 2616 #endif 2617 2618 gSPDisplayList(gDisplayListHead++, dl_ia_text_end); 2619 } 2620 2621 #if defined(VERSION_JP) || defined(VERSION_SH) 2622 #define TXT1_X 4 2623 #define TXT2_X 116 2624 #define Y_VAL7 0 2625 #else 2626 #define TXT1_X 3 2627 #define TXT2_X 119 2628 #define Y_VAL7 2 2629 #endif 2630 2631 void render_pause_camera_options(s16 x, s16 y, s8 *index, s16 xIndex) { 2632 UNUSED_CN u8 textLakituMario[] = { TEXT_LAKITU_MARIO }; 2633 UNUSED_CN u8 textLakituStop[] = { TEXT_LAKITU_STOP }; 2634 #ifdef VERSION_EU 2635 u8 textNormalUpClose[][20] = { 2636 { TEXT_NORMAL_UPCLOSE }, 2637 { TEXT_NORMAL_UPCLOSE_FR }, 2638 { TEXT_NORMAL_UPCLOSE_DE } 2639 }; 2640 u8 textNormalFixed[][17] = { 2641 { TEXT_NORMAL_FIXED }, 2642 { TEXT_NORMAL_FIXED_FR }, 2643 { TEXT_NORMAL_FIXED_DE }, 2644 }; 2645 #else 2646 u8 textNormalUpClose[] = { TEXT_NORMAL_UPCLOSE }; 2647 u8 textNormalFixed[] = { TEXT_NORMAL_FIXED }; 2648 #endif 2649 2650 handle_menu_scrolling(MENU_SCROLL_HORIZONTAL, index, 1, 2); 2651 2652 gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); 2653 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha); 2654 2655 #ifdef VERSION_CN 2656 print_generic_string(x + 14, y + 2, textNormalUpClose); 2657 print_generic_string(x + 124, y + 2, textNormalFixed); 2658 #else 2659 print_generic_string(x + 14, y + 2, textLakituMario); 2660 print_generic_string(x + TXT1_X, y - 13, LANGUAGE_ARRAY(textNormalUpClose)); 2661 print_generic_string(x + 124, y + 2, textLakituStop); 2662 print_generic_string(x + TXT2_X, y - 13, LANGUAGE_ARRAY(textNormalFixed)); 2663 #endif 2664 2665 gSPDisplayList(gDisplayListHead++, dl_ia_text_end); 2666 create_dl_translation_matrix(MENU_MTX_PUSH, x + ((*index - 1) * xIndex), y + Y_VAL7, 0); 2667 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha); 2668 gSPDisplayList(gDisplayListHead++, dl_draw_triangle); 2669 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 2670 2671 switch (*index) { 2672 case CAM_SELECTION_MARIO: 2673 cam_select_alt_mode(CAM_SELECTION_MARIO); 2674 break; 2675 case CAM_SELECTION_FIXED: 2676 cam_select_alt_mode(CAM_SELECTION_FIXED); 2677 break; 2678 } 2679 } 2680 2681 #if defined(VERSION_JP) || defined(VERSION_SH) 2682 #define X_VAL8 0 2683 #define Y_VAL8 4 2684 #define Y_OFFSET1 17 2685 #define Y_OFFSET2 33 2686 #elif defined(VERSION_US) || defined(VERSION_EU) 2687 #define X_VAL8 4 2688 #define Y_VAL8 2 2689 #define Y_OFFSET1 17 2690 #define Y_OFFSET2 33 2691 #elif defined(VERSION_CN) 2692 #define X_VAL8 4 2693 #define Y_VAL8 2 2694 #define Y_OFFSET1 20 2695 #define Y_OFFSET2 38 2696 #endif 2697 2698 void render_pause_course_options(s16 x, s16 y, s8 *index, s16 yIndex) { 2699 #ifdef VERSION_EU 2700 u8 textContinue[][10] = { 2701 { TEXT_CONTINUE }, 2702 { TEXT_CONTINUE_FR }, 2703 { TEXT_CONTINUE_DE } 2704 }; 2705 u8 textExitCourse[][15] = { 2706 { TEXT_EXIT_COURSE }, 2707 { TEXT_EXIT_COURSE_FR }, 2708 { TEXT_EXIT_COURSE_DE } 2709 }; 2710 u8 textCameraAngleR[][24] = { 2711 { TEXT_CAMERA_ANGLE_R }, 2712 { TEXT_CAMERA_ANGLE_R_FR }, 2713 { TEXT_CAMERA_ANGLE_R_DE } 2714 }; 2715 #else 2716 u8 textContinue[] = { TEXT_CONTINUE }; 2717 u8 textExitCourse[] = { TEXT_EXIT_COURSE }; 2718 u8 textCameraAngleR[] = { TEXT_CAMERA_ANGLE_R }; 2719 #endif 2720 2721 handle_menu_scrolling(MENU_SCROLL_VERTICAL, index, 1, 3); 2722 2723 gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); 2724 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha); 2725 2726 print_generic_string(x + 10, y - 2, LANGUAGE_ARRAY(textContinue)); 2727 print_generic_string(x + 10, y - Y_OFFSET1, LANGUAGE_ARRAY(textExitCourse)); 2728 2729 if (*index != MENU_OPT_CAMERA_ANGLE_R) { 2730 print_generic_string(x + 10, y - Y_OFFSET2, LANGUAGE_ARRAY(textCameraAngleR)); 2731 gSPDisplayList(gDisplayListHead++, dl_ia_text_end); 2732 2733 create_dl_translation_matrix(MENU_MTX_PUSH, x - X_VAL8, (y - ((*index - 1) * yIndex)) - Y_VAL8, 0); 2734 2735 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha); 2736 gSPDisplayList(gDisplayListHead++, dl_draw_triangle); 2737 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 2738 } 2739 2740 if (*index == MENU_OPT_CAMERA_ANGLE_R) { 2741 render_pause_camera_options(x - 42, y - 42, &gDialogCameraAngleIndex, 110); 2742 } 2743 } 2744 2745 void render_pause_castle_menu_box(s16 x, s16 y) { 2746 create_dl_translation_matrix(MENU_MTX_PUSH, x - 78, y - 32, 0); 2747 create_dl_scale_matrix(MENU_MTX_NOPUSH, 1.2f, 0.8f, 1.0f); 2748 gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, 105); 2749 gSPDisplayList(gDisplayListHead++, dl_draw_text_bg_box); 2750 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 2751 2752 create_dl_translation_matrix(MENU_MTX_PUSH, x + 6, y - 28, 0); 2753 create_dl_rotation_matrix(MENU_MTX_NOPUSH, 90.0f, 0, 0, 1.0f); 2754 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha); 2755 gSPDisplayList(gDisplayListHead++, dl_draw_triangle); 2756 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 2757 2758 create_dl_translation_matrix(MENU_MTX_PUSH, x - 9, y - 101, 0); 2759 create_dl_rotation_matrix(MENU_MTX_NOPUSH, 270.0f, 0, 0, 1.0f); 2760 gSPDisplayList(gDisplayListHead++, dl_draw_triangle); 2761 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 2762 } 2763 2764 void highlight_last_course_complete_stars(void) { 2765 u8 completedCourseIndex; 2766 2767 if (gLastCompletedCourseNum == COURSE_NONE) { 2768 completedCourseIndex = 0; 2769 } else { 2770 completedCourseIndex = COURSE_NUM_TO_INDEX(gLastCompletedCourseNum); 2771 2772 if (completedCourseIndex >= COURSE_NUM_TO_INDEX(COURSE_BONUS_STAGES)) { 2773 completedCourseIndex = COURSE_NUM_TO_INDEX(COURSE_BONUS_STAGES); 2774 } 2775 } 2776 2777 gMenuLineNum = completedCourseIndex; 2778 } 2779 2780 #if defined(VERSION_EU) 2781 #define PAUSE_X get_str_x_pos_from_center_scale(SCREEN_WIDTH / 2, textPause, 12.0f) 2782 #elif defined(VERSION_CN) 2783 #define PAUSE_X 113 2784 #else 2785 #define PAUSE_X 123 2786 #endif 2787 2788 void print_hud_pause_colorful_str(void) { 2789 u8 textPause[] = { TEXT_PAUSE }; 2790 2791 gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin); 2792 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha); 2793 2794 print_hud_lut_string(HUD_LUT_GLOBAL, PAUSE_X, 81, textPause); 2795 2796 gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end); 2797 } 2798 2799 void render_pause_castle_course_stars(s16 x, s16 y, s16 fileIndex, s16 courseIndex) { 2800 s16 hasStar = 0; 2801 2802 #ifdef VERSION_CN 2803 u8 str[60]; 2804 #else 2805 u8 str[30]; 2806 #endif 2807 2808 u8 textStar[] = { TEXT_STAR }; 2809 2810 u8 starFlags = save_file_get_star_flags(fileIndex, courseIndex); 2811 u16 starCount = save_file_get_course_star_count(fileIndex, courseIndex); 2812 2813 u16 nextStar = 0; 2814 2815 if (starFlags & (1 << 6)) { 2816 starCount--; 2817 print_generic_string(x + 89, y - 5, textStar); 2818 } 2819 2820 while (hasStar != starCount) { 2821 if (starFlags & (1 << nextStar)) { 2822 #ifdef VERSION_CN 2823 str[nextStar * 4] = 0x00; 2824 str[nextStar * 4 + 1] = DIALOG_CHAR_STAR_FILLED; 2825 #else 2826 str[nextStar * 2] = DIALOG_CHAR_STAR_FILLED; 2827 #endif 2828 hasStar++; 2829 } else { 2830 #ifdef VERSION_CN 2831 str[nextStar * 4] = 0x00; 2832 str[nextStar * 4 + 1] = DIALOG_CHAR_STAR_OPEN; 2833 #else 2834 str[nextStar * 2] = DIALOG_CHAR_STAR_OPEN; 2835 #endif 2836 } 2837 2838 #ifdef VERSION_CN 2839 str[nextStar * 4 + 2] = DIALOG_CHAR_SPECIAL_MODIFIER; 2840 str[nextStar * 4 + 3] = DIALOG_CHAR_SPACE; 2841 #else 2842 str[nextStar * 2 + 1] = DIALOG_CHAR_SPACE; 2843 #endif 2844 nextStar++; 2845 } 2846 2847 if (starCount == nextStar && starCount != 6) { 2848 #ifdef VERSION_CN 2849 str[nextStar * 4] = DIALOG_CHAR_SPECIAL_MODIFIER; 2850 str[nextStar * 4 + 1] = DIALOG_CHAR_SLASH; //! Meant to be DIALOG_CHAR_STAR_OPEN? 2851 str[nextStar * 4 + 2] = DIALOG_CHAR_SPECIAL_MODIFIER; 2852 str[nextStar * 4 + 3] = DIALOG_CHAR_SPACE; 2853 #else 2854 str[nextStar * 2] = DIALOG_CHAR_STAR_OPEN; 2855 str[nextStar * 2 + 1] = DIALOG_CHAR_SPACE; 2856 #endif 2857 nextStar++; 2858 } 2859 2860 #ifdef VERSION_CN 2861 str[nextStar * 4] = DIALOG_CHAR_SPECIAL_MODIFIER; 2862 str[nextStar * 4 + 1] = DIALOG_CHAR_TERMINATOR; 2863 #else 2864 str[nextStar * 2] = DIALOG_CHAR_TERMINATOR; 2865 #endif 2866 2867 print_generic_string(x + 14, y + 13, str); 2868 } 2869 2870 void render_pause_castle_main_strings(s16 x, s16 y) { 2871 #ifdef VERSION_EU 2872 void **courseNameTbl; 2873 #else 2874 void **courseNameTbl = segmented_to_virtual(seg2_course_name_table); 2875 #endif 2876 2877 #ifdef VERSION_EU 2878 u8 textCoin[] = { TEXT_COIN }; 2879 u8 textX[] = { TEXT_VARIABLE_X }; 2880 #else 2881 u8 textCoin[] = { TEXT_COIN_X }; 2882 #endif 2883 2884 void *courseName; 2885 2886 u8 strVal[8]; 2887 s16 prevCourseIndex = gMenuLineNum; 2888 2889 #ifdef VERSION_EU 2890 switch (gInGameLanguage) { 2891 case LANGUAGE_ENGLISH: 2892 courseNameTbl = segmented_to_virtual(course_name_table_eu_en); 2893 break; 2894 case LANGUAGE_FRENCH: 2895 courseNameTbl = segmented_to_virtual(course_name_table_eu_fr); 2896 break; 2897 case LANGUAGE_GERMAN: 2898 courseNameTbl = segmented_to_virtual(course_name_table_eu_de); 2899 break; 2900 } 2901 #endif 2902 2903 handle_menu_scrolling( 2904 MENU_SCROLL_VERTICAL, &gMenuLineNum, 2905 COURSE_NUM_TO_INDEX(COURSE_MIN) - 1, COURSE_NUM_TO_INDEX(COURSE_BONUS_STAGES) + 1 2906 ); 2907 2908 if (gMenuLineNum == COURSE_NUM_TO_INDEX(COURSE_BONUS_STAGES) + 1) { 2909 gMenuLineNum = COURSE_NUM_TO_INDEX(COURSE_MIN); // Exceeded max, set to min 2910 } 2911 2912 if (gMenuLineNum == COURSE_NUM_TO_INDEX(COURSE_MIN) - 1) { 2913 gMenuLineNum = COURSE_NUM_TO_INDEX(COURSE_BONUS_STAGES); // Exceeded min, set to max 2914 } 2915 2916 if (gMenuLineNum != COURSE_NUM_TO_INDEX(COURSE_BONUS_STAGES)) { 2917 while (save_file_get_course_star_count(gCurrSaveFileNum - 1, gMenuLineNum) == 0) { 2918 if (gMenuLineNum >= prevCourseIndex) { 2919 gMenuLineNum++; 2920 } else { 2921 gMenuLineNum--; 2922 } 2923 2924 if (gMenuLineNum == COURSE_NUM_TO_INDEX(COURSE_STAGES_MAX) + 1 2925 || gMenuLineNum == COURSE_NUM_TO_INDEX(COURSE_MIN) - 1) { 2926 gMenuLineNum = COURSE_NUM_TO_INDEX(COURSE_BONUS_STAGES); 2927 break; 2928 } 2929 } 2930 } 2931 2932 gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); 2933 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha); 2934 2935 if (gMenuLineNum <= COURSE_NUM_TO_INDEX(COURSE_STAGES_MAX)) { // Main courses 2936 courseName = segmented_to_virtual(courseNameTbl[gMenuLineNum]); 2937 render_pause_castle_course_stars(x, y, gCurrSaveFileNum - 1, gMenuLineNum); 2938 print_generic_string(x + 34, y - 5, textCoin); 2939 #ifdef VERSION_EU 2940 print_generic_string(x + 44, y - 5, textX); 2941 #endif 2942 INT_TO_STR_DIFF(save_file_get_course_coin_score(gCurrSaveFileNum - 1, gMenuLineNum), strVal); 2943 print_generic_string(x + 54, y - 5, strVal); 2944 #ifdef VERSION_EU 2945 print_generic_string(x - 17, y + 30, courseName); 2946 #endif 2947 } else { // Castle secret stars 2948 u8 textStarX[] = { TEXT_STAR_X }; 2949 courseName = segmented_to_virtual(courseNameTbl[COURSE_MAX]); 2950 print_generic_string(x + 40, y + 13, textStarX); 2951 INT_TO_STR_DIFF(save_file_get_total_star_count(gCurrSaveFileNum - 1, COURSE_BONUS_STAGES - 1, COURSE_MAX - 1), strVal); 2952 print_generic_string(x + 60, y + 13, strVal); 2953 #ifdef VERSION_EU 2954 print_generic_string(get_str_x_pos_from_center(x + 51, courseName, 10.0f), y + 30, courseName); 2955 #endif 2956 } 2957 2958 #ifndef VERSION_EU 2959 print_generic_string(x - 9, y + 30, courseName); 2960 #endif 2961 2962 gSPDisplayList(gDisplayListHead++, dl_ia_text_end); 2963 } 2964 2965 s8 gCourseCompleteCoinsEqual = FALSE; 2966 s32 gCourseCompleteScreenTimer = 0; 2967 s32 gCourseCompleteCoins = 0; 2968 s8 gHudFlash = 0; 2969 2970 s16 render_pause_screen(void) { 2971 s16 index; 2972 2973 #ifdef VERSION_EU 2974 gInGameLanguage = eu_get_language(); 2975 #endif 2976 2977 switch (gMenuState) { 2978 case MENU_STATE_PAUSE_SCREEN_OPENING: 2979 gMenuLineNum = MENU_OPT_DEFAULT; 2980 gMenuTextAlpha = 0; 2981 level_set_transition(-1, NULL); 2982 play_sound(SOUND_MENU_PAUSE, gGlobalSoundSource); 2983 2984 if (gCurrCourseNum >= COURSE_MIN && gCurrCourseNum <= COURSE_MAX) { 2985 change_dialog_camera_angle(); 2986 gMenuState = MENU_STATE_PAUSE_SCREEN_COURSE; 2987 } else { 2988 highlight_last_course_complete_stars(); 2989 gMenuState = MENU_STATE_PAUSE_SCREEN_CASTLE; 2990 } 2991 break; 2992 2993 case MENU_STATE_PAUSE_SCREEN_COURSE: 2994 shade_screen(); 2995 render_pause_my_score_coins(); 2996 render_pause_red_coins(); 2997 2998 if (gMarioStates[0].action & ACT_FLAG_PAUSE_EXIT) { 2999 render_pause_course_options(99, 93, &gMenuLineNum, 15); 3000 } 3001 3002 #ifdef VERSION_EU 3003 if (gPlayer3Controller->buttonPressed & (A_BUTTON | START_BUTTON | Z_TRIG)) 3004 #else 3005 if ((gPlayer3Controller->buttonPressed & A_BUTTON) 3006 || (gPlayer3Controller->buttonPressed & START_BUTTON)) 3007 #endif 3008 { 3009 level_set_transition(0, NULL); 3010 play_sound(SOUND_MENU_PAUSE_2, gGlobalSoundSource); 3011 gMenuState = MENU_STATE_DEFAULT; 3012 gMenuMode = MENU_MODE_NONE; 3013 3014 if (gMenuLineNum == MENU_OPT_EXIT_COURSE) { 3015 index = gMenuLineNum; 3016 } else { // MENU_OPT_CONTINUE or MENU_OPT_CAMERA_ANGLE_R 3017 index = MENU_OPT_DEFAULT; 3018 } 3019 3020 return index; 3021 } 3022 break; 3023 3024 case MENU_STATE_PAUSE_SCREEN_CASTLE: 3025 shade_screen(); 3026 print_hud_pause_colorful_str(); 3027 render_pause_castle_menu_box(160, 143); 3028 render_pause_castle_main_strings(104, 60); 3029 3030 #ifdef VERSION_EU 3031 if (gPlayer3Controller->buttonPressed & (A_BUTTON | START_BUTTON | Z_TRIG)) 3032 #else 3033 if ((gPlayer3Controller->buttonPressed & A_BUTTON) 3034 || (gPlayer3Controller->buttonPressed & START_BUTTON)) 3035 #endif 3036 { 3037 level_set_transition(0, NULL); 3038 play_sound(SOUND_MENU_PAUSE_2, gGlobalSoundSource); 3039 gMenuMode = MENU_MODE_NONE; 3040 gMenuState = MENU_STATE_DEFAULT; 3041 3042 return MENU_OPT_DEFAULT; 3043 } 3044 break; 3045 } 3046 3047 if (gMenuTextAlpha < 250) { 3048 gMenuTextAlpha += 25; 3049 } 3050 3051 return MENU_OPT_NONE; 3052 } 3053 3054 #if defined(VERSION_JP) 3055 #define TXT_HISCORE_X 112 3056 #define TXT_HISCORE_Y 48 3057 #define TXT_CONGRATS_X 60 3058 #define TXT_CONGRATS_Y 67 3059 #elif defined(VERSION_US) 3060 #define TXT_HISCORE_X 109 3061 #define TXT_HISCORE_Y 36 3062 #define TXT_CONGRATS_X 70 3063 #define TXT_CONGRATS_Y 67 3064 #elif defined(VERSION_EU) 3065 #define TXT_HISCORE_X get_str_x_pos_from_center_scale(160, LANGUAGE_ARRAY(textHiScore), 12.0f) 3066 #define TXT_HISCORE_Y 36 3067 #define TXT_CONGRATS_X get_str_x_pos_from_center_scale(160, LANGUAGE_ARRAY(textCongratulations), 12.0f) 3068 #define TXT_CONGRATS_Y 67 3069 #elif defined(VERSION_SH) 3070 #define TXT_HISCORE_X 118 3071 #define TXT_HISCORE_Y 48 3072 #define TXT_CONGRATS_X 70 3073 #define TXT_CONGRATS_Y 67 3074 #elif defined(VERSION_CN) 3075 #define TXT_HISCORE_X 109 3076 #define TXT_HISCORE_Y 26 3077 #define TXT_CONGRATS_X 100 3078 #define TXT_CONGRATS_Y 62 3079 #endif 3080 3081 #define HUD_PRINT_HISCORE 0 3082 #define HUD_PRINT_CONGRATULATIONS 1 3083 3084 void print_hud_course_complete_string(s8 str) { 3085 #ifdef VERSION_EU 3086 u8 textHiScore[][15] = { 3087 { TEXT_HUD_HI_SCORE }, 3088 { TEXT_HUD_HI_SCORE_FR }, 3089 { TEXT_HUD_HI_SCORE_DE } 3090 }; 3091 u8 textCongratulations[][16] = { 3092 { TEXT_HUD_CONGRATULATIONS }, 3093 { TEXT_HUD_CONGRATULATIONS_FR }, 3094 { TEXT_HUD_CONGRATULATIONS_DE } 3095 }; 3096 #else 3097 u8 textHiScore[] = { TEXT_HUD_HI_SCORE }; 3098 u8 textCongratulations[] = { TEXT_HUD_CONGRATULATIONS }; 3099 #endif 3100 3101 u8 color = sins(gMenuTextColorTransTimer) * 50.0f + 200.0f; 3102 3103 gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin); 3104 gDPSetEnvColor(gDisplayListHead++, color, color, color, 255); 3105 3106 if (str == HUD_PRINT_HISCORE) { 3107 print_hud_lut_string(HUD_LUT_GLOBAL, TXT_HISCORE_X, TXT_HISCORE_Y, LANGUAGE_ARRAY(textHiScore)); 3108 } else { // HUD_PRINT_CONGRATULATIONS 3109 print_hud_lut_string(HUD_LUT_GLOBAL, TXT_CONGRATS_X, TXT_CONGRATS_Y, LANGUAGE_ARRAY(textCongratulations)); 3110 } 3111 3112 gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end); 3113 } 3114 3115 void print_hud_course_complete_coins(s16 x, s16 y) { 3116 u8 courseCompleteCoinsStr[4]; 3117 u8 hudTextSymCoin[] = { GLYPH_COIN, GLYPH_SPACE }; 3118 u8 hudTextSymX[] = { GLYPH_MULTIPLY, GLYPH_SPACE }; 3119 3120 gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin); 3121 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, 255); 3122 3123 print_hud_lut_string(HUD_LUT_GLOBAL, x, y, hudTextSymCoin); 3124 print_hud_lut_string(HUD_LUT_GLOBAL, x + 16, y, hudTextSymX); 3125 3126 int_to_str(gCourseCompleteCoins, courseCompleteCoinsStr); 3127 print_hud_lut_string(HUD_LUT_GLOBAL, x + 32, y, courseCompleteCoinsStr); 3128 3129 gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end); 3130 3131 if (gCourseCompleteCoins >= gHudDisplay.coins) { 3132 gCourseCompleteCoinsEqual = TRUE; 3133 gCourseCompleteCoins = gHudDisplay.coins; 3134 3135 if (gGotFileCoinHiScore) { 3136 print_hud_course_complete_string(HUD_PRINT_HISCORE); 3137 } 3138 } else { 3139 if ((gCourseCompleteScreenTimer & 1) || gHudDisplay.coins > 70) { 3140 gCourseCompleteCoins++; 3141 play_sound(SOUND_MENU_YOSHI_GAIN_LIVES, gGlobalSoundSource); 3142 3143 if (gCourseCompleteCoins == 50 || gCourseCompleteCoins == 100 || gCourseCompleteCoins == 150) { 3144 play_sound(SOUND_GENERAL_COLLECT_1UP, gGlobalSoundSource); 3145 gMarioState->numLives++; 3146 } 3147 } 3148 3149 if (gCourseCompleteCoins == gHudDisplay.coins && gGotFileCoinHiScore) { 3150 play_sound(SOUND_MENU_MARIO_CASTLE_WARP2, gGlobalSoundSource); 3151 } 3152 } 3153 } 3154 3155 void play_star_fanfare_and_flash_hud(s32 arg, u8 starFlag) { 3156 if (gCourseCompleteCoins == gHudDisplay.coins && !(gCurrCourseStarFlags & starFlag) && gHudFlash == 0) { 3157 play_star_fanfare(); 3158 gHudFlash = arg; 3159 } 3160 } 3161 3162 #ifdef VERSION_EU 3163 #define TXT_NAME_X1 centerX 3164 #define TXT_NAME_X2 centerX - 1 3165 #else 3166 #define TXT_NAME_X1 71 3167 #define TXT_NAME_X2 TXT_NAME_X1 - 2 3168 #endif 3169 3170 #if defined(VERSION_JP) || defined(VERSION_SH) 3171 #define CRS_NUM_X2 95 3172 #define CRS_NUM_X3 CRS_NUM_X2 - 2 3173 #define TXT_CLEAR_X1 205 3174 #define TXT_CLEAR_X2 TXT_CLEAR_X1 - 2 3175 #else 3176 #define CRS_NUM_X2 104 3177 #define CRS_NUM_X3 CRS_NUM_X2 - 2 3178 #define TXT_CLEAR_X1 get_string_width(name) + 81 3179 #define TXT_CLEAR_X2 TXT_CLEAR_X1 - 2 3180 #endif 3181 3182 void render_course_complete_lvl_info_and_hud_str(void) { 3183 #if defined(VERSION_JP) 3184 u8 textSymStar[] = { GLYPH_STAR, GLYPH_SPACE }; 3185 u8 textCourse[] = { TEXT_COURSE }; 3186 u8 textCatch[] = { TEXT_CATCH }; 3187 u8 textClear[] = { TEXT_CLEAR }; 3188 #elif defined(VERSION_EU) 3189 UNUSED u8 textCatch[] = { TEXT_CATCH }; // unused in EU 3190 u8 textSymStar[] = { GLYPH_STAR, GLYPH_SPACE }; 3191 #else 3192 u8 textCourse[] = { TEXT_COURSE }; 3193 UNUSED u8 textCatch[] = { TEXT_CATCH }; // unused in US and iQue 3194 u8 textClear[] = { TEXT_CLEAR }; 3195 u8 textSymStar[] = { GLYPH_STAR, GLYPH_SPACE }; 3196 #endif 3197 3198 void **actNameTbl; 3199 void **courseNameTbl; 3200 u8 *name; 3201 3202 u8 strCourseNum[4]; 3203 3204 #ifdef VERSION_EU 3205 s16 centerX; 3206 switch (gInGameLanguage) { 3207 case LANGUAGE_ENGLISH: 3208 actNameTbl = segmented_to_virtual(act_name_table_eu_en); 3209 courseNameTbl = segmented_to_virtual(course_name_table_eu_en); 3210 break; 3211 case LANGUAGE_FRENCH: 3212 actNameTbl = segmented_to_virtual(act_name_table_eu_fr); 3213 courseNameTbl = segmented_to_virtual(course_name_table_eu_fr); 3214 break; 3215 case LANGUAGE_GERMAN: 3216 actNameTbl = segmented_to_virtual(act_name_table_eu_de); 3217 courseNameTbl = segmented_to_virtual(course_name_table_eu_de); 3218 break; 3219 } 3220 #else 3221 actNameTbl = segmented_to_virtual(seg2_act_name_table); 3222 courseNameTbl = segmented_to_virtual(seg2_course_name_table); 3223 #endif 3224 3225 if (gLastCompletedCourseNum <= COURSE_STAGES_MAX) { // Main courses 3226 print_hud_course_complete_coins(118, 103); 3227 play_star_fanfare_and_flash_hud(1, 1 << (gLastCompletedStarNum - 1)); 3228 3229 if (gLastCompletedStarNum == 7) { 3230 name = segmented_to_virtual(actNameTbl[COURSE_STAGES_MAX * 6 + 1]); 3231 } else { 3232 name = segmented_to_virtual(actNameTbl[COURSE_NUM_TO_INDEX(gLastCompletedCourseNum) * 6 + gLastCompletedStarNum - 1]); 3233 } 3234 3235 // Print course number 3236 gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); 3237 3238 INT_TO_STR_DIFF(gLastCompletedCourseNum, strCourseNum); 3239 3240 gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, gMenuTextAlpha); 3241 print_generic_string(65, 165, LANGUAGE_ARRAY(textCourse)); 3242 print_generic_string(CRS_NUM_X2, 165, strCourseNum); 3243 3244 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha); 3245 print_generic_string(63, 167, LANGUAGE_ARRAY(textCourse)); 3246 print_generic_string(CRS_NUM_X3, 167, strCourseNum); 3247 3248 gSPDisplayList(gDisplayListHead++, dl_ia_text_end); 3249 } else if (gLastCompletedCourseNum == COURSE_BITDW || gLastCompletedCourseNum == COURSE_BITFS) { // Bowser courses 3250 name = segmented_to_virtual(courseNameTbl[COURSE_NUM_TO_INDEX(gLastCompletedCourseNum)]); 3251 3252 // Print course name and clear text 3253 gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); 3254 3255 gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, gMenuTextAlpha); 3256 #ifdef VERSION_EU 3257 centerX = get_str_x_pos_from_center(153, name, 12.0f); 3258 #endif 3259 print_generic_string(TXT_NAME_X1, 130, name); 3260 #ifndef VERSION_EU 3261 print_generic_string(TXT_CLEAR_X1, 130, textClear); 3262 #endif 3263 3264 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha); 3265 print_generic_string(TXT_NAME_X2, 132, name); 3266 #ifndef VERSION_EU 3267 print_generic_string(TXT_CLEAR_X2, 132, textClear); 3268 #endif 3269 3270 gSPDisplayList(gDisplayListHead++, dl_ia_text_end); 3271 3272 print_hud_course_complete_string(HUD_PRINT_CONGRATULATIONS); 3273 print_hud_course_complete_coins(118, 111); 3274 play_star_fanfare_and_flash_hud(2, 0); //! 2 isn't defined, originally for key hud? 3275 3276 return; 3277 } else { // Castle secret stars 3278 name = segmented_to_virtual(actNameTbl[COURSE_STAGES_MAX * 6]); 3279 3280 print_hud_course_complete_coins(118, 103); 3281 play_star_fanfare_and_flash_hud(1, 1 << (gLastCompletedStarNum - 1)); 3282 } 3283 3284 // Print star glyph 3285 gSPDisplayList(gDisplayListHead++, dl_rgba16_text_begin); 3286 3287 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha); 3288 print_hud_lut_string(HUD_LUT_GLOBAL, 55, 77, textSymStar); 3289 3290 gSPDisplayList(gDisplayListHead++, dl_rgba16_text_end); 3291 3292 // Print act name and catch text 3293 gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); 3294 3295 gDPSetEnvColor(gDisplayListHead++, 0, 0, 0, gMenuTextAlpha); 3296 print_generic_string(76, 145, name); 3297 #if defined(VERSION_JP) || defined(VERSION_SH) 3298 print_generic_string(220, 145, textCatch); 3299 #endif 3300 3301 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha); 3302 print_generic_string(74, 147, name); 3303 #if defined(VERSION_JP) || defined(VERSION_SH) 3304 print_generic_string(218, 147, textCatch); 3305 #endif 3306 3307 gSPDisplayList(gDisplayListHead++, dl_ia_text_end); 3308 } 3309 3310 #if defined(VERSION_JP) || defined(VERSION_SH) 3311 #define X_VAL9 x 3312 #define TXT_SAVEOPTIONS_X x + 10 3313 #define TXT_SAVECONT_Y 2 3314 #define TXT_SAVEQUIT_Y 18 3315 #define TXT_CONTNOSAVE_Y 38 3316 #else 3317 #ifdef VERSION_EU 3318 #define X_VAL9 xOffset - 12 3319 #define TXT_SAVEOPTIONS_X xOffset 3320 #else 3321 #define X_VAL9 x 3322 #define TXT_SAVEOPTIONS_X x + 12 3323 #endif 3324 #define TXT_SAVECONT_Y 0 3325 #define TXT_SAVEQUIT_Y 20 3326 #define TXT_CONTNOSAVE_Y 40 3327 #endif 3328 3329 #ifdef VERSION_EU 3330 void render_save_confirmation(s16 y, s8 *index, s16 yOffset) 3331 #else 3332 void render_save_confirmation(s16 x, s16 y, s8 *index, s16 yOffset) 3333 #endif 3334 { 3335 #ifdef VERSION_EU 3336 u8 textSaveAndContinue[][24] = { 3337 { TEXT_SAVE_AND_CONTINUE }, 3338 { TEXT_SAVE_AND_CONTINUE_FR }, 3339 { TEXT_SAVE_AND_CONTINUE_DE } 3340 }; 3341 u8 textSaveAndQuit[][22] = { 3342 { TEXT_SAVE_AND_QUIT }, 3343 { TEXT_SAVE_AND_QUIT_FR }, 3344 { TEXT_SAVE_AND_QUIT_DE } 3345 }; 3346 u8 textContinueWithoutSave[][27] = { 3347 { TEXT_CONTINUE_WITHOUT_SAVING }, 3348 { TEXT_CONTINUE_WITHOUT_SAVING_FR }, 3349 { TEXT_CONTINUE_WITHOUT_SAVING_DE } 3350 }; 3351 s16 xOffset = get_str_x_pos_from_center(160, LANGUAGE_ARRAY(textContinueWithoutSave), 12.0f); 3352 #else 3353 u8 textSaveAndContinue[] = { TEXT_SAVE_AND_CONTINUE }; 3354 u8 textSaveAndQuit[] = { TEXT_SAVE_AND_QUIT }; 3355 u8 textContinueWithoutSave[] = { TEXT_CONTINUE_WITHOUT_SAVING }; 3356 #endif 3357 3358 handle_menu_scrolling(MENU_SCROLL_VERTICAL, index, 1, 3); 3359 3360 gSPDisplayList(gDisplayListHead++, dl_ia_text_begin); 3361 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha); 3362 3363 print_generic_string(TXT_SAVEOPTIONS_X, y + TXT_SAVECONT_Y, LANGUAGE_ARRAY(textSaveAndContinue)); 3364 print_generic_string(TXT_SAVEOPTIONS_X, y - TXT_SAVEQUIT_Y, LANGUAGE_ARRAY(textSaveAndQuit)); 3365 print_generic_string(TXT_SAVEOPTIONS_X, y - TXT_CONTNOSAVE_Y, LANGUAGE_ARRAY(textContinueWithoutSave)); 3366 3367 gSPDisplayList(gDisplayListHead++, dl_ia_text_end); 3368 3369 create_dl_translation_matrix(MENU_MTX_PUSH, X_VAL9, y - ((*index - 1) * yOffset), 0); 3370 3371 gDPSetEnvColor(gDisplayListHead++, 255, 255, 255, gMenuTextAlpha); 3372 gSPDisplayList(gDisplayListHead++, dl_draw_triangle); 3373 3374 gSPPopMatrix(gDisplayListHead++, G_MTX_MODELVIEW); 3375 } 3376 3377 s16 render_course_complete_screen(void) { 3378 s16 index; 3379 #ifdef VERSION_EU 3380 gInGameLanguage = eu_get_language(); 3381 #endif 3382 3383 switch (gMenuState) { 3384 case MENU_STATE_COURSE_COMPLETE_SCREEN_OPENING: 3385 render_course_complete_lvl_info_and_hud_str(); 3386 if (gCourseCompleteScreenTimer > 100 && gCourseCompleteCoinsEqual == TRUE) { 3387 gMenuState = MENU_STATE_COURSE_COMPLETE_SCREEN_OPEN; 3388 level_set_transition(-1, NULL); 3389 gMenuTextAlpha = 0; 3390 gMenuLineNum = MENU_OPT_DEFAULT; 3391 } 3392 break; 3393 3394 case MENU_STATE_COURSE_COMPLETE_SCREEN_OPEN: 3395 shade_screen(); 3396 render_course_complete_lvl_info_and_hud_str(); 3397 #ifdef VERSION_EU 3398 render_save_confirmation(86, &gMenuLineNum, 20); 3399 #else 3400 render_save_confirmation(100, 86, &gMenuLineNum, 20); 3401 #endif 3402 3403 if (gCourseCompleteScreenTimer > 110 3404 && ((gPlayer3Controller->buttonPressed & A_BUTTON) 3405 || (gPlayer3Controller->buttonPressed & START_BUTTON) 3406 #ifdef VERSION_EU 3407 || (gPlayer3Controller->buttonPressed & Z_TRIG) 3408 #endif 3409 )) { 3410 level_set_transition(0, NULL); 3411 play_sound(SOUND_MENU_STAR_SOUND, gGlobalSoundSource); 3412 gMenuState = MENU_STATE_DEFAULT; 3413 gMenuMode = MENU_MODE_NONE; 3414 index = gMenuLineNum; 3415 gCourseCompleteScreenTimer = 0; 3416 gCourseCompleteCoins = 0; 3417 gCourseCompleteCoinsEqual = FALSE; 3418 gHudFlash = 0; 3419 3420 return index; 3421 } 3422 break; 3423 } 3424 3425 if (gMenuTextAlpha < 250) { 3426 gMenuTextAlpha += 25; 3427 } 3428 3429 gCourseCompleteScreenTimer++; 3430 3431 return MENU_OPT_NONE; 3432 } 3433 3434 s16 render_menus_and_dialogs(void) { 3435 s16 index = MENU_OPT_NONE; 3436 3437 create_dl_ortho_matrix(); 3438 3439 if (gMenuMode != MENU_MODE_NONE) { 3440 switch (gMenuMode) { 3441 case MENU_MODE_UNUSED_0: 3442 index = render_pause_screen(); 3443 break; 3444 case MENU_MODE_RENDER_PAUSE_SCREEN: 3445 index = render_pause_screen(); 3446 break; 3447 case MENU_MODE_RENDER_COURSE_COMPLETE_SCREEN: 3448 index = render_course_complete_screen(); 3449 break; 3450 case MENU_MODE_UNUSED_3: 3451 index = render_course_complete_screen(); 3452 break; 3453 } 3454 3455 gMenuTextColorTransTimer = (s16) gMenuTextColorTransTimer + 0x1000; 3456 } else if (gDialogID != DIALOG_NONE) { 3457 // The Peach "Dear Mario" message needs to be repositioned separately 3458 if (gDialogID == DIALOG_020) { 3459 print_peach_letter_message(); 3460 return index; 3461 } 3462 3463 render_dialog_entries(); 3464 gMenuTextColorTransTimer = (s16) gMenuTextColorTransTimer + 0x1000; 3465 } 3466 3467 return index; 3468 }