#include "common.h" #include "ld_addrs.h" #include "message_ids.h" #include "sprite.h" #include "charset/postcard.png.h" #include "charset/letter_content_1.png.h" enum RewindArrowStates { REWIND_ARROW_STATE_INIT = 0, REWIND_ARROW_STATE_GROW = 1, REWIND_ARROW_STATE_NEUTRAL = 2, REWIND_ARROW_STATE_CHANGE_COLOR = 3, REWIND_ARROW_STATE_CHANGE_COLOR_BACK = 4, }; #ifdef SHIFT #define MSG_ROM_START (s32)msg_ROM_START #elif VERSION_JP #define MSG_ROM_START 0x1D40000 #else #define MSG_ROM_START 0x1B83000 #endif #if VERSION_PAL #define CHOICE_POINTER_MOVE_RATE 5.0 #else #define CHOICE_POINTER_MOVE_RATE 6.0 #endif typedef MessageImageData* MessageImageDataList[1]; s32 D_8014C280[] = { 0x028001E0, 0x01FF0000, 0x028001E0, 0x01FF0000 }; #if !VERSION_JP u8 MessagePlural[] = { MSG_CHAR_LOWER_S, MSG_CHAR_READ_END }; #if VERSION_PAL u8 MessagePlural_de[] = { MSG_CHAR_LOWER_N, MSG_CHAR_READ_END }; #endif u8 MessageSingular[] = { MSG_CHAR_READ_ENDL, MSG_CHAR_READ_END }; #endif #if VERSION_PAL s32 gCurrentLanguage = 0; void* D_PAL_8014AE50[] = { [LANGUAGE_EN] = msg_pal_en_ROM_START, [LANGUAGE_DE] = msg_pal_de_ROM_START, [LANGUAGE_FR] = msg_pal_fr_ROM_START, [LANGUAGE_ES] = msg_pal_es_ROM_START, }; #endif s16 gNextMessageBuffer = 0; //TODO Vtx ALIGNED(8) s32 gRewindArrowQuad[] = { 0xFFF00009, 0x00000000, 0x00000000, 0xFFFFFFFF, 0x00100009, 0x00000000, 0x04000000, 0xFFFFFFFF, 0xFFF0FFF7, 0x00000000, 0x00000240, 0xFFFFFFFF, 0x0010FFF7, 0x00000000, 0x04000240, 0xFFFFFFFF, }; Gfx D_8014C2D8[] = { gsDPSetCycleType(G_CYC_2CYCLE), gsSPClearGeometryMode(G_CULL_BOTH | G_LIGHTING), gsSPSetGeometryMode(G_SHADE | G_SHADING_SMOOTH), gsDPSetColorDither(G_CD_DISABLE), gsDPSetAlphaDither(G_AD_DISABLE), gsDPSetAlphaCompare(G_AC_NONE), gsSPTexture(-1, -1, 0, G_TX_RENDERTILE, G_ON), gsDPSetTexturePersp(G_TP_PERSP), gsDPSetTextureLUT(G_TT_NONE), gsDPSetTextureFilter(G_TF_AVERAGE), gsDPSetRenderMode(IM_RD | CVG_DST_SAVE | ZMODE_XLU | FORCE_BL | G_RM_PASS, IM_RD | CVG_DST_SAVE | ZMODE_XLU | FORCE_BL | GBL_c2(G_BL_CLR_IN, G_BL_A_IN, G_BL_CLR_MEM, G_BL_1MA)), gsDPSetCombineMode(PM_CC_MSG_UP_ARROW, G_CC_PASS2), gsSPEndDisplayList(), }; SHIFT_BSS s32 gMsgBGScrollAmtX; SHIFT_BSS u16 gMsgGlobalWaveCounter; SHIFT_BSS MessageImageDataList gMsgVarImages; SHIFT_BSS s32 gMsgBGScrollAmtY; SHIFT_BSS u8* D_8015131C; SHIFT_BSS Gfx* D_80151338; SHIFT_BSS char gMessageBuffers[2][1024]; SHIFT_BSS u8 gMessageMsgVars[3][32]; SHIFT_BSS s16 D_80155C98; SHIFT_BSS Mtx gMessageWindowProjMatrix[2]; SHIFT_BSS MessageDrawState D_80155D20; SHIFT_BSS MessageDrawState* msg_drawState; SHIFT_BSS IMG_BIN D_80159B50[0x200]; SHIFT_BSS PAL_BIN D_8015C7E0[0x10]; SHIFT_BSS MessagePrintState gMessagePrinters[3]; #if VERSION_IQUE SHIFT_BSS IMG_BIN D_801544A0[120][128]; #endif extern s16 MsgStyleVerticalLineOffsets[]; extern IMG_BIN ui_msg_bubble_left_png[]; extern IMG_BIN ui_msg_bubble_mid_png[]; extern IMG_BIN ui_msg_bubble_right_png[]; extern IMG_BIN ui_msg_arrow_png[]; extern unsigned char ui_msg_palettes[16][32]; extern IMG_BIN ui_msg_sign_corner_topleft_png[]; extern IMG_BIN ui_msg_sign_corner_topright_png[]; extern IMG_BIN ui_msg_sign_corner_bottomleft_png[]; extern IMG_BIN ui_msg_sign_corner_bottomright_png[]; extern IMG_BIN ui_msg_lamppost_corner_bottomright_png[]; extern IMG_BIN ui_msg_sign_side_top_png[]; extern IMG_BIN ui_msg_sign_side_left_png[]; extern IMG_BIN ui_msg_sign_side_right_png[]; extern IMG_BIN ui_msg_sign_side_bottom_png[]; extern IMG_BIN ui_msg_sign_fill_png[]; extern PAL_BIN ui_msg_sign_pal[]; extern PAL_BIN ui_msg_lamppost_pal[]; extern IMG_BIN ui_msg_background_png[]; extern IMG_BIN ui_msg_rewind_arrow_png[]; extern PAL_BIN ui_msg_rewind_arrow_pal[]; extern IMG_BIN ui_msg_star_png[]; extern IMG_BIN ui_msg_star_silhouette_png[]; extern IMG_BIN D_802ED550[]; extern PAL_BIN D_802ED670[]; extern IMG_BIN MsgCharImgTitle[]; extern IMG_BIN MsgCharImgNormal[]; extern MessageCharset* MsgCharsets[5]; extern IMG_BIN MsgCharImgSubtitle[]; extern PAL_BIN D_802F4560[80][8]; #if VERSION_JP extern IMG_BIN MsgCharImgKana[]; extern IMG_BIN MsgCharImgLatin[]; extern IMG_BIN MsgCharImgMenuKana[]; extern IMG_BIN MsgCharImgMenuLatin[]; #endif extern s32 gMessageBoxFrameParts[2][16]; extern IMG_BIN ui_point_right_png[]; extern PAL_BIN ui_point_right_pal[]; MessageNumber gMsgNumbers[] = { #if VERSION_JP { .rasters = &MsgCharImgKana[0x4910], .texSize = 112, .texWidth = 16, .texHeight = 14, .digitWidth = {11, 8, 11, 11, 11, 11, 11, 11, 11, 11}, .fixedWidth = 11 }, { .rasters = &MsgCharImgMenuKana[0x2EF8], .texSize = 72, .texWidth = 12, .texHeight = 12, .digitWidth = {9, 8, 9, 9, 9, 9, 9, 9, 9, 9}, .fixedWidth = 9 } #else { .rasters = &MsgCharImgNormal[0x800], .texSize = 128, .texWidth = 16, .texHeight = 16, .digitWidth = {11, 8, 11, 11, 11, 11, 11, 11, 11, 11}, .fixedWidth = 11 }, { .rasters = &MsgCharImgNormal[0x800], .texSize = 128, .texWidth = 16, .texHeight = 16, .digitWidth = {9, 8, 9, 9, 9, 9, 9, 9, 9, 9}, .fixedWidth = 9 } #endif }; Gfx gMsgDlistInitDrawNumber[] = { gsDPPipeSync(), gsDPSetCycleType(G_CYC_1CYCLE), gsDPSetTextureFilter(G_TF_POINT), gsDPSetTexturePersp(G_TP_NONE), gsDPSetColorDither(G_CD_DISABLE), gsDPSetAlphaDither(G_AD_DISABLE), gsDPSetCombineKey(G_CK_NONE), gsDPSetAlphaCompare(G_AC_NONE), gsDPSetTextureLUT(G_TT_RGBA16), gsSPTexture(-1, -1, 0, G_TX_RENDERTILE, G_ON), gsSPEndDisplayList(), }; s32 draw_image_with_clipping(IMG_PTR raster, s32 width, s32 height, s32 fmt, s32 bitDepth, s16 posX, s16 posY, u16 clipULx, u16 clipULy, u16 clipLRx, u16 clipRLy); s32 _update_message(MessagePrintState* printer); void msg_copy_to_print_buffer(MessagePrintState* printer, s32 arg1, s32 arg2); void initialize_printer(MessagePrintState* printer, s32 arg1, s32 arg2); MessagePrintState* _msg_get_printer_for_msg(s32 msgID, s32* donePrintingWriteback, s32 arg2); void msg_update_rewind_arrow(s32); void msg_draw_rewind_arrow(s32); void msg_draw_choice_pointer(MessagePrintState* printer); void draw_message_window(MessagePrintState* printer); void appendGfx_message(MessagePrintState*, s16, s16, u16, u16, u16, u8); void appendGfx_msg_prim_rect(u8 r, u8 g, u8 b, u8 a, u16 ulX, u16 ulY, u16 lrX, u16 lrY); void msg_reset_gfx_state(void); void msg_draw_char(MessagePrintState* printer, MessageDrawState* drawState, s32 charIndex, s32 palette, s32 posX, s32 posY); void msg_draw_prim_rect(u8 r, u8 g, u8 b, u8 a, u16 posX, u16 posY, u16 sizeX, u16 sizeY); void msg_draw_speech_arrow(MessagePrintState* printer); void msg_draw_frame(s32 posX, s32 posY, s32 sizeX, s32 sizeY, s32 style, s32 palette, s32 fading, s32 bgAlpha, s32 frameAlpha); void msg_draw_speech_bubble(MessagePrintState* printer, s16 posX, s16 posY, s16 straightWidth, s16 curveWidth, s16 height, f32 scaleX, f32 scaleY, u8 opacity, s32 arg9); void clear_character_set(void) { D_80155C98 = -1; } void clear_printers(void) { s32 i; for (i = 0; i < ARRAY_COUNT(gMessagePrinters); i++) { initialize_printer(&gMessagePrinters[i], 0, 0); } gMsgBGScrollAmtX = 0; gMsgBGScrollAmtY = 0; for (i = 0; i < ARRAY_COUNT(gMessageMsgVars); i++) { gMessageMsgVars[i][0] = 0; } D_80151338 = NULL; gMsgGlobalWaveCounter = 0; load_font(0); } #if VERSION_IQUE void load_font_data(Addr offset, u32 size, void* dest) { #else void load_font_data(Addr offset, u16 size, void* dest) { #endif u8* base = charset_ROM_START + (s32) offset; dma_copy(base, base + size, dest); } void load_font(s32 font) { if (font != D_80155C98) { if (font == 0) { #if VERSION_JP load_font_data(charset_kana_OFFSET, 0x5710, MsgCharImgKana); load_font_data(charset_latin_OFFSET, 0xBD0, MsgCharImgLatin); load_font_data(charset_kanji_OFFSET, 0x34F0, MsgCharImgTitle); // huh load_font_data(charset_buttons_OFFSET, 0x460, MsgCharImgSubtitle); // what load_font_data(charset_menu_kana_OFFSET, 0x37F8, MsgCharImgMenuKana); load_font_data(charset_menu_latin_OFFSET, 0x798, MsgCharImgMenuLatin); #else load_font_data(charset_standard_OFFSET, 0x5100, MsgCharImgNormal); #endif load_font_data(charset_standard_pal_OFFSET, 0x500, D_802F4560); } else if (font == 1) { load_font_data(charset_title_OFFSET, 0xF60, MsgCharImgTitle); load_font_data(charset_subtitle_OFFSET, 0xB88, MsgCharImgSubtitle); load_font_data(charset_credits_pal_OFFSET, 0x80, D_802F4560); } } } void update_messages(void) { s32 i; gMsgGlobalWaveCounter++; if (gMsgGlobalWaveCounter >= 360) { gMsgGlobalWaveCounter = 0; } for (i = 0; i < ARRAY_COUNT(gMessagePrinters); i++) { if (gMessagePrinters[i].stateFlags & MSG_STATE_FLAG_2) { _update_message(&gMessagePrinters[i]); } } gMsgBGScrollAmtX += 12; gMsgBGScrollAmtY -= 12; if (gMsgBGScrollAmtX >= 2048) { gMsgBGScrollAmtX -= 2048; } if (gMsgBGScrollAmtY < 0) { gMsgBGScrollAmtY += 2048; } } s32 _update_message(MessagePrintState* printer) { f32 speechPan; u8 cond; s32 buttons; s16 endPosDist; s16 lineIncAmt; s32 charsToPrint; s32 i; printer->effectFrameCounter++; if (printer->effectFrameCounter >= 3600) { printer->effectFrameCounter = 0; } speechPan = (((f32)printer->initOpenPos.x - (SCREEN_WIDTH / 2.0)) / 3.8) + 64.0; if (speechPan < 5.0) { speechPan = 5.0f; } else if (speechPan > 122.0) { speechPan = 122.0f; } printer->speechPan = speechPan; cond = FALSE; if (!(printer->stateFlags & MSG_STATE_FLAG_40)) { if (!(printer->stateFlags & (MSG_STATE_FLAG_20 | MSG_STATE_FLAG_10))) { s32 buttons = BUTTON_A; switch (printer->windowState) { case MSG_WINDOW_STATE_WAITING: if (printer->stateFlags & MSG_STATE_FLAG_80000) { buttons = BUTTON_A | BUTTON_C_DOWN; } if ((buttons & gGameStatusPtr->pressedButtons[0]) || (gGameStatusPtr->curButtons[0] & BUTTON_B)) { printer->windowState = MSG_WINDOW_STATE_PRINTING; printer->curPrintDelay = 0; printer->stateFlags |= MSG_STATE_FLAG_4; if (gGameStatusPtr->pressedButtons[0] & (BUTTON_A | BUTTON_C_DOWN)) { cond = TRUE; sfx_play_sound_with_params(SOUND_MENU_NEXT, 0, 0, 0); } else if (printer->srcBuffer[printer->srcBufferPos] != MSG_CHAR_READ_END) { printer->stateFlags |= MSG_STATE_FLAG_PRINT_QUICKLY | MSG_STATE_FLAG_4; if (printer->fontVariant != 0 || printer->srcBuffer[printer->srcBufferPos] != MSG_CHAR_UNK_C3) { printer->stateFlags |= MSG_STATE_FLAG_PRINT_QUICKLY | MSG_STATE_FLAG_SPEAKING | MSG_STATE_FLAG_4; } sfx_play_sound_with_params(SOUND_MSG_SKIP, 0, 0, 0); } else if (printer->style == MSG_STYLE_RIGHT || printer->style == MSG_STYLE_LEFT || printer->style == MSG_STYLE_CENTER || printer->style == MSG_STYLE_TATTLE) { sfx_play_sound_with_params(SOUND_MENU_NEXT, 0, 0, 0); } } else if ((gGameStatusPtr->pressedButtons[0] & BUTTON_Z) && !(printer->stateFlags & MSG_STATE_FLAG_40000) && (printer->curLine != 0)) { printer->windowState = MSG_WINDOW_STATE_B; printer->unk_4CC = 0; printer->unkArraySize = printer->curLine - 1; printer->unk_4C8 = abs(printer->curLinePos - printer->lineEndPos[printer->unkArraySize]); sfx_play_sound_with_params(SOUND_MSG_REWIND, 0, 0, 0); } break; case MSG_WINDOW_STATE_C: if (gGameStatusPtr->pressedButtons[0] & BUTTON_B) { printer->windowState = MSG_WINDOW_STATE_B; printer->unk_4CC = 0; printer->unkArraySize = printer->curLine; printer->unk_4C8 = abs(printer->curLinePos - printer->lineEndPos[printer->unkArraySize]); sfx_play_sound_with_params(SOUND_MSG_SKIP, 0, 0, 0); } else if (gGameStatusPtr->pressedButtons[0] & BUTTON_Z) { if (printer->unkArraySize > 0) { printer->windowState = MSG_WINDOW_STATE_B; printer->unk_4CC = 0; printer->unkArraySize--; printer->unk_4C8 = abs(printer->curLinePos - printer->lineEndPos[printer->unkArraySize]); sfx_play_sound_with_params(SOUND_MSG_REWIND, 0, 0, 0); } } else { if (gGameStatusPtr->pressedButtons[0] & BUTTON_A) { printer->windowState = MSG_WINDOW_STATE_B; printer->unk_4CC = 0; printer->unkArraySize++; printer->unk_4C8 = abs(printer->curLinePos - printer->lineEndPos[printer->unkArraySize]); sfx_play_sound_with_params(SOUND_MSG_UNREWIND, 0, 0, 0); } } break; case MSG_WINDOW_STATE_WAITING_FOR_CHOICE: if (gGameStatusPtr->pressedButtons[0] & BUTTON_A) { printer->madeChoice = 1; printer->windowState = MSG_WINDOW_STATE_PRINTING; printer->scrollingTime = 0; printer->stateFlags |= MSG_STATE_FLAG_20000; sfx_play_sound_with_params(SOUND_MENU_NEXT, 0, 0, 0); } else if (printer->cancelOption != 0xFF && (gGameStatusPtr->pressedButtons[0] & BUTTON_B)) { if (printer->cancelOption >= printer->maxOption) { printer->selectedOption = printer->curOption; } else { printer->selectedOption = printer->cancelOption; } printer->madeChoice = 1; printer->windowState = MSG_WINDOW_STATE_PRINTING; printer->scrollingTime = 0; printer->curOption = printer->cancelOption; printer->stateFlags |= MSG_STATE_FLAG_20000; sfx_play_sound_with_params(SOUND_MENU_BACK, 0, 0, 0); } else if (gGameStatusPtr->heldButtons[0] & BUTTON_STICK_DOWN) { if (printer->curOption != printer->maxOption - 1) { printer->targetOption = printer->curOption + 1; printer->windowState = MSG_WINDOW_STATE_SCROLLING_BACK; printer->scrollingTime = 1; sfx_play_sound_with_params(SOUND_MENU_CHANGE_SELECTION, 0, 0, 0); } } else if (gGameStatusPtr->heldButtons[0] & BUTTON_STICK_UP) { if (printer->curOption != 0) { printer->targetOption = printer->curOption - 1; printer->windowState = MSG_WINDOW_STATE_SCROLLING_BACK; printer->scrollingTime = 1; sfx_play_sound_with_params(SOUND_MENU_CHANGE_SELECTION, 0, 0, 0); } } if (printer->windowState != MSG_WINDOW_STATE_SCROLLING_BACK) { break; } case MSG_WINDOW_STATE_SCROLLING_BACK: printer->scrollingTime++; if (printer->scrollingTime >= (s32)(5 * DT)) { printer->windowState = MSG_WINDOW_STATE_WAITING_FOR_CHOICE; printer->curOption = printer->targetOption; printer->selectedOption = printer->curOption; } break; } } else if (!(printer->stateFlags & MSG_STATE_FLAG_20) && printer->windowState == MSG_WINDOW_STATE_WAITING && (gGameStatusPtr->pressedButtons[0] & BUTTON_A)) { printer->windowState = MSG_WINDOW_STATE_PRINTING; printer->curPrintDelay = 0; printer->stateFlags |= MSG_STATE_FLAG_4; } if (printer->stateFlags & MSG_STATE_FLAG_4 && !(gGameStatusPtr->curButtons[0] & BUTTON_A)) { printer->stateFlags &= ~MSG_STATE_FLAG_4; } for (i = 0; i < ARRAY_COUNT(printer->animTimers); i++) { if (printer->animTimers[i] > 0) { printer->animTimers[i]--; } } switch (printer->windowState) { case MSG_WINDOW_STATE_PRINTING: if ((gGameStatusPtr->pressedButtons[0] & BUTTON_A) | (gGameStatusPtr->curButtons[0] & BUTTON_B)) { if (!(printer->stateFlags & (MSG_STATE_FLAG_20 | MSG_STATE_FLAG_10)) && !cond) { printer->stateFlags |= MSG_STATE_FLAG_PRINT_QUICKLY; } } // fallthrough case MSG_WINDOW_STATE_INIT: charsToPrint = printer->charsPerChunk; if (printer->windowState == MSG_WINDOW_STATE_INIT) { printer->windowState = MSG_WINDOW_STATE_PRINTING; printer->curPrintDelay = 0; } else if (printer->stateFlags & MSG_STATE_FLAG_PRINT_QUICKLY) { charsToPrint = 12; printer->curPrintDelay = 0; } else if (!(printer->stateFlags & MSG_STATE_FLAG_4)) { if (!(printer->stateFlags & (MSG_STATE_FLAG_20 | MSG_STATE_FLAG_10)) && (gGameStatusPtr->curButtons[0] & BUTTON_A)) { charsToPrint = 6; printer->curPrintDelay = 0; } } if ((printer->curPrintDelay == 0) || --printer->curPrintDelay == 0) { msg_copy_to_print_buffer(printer, charsToPrint, 0); } break; case MSG_WINDOW_STATE_SCROLLING: if (gGameStatusPtr->pressedButtons[0] & (BUTTON_A | BUTTON_B)) { if (!(printer->stateFlags & (MSG_STATE_FLAG_20 | MSG_STATE_FLAG_10))) { printer->stateFlags |= MSG_STATE_FLAG_PRINT_QUICKLY; } } printer->curLinePos += printer->windowScrollRate; if ((printer->stateFlags & MSG_STATE_FLAG_PRINT_QUICKLY) || (!(printer->stateFlags & (MSG_STATE_FLAG_10 | MSG_STATE_FLAG_4)) && (gGameStatusPtr->curButtons[0] & BUTTON_A))) { printer->curLinePos += (s32)(6 / DT); } if (printer->curLinePos >= printer->nextLinePos) { printer->windowState = MSG_WINDOW_STATE_PRINTING; printer->curLinePos = printer->nextLinePos; printer->stateFlags |= MSG_STATE_FLAG_4; if (printer->style == MSG_STYLE_SIGN || printer->style == MSG_STYLE_LAMPPOST || printer->srcBuffer[printer->srcBufferPos] == MSG_CHAR_READ_WAIT) { printer->curPrintDelay = 0; } else { printer->curPrintDelay = 5; } printer->lineEndPos[printer->curLine] = printer->curLinePos; } break; case MSG_WINDOW_STATE_B: printer->unk_4CC += 1; endPosDist = abs(printer->curLinePos - printer->lineEndPos[printer->unkArraySize]); lineIncAmt = 2; if (printer->unk_4C8 <= 16) { if (endPosDist >= 15) { lineIncAmt = 4; } else if (endPosDist >= 9) { lineIncAmt = 3; } } else if (endPosDist > 96) { lineIncAmt = 10; } else if (endPosDist > 48) { lineIncAmt = 9; } else if (endPosDist >= 24) { lineIncAmt = 7; } else if (endPosDist >= 16) { lineIncAmt = 5; } else if (endPosDist >= 8) { lineIncAmt = 4; } else if (endPosDist > 4) { lineIncAmt = 3; } printer->unk_4CA = lineIncAmt; if (printer->lineEndPos[printer->unkArraySize] < printer->curLinePos) { printer->curLinePos -= printer->unk_4CA; if (printer->lineEndPos[printer->unkArraySize] >= printer->curLinePos) { printer->curLinePos = printer->lineEndPos[printer->unkArraySize]; printer->windowState = MSG_WINDOW_STATE_C; } } else { printer->curLinePos += printer->unk_4CA; if (printer->curLinePos >= printer->lineEndPos[printer->unkArraySize]) { printer->curLinePos = printer->lineEndPos[printer->unkArraySize]; printer->windowState = MSG_WINDOW_STATE_C; if (printer->unkArraySize == printer->curLine) { printer->windowState = MSG_WINDOW_STATE_WAITING; printer->rewindArrowAnimState = REWIND_ARROW_STATE_INIT; printer->rewindArrowCounter = 0; } } } break; case MSG_WINDOW_STATE_OPENING: case MSG_WINDOW_STATE_CLOSING: case MSG_WINDOW_STATE_WAITING: case MSG_WINDOW_STATE_WAITING_FOR_CHOICE: case MSG_WINDOW_STATE_SCROLLING_BACK: case MSG_WINDOW_STATE_VIEWING_PREV: case MSG_WINDOW_STATE_A: case MSG_WINDOW_STATE_C: case MSG_WINDOW_STATE_D: case MSG_WINDOW_STATE_E: break; } } if (printer->stateFlags & MSG_STATE_FLAG_1) { printer->windowState = MSG_WINDOW_STATE_DONE; printer->stateFlags = 0; if (printer->letterBackgroundImg != NULL) { general_heap_free(printer->letterBackgroundImg); } if (printer->letterBackgroundPal != NULL) { general_heap_free(printer->letterBackgroundPal); } if (printer->letterContentImg != NULL) { general_heap_free(printer->letterContentImg); } if (printer->letterContentPal != NULL) { general_heap_free(printer->letterContentPal); } if (printer->closedWritebackBool != NULL) { *printer->closedWritebackBool = TRUE; printer->closedWritebackBool = NULL; } } return printer->windowState; } void render_messages(void) { Mtx* matrix = &gMessageWindowProjMatrix[gCurrentDisplayContextIndex]; s32 i; for (i = 0; i < ARRAY_COUNT(gMessagePrinters); i++) { if (gMessagePrinters[i].stateFlags & MSG_STATE_FLAG_2) { gSPViewport(gMainGfxPos++, D_8014C280); guOrtho(matrix, 0.0f, 319.0f, -240.0f, 0.0f, -500.0f, 500.0f, 1.0f); gSPMatrix(gMainGfxPos++, OS_K0_TO_PHYSICAL(matrix), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); gDPPipeSync(gMainGfxPos++); gDPSetCycleType(gMainGfxPos++, G_CYC_1CYCLE); gSPClearGeometryMode(gMainGfxPos++, G_SHADE | G_CULL_BOTH | G_FOG | G_LIGHTING | G_TEXTURE_GEN | G_TEXTURE_GEN_LINEAR | G_LOD | G_SHADING_SMOOTH); gSPSetGeometryMode(gMainGfxPos++, G_SHADE | G_SHADING_SMOOTH); break; } } for (i = 0; i < ARRAY_COUNT(gMessagePrinters); i++) { if (gMessagePrinters[i].stateFlags & MSG_STATE_FLAG_2) { draw_message_window(&gMessagePrinters[i]); if (gMessagePrinters[i].windowState == MSG_WINDOW_STATE_WAITING) { if (!(gMessagePrinters[i].stateFlags & MSG_STATE_FLAG_8000) && !(gMessagePrinters[i].stateFlags & MSG_STATE_FLAG_40)) { msg_update_rewind_arrow(i); } } else if (gMessagePrinters[i].windowState == MSG_WINDOW_STATE_C) { msg_draw_rewind_arrow(i); } else if (gMessagePrinters[i].windowState == MSG_WINDOW_STATE_WAITING_FOR_CHOICE || gMessagePrinters[i].windowState == MSG_WINDOW_STATE_SCROLLING_BACK || gMessagePrinters[i].stateFlags & MSG_STATE_FLAG_10000 || gMessagePrinters[i].stateFlags & MSG_STATE_FLAG_20000) { msg_draw_choice_pointer(&gMessagePrinters[i]); } } } } void msg_play_speech_sound(MessagePrintState* printer, u8 character) { f32 volTemp; s16 volume; s32 pitchShift; s32 flag = 1; s32 baseShift = 100; if (printer->stateFlags & MSG_STATE_FLAG_800000 && !(printer->delayFlags & (MSG_DELAY_FLAG_2 | MSG_DELAY_FLAG_4)) && printer->volume != 0) { volTemp = (f32)printer->volume / 100.0; pitchShift = ((character % 20) * 10) + (printer->speechPitchShift - baseShift); volume = ((rand_int(15) + 78) * volTemp); if (volume > 255) { volume = 255; } if (character & flag) { sfx_play_sound_with_params(printer->speechSoundIDA, volume, printer->speechPan, pitchShift); } else { sfx_play_sound_with_params(printer->speechSoundIDB, volume, printer->speechPan, pitchShift); } } } extern s32 gItemIconRasterOffsets[]; extern s32 gItemIconPaletteOffsets[]; extern s32 MsgLetterRasterOffsets[]; extern s32 MsgLetterPaletteOffsets[]; extern MsgVoice MsgVoices[]; #if VERSION_PAL INCLUDE_ASM(s32, "msg", msg_copy_to_print_buffer); #else void msg_copy_to_print_buffer(MessagePrintState* printer, s32 arg1, s32 arg2) { u8 arg; u8 argQ; u8 argW; u8 argE; u8 sp10[4]; s32 addr; s16 offset; s32 i; u8* romAddr; u8* romEnd; s32 temp; void* a2; s8 s8 = arg2 & 1; u8* printBuf = &printer->printBuffer[printer->printBufferPos]; u8* srcBuf = &printer->srcBuffer[printer->srcBufferPos]; do { u8 c = *srcBuf++; // a1 u8 nextArg = *srcBuf; // a2 switch (c) { case MSG_CHAR_READ_ENDL: *printBuf++ = MSG_CHAR_PRINT_ENDL; printer->lineCount += (u8)printer->sizeScale; break; case MSG_CHAR_READ_WAIT: printer->windowState = MSG_WINDOW_STATE_WAITING; printer->delayFlags |= MSG_DELAY_FLAG_1; printer->delayFlags &= ~MSG_DELAY_FLAG_2; printer->rewindArrowAnimState = REWIND_ARROW_STATE_INIT; printer->rewindArrowCounter = 0; printer->stateFlags &= ~MSG_STATE_FLAG_SPEAKING; printer->stateFlags &= ~MSG_STATE_FLAG_PRINT_QUICKLY; if (printer->style != MSG_STYLE_F) { sfx_play_sound_with_params(SOUND_MSG_WAIT, 0, 0, 0); } break; case MSG_CHAR_READ_PAUSE: printer->curPrintDelay = *srcBuf++; printer->delayFlags |= MSG_DELAY_FLAG_1; printer->stateFlags &= ~MSG_STATE_FLAG_SPEAKING; break; case MSG_CHAR_READ_VARIANT0: case MSG_CHAR_READ_VARIANT1: case MSG_CHAR_READ_VARIANT2: case MSG_CHAR_READ_VARIANT3: *printBuf++ = MSG_CHAR_PRINT_VARIANT0 + (c - MSG_CHAR_READ_VARIANT0); printer->fontVariant = c + 13; break; case MSG_CHAR_READ_SPACE: *printBuf++ = MSG_CHAR_PRINT_SPACE; break; case MSG_CHAR_READ_FULL_SPACE: *printBuf++ = MSG_CHAR_PRINT_FULL_SPACE; break; case MSG_CHAR_READ_HALF_SPACE: *printBuf++ = MSG_CHAR_PRINT_HALF_SPACE; break; case MSG_CHAR_READ_UNK_CHAR_FA: *printBuf++ = MSG_CHAR_PRINT_UNK_CHAR_FA; arg1--; break; case MSG_CHAR_READ_NEXT: if (printer->lineCount != 0) { printer->lineEndPos[printer->curLine] = printer->curLinePos; printer->curLine++; *printBuf++ = MSG_CHAR_PRINT_NEXT; printer->nextLinePos = printer->curLinePos + (MsgCharsets[printer->font]->newLineY #if !VERSION_JP + MsgStyleVerticalLineOffsets[printer->style] #endif ) * printer->lineCount; printer->windowState = MSG_WINDOW_STATE_SCROLLING; printer->delayFlags |= MSG_DELAY_FLAG_1; } printer->lineCount = 0; break; case MSG_CHAR_READ_STYLE: *printBuf++ = MSG_CHAR_PRINT_STYLE; arg = *srcBuf++; printer->style = arg; *printBuf++ = arg; printer->fadeInCounter = 0; switch (arg) { case MSG_STYLE_RIGHT: case MSG_STYLE_LEFT: case MSG_STYLE_CENTER: case MSG_STYLE_TATTLE: if (arg == MSG_STYLE_RIGHT || arg == MSG_STYLE_LEFT || arg == MSG_STYLE_CENTER) { printer->maxLinesPerPage = 3; } printer->delayFlags |= MSG_DELAY_FLAG_1; printer->stateFlags |= MSG_STATE_FLAG_800000 | MSG_STATE_FLAG_800; if (nextArg != MSG_CHAR_UNK_C3) { printer->stateFlags |= MSG_STATE_FLAG_SPEAKING; } printer->speechSoundIDA = SOUND_MSG_VOICE_1A; printer->speechSoundIDB = SOUND_MSG_VOICE_1B; printer->windowState = MSG_WINDOW_STATE_OPENING; break; case MSG_STYLE_CHOICE: do { printer->windowBasePos.x = *srcBuf++; printer->windowBasePos.y = *srcBuf++; printer->windowSize.x = *srcBuf++; printer->windowSize.y = *srcBuf++; printer->windowState = MSG_WINDOW_STATE_OPENING; printer->stateFlags |= MSG_STATE_FLAG_800; } while (0); break; case MSG_STYLE_INSPECT: case MSG_STYLE_NARRATE: case MSG_STYLE_F: if (!s8) { #if VERSION_JP printer->windowBasePos.x = 40; #else printer->windowBasePos.x = 20; #endif printer->windowBasePos.y = 28; printer->windowSize.y = 58; #if VERSION_JP printer->windowSize.x = 240; #else printer->windowSize.x = 280; #endif printer->windowState = MSG_WINDOW_STATE_OPENING; printer->stateFlags |= MSG_STATE_FLAG_800; printer->delayFlags |= MSG_DELAY_FLAG_1; if (arg == MSG_STYLE_INSPECT) { sfx_play_sound_with_params(SOUND_APPROVE, 0, 0, 0); } } break; case MSG_STYLE_UPGRADE: printer->windowBasePos.x = *srcBuf++; printer->windowBasePos.y = *srcBuf++; printer->windowSize.x = *srcBuf++; printer->windowSize.y = *srcBuf++; sfx_play_sound_with_params(SOUND_APPROVE, 0, 0, 0); printer->windowState = MSG_WINDOW_STATE_OPENING; printer->delayFlags |= MSG_DELAY_FLAG_1; printer->stateFlags |= MSG_STATE_FLAG_800; break; case MSG_STYLE_LAMPPOST: printer->windowSize.y = *srcBuf++; // fallthrough case MSG_STYLE_SIGN: do { if (!s8) { printer->windowState = MSG_WINDOW_STATE_OPENING; printer->stateFlags |= MSG_STATE_FLAG_800; printer->delayFlags |= MSG_DELAY_FLAG_1; } } while (0); break; case MSG_STYLE_POSTCARD: arg = *srcBuf++; printer->windowState = MSG_WINDOW_STATE_OPENING; printer->stateFlags |= MSG_STATE_FLAG_800; printer->delayFlags |= MSG_DELAY_FLAG_1; printer->letterBackgroundImg = heap_malloc(((charset_postcard_png_width * charset_postcard_png_height) / 2)); romAddr = charset_ROM_START + (s32)charset_postcard_OFFSET; dma_copy(romAddr, romAddr + ((charset_postcard_png_width * charset_postcard_png_height) / 2), printer->letterBackgroundImg); printer->letterBackgroundPal = heap_malloc(0x20); romAddr = charset_ROM_START + ((s32)charset_postcard_pal_OFFSET + 5); dma_copy(romAddr, romAddr + 0x20, printer->letterBackgroundPal); printer->letterContentImg = heap_malloc(charset_letter_content_1_png_width * charset_letter_content_1_png_height); romAddr = charset_ROM_START + MsgLetterRasterOffsets[arg]; dma_copy(romAddr, romAddr + (charset_letter_content_1_png_width * charset_letter_content_1_png_height), printer->letterContentImg); printer->letterContentPal = heap_malloc(0x200); romAddr = charset_ROM_START + MsgLetterPaletteOffsets[arg]; dma_copy(romAddr, romAddr + 0x200, printer->letterContentPal); break; case MSG_STYLE_POPUP: case MSG_STYLE_B: printer->windowSize.x = printer->msgWidth + 32; #if VERSION_JP printer->windowSize.y = 32; #else printer->windowSize.y = 40; #endif printer->stateFlags |= MSG_STATE_FLAG_8000; do { if (!s8) { printer->stateFlags |= MSG_STATE_FLAG_8000 | MSG_STATE_FLAG_800; printer->windowState = MSG_WINDOW_STATE_D; printer->delayFlags |= MSG_DELAY_FLAG_1; } } while (0); break; case MSG_STYLE_EPILOGUE: printer->windowState = MSG_WINDOW_STATE_PRINTING; break; } if ((printer->delayFlags & MSG_DELAY_FLAG_1) && (printer->delayFlags & (MSG_DELAY_FLAG_4 | MSG_DELAY_FLAG_2))) { printer->delayFlags &= ~MSG_DELAY_FLAG_1; } break; case MSG_CHAR_READ_END: *printBuf++ = MSG_CHAR_PRINT_END; if (printer->stateFlags & MSG_STATE_FLAG_800) { if (printer->stateFlags & MSG_STATE_FLAG_1000) { if (printer->closedWritebackBool != NULL) { *printer->closedWritebackBool = TRUE; } } if (printer->style != MSG_STYLE_POPUP && printer->style != MSG_STYLE_B) { printer->windowState = MSG_WINDOW_STATE_CLOSING; } else { printer->windowState = MSG_WINDOW_STATE_E; } printer->fadeOutCounter = 0; } else { printer->stateFlags |= MSG_STATE_FLAG_1; } printer->delayFlags |= MSG_DELAY_FLAG_1; printer->delayFlags &= ~MSG_DELAY_FLAG_2; break; case MSG_CHAR_READ_FUNCTION: switch (*srcBuf++) { case MSG_READ_FUNC_FONT: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_FONT; *printBuf++ = printer->font = *srcBuf++; break; case MSG_READ_FUNC_VARIANT: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_VARIANT; *printBuf++ = printer->fontVariant = *srcBuf++; break; case MSG_READ_FUNC_COLOR: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_COLOR; *printBuf++ = *srcBuf++; break; case MSG_READ_FUNC_NO_SKIP: printer->stateFlags |= MSG_STATE_FLAG_10; break; case MSG_READ_FUNC_INPUT_OFF: printer->stateFlags |= MSG_STATE_FLAG_20; printer->stateFlags &= ~MSG_STATE_FLAG_PRINT_QUICKLY; break; case MSG_READ_FUNC_INPUT_ON: printer->stateFlags &= ~MSG_STATE_FLAG_20; break; case MSG_READ_FUNC_SPACING: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_SPACING; *printBuf++ = *srcBuf++; break; case MSG_READ_FUNC_DELAY_OFF: printer->delayFlags |= MSG_DELAY_FLAG_2; break; case MSG_READ_FUNC_DELAY_ON: printer->delayFlags &= ~MSG_DELAY_FLAG_2; printer->delayFlags |= MSG_DELAY_FLAG_1; break; case MSG_READ_FUNC_SCROLL: printer->lineEndPos[printer->curLine] = printer->curLinePos; printer->curLine++; *printBuf++ = MSG_CHAR_PRINT_NEXT; arg = *srcBuf++; printer->nextLinePos = printer->curLinePos + (MsgCharsets[printer->font]->newLineY #if !VERSION_JP + MsgStyleVerticalLineOffsets[printer->style] #endif ) * arg; printer->windowState = MSG_WINDOW_STATE_SCROLLING; printer->delayFlags |= MSG_DELAY_FLAG_1; printer->lineCount = 0; break; #if !VERSION_IQUE case MSG_READ_FUNC_SIZE: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_SIZE; *printBuf++ = *srcBuf++; *printBuf++ = arg = *srcBuf++; printer->sizeScale = (arg >> 4) + (arg & 0xF) * 0.0625f; break; case MSG_READ_FUNC_SIZE_RESET: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_SIZE_RESET; printer->sizeScale = 1.0f; break; #endif case MSG_READ_FUNC_SPEED: printer->printDelayTime = *srcBuf++; printer->charsPerChunk = *srcBuf++; break; case MSG_READ_FUNC_SET_X: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_SET_X; *printBuf++ = *srcBuf++; *printBuf++ = *srcBuf++; break; case MSG_READ_FUNC_SET_Y: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_SET_Y; *printBuf++ = *srcBuf++; break; case MSG_READ_FUNC_RIGHT: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_RIGHT; *printBuf++ = *srcBuf++; break; case MSG_READ_FUNC_DOWN: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_DOWN; *printBuf++ = *srcBuf++; break; case MSG_READ_FUNC_UP: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_UP; *printBuf++ = *srcBuf++; break; case MSG_READ_FUNC_INLINE_IMAGE: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_INLINE_IMAGE; *printBuf++ = *srcBuf++; arg1--; printer->curPrintDelay = printer->printDelayTime; if (arg1 <= 0) { printer->delayFlags |= MSG_DELAY_FLAG_1; } if (printer->delayFlags & (MSG_DELAY_FLAG_4 | MSG_DELAY_FLAG_2)) { printer->delayFlags &= ~MSG_DELAY_FLAG_1; } break; case MSG_READ_FUNC_ANIM_SPRITE: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_ANIM_SPRITE; *printBuf++ = *srcBuf++; *printBuf++ = *srcBuf++; *printBuf++ = *srcBuf++; printer->curPrintDelay = printer->printDelayTime; if (--arg1 <= 0) { printer->delayFlags |= MSG_DELAY_FLAG_1; } if (printer->delayFlags & (MSG_DELAY_FLAG_4 | MSG_DELAY_FLAG_2)) { printer->delayFlags &= ~MSG_DELAY_FLAG_1; } break; case MSG_READ_FUNC_ITEM_ICON: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_ITEM_ICON; arg = *srcBuf++; argQ = *srcBuf++; a2 = D_80159B50; offset = arg << 8 | argQ; D_8015131C = D_80159B50; dma_copy(icon_ROM_START + gItemIconRasterOffsets[offset], icon_ROM_START + gItemIconRasterOffsets[offset] + 0x200, a2); romEnd = icon_ROM_START + gItemIconPaletteOffsets[offset] + 0x20; dma_copy(icon_ROM_START + gItemIconPaletteOffsets[offset], romEnd, D_8015C7E0); printer->curPrintDelay = printer->printDelayTime; if (--arg1 <= 0) { printer->delayFlags |= MSG_DELAY_FLAG_1; } if (printer->delayFlags & (MSG_DELAY_FLAG_4 | MSG_DELAY_FLAG_2)) { printer->delayFlags &= ~MSG_DELAY_FLAG_1; } break; case MSG_READ_FUNC_IMAGE: printer->curImageIndex = *srcBuf++; arg = *srcBuf++; argQ = *srcBuf++; printer->varImageScreenPos.x = arg << 8 | argQ; printer->varImageScreenPos.y = *srcBuf++; printer->varImgHasBorder = *srcBuf++; printer->varImgFinalAlpha = *srcBuf++; printer->varImgAlphaFadeStep = *srcBuf++; printer->varImageDisplayState = 0; printer->varImageFadeTimer = 0; if (--arg1 <= 0) { printer->delayFlags |= MSG_DELAY_FLAG_1; } if (printer->delayFlags & (MSG_DELAY_FLAG_4 | MSG_DELAY_FLAG_2)) { printer->delayFlags &= ~MSG_DELAY_FLAG_1; } break; case MSG_READ_FUNC_HIDE_IMAGE: arg = *srcBuf++; if (arg != 0) { printer->varImageDisplayState = 2; printer->varImgAlphaFadeStep = arg; printer->varImageFadeTimer = 0; } else { printer->varImageScreenPos.x = 0; } break; case MSG_READ_FUNC_ANIM_DELAY: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_ANIM_DELAY; *printBuf++ = *srcBuf++; *printBuf++ = *srcBuf++; *printBuf++ = *srcBuf++; printer->delayFlags |= MSG_DELAY_FLAG_4; break; case MSG_READ_FUNC_ANIM_LOOP: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_ANIM_LOOP; *printBuf++ = *srcBuf++; *printBuf++ = *srcBuf++; break; case MSG_READ_FUNC_ANIM_DONE: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_ANIM_DONE; *printBuf++ = *srcBuf++; printer->delayFlags &= ~MSG_DELAY_FLAG_4; if (--arg1 <= 0) { printer->delayFlags |= MSG_DELAY_FLAG_1; } break; case MSG_READ_FUNC_SET_CURSOR: arg = *srcBuf++; argQ = *srcBuf++; argW = *srcBuf++; argE = *srcBuf++; printer->cursorPosX[arg] = argQ << 8 | argW; printer->cursorPosY[arg] = argE; break; case MSG_READ_FUNC_CURSOR: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_CURSOR; *printBuf++ = *srcBuf++; break; case MSG_READ_FUNC_END_CHOICE: sfx_play_sound_with_params(SOUND_MENU_SHOW_CHOICE, 0, 0, 0); printer->maxOption = *srcBuf++; printer->madeChoice = 0; printer->curOption = 0; printer->selectedOption = 0; printer->windowState = MSG_WINDOW_STATE_WAITING_FOR_CHOICE; printer->delayFlags |= MSG_DELAY_FLAG_1; break; case MSG_READ_FUNC_SET_CANCEL: printer->cancelOption = *srcBuf++; break; case MSG_READ_FUNC_OPTION: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_OPTION; *printBuf++ = *srcBuf++; break; case MSG_READ_FUNC_RESET_GFX: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_RESET_GFX; break; case MSG_READ_FUNC_YIELD: printer->stateFlags |= MSG_STATE_FLAG_100000 | MSG_STATE_FLAG_40; printer->delayFlags |= MSG_DELAY_FLAG_1; printer->stateFlags &= ~MSG_STATE_FLAG_SPEAKING; printer->stateFlags &= ~MSG_STATE_FLAG_PRINT_QUICKLY; break; case MSG_READ_FUNC_SAVE_POS: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_SAVE_POS; break; case MSG_READ_FUNC_RESTORE_POS: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_RESTORE_POS; break; case MSG_READ_FUNC_SAVE_COLOR: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_SAVE_COLOR; break; case MSG_READ_FUNC_RESTORE_COLOR: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_RESTORE_COLOR; break; case MSG_READ_FUNC_START_FX: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_START_FX; arg = *srcBuf++; switch (arg) { case MSG_FX_SHAKE: case MSG_FX_WAVE: case MSG_FX_NOISE_OUTLINE: case MSG_FX_RAINBOW: case MSG_FX_GLOBAL_WAVE: case MSG_FX_GLOBAL_RAINBOW: case MSG_FX_RISE_PRINT: case MSG_FX_GROW_PRINT: case MSG_FX_SIZE_JITTER: case MSG_FX_SIZE_WAVE: case MSG_FX_DROP_SHADOW: *printBuf++ = arg; break; case MSG_FX_STATIC: case MSG_FX_BLUR: case MSG_FX_DITHER_FADE: *printBuf++ = arg; *printBuf++ = *srcBuf++; break; } break; case MSG_READ_FUNC_END_FX: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_END_FX; *printBuf++ = *srcBuf++; break; case MSG_READ_FUNC_VAR: arg = *srcBuf++; srcBuf -= 3; if (printer->varBufferReadPos == 0) { printer->unk_52A = printer->fontVariant; *printBuf++ = MSG_CHAR_PRINT_VARIANT0; } do { s32 a0 = 1; argQ = gMessageMsgVars[arg][printer->varBufferReadPos++]; if (argQ >= MSG_CONTROL_CHAR) { s32 tmp; switch (argQ) { case MSG_CHAR_READ_ENDL: if (gMessageMsgVars[arg][printer->varBufferReadPos] != MSG_CHAR_READ_END) { sp10[0] = MSG_CHAR_PRINT_ENDL; } else { a0 = 0; } break; case MSG_CHAR_READ_VARIANT0: case MSG_CHAR_READ_VARIANT1: case MSG_CHAR_READ_VARIANT2: case MSG_CHAR_READ_VARIANT3: sp10[0] = (argQ - MSG_CHAR_READ_VARIANT0) + MSG_CHAR_PRINT_VARIANT0; printer->fontVariant = sp10[0] + 0x10F; break; #if !VERSION_JP case MSG_CHAR_READ_SPACE: sp10[0] = MSG_CHAR_PRINT_SPACE; break; #endif case MSG_CHAR_READ_FUNCTION: sp10[0] = MSG_CHAR_PRINT_FUNCTION; switch (gMessageMsgVars[arg][printer->varBufferReadPos++]) { case MSG_READ_FUNC_COLOR: sp10[1] = MSG_PRINT_FUNC_COLOR; sp10[2] = gMessageMsgVars[arg][printer->varBufferReadPos++]; a0 = 3; break; case MSG_READ_FUNC_SAVE_COLOR: sp10[1] = MSG_PRINT_FUNC_SAVE_COLOR; a0 = 2; break; case MSG_READ_FUNC_RESTORE_COLOR: sp10[1] = MSG_PRINT_FUNC_RESTORE_COLOR; a0 = 2; break; } break; } } else { sp10[0] = argQ; } for (i = 0; i < a0; i++) { arg1--; *printBuf++ = sp10[i]; } if (gMessageMsgVars[arg][printer->varBufferReadPos] == MSG_CHAR_READ_END) { srcBuf += 3; printer->varBufferReadPos = 0; printer->fontVariant = printer->unk_52A; *printBuf++ = MSG_CHAR_PRINT_VARIANT0 + printer->fontVariant; break; } } while ((printer->delayFlags & (MSG_DELAY_FLAG_4 | MSG_DELAY_FLAG_2)) || arg1 > 0); if (!(printer->delayFlags & (MSG_DELAY_FLAG_4 | MSG_DELAY_FLAG_2)) && arg1 <= 0) { printer->delayFlags |= MSG_DELAY_FLAG_1; printer->curPrintDelay = printer->printDelayTime; } msg_play_speech_sound(printer, argQ); if (printer->stateFlags & MSG_STATE_FLAG_800000) { printer->stateFlags |= MSG_STATE_FLAG_SPEAKING; } break; case MSG_READ_FUNC_VOICE: arg = *srcBuf++; printer->speechSoundType = arg; printer->speechSoundIDA = MsgVoices[arg].voiceA; printer->speechSoundIDB = MsgVoices[arg].voiceB; printer->speechPitchShift = MsgVoices[arg].pitchShift; break; case MSG_READ_FUNC_VOLUME: printer->volume = *srcBuf++; break; case MSG_READ_FUNC_CUSTOM_VOICE: arg = *srcBuf++; argQ = *srcBuf++; argW = *srcBuf++; argE = *srcBuf++; printer->speechSoundIDA = (arg << 0x18) + (argQ << 0x10) + (argW << 0x8) + (argE); arg = *srcBuf++; argQ = *srcBuf++; argW = *srcBuf++; argE = *srcBuf++; printer->speechSoundIDB = (arg << 0x18) + (argQ << 0x10) + (argW << 0x8) + (argE); break; case MSG_READ_FUNC_CENTER_X: *printBuf++ = MSG_CHAR_PRINT_FUNCTION; *printBuf++ = MSG_PRINT_FUNC_CENTER_X; *printBuf++ = *srcBuf++; break; case MSG_READ_FUNC_SET_REWIND: if (*srcBuf++) { printer->stateFlags |= MSG_STATE_FLAG_40000; } else { printer->stateFlags &= ~MSG_STATE_FLAG_40000; } break; case MSG_READ_FUNC_ENABLE_CDOWN_NEXT: printer->stateFlags |= MSG_STATE_FLAG_80000; break; } break; default: *printBuf++ = c; arg1--; #if VERSION_IQUE if (c >= MSG_CHAR_MULTIBYTE_FIRST && c <= MSG_CHAR_MULTIBYTE_LAST) { *printBuf++ = nextArg; srcBuf++; arg1--; } #endif if (printer->fontVariant == 0 && c == MSG_CHAR_UNK_C3) { printer->stateFlags &= ~MSG_STATE_FLAG_SPEAKING; } else { msg_play_speech_sound(printer, c); if (printer->stateFlags & MSG_STATE_FLAG_800000) { printer->stateFlags |= MSG_STATE_FLAG_SPEAKING; } } break; } if (!(printer->delayFlags & (MSG_DELAY_FLAG_4 | MSG_DELAY_FLAG_2)) && arg1 <= 0) { printer->delayFlags |= MSG_DELAY_FLAG_1; printer->curPrintDelay = printer->printDelayTime; } if (!(printer->delayFlags & MSG_DELAY_FLAG_1)) { continue; } if (!s8) { break; } if (srcBuf[-1] == MSG_CHAR_READ_END) { break; } arg1 = 10000; } while (TRUE); printer->printBufferPos = printBuf - printer->printBuffer; printer->delayFlags = 0; printer->srcBufferPos = (u16)(s32)(srcBuf - (s32)printer->srcBuffer); *printBuf = MSG_CHAR_PRINT_END; } #endif void initialize_printer(MessagePrintState* printer, s32 arg1, s32 arg2) { s32 i; printer->printBufferSize = ARRAY_COUNT(printer->printBuffer); printer->printBuffer[0] = MSG_CHAR_PRINT_END; printer->printDelayTime = 1; printer->charsPerChunk = 1; printer->windowScrollRate = (s32)(6 / DT); printer->srcBuffer = NULL; printer->msgID = 0; printer->curPrintDelay = 0; printer->windowOffsetPos.x = 0; printer->windowOffsetPos.y = 0; printer->windowBasePos.x = 0; printer->windowBasePos.y = 0; printer->rewindArrowAnimState = REWIND_ARROW_STATE_INIT; printer->rewindArrowCounter = 0; printer->rewindArrowPos.x = 0; printer->rewindArrowPos.y = 0; printer->curLine = 0; printer->unkArraySize = 0; printer->maxOption = 0; printer->madeChoice = 0; printer->curOption = 0; printer->selectedOption = 0; printer->cancelOption = -1; printer->windowState = MSG_WINDOW_STATE_DONE; printer->stateFlags = 0; printer->delayFlags = 0; printer->closedWritebackBool = NULL; printer->printBufferPos = 0; printer->srcBufferPos = 0; printer->font = 0; printer->fontVariant = 0; printer->effectFrameCounter = 0; printer->curLinePos = 0; printer->unk_46C = 0; printer->lineCount = 0; for (i = 0; i < ARRAY_COUNT(printer->animTimers); i++) { printer->curAnimFrame[i] = 0; printer->animTimers[i] = -1; } printer->initOpenPos.x = 160; printer->initOpenPos.y = 40; printer->speechSoundType = -1; printer->speechPan = 64; printer->volume = 75; printer->rewindArrowCounter = 0; printer->style = 0; printer->fadeInCounter = 0; printer->openStartPos.x = 0; printer->openStartPos.y = 0; printer->fadeOutCounter = 0; printer->windowSize.y = 0; printer->windowSize.x = 0; printer->speechPitchShift = 0; printer->speechSoundIDA = 0; printer->speechSoundIDB = 0; printer->varBufferReadPos = 0; printer->curImageIndex = 0; printer->varImageScreenPos.x = 0; printer->varImageScreenPos.y = 0; printer->varImgHasBorder = 0; printer->varImgFinalAlpha = 255; printer->varImageDisplayState = 0; printer->varImageFadeTimer = 0; printer->letterBackgroundImg = NULL; printer->letterBackgroundPal = NULL; printer->letterContentImg = NULL; printer->letterContentPal = NULL; printer->sizeScale = 1.0f; } #if VERSION_PAL void dma_load_msg(u32 msgID, void* dest) { u8* langPtr = D_PAL_8014AE50[gCurrentLanguage]; u8* new_langPtr; u8* addr = (u8*) langPtr + (msgID >> 14); // (msgID >> 16) * 4 u8* offset[2]; // start, end dma_copy(addr, addr + 4, &offset[0]); // Load section offset new_langPtr = langPtr; new_langPtr = offset[0] + ((u32) new_langPtr); addr = new_langPtr + ((msgID & 0xFFFF) * 4); dma_copy(addr, addr + 8, &offset); // Load message start and end offsets // Load the msg data dma_copy(&langPtr[(u32)offset[0]], &langPtr[(u32)offset[1]], dest); } #else void dma_load_msg(u32 msgID, void* dest) { u8* addr = (u8*) MSG_ROM_START + (msgID >> 14); // (msgID >> 16) * 4 u8* offset[2]; // start, end dma_copy(addr, addr + 4, &offset[0]); // Load section offset addr = MSG_ROM_START + offset[0] + (msgID & 0xFFFF) * 4; dma_copy(addr, addr + 8, &offset); // Load message start and end offsets // Load the msg data dma_copy(MSG_ROM_START + offset[0], MSG_ROM_START + offset[1], dest); } #endif s8* load_message_to_buffer(s32 msgID) { s8* prevBufferPos; dma_load_msg(msgID, &gMessageBuffers[gNextMessageBuffer]); prevBufferPos = gMessageBuffers[gNextMessageBuffer]; gNextMessageBuffer++; if (gNextMessageBuffer >= ARRAY_COUNT(gMessageBuffers)) { gNextMessageBuffer = 0; } return prevBufferPos; } MessagePrintState* msg_get_printer_for_msg(s32 msgID, s32* donePrintingWriteback) { return _msg_get_printer_for_msg(msgID, donePrintingWriteback, 0); } MessagePrintState* _msg_get_printer_for_msg(s32 msgID, s32* donePrintingWriteback, s32 arg2) { MessagePrintState* printer; s8* srcBuffer; s32 height; s32 width; s32 maxLineChars; s32 numLines; s32 maxLinesPerPage; s32 i; if (msgID == MSG_NONE) { return NULL; } srcBuffer = (s8*) msgID; if (msgID >= 0) { srcBuffer = load_message_to_buffer((s32)srcBuffer); } for (i = 0; i < ARRAY_COUNT(gMessagePrinters); i++) { printer = &gMessagePrinters[i]; if (!(printer->stateFlags & MSG_STATE_FLAG_2)) { initialize_printer(printer, 1, arg2); printer->windowState = MSG_WINDOW_STATE_INIT; printer->srcBuffer = srcBuffer; printer->msgID = msgID; printer->stateFlags |= MSG_STATE_FLAG_2; get_msg_properties(msgID, &height, &width, &maxLineChars, &numLines, &maxLinesPerPage, NULL, 0); printer->msgHeight = height; printer->msgWidth = width; printer->maxLineChars = maxLineChars; printer->numLines = numLines; printer->maxLinesPerPage = maxLinesPerPage; printer->closedWritebackBool = donePrintingWriteback; if (donePrintingWriteback != NULL) { *donePrintingWriteback = FALSE; } return printer; } } return NULL; } s32 msg_printer_load_msg(s32 msgID, MessagePrintState* printer) { s8* buffer; if (msgID >= 0) { buffer = load_message_to_buffer(msgID); } else { buffer = (s8*) msgID; } printer->srcBuffer = buffer; printer->srcBufferPos = 0; printer->stateFlags &= ~MSG_STATE_FLAG_40; return 1; } void msg_printer_set_origin_pos(MessagePrintState* msgPrintState, s32 x, s32 y) { msgPrintState->initOpenPos.x = x; msgPrintState->initOpenPos.y = y; if (msgPrintState->initOpenPos.x < 0) { msgPrintState->initOpenPos.x = 0; } if (msgPrintState->initOpenPos.x > SCREEN_WIDTH) { msgPrintState->initOpenPos.x = SCREEN_WIDTH; } if (msgPrintState->initOpenPos.y < 0) { msgPrintState->initOpenPos.y = 0; } if (msgPrintState->initOpenPos.y > 220) { msgPrintState->initOpenPos.y = 220; } } s32 cancel_message(MessagePrintState* msgPrintState) { if (!(msgPrintState->stateFlags & MSG_STATE_FLAG_2)) { return FALSE; } msgPrintState->stateFlags |= MSG_STATE_FLAG_1; return TRUE; } void set_message_images(MessageImageData* images) { *gMsgVarImages = images; } void set_message_text_var(s32 msgID, s32 index) { u8* mallocSpace = NULL; s32 i; u8* msgVars; if (msgID >= 0) { mallocSpace = general_heap_malloc(0x400); dma_load_msg(msgID, mallocSpace); msgID = (s32)mallocSpace; } i = 0; msgVars = gMessageMsgVars[index]; while (TRUE) { msgVars[i] = ((u8*)msgID)[i]; if (((u8*)msgID)[i] == MSG_CHAR_READ_END) { break; } if (++i >= 32) { msgVars[i - 1] = MSG_CHAR_READ_END; break; } } if (mallocSpace != NULL) { general_heap_free(mallocSpace); } } void set_message_int_var(s32 value, s32 index) { s8 strBuffer[ARRAY_COUNT(gMessageMsgVars[index])]; s8* bufferIt; s32 i; int_to_string(value, strBuffer, 10); for (i = 0, bufferIt = strBuffer; i < ARRAY_COUNT(gMessageMsgVars[index]) - 1; i++) { s8 thisChar = bufferIt[i]; if (thisChar == 0) { break; } gMessageMsgVars[index][i] = thisChar - '0' + MSG_CHAR_DIGIT_0; } gMessageMsgVars[index][i] = MSG_CHAR_READ_END; } void close_message(MessagePrintState* msgPrintState) { msgPrintState->stateFlags &= ~MSG_STATE_FLAG_40; } #if VERSION_JP #define CHAR_SPACE_MULTIPLIER 0.7 #else #define CHAR_SPACE_MULTIPLIER 0.6 #endif s32 msg_get_print_char_width(s32 character, s32 charset, s32 variation, f32 msgScale, s32 overrideCharWidth, u8 flags) { f32 charWidth; if (character >= MSG_CONTROL_CHAR && (character != MSG_CHAR_READ_SPACE && character != MSG_CHAR_READ_FULL_SPACE && character != MSG_CHAR_READ_HALF_SPACE)) { return 0; } #if VERSION_IQUE if (character >= MSG_CHAR_MULTIBYTE_FIRST && character <= MSG_CHAR_MULTIBYTE_LAST) { charWidth = 16.0; return charWidth * msgScale; } #endif if (overrideCharWidth != 0) { charWidth = overrideCharWidth; } else if (flags != 0) { u8* charWidthTable = MsgCharsets[charset]->rasters[variation].charWidthTable; if (charWidthTable != NULL && character != MSG_CHAR_READ_SPACE && character != MSG_CHAR_READ_FULL_SPACE && character != MSG_CHAR_READ_HALF_SPACE) { charWidth = charWidthTable[character]; } else { charWidth = MsgCharsets[charset]->rasters[variation].monospaceWidth; } } else { charWidth = MsgCharsets[charset]->rasters[variation].monospaceWidth; } if (character == MSG_CHAR_READ_SPACE) { return charWidth * msgScale * CHAR_SPACE_MULTIPLIER; } if (character == MSG_CHAR_READ_FULL_SPACE) { f64 retWidth = charWidth * msgScale; return retWidth; } if (character == MSG_CHAR_READ_HALF_SPACE) { return charWidth * msgScale * 0.5; } if (character >= MSG_CONTROL_CHAR) { return 0; } return charWidth * msgScale; } s32 msg_get_draw_char_width(s32 character, s32 charset, s32 variation, f32 msgScale, s32 overrideCharWidth, u16 flags) { f32 baseWidth; if (character >= MSG_CONTROL_CHAR && (character != MSG_CHAR_PRINT_SPACE && character != MSG_CHAR_PRINT_FULL_SPACE && character != MSG_CHAR_PRINT_HALF_SPACE)) { return 0; } if (overrideCharWidth != 0) { baseWidth = overrideCharWidth; } else if (flags & MSG_PRINT_FLAG_100) { u8* charWidthTable = MsgCharsets[charset]->rasters[variation].charWidthTable; if (charWidthTable != NULL && character != MSG_CHAR_PRINT_SPACE && character != MSG_CHAR_PRINT_FULL_SPACE && character != MSG_CHAR_PRINT_HALF_SPACE) { baseWidth = charWidthTable[character]; } else { baseWidth = MsgCharsets[charset]->rasters[variation].monospaceWidth; } } else { baseWidth = MsgCharsets[charset]->rasters[variation].monospaceWidth; } if (character == MSG_CHAR_PRINT_SPACE) { return baseWidth * msgScale * CHAR_SPACE_MULTIPLIER; } if (character == MSG_CHAR_PRINT_FULL_SPACE) { f64 charWidth = baseWidth * msgScale; return charWidth; } if (character == MSG_CHAR_PRINT_HALF_SPACE) { return baseWidth * msgScale * 0.5; } if (character >= MSG_CONTROL_CHAR) { return 0; } return baseWidth * msgScale; } void get_msg_properties(s32 msgID, s32* height, s32* width, s32* maxLineChars, s32* numLines, s32* maxLinesPerPage, s32* numSpaces, u16 charset) { u8* message; s32 i; u16 pageCount; s32 linesOnPage; u8 stop; s32 lineWidth; s32 charCount; u16 lineIndex; s32 msgStyle; s32 functionCode; u8 packedScaleY; f32 scale; s32 temp; u16 lineWidths[32]; u16 lineCharNumbers[32]; u16 linesPerPage[32]; s32 lineCount; u16 varIndex; u16 font; u8* buffer; u16 maxLineWidth; u16 maxCharsPerLine; u16 maxLinesOnPage; u16 spaceCount; u16 endl; u8 c; u8 prevChar; scale = 1.0f; c = 0; lineIndex = 0; pageCount = 0; varIndex = 0; font = 0; buffer = NULL; maxLineWidth = 0; maxCharsPerLine = 0; maxLinesOnPage = 0; spaceCount = 0; if (msgID == MSG_NONE) { return; } if (msgID >= 0) { buffer = general_heap_malloc(0x400); dma_load_msg(msgID, buffer); message = buffer; } else { message = (u8*)msgID; } if (charset & 1) { font = 1; } i = 0; stop = FALSE; lineWidth = 0; linesOnPage = 0; charCount = 0; endl = TRUE; lineCount = 0; do { prevChar = c; c = message[i++]; switch (c) { case MSG_CHAR_READ_VARIANT0: case MSG_CHAR_READ_VARIANT1: case MSG_CHAR_READ_VARIANT2: case MSG_CHAR_READ_VARIANT3: varIndex = c - MSG_CHAR_READ_VARIANT0; break; case MSG_CHAR_READ_PAUSE: i++; break; case MSG_CHAR_READ_WAIT: case MSG_CHAR_READ_NEXT: if (linesOnPage != 0) { linesPerPage[pageCount] = linesOnPage; pageCount++; if (pageCount >= 32) { stop = 1; } linesOnPage = 0; } break; case MSG_CHAR_READ_ENDL: lineWidths[lineIndex] = lineWidth; lineCharNumbers[lineIndex] = charCount; lineIndex++; if (lineIndex >= 32) { stop = 1; } lineWidth = 0; charCount = 0; endl = TRUE; break; case MSG_CHAR_READ_STYLE: msgStyle = message[i++]; switch (msgStyle) { case MSG_STYLE_CHOICE: i += 4; break; case MSG_STYLE_POSTCARD: i++; break; case MSG_STYLE_RIGHT: case MSG_STYLE_LEFT: case MSG_STYLE_CENTER: case MSG_STYLE_TATTLE: case MSG_STYLE_INSPECT: case MSG_STYLE_SIGN: case MSG_STYLE_LAMPPOST: case MSG_STYLE_POPUP: case MSG_STYLE_B: break; } break; case MSG_CHAR_READ_END: lineWidths[lineIndex] = lineWidth; lineCharNumbers[lineIndex] = charCount; lineIndex++; stop = TRUE; break; case MSG_CHAR_READ_FUNCTION: functionCode = message[i++]; switch (functionCode) { case MSG_READ_FUNC_FONT: font = message[i++]; break; case MSG_READ_FUNC_RESET_GFX: case MSG_READ_FUNC_NO_SKIP: case MSG_READ_FUNC_INPUT_OFF: case MSG_READ_FUNC_INPUT_ON: case MSG_READ_FUNC_DELAY_OFF: case MSG_READ_FUNC_DELAY_ON: case MSG_READ_FUNC_SAVE_POS: case MSG_READ_FUNC_RESTORE_POS: case MSG_READ_FUNC_SAVE_COLOR: case MSG_READ_FUNC_RESTORE_COLOR: case MSG_READ_FUNC_ENABLE_CDOWN_NEXT: break; default: stop = TRUE; break; case MSG_READ_FUNC_CUSTOM_VOICE: i++; // fallthrough temp = 4; case MSG_READ_FUNC_IMAGE: i += temp; // fallthrough case MSG_READ_FUNC_ANIM_SPRITE: case MSG_READ_FUNC_ANIM_DELAY: i++; // fallthrough case MSG_READ_FUNC_SPEED: case MSG_READ_FUNC_SET_X: case MSG_READ_FUNC_ANIM_LOOP: i++; // fallthrough case MSG_READ_FUNC_COLOR: case MSG_READ_FUNC_SPACING: case MSG_READ_FUNC_SCROLL: case MSG_READ_FUNC_SET_Y: case MSG_READ_FUNC_RIGHT: case MSG_READ_FUNC_DOWN: case MSG_READ_FUNC_UP: case MSG_READ_FUNC_INLINE_IMAGE: case MSG_READ_FUNC_ITEM_ICON: case MSG_READ_FUNC_HIDE_IMAGE: case MSG_READ_FUNC_ANIM_DONE: case MSG_READ_FUNC_CURSOR: case MSG_READ_FUNC_END_CHOICE: case MSG_READ_FUNC_SET_CANCEL: case MSG_READ_FUNC_OPTION: case MSG_READ_FUNC_END_FX: case MSG_READ_FUNC_SET_REWIND: case MSG_READ_FUNC_VOICE: i++; break; case MSG_READ_FUNC_CENTER_X: if (message[i] == 0) { stop = TRUE; } i++; break; case MSG_READ_FUNC_YIELD: if (message[i] == MSG_CHAR_READ_END) { stop = TRUE; } break; case MSG_READ_FUNC_SIZE: packedScaleY = message[i + 1]; i += 2; scale = (f32)(packedScaleY >> 4) + ((packedScaleY & 0xF) * 0.0625f); break; case MSG_READ_FUNC_SIZE_RESET: scale = 1.0f; break; case MSG_READ_FUNC_START_FX: switch (message[i++]) { case MSG_FX_STATIC: case MSG_FX_BLUR: case MSG_FX_DITHER_FADE: i++; break; case MSG_FX_SHAKE: case MSG_FX_WAVE: case MSG_FX_NOISE_OUTLINE: case MSG_FX_RAINBOW: case MSG_FX_GLOBAL_WAVE: case MSG_FX_GLOBAL_RAINBOW: case MSG_FX_RISE_PRINT: case MSG_FX_GROW_PRINT: case MSG_FX_SIZE_JITTER: case MSG_FX_SIZE_WAVE: case MSG_FX_DROP_SHADOW: break; } break; case MSG_READ_FUNC_VAR: lineWidth += get_msg_width((s32)gMessageMsgVars[message[i++]], 0); break; } break; case MSG_CHAR_READ_UNK_CHAR_FA: break; case MSG_CHAR_READ_SPACE: case MSG_CHAR_READ_FULL_SPACE: case MSG_CHAR_READ_HALF_SPACE: spaceCount++; // fallthrough default: if (endl) { lineCount++; linesOnPage++; endl = FALSE; } #if VERSION_IQUE if (prevChar >= MSG_CHAR_MULTIBYTE_FIRST && prevChar <= MSG_CHAR_MULTIBYTE_LAST) { break; } #endif lineWidth += msg_get_print_char_width(c, font, varIndex, scale, 0, 1); charCount++; break; } } while (!stop); if (buffer != NULL) { general_heap_free(buffer); } for (i = 0; i < lineIndex; i++) { if (maxLineWidth < lineWidths[i]) { maxLineWidth = lineWidths[i]; } if (maxCharsPerLine < lineCharNumbers[i]) { maxCharsPerLine = lineCharNumbers[i]; } } if (pageCount == 0) { maxLinesOnPage = linesOnPage; } else { for (i = 0; i < pageCount; i++) { if (maxLinesOnPage < linesPerPage[i]) { maxLinesOnPage = linesPerPage[i]; } } } if (width != NULL) { *width = maxLineWidth; } if (height != NULL) { *height = lineCount * MsgCharsets[font]->newLineY; } if (maxLineChars != NULL) { *maxLineChars = maxCharsPerLine; } if (numLines != NULL) { *numLines = lineCount; } if (maxLinesPerPage != NULL) { *maxLinesPerPage = maxLinesOnPage; } if (numSpaces != NULL) { *numSpaces = spaceCount; } } #if !VERSION_JP static const f32 padding = 0.0f; #endif s32 get_msg_width(s32 msgID, u16 charset) { s32 width; get_msg_properties(msgID, NULL, &width, NULL, NULL, NULL, NULL, charset); return width; } #if !VERSION_JP s32 get_msg_lines(s32 msgID) { s32 numLines; get_msg_properties(msgID, NULL, NULL, NULL, &numLines, NULL, NULL, 0); return numLines; } #endif void draw_msg(s32 msgID, s32 posX, s32 posY, s32 opacity, s32 palette, u8 style) { MessagePrintState stackPrinter; MessagePrintState* printer; u16 bufferPos; s8* mallocSpace; s32 charset; u16 flags; s32 width; flags = 0; bufferPos = 0; mallocSpace = NULL; charset = 0; if (msgID != 0) { if (style & DRAW_MSG_STYLE_MENU) { flags = 2; charset = 1; } if (opacity < 0xFF) { flags |= 1; } printer = &stackPrinter; initialize_printer(printer, 1, 0); if (msgID < 0) { printer->srcBuffer = (u8*)msgID; } else { mallocSpace = general_heap_malloc(0x400); dma_load_msg(msgID, mallocSpace); printer->srcBuffer = mallocSpace; get_msg_properties((s32) printer->srcBuffer, 0, &width, 0, 0, 0, 0, charset); printer->msgWidth = width; } if (palette >= 0) { printer->printBuffer[bufferPos++] = MSG_CHAR_PRINT_FUNCTION; printer->printBuffer[bufferPos++] = MSG_PRINT_FUNC_COLOR; printer->printBuffer[bufferPos++] = palette; printer->printBufferPos += 3; } if (style & DRAW_MSG_STYLE_WAVY) { printer->printBuffer[bufferPos++] = MSG_CHAR_PRINT_FUNCTION; printer->printBuffer[bufferPos++] = MSG_PRINT_FUNC_START_FX; printer->printBuffer[bufferPos++] = MSG_FX_GLOBAL_WAVE; printer->printBufferPos += 3; } if (style & DRAW_MSG_STYLE_RAINBOW) { printer->printBuffer[bufferPos++] = MSG_CHAR_PRINT_FUNCTION; printer->printBuffer[bufferPos++] = MSG_PRINT_FUNC_START_FX; printer->printBuffer[bufferPos++] = MSG_FX_GLOBAL_RAINBOW; printer->printBufferPos += 3; } if (style & DRAW_MSG_STYLE_DROP_SHADOW) { printer->printBuffer[bufferPos++] = MSG_CHAR_PRINT_FUNCTION; printer->printBuffer[bufferPos++] = MSG_PRINT_FUNC_START_FX; printer->printBuffer[bufferPos++] = MSG_FX_DROP_SHADOW; printer->printBufferPos += 3; } msg_copy_to_print_buffer(printer, 10000, 1); appendGfx_message(printer, (s16)posX, (s16)posY, 0, 0, flags, opacity & 0xFF); if (mallocSpace != NULL) { general_heap_free(mallocSpace); } } } void msg_update_rewind_arrow(s32 printerIndex) { MessagePrintState* printer = &gMessagePrinters[printerIndex]; f32 angle = 0.0f; f32 scale = 1.0f; f32 colorG = 255.0f; f32 colorR = 255.0f; f32 colorB = 255.0f; Matrix4f sp18; Matrix4f sp58; f32 temp; gDPPipeSync(gMainGfxPos++); gSPDisplayList(gMainGfxPos++, D_8014C2D8); switch (printer->rewindArrowAnimState) { case REWIND_ARROW_STATE_INIT: printer->rewindArrowCounter = 0; printer->rewindArrowSwingPhase = 0; printer->rewindArrowAnimState = REWIND_ARROW_STATE_GROW; // fallthrough case REWIND_ARROW_STATE_GROW: temp = printer->rewindArrowCounter; scale = temp * 0.2 + 0.5; if (++printer->rewindArrowCounter >= 4) { printer->rewindArrowCounter = 0; printer->rewindArrowAnimState = REWIND_ARROW_STATE_NEUTRAL; } break; case REWIND_ARROW_STATE_NEUTRAL: if (++printer->rewindArrowCounter >= 25) { printer->rewindArrowCounter = 0; printer->rewindArrowAnimState = REWIND_ARROW_STATE_CHANGE_COLOR; } break; case REWIND_ARROW_STATE_CHANGE_COLOR: colorR = update_lerp(EASING_LINEAR, 255.0f, 224.0f, printer->rewindArrowCounter, 15); colorG = update_lerp(EASING_LINEAR, 255.0f, 224.0f, printer->rewindArrowCounter, 15); colorB = update_lerp(EASING_LINEAR, 255.0f, 208.0f, printer->rewindArrowCounter, 15); if (++printer->rewindArrowCounter >= 15) { printer->rewindArrowCounter = 0; printer->rewindArrowAnimState = REWIND_ARROW_STATE_CHANGE_COLOR_BACK; } break; case REWIND_ARROW_STATE_CHANGE_COLOR_BACK: colorR = update_lerp(EASING_LINEAR, 224.0f, 255.0f, printer->rewindArrowCounter, 15); colorG = update_lerp(EASING_LINEAR, 224.0f, 255.0f, printer->rewindArrowCounter, 15); colorB = update_lerp(EASING_LINEAR, 208.0f, 255.0f, printer->rewindArrowCounter, 15); if (++printer->rewindArrowCounter >= 15) { printer->rewindArrowCounter = 0; printer->rewindArrowAnimState = REWIND_ARROW_STATE_NEUTRAL; } break; } gDPSetPrimColor(gMainGfxPos++, 0, 0, colorR, colorG, colorB, 255); if (printer->rewindArrowAnimState == REWIND_ARROW_STATE_NEUTRAL || printer->rewindArrowAnimState == REWIND_ARROW_STATE_CHANGE_COLOR || printer->rewindArrowAnimState == REWIND_ARROW_STATE_CHANGE_COLOR_BACK) { angle = cosine(printer->rewindArrowSwingPhase) * 30.0f; printer->rewindArrowSwingPhase += 15; if (printer->rewindArrowSwingPhase >= 360) { printer->rewindArrowSwingPhase -= 360; } } guTranslateF(sp18, printer->rewindArrowPos.x + 12, -(printer->rewindArrowPos.y + 12), 0); if (angle != 0.0) { guRotateF(sp58, angle, 0, 0, 1.0f); guMtxCatF(sp58, sp18, sp18); } if (scale != 1.0) { guScaleF(sp58, scale, scale, 1.0f); guMtxCatF(sp58, sp18, sp18); } guMtxF2L(sp18, &gDisplayContext->matrixStack[gMatrixListPos]); gSPMatrix(gMainGfxPos++, VIRTUAL_TO_PHYSICAL(&gDisplayContext->matrixStack[gMatrixListPos++]), G_MTX_PUSH | G_MTX_LOAD | G_MTX_MODELVIEW); gDPLoadTextureTile(gMainGfxPos++, ui_msg_star_png, G_IM_FMT_RGBA, G_IM_SIZ_16b, 16, 0, 0, 0, 15, 17, 0, G_TX_MIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 4, 4, G_TX_NOLOD, G_TX_NOLOD); gDPLoadMultiTile_4b(gMainGfxPos++, ui_msg_star_silhouette_png, 0x0100, 1, G_IM_FMT_I, 16, 0, 0, 0, 15, 18, 0, G_TX_MIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 4, 5, G_TX_NOLOD, G_TX_NOLOD); gSPVertex(gMainGfxPos++, gRewindArrowQuad, 4, 0); gSP2Triangles(gMainGfxPos++, 0, 2, 1, 0, 1, 2, 3, 0); } void msg_draw_rewind_arrow(s32 printerIndex) { MessagePrintState* printer = &gMessagePrinters[printerIndex]; if (printer->rewindArrowCounter < 6) { draw_ci_image_with_clipping(ui_msg_rewind_arrow_png, 24, 24, G_IM_FMT_CI, G_IM_SIZ_4b, ui_msg_rewind_arrow_pal, printer->rewindArrowPos.x, printer->rewindArrowPos.y, 10, 10, SCREEN_WIDTH - 20, SCREEN_HEIGHT - 20, 255); } printer->rewindArrowCounter++; if (printer->rewindArrowCounter >= 12) { printer->rewindArrowCounter = 0; } } void msg_draw_choice_pointer(MessagePrintState* printer) { s32 pointerAlpha = 255; s32 shadowAlpha = 72; s32 posInterpPhase = gGameStatusPtr->frameCounter % 360; s32 posX, posY; if (printer->windowState == MSG_WINDOW_STATE_WAITING_FOR_CHOICE || (printer->stateFlags & MSG_STATE_FLAG_20000)) { posX = printer->windowOffsetPos.x + printer->windowBasePos.x + printer->cursorPosX[printer->selectedOption]; posY = printer->windowOffsetPos.y + printer->windowBasePos.y + printer->cursorPosY[printer->selectedOption]; } else { s32 baseX, baseY, targetX, targetY; f32 moveToTargetAlpha = (f32)(printer->scrollingTime + 1.0) / CHOICE_POINTER_MOVE_RATE; baseX = printer->windowOffsetPos.x + printer->windowBasePos.x + printer->cursorPosX[printer->selectedOption]; targetX = printer->windowOffsetPos.x + printer->windowBasePos.x + printer->cursorPosX[printer->targetOption]; posX = baseX + (targetX - baseX) * moveToTargetAlpha; baseY = printer->windowOffsetPos.y + printer->windowBasePos.y + printer->cursorPosY[printer->selectedOption]; targetY = printer->windowOffsetPos.y + printer->windowBasePos.y + printer->cursorPosY[printer->targetOption]; posY = baseY + (targetY - baseY) * moveToTargetAlpha; } posY += 1; posX += (cosine(posInterpPhase * 38 + 270) + 1.0) * 0.5 * 3.2; posX -= 2; if (printer->stateFlags & MSG_STATE_FLAG_20000) { u32 opacity; opacity = 255.0 - printer->fadeOutCounter * 46.0; pointerAlpha = opacity; opacity = 72.0 - printer->fadeOutCounter * 14.4; shadowAlpha = opacity; } gDPPipeSync(gMainGfxPos++); gDPSetTextureLUT(gMainGfxPos++, G_TT_RGBA16); gDPLoadTLUT_pal16(gMainGfxPos++, 0, ui_point_right_pal); gDPSetRenderMode(gMainGfxPos++, G_RM_XLU_SURF, G_RM_XLU_SURF2); gDPSetCombineMode(gMainGfxPos++, PM_CC_07, PM_CC_07); gDPSetPrimColor(gMainGfxPos++, 0, 0, 40, 40, 40, shadowAlpha); draw_image_with_clipping(ui_point_right_png, 16, 16, G_IM_FMT_CI, G_IM_SIZ_4b, posX + 2, posY + 2, 10, 10, 300, 220); draw_ci_image_with_clipping(ui_point_right_png, 16, 16, G_IM_FMT_CI, G_IM_SIZ_4b, ui_point_right_pal, posX, posY, 20, 20, 300, 200, pointerAlpha); } void draw_digit(IMG_PTR img, s32 charset, s32 posX, s32 posY) { MessageNumber* num = &gMsgNumbers[charset]; gDPLoadTextureTile_4b(gMainGfxPos++, img, G_IM_FMT_CI, num->texWidth , num->texHeight, 0, 0, num->texWidth - 1, num->texHeight - 1, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); gSPTextureRectangle(gMainGfxPos++, 4 * posX, 4 * posY, 4 * (posX + num->texWidth), 4 * (posY + num->texHeight), G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); } void draw_number(s32 value, s32 x, s32 y, s32 charset, s32 palette, s32 opacity, u16 style) { u8 valueStr[24]; u8 digits[24]; s32 digitPosX[24]; s32 i; s32 count; s32 posX; IMG_PTR raster = gMsgNumbers[charset].rasters; s32 texSize = gMsgNumbers[charset].texSize; #if !VERSION_JP y -= 2; #endif if (y < 0 || y > 240) { return; } int_to_string(value, valueStr, 10); for (i = 0; i < 10; i++) { u8 digit; if (valueStr[i] == '\0') { break; } digit = valueStr[i] - '0'; if (digit < 10){ digits[i] = digit; } } posX = x; count = i; gSPDisplayList(gMainGfxPos++, gMsgDlistInitDrawNumber); if (style & DRAW_NUMBER_STYLE_ALIGN_RIGHT) { for (i = count - 1; i >= 0; i--) { if (style & DRAW_NUMBER_STYLE_MONOSPACE) { posX -= gMsgNumbers[charset].fixedWidth; } else { posX -= gMsgNumbers[charset].digitWidth[digits[i]]; } digitPosX[i] = posX; } } else { for (i = 0; i < count; i++) { digitPosX[i] = posX; if (style & DRAW_NUMBER_STYLE_MONOSPACE) { posX += gMsgNumbers[charset].fixedWidth; } else { posX += gMsgNumbers[charset].digitWidth[digits[i]]; } } } if (style & DRAW_NUMBER_STYLE_DROP_SHADOW) { for (i = 0; i < count; i++) { gDPPipeSync(gMainGfxPos++); gDPSetRenderMode(gMainGfxPos++, G_RM_XLU_SURF, G_RM_XLU_SURF2); gDPSetCombineMode(gMainGfxPos++, PM_CC_07, PM_CC_07); gDPSetPrimColor(gMainGfxPos++, 0, 0, 40, 40, 40, 72); draw_digit(raster + digits[i] * texSize, charset, digitPosX[i] + 2, y + 2); gDPPipeSync(gMainGfxPos++); } } if (opacity == 255) { gDPSetRenderMode(gMainGfxPos++, G_RM_TEX_EDGE, G_RM_TEX_EDGE2); gDPSetCombineMode(gMainGfxPos++, G_CC_DECALRGBA, G_CC_DECALRGBA); } else { gDPSetRenderMode(gMainGfxPos++, G_RM_XLU_SURF, G_RM_XLU_SURF2); gDPSetCombineMode(gMainGfxPos++, PM_CC_01, PM_CC_01); gDPSetPrimColor(gMainGfxPos++, 0, 0, 255, 255, 255, opacity); } gDPLoadTLUT_pal16(gMainGfxPos++, 0, D_802F4560[palette]); for (i = 0; i < count; i++) { posX = digitPosX[i]; if (posX > 0 && posX < 320) { draw_digit(raster + digits[i] * texSize, charset, posX, y); } } gDPPipeSync(gMainGfxPos++); }