pokeemerald/src/union_room_chat.c
2021-11-04 12:11:31 -04:00

3304 lines
86 KiB
C
Executable File

#include "global.h"
#include "malloc.h"
#include "bg.h"
#include "decompress.h"
#include "dma3.h"
#include "dynamic_placeholder_text_util.h"
#include "gpu_regs.h"
#include "graphics.h"
#include "link.h"
#include "link_rfu.h"
#include "load_save.h"
#include "main.h"
#include "menu.h"
#include "overworld.h"
#include "palette.h"
#include "save.h"
#include "scanline_effect.h"
#include "sound.h"
#include "sprite.h"
#include "string_util.h"
#include "strings.h"
#include "task.h"
#include "text.h"
#include "text_window.h"
#include "union_room_chat.h"
#include "window.h"
#include "constants/rgb.h"
#include "constants/songs.h"
enum
{
UNION_ROOM_KB_PAGE_UPPER,
UNION_ROOM_KB_PAGE_LOWER,
UNION_ROOM_KB_PAGE_EMOJI,
UNION_ROOM_KB_PAGE_REGISTER,
UNION_ROOM_KB_PAGE_COUNT,
};
#define MAX_MESSAGE_LENGTH 15
enum {
CHAT_MESSAGE_NONE,
CHAT_MESSAGE_CHAT,
CHAT_MESSAGE_JOIN,
CHAT_MESSAGE_LEAVE,
CHAT_MESSAGE_DROP,
CHAT_MESSAGE_DISBAND,
};
enum {
STDMESSAGE_QUIT_CHATTING,
STDMESSAGE_REGISTER_WHERE,
STDMESSAGE_REGISTER_HERE,
STDMESSAGE_INPUT_TEXT,
STDMESSAGE_EXITING_CHAT,
STDMESSAGE_LEADER_LEFT,
STDMESSAGE_ASK_SAVE,
STDMESSAGE_ASK_OVERWRITE,
STDMESSAGE_SAVING_NO_OFF,
STDMESSAGE_SAVED_THE_GAME,
STDMESSAGE_WARN_LEADER_LEAVE,
};
enum {
CHAT_FUNC_JOIN,
CHAT_FUNC_HANDLE_INPUT,
CHAT_FUNC_SWITCH,
CHAT_FUNC_ASK_QUIT,
CHAT_FUNC_SEND,
CHAT_FUNC_REGISTER,
CHAT_FUNC_EXIT,
CHAT_FUNC_DROP,
CHAT_FUNC_DISBANDED,
CHAT_FUNC_SAVE_AND_EXIT,
};
enum {
CHATDISPLAY_FUNC_LOAD_GFX,
CHATDISPLAY_FUNC_MOVE_KB_CURSOR,
CHATDISPLAY_FUNC_CURSOR_BLINK,
CHATDISPLAY_FUNC_SHOW_KB_SWAP_MENU,
CHATDISPLAY_FUNC_HIDE_KB_SWAP_MENU,
CHATDISPLAY_FUNC_SWITCH_PAGES,
CHATDISPLAY_FUNC_ASK_QUIT_CHATTING,
CHATDISPLAY_FUNC_DESTROY_YESNO,
CHATDISPLAY_FUNC_UPDATE_MSG,
CHATDISPLAY_FUNC_ASK_REGISTER_TEXT,
CHATDISPLAY_FUNC_CANCEL_REGISTER,
CHATDISPLAY_FUNC_RETURN_TO_KB,
CHATDISPLAY_FUNC_SCROLL_CHAT,
CHATDISPLAY_FUNC_PRINT_INPUT_TEXT,
CHATDISPLAY_FUNC_ASK_SAVE,
CHATDISPLAY_FUNC_ASK_OVERWRITE_SAVE,
CHATDISPLAY_FUNC_PRINT_SAVING,
CHATDISPLAY_FUNC_PRINT_SAVED_GAME,
CHATDISPLAY_FUNC_PRINT_EXITING_CHAT,
CHATDISPLAY_FUNC_PRINT_LEADER_LEFT,
CHATDISPLAY_FUNC_ASK_CONFIRM_LEADER_LEAVE,
};
enum {
CHAT_EXIT_NONE,
CHAT_EXIT_ONLY_LEADER,
CHAT_EXIT_DROPPED,
CHAT_EXIT_DISBANDED,
};
enum {
GFXTAG_KEYBOARD_CURSOR,
GFXTAG_TEXT_ENTRY_ARROW,
GFXTAG_TEXT_ENTRY_CURSOR,
GFXTAG_RBUTTON_ICON,
GFXTAG_RBUTTON_LABELS,
};
// Shared by all above
#define PALTAG_INTERFACE 0
struct UnionRoomChat
{
u32 filler1;
u16 funcId;
u16 funcState;
u16 filler2;
u16 exitDelayTimer;
u8 filler3;
u8 linkPlayerCount;
u8 handleInputTask;
u8 receiveMessagesTask;
u8 currentPage;
u8 currentCol;
u8 currentRow;
u8 multiplayerId;
u8 lastBufferCursorPos;
u8 bufferCursorPos;
u8 receivedPlayerIndex;
u8 exitType; // CHAT_EXIT_*
bool8 changedRegisteredTexts;
u8 afterSaveTimer;
u8 messageEntryBuffer[2 * MAX_MESSAGE_LENGTH + 1];
u8 receivedMessage[64];
u8 hostName[64];
u8 registeredTexts[UNION_ROOM_KB_ROW_COUNT][21];
u8 filler4[5];
u8 sendMessageBuffer[40];
u16 tryQuitAgainTimer;
};
struct UnionRoomChatDisplay_Subtask
{
bool32 (* callback)(u8 *);
bool8 active;
u8 state;
};
struct UnionRoomChatDisplay
{
struct UnionRoomChatDisplay_Subtask subtasks[3];
u16 yesNoMenuWindowId;
u16 currLine;
u16 scrollCount;
u16 messageWindowId;
s16 bg1hofs;
u8 expandedPlaceholdersBuffer[0x106];
u8 bg0Buffer[BG_SCREEN_SIZE];
u8 bg1Buffer[BG_SCREEN_SIZE];
u8 bg3Buffer[BG_SCREEN_SIZE];
u8 bg2Buffer[BG_SCREEN_SIZE];
u8 unk2128[0x20];
u8 unk2148[0x20];
};
struct UnionRoomChatSprites
{
struct Sprite *keyboardCursor;
struct Sprite *textEntryArrow;
struct Sprite *textEntryCursor;
struct Sprite *rButtonIcon;
struct Sprite *rButtonLabel;
u16 cursorBlinkTimer;
};
struct SubtaskInfo
{
u16 idx;
bool32 (* callback)(u8 *);
};
struct MessageWindowInfo
{
const u8 *text;
u8 boxType;
u8 x;
u8 y;
u8 letterSpacing;
u8 lineSpacing;
bool8 hasPlaceholders;
bool8 useWiderBox;
};
static void InitUnionRoomChat(struct UnionRoomChat *);
static void CB2_LoadInterface(void);
static void VBlankCB_UnionRoomChatMain(void);
static void CB2_UnionRoomChatMain(void);
static void Task_HandlePlayerInput(u8 taskId);
static void Chat_Join(void);
static void Chat_HandleInput(void);
static void Chat_Switch(void);
static void Chat_AskQuitChatting(void);
static void Chat_Exit(void);
static void Chat_Drop(void);
static void Chat_Disbanded(void);
static void Chat_SendMessage(void);
static void Chat_Register(void);
static void Chat_SaveAndExit(void);
static void SetChatFunction(u16);
static bool32 HandleDPadInput(void);
static void AppendTextToMessage(void);
static void DeleteLastMessageCharacter(void);
static void SwitchCaseOfLastMessageCharacter(void);
static bool32 ChatMessageIsNotEmpty(void);
static void RegisterTextAtRow(void);
static void ResetMessageEntryBuffer(void);
static void SaveRegisteredTexts(void);
static u8 *GetEndOfMessagePtr(void);
static u8 *GetLastCharOfMessagePtr(void);
static void PrepareSendBuffer_Null(u8 *);
static void PrepareSendBuffer_Join(u8 *);
static void PrepareSendBuffer_Chat(u8 *);
static void PrepareSendBuffer_Leave(u8 *);
static void PrepareSendBuffer_Drop(u8 *);
static void PrepareSendBuffer_Disband(u8 *);
static u8 *GetLimitedMessageStartPtr(void);
static void Task_ReceiveChatMessage(u8 taskId);
static bool8 TryAllocDisplay(void);
static bool32 IsDisplaySubtask0Active(void);
static void FreeDisplay(void);
static void RunDisplaySubtasks(void);
static void StartDisplaySubtask(u16, u8);
static bool8 IsDisplaySubtaskActive(u8);
static s8 ProcessMenuInput(void);
static bool32 TryAllocSprites(void);
static void InitScanlineEffect(void);
static void InitDisplay(struct UnionRoomChatDisplay *);
static void ResetDisplaySubtasks(void);
static void FreeSprites(void);
static void ResetGpuBgState(void);
static void SetBgTilemapBuffers(void);
static void ClearBg0(void);
static void LoadChatWindowBorderGfx(void);
static void LoadChatWindowGfx(void);
static void LoadChatUnkPalette(void);
static void LoadChatMessagesWindow(void);
static void LoadKeyboardWindow(void);
static void LoadKeyboardSwapWindow(void);
static void LoadTextEntryWindow(void);
static void CreateKeyboardCursorSprite(void);
static void CreateTextEntrySprites(void);
static void CreateRButtonSprites(void);
static void ShowKeyboardSwapMenu(void);
static void HideKeyboardSwapMenu(void);
static void SetKeyboardCursorInvisibility(bool32);
static bool32 SlideKeyboardPageOut(void);
static void PrintCurrentKeyboardPage(void);
static bool32 SlideKeyboardPageIn(void);
static void MoveKeyboardCursor(void);
static void UpdateRButtonLabel(void);
static void AddStdMessageWindow(int, u16);
static void AddYesNoMenuAt(u8, u8, u8);
static void HideStdMessageWindow(void);
static void HideYesNoMenuWindow(void);
static void DestroyStdMessageWindow(void);
static void DestroyYesNoMenuWindow(void);
static void FillTextEntryWindow(u16, u16, u8);
static void DrawTextEntryMessage(u16, u8 *, u8, u8, u8);
static void SetRegisteredTextPalette(bool32);
static void PrintChatMessage(u16, u8 *, u8);
static void StartKeyboardCursorAnim(void);
static bool32 TryKeyboardCursorReopen(void);
static void UpdateSlidingKeyboard(s16);
static void FinishSlidingKeyboard(s16);
static bool32 Display_Dummy(u8 *);
static bool32 Display_LoadGfx(u8 *state);
static bool32 Display_ShowKeyboardSwapMenu(u8 *state);
static bool32 Display_HideKeyboardSwapMenu(u8 *state);
static bool32 Display_SwitchPages(u8 *state);
static bool32 Display_MoveKeyboardCursor(u8 *state);
static bool32 Display_AskQuitChatting(u8 *state);
static bool32 Display_DestroyYesNoDialog(u8 *state);
static bool32 Display_UpdateMessageBuffer(u8 *state);
static bool32 Display_AskRegisterText(u8 *state);
static bool32 Display_CancelRegister(u8 *state);
static bool32 Display_ReturnToKeyboard(u8 *state);
static bool32 Display_ScrollChat(u8 *state);
static bool32 Display_AnimateKeyboardCursor(u8 *state);
static bool32 Display_PrintInputText(u8 *state);
static bool32 Display_PrintExitingChat(u8 *state);
static bool32 Display_PrintLeaderLeft(u8 *state);
static bool32 Display_AskSave(u8 *state);
static bool32 Display_AskOverwriteSave(u8 *state);
static bool32 Display_PrintSavingDontTurnOff(u8 *state);
static bool32 Display_PrintSavedTheGame(u8 *state);
static bool32 Display_AskConfirmLeaderLeave(u8 *state);
static void SpriteCB_TextEntryCursor(struct Sprite *sprite);
static void SpriteCB_TextEntryArrow(struct Sprite *sprite);
static EWRAM_DATA struct UnionRoomChat *sChat = NULL;
static EWRAM_DATA struct UnionRoomChatDisplay *sDisplay = NULL;
static EWRAM_DATA struct UnionRoomChatSprites *sSprites = NULL;
static void (*const sChatMainFunctions[])(void) = {
[CHAT_FUNC_JOIN] = Chat_Join,
[CHAT_FUNC_HANDLE_INPUT] = Chat_HandleInput,
[CHAT_FUNC_SWITCH] = Chat_Switch,
[CHAT_FUNC_ASK_QUIT] = Chat_AskQuitChatting,
[CHAT_FUNC_SEND] = Chat_SendMessage,
[CHAT_FUNC_REGISTER] = Chat_Register,
[CHAT_FUNC_EXIT] = Chat_Exit,
[CHAT_FUNC_DROP] = Chat_Drop,
[CHAT_FUNC_DISBANDED] = Chat_Disbanded,
[CHAT_FUNC_SAVE_AND_EXIT] = Chat_SaveAndExit
};
static const u8 sKeyboardPageMaxRow[UNION_ROOM_KB_PAGE_COUNT] =
{
[UNION_ROOM_KB_PAGE_UPPER] = 9,
[UNION_ROOM_KB_PAGE_LOWER] = 9,
[UNION_ROOM_KB_PAGE_EMOJI] = 9,
[UNION_ROOM_KB_PAGE_REGISTER] = 9
};
static const u8 sCaseToggleTable[256] = {
[CHAR_A] = CHAR_a,
[CHAR_B] = CHAR_b,
[CHAR_C] = CHAR_c,
[CHAR_D] = CHAR_d,
[CHAR_E] = CHAR_e,
[CHAR_F] = CHAR_f,
[CHAR_G] = CHAR_g,
[CHAR_H] = CHAR_h,
[CHAR_I] = CHAR_i,
[CHAR_J] = CHAR_j,
[CHAR_K] = CHAR_k,
[CHAR_L] = CHAR_l,
[CHAR_M] = CHAR_m,
[CHAR_N] = CHAR_n,
[CHAR_O] = CHAR_o,
[CHAR_P] = CHAR_p,
[CHAR_Q] = CHAR_q,
[CHAR_R] = CHAR_r,
[CHAR_S] = CHAR_s,
[CHAR_T] = CHAR_t,
[CHAR_U] = CHAR_u,
[CHAR_V] = CHAR_v,
[CHAR_W] = CHAR_w,
[CHAR_X] = CHAR_x,
[CHAR_Y] = CHAR_y,
[CHAR_Z] = CHAR_z,
[CHAR_a] = CHAR_A,
[CHAR_b] = CHAR_B,
[CHAR_c] = CHAR_C,
[CHAR_d] = CHAR_D,
[CHAR_e] = CHAR_E,
[CHAR_f] = CHAR_F,
[CHAR_g] = CHAR_G,
[CHAR_h] = CHAR_H,
[CHAR_i] = CHAR_I,
[CHAR_j] = CHAR_J,
[CHAR_k] = CHAR_K,
[CHAR_l] = CHAR_L,
[CHAR_m] = CHAR_M,
[CHAR_n] = CHAR_N,
[CHAR_o] = CHAR_O,
[CHAR_p] = CHAR_P,
[CHAR_q] = CHAR_Q,
[CHAR_r] = CHAR_R,
[CHAR_s] = CHAR_S,
[CHAR_t] = CHAR_T,
[CHAR_u] = CHAR_U,
[CHAR_v] = CHAR_V,
[CHAR_w] = CHAR_W,
[CHAR_x] = CHAR_X,
[CHAR_y] = CHAR_Y,
[CHAR_z] = CHAR_Z,
[CHAR_A_GRAVE] = CHAR_a_GRAVE,
[CHAR_A_ACUTE] = CHAR_a_ACUTE,
[CHAR_A_CIRCUMFLEX] = CHAR_a_CIRCUMFLEX,
[CHAR_A_DIAERESIS] = CHAR_a_DIAERESIS,
[CHAR_C_CEDILLA] = CHAR_c_CEDILLA,
[CHAR_E_GRAVE] = CHAR_e_GRAVE,
[CHAR_E_ACUTE] = CHAR_e_ACUTE,
[CHAR_E_CIRCUMFLEX] = CHAR_e_CIRCUMFLEX,
[CHAR_E_DIAERESIS] = CHAR_e_DIAERESIS,
[CHAR_I_GRAVE] = CHAR_i_GRAVE,
[CHAR_I_ACUTE] = CHAR_i_ACUTE,
[CHAR_I_CIRCUMFLEX] = CHAR_i_CIRCUMFLEX,
[CHAR_I_DIAERESIS] = CHAR_i_DIAERESIS,
[CHAR_O_GRAVE] = CHAR_o_GRAVE,
[CHAR_O_ACUTE] = CHAR_o_ACUTE,
[CHAR_O_CIRCUMFLEX] = CHAR_o_CIRCUMFLEX,
[CHAR_O_DIAERESIS] = CHAR_o_DIAERESIS,
[CHAR_OE] = CHAR_oe,
[CHAR_U_GRAVE] = CHAR_u_GRAVE,
[CHAR_U_ACUTE] = CHAR_u_ACUTE,
[CHAR_U_CIRCUMFLEX] = CHAR_u_CIRCUMFLEX,
[CHAR_U_DIAERESIS] = CHAR_u_DIAERESIS,
[CHAR_N_TILDE] = CHAR_n_TILDE,
[CHAR_ESZETT] = CHAR_ESZETT,
[CHAR_a_GRAVE] = CHAR_A_GRAVE,
[CHAR_a_ACUTE] = CHAR_A_ACUTE,
[CHAR_a_CIRCUMFLEX] = CHAR_A_CIRCUMFLEX,
[CHAR_a_DIAERESIS] = CHAR_A_DIAERESIS,
[CHAR_c_CEDILLA] = CHAR_C_CEDILLA,
[CHAR_e_GRAVE] = CHAR_E_GRAVE,
[CHAR_e_ACUTE] = CHAR_E_ACUTE,
[CHAR_e_CIRCUMFLEX] = CHAR_E_CIRCUMFLEX,
[CHAR_e_DIAERESIS] = CHAR_E_DIAERESIS,
[CHAR_i_GRAVE] = CHAR_I_GRAVE,
[CHAR_i_ACUTE] = CHAR_I_ACUTE,
[CHAR_i_CIRCUMFLEX] = CHAR_I_CIRCUMFLEX,
[CHAR_i_DIAERESIS] = CHAR_I_DIAERESIS,
[CHAR_o_GRAVE] = CHAR_O_GRAVE,
[CHAR_o_ACUTE] = CHAR_O_ACUTE,
[CHAR_o_CIRCUMFLEX] = CHAR_O_CIRCUMFLEX,
[CHAR_o_DIAERESIS] = CHAR_O_DIAERESIS,
[CHAR_oe] = CHAR_OE,
[CHAR_u_GRAVE] = CHAR_U_GRAVE,
[CHAR_u_ACUTE] = CHAR_U_ACUTE,
[CHAR_u_CIRCUMFLEX] = CHAR_U_CIRCUMFLEX,
[CHAR_u_DIAERESIS] = CHAR_U_DIAERESIS,
[CHAR_n_TILDE] = CHAR_N_TILDE,
[CHAR_0] = CHAR_0,
[CHAR_1] = CHAR_1,
[CHAR_2] = CHAR_2,
[CHAR_3] = CHAR_3,
[CHAR_4] = CHAR_4,
[CHAR_5] = CHAR_5,
[CHAR_6] = CHAR_6,
[CHAR_7] = CHAR_7,
[CHAR_8] = CHAR_8,
[CHAR_9] = CHAR_9,
[CHAR_PK] = CHAR_PK,
[CHAR_MN] = CHAR_MN,
[CHAR_PO] = CHAR_PO,
[CHAR_KE] = CHAR_KE,
[CHAR_SUPER_E] = CHAR_SUPER_E,
[CHAR_SUPER_ER] = CHAR_SUPER_ER,
[CHAR_SUPER_RE] = CHAR_SUPER_RE,
[CHAR_PERIOD] = CHAR_PERIOD,
[CHAR_COMMA] = CHAR_COMMA,
[CHAR_COLON] = CHAR_COLON,
[CHAR_SEMICOLON] = CHAR_SEMICOLON,
[CHAR_EXCL_MARK] = CHAR_EXCL_MARK,
[CHAR_QUESTION_MARK] = CHAR_QUESTION_MARK,
[CHAR_HYPHEN] = CHAR_HYPHEN,
[CHAR_SLASH] = CHAR_SLASH,
[CHAR_ELLIPSIS] = CHAR_ELLIPSIS,
[CHAR_LEFT_PAREN] = CHAR_LEFT_PAREN,
[CHAR_RIGHT_PAREN] = CHAR_RIGHT_PAREN,
[CHAR_AMPERSAND] = CHAR_AMPERSAND,
[CHAR_DBL_QUOTE_LEFT] = CHAR_DBL_QUOTE_LEFT,
[CHAR_DBL_QUOTE_RIGHT] = CHAR_DBL_QUOTE_RIGHT,
[CHAR_SGL_QUOTE_LEFT] = CHAR_SGL_QUOTE_LEFT,
[CHAR_SGL_QUOTE_RIGHT] = CHAR_SGL_QUOTE_RIGHT,
[CHAR_MASCULINE_ORDINAL] = CHAR_MASCULINE_ORDINAL,
[CHAR_FEMININE_ORDINAL] = CHAR_FEMININE_ORDINAL,
[CHAR_BULLET] = CHAR_BULLET,
[CHAR_EQUALS] = CHAR_EQUALS,
[CHAR_MULT_SIGN] = CHAR_MULT_SIGN,
[CHAR_PERCENT] = CHAR_PERCENT,
[CHAR_LESS_THAN] = CHAR_LESS_THAN,
[CHAR_GREATER_THAN] = CHAR_GREATER_THAN,
[CHAR_MALE] = CHAR_MALE,
[CHAR_FEMALE] = CHAR_FEMALE,
[CHAR_CURRENCY] = CHAR_CURRENCY,
[CHAR_BLACK_TRIANGLE] = CHAR_BLACK_TRIANGLE,
};
// Excludes UNION_ROOM_KB_PAGE_REGISTER, the text for which is chosen by the player
static const u8 *const sUnionRoomKeyboardText[UNION_ROOM_KB_PAGE_COUNT - 1][UNION_ROOM_KB_ROW_COUNT] =
{
[UNION_ROOM_KB_PAGE_UPPER] =
{
gText_UnionRoomChatKeyboard_ABCDE,
gText_UnionRoomChatKeyboard_FGHIJ,
gText_UnionRoomChatKeyboard_KLMNO,
gText_UnionRoomChatKeyboard_PQRST,
gText_UnionRoomChatKeyboard_UVWXY,
gText_UnionRoomChatKeyboard_Z,
gText_UnionRoomChatKeyboard_01234Upper,
gText_UnionRoomChatKeyboard_56789Upper,
gText_UnionRoomChatKeyboard_PunctuationUpper,
gText_UnionRoomChatKeyboard_SymbolsUpper
},
[UNION_ROOM_KB_PAGE_LOWER] =
{
gText_UnionRoomChatKeyboard_abcde,
gText_UnionRoomChatKeyboard_fghij,
gText_UnionRoomChatKeyboard_klmno,
gText_UnionRoomChatKeyboard_pqrst,
gText_UnionRoomChatKeyboard_uvwxy,
gText_UnionRoomChatKeyboard_z,
gText_UnionRoomChatKeyboard_01234Lower,
gText_UnionRoomChatKeyboard_56789Lower,
gText_UnionRoomChatKeyboard_PunctuationLower,
gText_UnionRoomChatKeyboard_SymbolsLower
},
[UNION_ROOM_KB_PAGE_EMOJI] =
{
gText_UnionRoomChatKeyboard_Emoji1,
gText_UnionRoomChatKeyboard_Emoji2,
gText_UnionRoomChatKeyboard_Emoji3,
gText_UnionRoomChatKeyboard_Emoji4,
gText_UnionRoomChatKeyboard_Emoji5,
gText_UnionRoomChatKeyboard_Emoji6,
gText_UnionRoomChatKeyboard_Emoji7,
gText_UnionRoomChatKeyboard_Emoji8,
gText_UnionRoomChatKeyboard_Emoji9,
gText_UnionRoomChatKeyboard_Emoji10
}
};
static const u16 sUnk_Palette1[] = INCBIN_U16("graphics/union_room_chat/unk_palette1.gbapal");
static const u16 sUnk_Palette2[] = INCBIN_U16("graphics/union_room_chat/unk_palette2.gbapal");
static const struct BgTemplate sBgTemplates[] = {
{
.bg = 0,
.charBaseIndex = 0,
.mapBaseIndex = 7,
.screenSize = 0,
.paletteMode = 0,
.priority = 0,
.baseTile = 0
}, {
.bg = 1,
.charBaseIndex = 3,
.mapBaseIndex = 31,
.screenSize = 0,
.paletteMode = 0,
.priority = 1,
.baseTile = 0
}, {
.bg = 2,
.charBaseIndex = 2,
.mapBaseIndex = 23,
.screenSize = 0,
.paletteMode = 0,
.priority = 2,
.baseTile = 0
}, {
.bg = 3,
.charBaseIndex = 1,
.mapBaseIndex = 15,
.screenSize = 0,
.paletteMode = 0,
.priority = 3,
.baseTile = 1
}
};
static const struct WindowTemplate sWinTemplates[] = {
{
.bg = 3,
.tilemapLeft = 8,
.tilemapTop = 1,
.width = 21,
.height = 19,
.paletteNum = 15,
.baseBlock = 0x0001,
}, {
.bg = 1,
.tilemapLeft = 9,
.tilemapTop = 18,
.width = 15,
.height = 2,
.paletteNum = 12,
.baseBlock = 0x007a,
}, {
.bg = 1,
.tilemapLeft = 0,
.tilemapTop = 2,
.width = 6,
.height = 15,
.paletteNum = 7,
.baseBlock = 0x0020,
}, {
.bg = 0,
.tilemapLeft = 1,
.tilemapTop = 2,
.width = 7,
.height = 9,
.paletteNum = 14,
.baseBlock = 0x0013,
}, DUMMY_WIN_TEMPLATE
};
static const struct SubtaskInfo sDisplaySubtasks[] = {
{CHATDISPLAY_FUNC_LOAD_GFX, Display_LoadGfx},
{CHATDISPLAY_FUNC_SHOW_KB_SWAP_MENU, Display_ShowKeyboardSwapMenu},
{CHATDISPLAY_FUNC_HIDE_KB_SWAP_MENU, Display_HideKeyboardSwapMenu},
{CHATDISPLAY_FUNC_SWITCH_PAGES, Display_SwitchPages},
{CHATDISPLAY_FUNC_MOVE_KB_CURSOR, Display_MoveKeyboardCursor},
{CHATDISPLAY_FUNC_ASK_QUIT_CHATTING, Display_AskQuitChatting},
{CHATDISPLAY_FUNC_DESTROY_YESNO, Display_DestroyYesNoDialog},
{CHATDISPLAY_FUNC_UPDATE_MSG, Display_UpdateMessageBuffer},
{CHATDISPLAY_FUNC_ASK_REGISTER_TEXT, Display_AskRegisterText},
{CHATDISPLAY_FUNC_CANCEL_REGISTER, Display_CancelRegister},
{CHATDISPLAY_FUNC_RETURN_TO_KB, Display_ReturnToKeyboard},
{CHATDISPLAY_FUNC_SCROLL_CHAT, Display_ScrollChat},
{CHATDISPLAY_FUNC_CURSOR_BLINK, Display_AnimateKeyboardCursor},
{CHATDISPLAY_FUNC_PRINT_INPUT_TEXT, Display_PrintInputText},
{CHATDISPLAY_FUNC_PRINT_EXITING_CHAT, Display_PrintExitingChat},
{CHATDISPLAY_FUNC_PRINT_LEADER_LEFT, Display_PrintLeaderLeft},
{CHATDISPLAY_FUNC_ASK_SAVE, Display_AskSave},
{CHATDISPLAY_FUNC_ASK_OVERWRITE_SAVE, Display_AskOverwriteSave},
{CHATDISPLAY_FUNC_PRINT_SAVING, Display_PrintSavingDontTurnOff},
{CHATDISPLAY_FUNC_PRINT_SAVED_GAME, Display_PrintSavedTheGame},
{CHATDISPLAY_FUNC_ASK_CONFIRM_LEADER_LEAVE, Display_AskConfirmLeaderLeave}
};
static const struct MessageWindowInfo sDisplayStdMessages[] = {
[STDMESSAGE_QUIT_CHATTING] = {
.text = gText_QuitChatting,
.boxType = 1,
.x = 0,
.y = 1,
.letterSpacing = 0,
.lineSpacing = 0,
.hasPlaceholders = FALSE,
.useWiderBox = FALSE
},
[STDMESSAGE_REGISTER_WHERE] = {
.text = gText_RegisterTextWhere,
.boxType = 1,
.x = 0,
.y = 1,
.letterSpacing = 0,
.lineSpacing = 0,
.hasPlaceholders = FALSE,
.useWiderBox = FALSE
},
[STDMESSAGE_REGISTER_HERE] = {
.text = gText_RegisterTextHere,
.boxType = 1,
.x = 0,
.y = 1,
.letterSpacing = 0,
.lineSpacing = 0,
.hasPlaceholders = FALSE,
.useWiderBox = FALSE
},
[STDMESSAGE_INPUT_TEXT] = {
.text = gText_InputText,
.boxType = 1,
.x = 0,
.y = 1,
.letterSpacing = 0,
.lineSpacing = 0,
.hasPlaceholders = FALSE,
.useWiderBox = FALSE
},
[STDMESSAGE_EXITING_CHAT] = {
.text = gText_ExitingChat,
.boxType = 2,
.x = 0,
.y = 1,
.letterSpacing = 0,
.lineSpacing = 0,
.hasPlaceholders = FALSE,
.useWiderBox = FALSE
},
[STDMESSAGE_LEADER_LEFT] = {
.text = gText_LeaderLeftEndingChat,
.boxType = 2,
.x = 0,
.y = 1,
.letterSpacing = 0,
.lineSpacing = 0,
.hasPlaceholders = TRUE,
.useWiderBox = FALSE
},
[STDMESSAGE_ASK_SAVE] = {
.text = gText_RegisteredTextChangedOKToSave,
.boxType = 2,
.x = 0,
.y = 1,
.letterSpacing = 0,
.lineSpacing = 0,
.hasPlaceholders = FALSE,
.useWiderBox = TRUE
},
[STDMESSAGE_ASK_OVERWRITE] = {
.text = gText_AlreadySavedFile_Chat,
.boxType = 2,
.x = 0,
.y = 1,
.letterSpacing = 0,
.lineSpacing = 0,
.hasPlaceholders = FALSE,
.useWiderBox = TRUE
},
[STDMESSAGE_SAVING_NO_OFF] = {
.text = gText_SavingDontTurnOff_Chat,
.boxType = 2,
.x = 0,
.y = 1,
.letterSpacing = 0,
.lineSpacing = 0,
.hasPlaceholders = FALSE,
.useWiderBox = TRUE
},
[STDMESSAGE_SAVED_THE_GAME] = {
.text = gText_PlayerSavedGame_Chat,
.boxType = 2,
.x = 0,
.y = 1,
.letterSpacing = 0,
.lineSpacing = 0,
.hasPlaceholders = TRUE,
.useWiderBox = TRUE
},
[STDMESSAGE_WARN_LEADER_LEAVE] = {
.text = gText_IfLeaderLeavesChatEnds,
.boxType = 2,
.x = 0,
.y = 1,
.letterSpacing = 0,
.lineSpacing = 0,
.hasPlaceholders = FALSE,
.useWiderBox = TRUE
}
};
static const u8 sText_Ellipsis[] = _("");
static const struct MenuAction sKeyboardPageTitleTexts[UNION_ROOM_KB_PAGE_COUNT + 1] = {
[UNION_ROOM_KB_PAGE_UPPER] = {gText_Upper, NULL},
[UNION_ROOM_KB_PAGE_LOWER] = {gText_Lower, NULL},
[UNION_ROOM_KB_PAGE_EMOJI] = {gText_Symbols, NULL},
[UNION_ROOM_KB_PAGE_REGISTER] = {gText_Register2, NULL},
[UNION_ROOM_KB_PAGE_COUNT] = {gText_Exit2, NULL},
};
static const u16 sUnionRoomChatInterfacePal[] = INCBIN_U16("graphics/union_room_chat/interface.gbapal");
static const u32 sKeyboardCursorTiles[] = INCBIN_U32("graphics/union_room_chat/keyboard_cursor.4bpp.lz");
static const u32 sTextEntryCursorTiles[] = INCBIN_U32("graphics/union_room_chat/text_entry_cursor.4bpp.lz");
static const u32 sTextEntryArrowTiles[] = INCBIN_U32("graphics/union_room_chat/text_entry_arrow.4bpp.lz");
static const u32 sRButtonGfxTiles[] = INCBIN_U32("graphics/union_room_chat/r_button.4bpp.lz");
static const struct CompressedSpriteSheet sSpriteSheets[] = {
{sKeyboardCursorTiles, 0x1000, 0},
{sTextEntryArrowTiles, 0x0040, 1},
{sTextEntryCursorTiles, 0x0040, 2},
{sRButtonGfxTiles, 0x0080, 3},
{gUnionRoomChat_RButtonLabels, 0x0400, 4}
};
static const struct SpritePalette sSpritePalette = {
sUnionRoomChatInterfacePal, 0
};
static const struct OamData sOam_KeyboardCursor = {
.shape = SPRITE_SHAPE(64x32),
.size = SPRITE_SIZE(64x32),
.priority = 1
};
static const union AnimCmd sAnim_KeyboardCursor_Open[] = {
ANIMCMD_FRAME(0x00, 30),
ANIMCMD_END
};
static const union AnimCmd sAnim_KeyboardCursor_Closed[] = {
ANIMCMD_FRAME(0x20, 30),
ANIMCMD_END
};
static const union AnimCmd sAnim_KeyboardCursorWide_Open[] = {
ANIMCMD_FRAME(0x40, 30),
ANIMCMD_END
};
static const union AnimCmd sAnim_KeyboardCursorWide_Closed[] = {
ANIMCMD_FRAME(0x60, 30),
ANIMCMD_END
};
static const union AnimCmd *const sAnims_KeyboardCursor[] = {
sAnim_KeyboardCursor_Open,
sAnim_KeyboardCursor_Closed,
sAnim_KeyboardCursorWide_Open,
sAnim_KeyboardCursorWide_Closed
};
static const struct SpriteTemplate sSpriteTemplate_KeyboardCursor = {
.tileTag = GFXTAG_KEYBOARD_CURSOR,
.paletteTag = PALTAG_INTERFACE,
.oam = &sOam_KeyboardCursor,
.anims = sAnims_KeyboardCursor,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy
};
static const struct OamData sOam_TextEntrySprite = {
.shape = SPRITE_SHAPE(8x16),
.size = SPRITE_SIZE(8x16),
.priority = 2
};
static const struct SpriteTemplate sSpriteTemplate_TextEntryCursor = {
.tileTag = GFXTAG_TEXT_ENTRY_CURSOR,
.paletteTag = PALTAG_INTERFACE,
.oam = &sOam_TextEntrySprite,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCB_TextEntryCursor
};
static const struct SpriteTemplate sSpriteTemplate_TextEntryArrow = {
.tileTag = GFXTAG_TEXT_ENTRY_ARROW,
.paletteTag = PALTAG_INTERFACE,
.oam = &sOam_TextEntrySprite,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCB_TextEntryArrow
};
static const struct OamData sOam_RButtonIcon = {
.shape = SPRITE_SHAPE(16x16),
.size = SPRITE_SIZE(16x16),
.priority = 2
};
static const struct OamData sOam_RButtonLabel = {
.shape = SPRITE_SHAPE(32x16),
.size = SPRITE_SIZE(32x16),
.priority = 2
};
static const union AnimCmd sAnim_ToggleCaseIcon[] = {
ANIMCMD_FRAME(0x00, 2),
ANIMCMD_END
};
static const union AnimCmd sAnim_ToggleCaseIcon_Duplicate1[] = {
ANIMCMD_FRAME(0x08, 2),
ANIMCMD_END
};
static const union AnimCmd sAnim_ToggleCaseIcon_Duplicate2[] = {
ANIMCMD_FRAME(0x10, 2),
ANIMCMD_END
};
static const union AnimCmd sAnim_RegisterIcon[] = {
ANIMCMD_FRAME(0x18, 2),
ANIMCMD_END
};
static const union AnimCmd *const sAnims_RButtonLabels[] = {
sAnim_ToggleCaseIcon,
sAnim_ToggleCaseIcon_Duplicate1,
sAnim_ToggleCaseIcon_Duplicate2,
sAnim_RegisterIcon
};
static const struct SpriteTemplate sSpriteTemplate_RButtonIcon = {
.tileTag = GFXTAG_RBUTTON_ICON,
.paletteTag = PALTAG_INTERFACE,
.oam = &sOam_RButtonIcon,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy
};
static const struct SpriteTemplate sSpriteTemplate_RButtonLabels = {
.tileTag = GFXTAG_RBUTTON_LABELS,
.paletteTag = PALTAG_INTERFACE,
.oam = &sOam_RButtonLabel,
.anims = sAnims_RButtonLabels,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCallbackDummy
};
void EnterUnionRoomChat(void)
{
sChat = Alloc(sizeof(*sChat));
InitUnionRoomChat(sChat);
gKeyRepeatStartDelay = 20;
SetVBlankCallback(NULL);
SetMainCallback2(CB2_LoadInterface);
}
static void InitUnionRoomChat(struct UnionRoomChat *chat)
{
int i;
chat->funcId = CHAT_FUNC_JOIN;
chat->funcState = 0;
chat->currentPage = 0;
chat->currentCol = 0;
chat->currentRow = 0;
chat->lastBufferCursorPos = 0;
chat->bufferCursorPos = 0;
chat->receivedPlayerIndex = 0;
chat->messageEntryBuffer[0] = EOS;
chat->linkPlayerCount = GetLinkPlayerCount();
chat->multiplayerId = GetMultiplayerId();
chat->exitType = CHAT_EXIT_NONE;
chat->changedRegisteredTexts = FALSE;
PrepareSendBuffer_Null(chat->sendMessageBuffer);
for (i = 0; i < UNION_ROOM_KB_ROW_COUNT; i++)
StringCopy(chat->registeredTexts[i], gSaveBlock1Ptr->registeredTexts[i]);
}
static void FreeUnionRoomChat(void)
{
DestroyTask(sChat->handleInputTask);
DestroyTask(sChat->receiveMessagesTask);
Free(sChat);
}
static void CB2_LoadInterface(void)
{
switch (gMain.state)
{
case 0:
ResetTasks();
ResetSpriteData();
FreeAllSpritePalettes();
TryAllocDisplay();
gMain.state++;
break;
case 1:
RunDisplaySubtasks();
if (!IsDisplaySubtask0Active())
{
BlendPalettes(PALETTES_ALL, 16, RGB_BLACK);
BeginNormalPaletteFade(PALETTES_ALL, -1, 16, 0, RGB_BLACK);
SetVBlankCallback(VBlankCB_UnionRoomChatMain);
gMain.state++;
}
break;
case 2:
UpdatePaletteFade();
if (!gPaletteFade.active)
{
SetMainCallback2(CB2_UnionRoomChatMain);
sChat->handleInputTask = CreateTask(Task_HandlePlayerInput, 8);
sChat->receiveMessagesTask = CreateTask(Task_ReceiveChatMessage, 7);
LoadWirelessStatusIndicatorSpriteGfx();
CreateWirelessStatusIndicatorSprite(232, 150);
}
break;
}
}
static void VBlankCB_UnionRoomChatMain(void)
{
TransferPlttBuffer();
LoadOam();
ProcessSpriteCopyRequests();
ScanlineEffect_InitHBlankDmaTransfer();
}
static void CB2_UnionRoomChatMain(void)
{
RunTasks();
RunDisplaySubtasks();
AnimateSprites();
BuildOamBuffer();
UpdatePaletteFade();
}
static void Task_HandlePlayerInput(u8 taskId)
{
// If exitType is not CHAT_EXIT_NONE, begin exit function.
// Otherwise just call main function below.
switch (sChat->exitType)
{
case CHAT_EXIT_ONLY_LEADER:
SetChatFunction(CHAT_FUNC_EXIT);
sChat->exitType = CHAT_EXIT_NONE;
break;
case CHAT_EXIT_DROPPED:
SetChatFunction(CHAT_FUNC_DROP);
sChat->exitType = CHAT_EXIT_NONE;
break;
case CHAT_EXIT_DISBANDED:
SetChatFunction(CHAT_FUNC_DISBANDED);
sChat->exitType = CHAT_EXIT_NONE;
break;
}
sChatMainFunctions[sChat->funcId]();
}
static void Chat_Join(void)
{
switch (sChat->funcState)
{
case 0:
PrepareSendBuffer_Join(sChat->sendMessageBuffer);
sChat->funcState++;
// fall through
case 1:
if (IsLinkTaskFinished() && !Rfu_IsPlayerExchangeActive())
{
if (SendBlock(0, sChat->sendMessageBuffer, sizeof(sChat->sendMessageBuffer)))
sChat->funcState++;
}
break;
case 2:
if (IsLinkTaskFinished())
SetChatFunction(CHAT_FUNC_HANDLE_INPUT);
break;
}
}
static void Chat_HandleInput(void)
{
bool8 updateMsgActive, cursorBlinkActive;
switch (sChat->funcState)
{
case 0:
if (JOY_NEW(START_BUTTON))
{
if (sChat->bufferCursorPos)
SetChatFunction(CHAT_FUNC_SEND);
}
else if (JOY_NEW(SELECT_BUTTON))
{
SetChatFunction(CHAT_FUNC_SWITCH);
}
else if (gMain.newAndRepeatedKeys & B_BUTTON)
{
if (sChat->bufferCursorPos)
{
DeleteLastMessageCharacter();
StartDisplaySubtask(CHATDISPLAY_FUNC_UPDATE_MSG, 0);
sChat->funcState = 1;
}
else
{
SetChatFunction(CHAT_FUNC_ASK_QUIT);
}
}
else if (JOY_NEW(A_BUTTON))
{
AppendTextToMessage();
StartDisplaySubtask(CHATDISPLAY_FUNC_UPDATE_MSG, 0);
StartDisplaySubtask(CHATDISPLAY_FUNC_CURSOR_BLINK, 1);
sChat->funcState = 1;
}
else if (JOY_NEW(R_BUTTON))
{
if (sChat->currentPage != UNION_ROOM_KB_PAGE_REGISTER)
{
SwitchCaseOfLastMessageCharacter();
StartDisplaySubtask(CHATDISPLAY_FUNC_UPDATE_MSG, 0);
sChat->funcState = 1;
}
else
{
SetChatFunction(CHAT_FUNC_REGISTER);
}
}
else if (HandleDPadInput())
{
StartDisplaySubtask(CHATDISPLAY_FUNC_MOVE_KB_CURSOR, 0);
sChat->funcState = 1;
}
break;
case 1:
updateMsgActive = IsDisplaySubtaskActive(0);
cursorBlinkActive = IsDisplaySubtaskActive(1);
if (!updateMsgActive && !cursorBlinkActive)
sChat->funcState = 0;
break;
}
}
static void Chat_Switch(void)
{
s16 input;
bool32 shouldSwitchPages;
switch (sChat->funcState)
{
case 0:
StartDisplaySubtask(CHATDISPLAY_FUNC_SHOW_KB_SWAP_MENU, 0);
sChat->funcState++;
break;
case 1:
if (!IsDisplaySubtaskActive(0))
sChat->funcState++;
break;
case 2:
input = Menu_ProcessInput();
switch (input)
{
default:
StartDisplaySubtask(CHATDISPLAY_FUNC_HIDE_KB_SWAP_MENU, 0);
shouldSwitchPages = TRUE;
if (sChat->currentPage == input || input > UNION_ROOM_KB_PAGE_REGISTER)
shouldSwitchPages = FALSE;
break;
case MENU_NOTHING_CHOSEN:
if (JOY_NEW(SELECT_BUTTON))
{
PlaySE(SE_SELECT);
Menu_MoveCursor(1);
}
return;
case MENU_B_PRESSED:
StartDisplaySubtask(CHATDISPLAY_FUNC_HIDE_KB_SWAP_MENU, 0);
sChat->funcState = 3;
return;
}
if (!shouldSwitchPages)
{
sChat->funcState = 3;
return;
}
sChat->currentCol = 0;
sChat->currentRow = 0;
StartDisplaySubtask(CHATDISPLAY_FUNC_SWITCH_PAGES, 1);
sChat->currentPage = input;
sChat->funcState = 4;
break;
case 3:
if (!IsDisplaySubtaskActive(0))
SetChatFunction(CHAT_FUNC_HANDLE_INPUT);
break;
case 4:
if (!IsDisplaySubtaskActive(0) && !IsDisplaySubtaskActive(1))
SetChatFunction(CHAT_FUNC_HANDLE_INPUT);
break;
}
}
static void Chat_AskQuitChatting(void)
{
s8 input;
switch (sChat->funcState)
{
case 0:
StartDisplaySubtask(CHATDISPLAY_FUNC_ASK_QUIT_CHATTING, 0);
sChat->funcState = 1;
break;
case 1:
if (!IsDisplaySubtaskActive(0))
sChat->funcState = 2;
break;
case 2:
input = ProcessMenuInput();
switch (input)
{
case -1:
case 1:
StartDisplaySubtask(CHATDISPLAY_FUNC_DESTROY_YESNO, 0);
sChat->funcState = 3;
break;
case 0:
if (sChat->multiplayerId == 0)
{
PrepareSendBuffer_Disband(sChat->sendMessageBuffer);
StartDisplaySubtask(CHATDISPLAY_FUNC_DESTROY_YESNO, 0);
sChat->funcState = 9;
}
else
{
PrepareSendBuffer_Leave(sChat->sendMessageBuffer);
sChat->funcState = 4;
}
break;
}
break;
case 3:
if (!IsDisplaySubtaskActive(0))
SetChatFunction(CHAT_FUNC_HANDLE_INPUT);
break;
case 9:
if (!IsDisplaySubtaskActive(0))
{
StartDisplaySubtask(CHATDISPLAY_FUNC_ASK_CONFIRM_LEADER_LEAVE, 0);
sChat->funcState = 10;
}
break;
case 10:
if (!IsDisplaySubtaskActive(0))
sChat->funcState = 8;
break;
case 8:
input = ProcessMenuInput();
switch (input)
{
case -1:
case 1:
StartDisplaySubtask(CHATDISPLAY_FUNC_DESTROY_YESNO, 0);
sChat->funcState = 3;
break;
case 0:
Rfu_StopPartnerSearch();
PrepareSendBuffer_Disband(sChat->sendMessageBuffer);
sChat->funcState = 4;
sChat->tryQuitAgainTimer = 0;
break;
}
break;
case 4:
if (IsLinkTaskFinished() && !Rfu_IsPlayerExchangeActive() && SendBlock(0, sChat->sendMessageBuffer, sizeof(sChat->sendMessageBuffer)))
{
if (!sChat->multiplayerId)
sChat->funcState = 6;
else
sChat->funcState = 5;
}
if (!gReceivedRemoteLinkPlayers)
SetChatFunction(CHAT_FUNC_SAVE_AND_EXIT);
break;
case 5:
if (!gReceivedRemoteLinkPlayers)
{
SetChatFunction(CHAT_FUNC_SAVE_AND_EXIT);
}
else if (++sChat->tryQuitAgainTimer > 300)
{
sChat->tryQuitAgainTimer = 0;
sChat->funcState = 4;
}
break;
}
}
static void Chat_Exit(void)
{
switch (sChat->funcState)
{
case 0:
if (!FuncIsActiveTask(Task_ReceiveChatMessage))
{
StartDisplaySubtask(CHATDISPLAY_FUNC_DESTROY_YESNO, 0);
sChat->funcState++;
}
break;
case 1:
if (!IsDisplaySubtaskActive(0))
{
StartDisplaySubtask(CHATDISPLAY_FUNC_PRINT_EXITING_CHAT, 0);
sChat->funcState++;
}
break;
case 2:
if (!IsDisplaySubtaskActive(0))
{
PrepareSendBuffer_Drop(sChat->sendMessageBuffer);
sChat->funcState++;
}
break;
case 3:
if (IsLinkTaskFinished() && !Rfu_IsPlayerExchangeActive() && SendBlock(0, sChat->sendMessageBuffer, sizeof(sChat->sendMessageBuffer)))
sChat->funcState++;
break;
case 4:
if ((GetBlockReceivedStatus() & 1) && !Rfu_IsPlayerExchangeActive())
sChat->funcState++;
break;
case 5:
if (IsLinkTaskFinished() && !Rfu_IsPlayerExchangeActive())
{
SetCloseLinkCallback();
sChat->exitDelayTimer = 0;
sChat->funcState++;
}
break;
case 6:
if (sChat->exitDelayTimer < 150)
sChat->exitDelayTimer++;
if (!gReceivedRemoteLinkPlayers)
sChat->funcState++;
break;
case 7:
if (sChat->exitDelayTimer >= 150)
SetChatFunction(CHAT_FUNC_SAVE_AND_EXIT);
else
sChat->exitDelayTimer++;
break;
}
}
static void Chat_Drop(void)
{
switch (sChat->funcState)
{
case 0:
if (!FuncIsActiveTask(Task_ReceiveChatMessage))
{
StartDisplaySubtask(CHATDISPLAY_FUNC_DESTROY_YESNO, 0);
sChat->funcState++;
}
break;
case 1:
if (!IsDisplaySubtaskActive(0) && IsLinkTaskFinished() && !Rfu_IsPlayerExchangeActive())
{
SetCloseLinkCallback();
sChat->exitDelayTimer = 0;
sChat->funcState++;
}
break;
case 2:
if (sChat->exitDelayTimer < 150)
sChat->exitDelayTimer++;
if (!gReceivedRemoteLinkPlayers)
sChat->funcState++;
break;
case 3:
if (sChat->exitDelayTimer >= 150)
SetChatFunction(CHAT_FUNC_SAVE_AND_EXIT);
else
sChat->exitDelayTimer++;
break;
}
}
static void Chat_Disbanded(void)
{
switch (sChat->funcState)
{
case 0:
if (!FuncIsActiveTask(Task_ReceiveChatMessage))
{
if (sChat->multiplayerId)
StartDisplaySubtask(CHATDISPLAY_FUNC_DESTROY_YESNO, 0);
sChat->funcState++;
}
break;
case 1:
if (!IsDisplaySubtaskActive(0))
{
if (sChat->multiplayerId)
StartDisplaySubtask(CHATDISPLAY_FUNC_PRINT_LEADER_LEFT, 0);
sChat->funcState++;
}
break;
case 2:
if (IsDisplaySubtaskActive(0) != TRUE && IsLinkTaskFinished() && !Rfu_IsPlayerExchangeActive())
{
SetCloseLinkCallback();
sChat->exitDelayTimer = 0;
sChat->funcState++;
}
break;
case 3:
if (sChat->exitDelayTimer < 150)
sChat->exitDelayTimer++;
if (!gReceivedRemoteLinkPlayers)
sChat->funcState++;
break;
case 4:
if (sChat->exitDelayTimer >= 150)
SetChatFunction(CHAT_FUNC_SAVE_AND_EXIT);
else
sChat->exitDelayTimer++;
break;
}
}
static void Chat_SendMessage(void)
{
switch (sChat->funcState)
{
case 0:
if (!gReceivedRemoteLinkPlayers)
{
SetChatFunction(CHAT_FUNC_HANDLE_INPUT);
break;
}
PrepareSendBuffer_Chat(sChat->sendMessageBuffer);
sChat->funcState++;
// fall through
case 1:
if (IsLinkTaskFinished() == TRUE && !Rfu_IsPlayerExchangeActive() && SendBlock(0, sChat->sendMessageBuffer, sizeof(sChat->sendMessageBuffer)))
sChat->funcState++;
break;
case 2:
ResetMessageEntryBuffer();
StartDisplaySubtask(CHATDISPLAY_FUNC_UPDATE_MSG, 0);
sChat->funcState++;
break;
case 3:
if (!IsDisplaySubtaskActive(0))
sChat->funcState++;
break;
case 4:
if (IsLinkTaskFinished())
SetChatFunction(CHAT_FUNC_HANDLE_INPUT);
break;
}
}
static void Chat_Register(void)
{
switch (sChat->funcState)
{
case 0:
if (ChatMessageIsNotEmpty())
{
StartDisplaySubtask(CHATDISPLAY_FUNC_ASK_REGISTER_TEXT, 0);
sChat->funcState = 2;
}
else
{
StartDisplaySubtask(CHATDISPLAY_FUNC_PRINT_INPUT_TEXT, 0);
sChat->funcState = 5;
}
break;
case 1:
if (JOY_NEW(A_BUTTON))
{
RegisterTextAtRow();
StartDisplaySubtask(CHATDISPLAY_FUNC_RETURN_TO_KB, 0);
sChat->funcState = 3;
}
else if (JOY_NEW(B_BUTTON))
{
StartDisplaySubtask(CHATDISPLAY_FUNC_CANCEL_REGISTER, 0);
sChat->funcState = 4;
}
else if (HandleDPadInput())
{
StartDisplaySubtask(CHATDISPLAY_FUNC_MOVE_KB_CURSOR, 0);
sChat->funcState = 2;
}
break;
case 2:
if (!IsDisplaySubtaskActive(0))
sChat->funcState = 1;
break;
case 3:
if (!IsDisplaySubtaskActive(0))
{
StartDisplaySubtask(CHATDISPLAY_FUNC_CANCEL_REGISTER, 0);
sChat->funcState = 4;
}
break;
case 4:
if (!IsDisplaySubtaskActive(0))
SetChatFunction(CHAT_FUNC_HANDLE_INPUT);
break;
case 5:
if (!IsDisplaySubtaskActive(0))
sChat->funcState = 6;
break;
case 6:
if (JOY_NEW(A_BUTTON | B_BUTTON))
{
StartDisplaySubtask(CHATDISPLAY_FUNC_DESTROY_YESNO, 0);
sChat->funcState = 4;
}
break;
}
}
static void Chat_SaveAndExit(void)
{
s8 input;
switch (sChat->funcState)
{
case 0:
if (!sChat->changedRegisteredTexts)
{
sChat->funcState = 12;
}
else
{
StartDisplaySubtask(CHATDISPLAY_FUNC_DESTROY_YESNO, 0);
sChat->funcState = 1;
}
break;
case 1:
if (!IsDisplaySubtaskActive(0))
{
StartDisplaySubtask(CHATDISPLAY_FUNC_ASK_SAVE, 0);
sChat->funcState = 2;
}
break;
case 2:
input = ProcessMenuInput();
switch (input)
{
case -1:
case 1:
sChat->funcState = 12;
break;
case 0:
StartDisplaySubtask(CHATDISPLAY_FUNC_DESTROY_YESNO, 0);
sChat->funcState = 3;
break;
}
break;
case 3:
if (!IsDisplaySubtaskActive(0))
{
StartDisplaySubtask(CHATDISPLAY_FUNC_ASK_OVERWRITE_SAVE, 0);
sChat->funcState = 4;
}
break;
case 4:
if (!IsDisplaySubtaskActive(0))
sChat->funcState = 5;
break;
case 5:
input = ProcessMenuInput();
switch (input)
{
case -1:
case 1:
sChat->funcState = 12;
break;
case 0:
StartDisplaySubtask(CHATDISPLAY_FUNC_DESTROY_YESNO, 0);
sChat->funcState = 6;
break;
}
break;
case 6:
if (!IsDisplaySubtaskActive(0))
{
StartDisplaySubtask(CHATDISPLAY_FUNC_PRINT_SAVING, 0);
SaveRegisteredTexts();
sChat->funcState = 7;
}
break;
case 7:
if (!IsDisplaySubtaskActive(0))
{
SetContinueGameWarpStatusToDynamicWarp();
TrySavingData(SAVE_NORMAL);
sChat->funcState = 8;
}
break;
case 8:
StartDisplaySubtask(CHATDISPLAY_FUNC_PRINT_SAVED_GAME, 0);
sChat->funcState = 9;
break;
case 9:
if (!IsDisplaySubtaskActive(0))
{
PlaySE(SE_SAVE);
ClearContinueGameWarpStatus2();
sChat->funcState = 10;
}
break;
case 10:
sChat->afterSaveTimer = 0;
sChat->funcState = 11;
break;
case 11:
sChat->afterSaveTimer++;
if (sChat->afterSaveTimer > 120)
sChat->funcState = 12;
break;
case 12:
BeginNormalPaletteFade(PALETTES_ALL, -1, 0, 16, RGB_BLACK);
sChat->funcState = 13;
break;
case 13:
if (!gPaletteFade.active)
{
FreeDisplay();
FreeUnionRoomChat();
SetMainCallback2(CB2_ReturnToField);
}
break;
}
}
static void SetChatFunction(u16 funcId)
{
sChat->funcId = funcId;
sChat->funcState = 0;
}
static bool32 HandleDPadInput(void)
{
do
{
if (JOY_REPEAT(DPAD_UP))
{
if (sChat->currentRow > 0)
sChat->currentRow--;
else
sChat->currentRow = sKeyboardPageMaxRow[sChat->currentPage];
break;
}
if (JOY_REPEAT(DPAD_DOWN))
{
if (sChat->currentRow < sKeyboardPageMaxRow[sChat->currentPage])
sChat->currentRow++;
else
sChat->currentRow = 0;
break;
}
if (sChat->currentPage != UNION_ROOM_KB_PAGE_REGISTER)
{
if (JOY_REPEAT(DPAD_LEFT))
{
if (sChat->currentCol > 0)
sChat->currentCol--;
else
sChat->currentCol = 4;
break;
}
else if (JOY_REPEAT(DPAD_RIGHT))
{
if (sChat->currentCol < 4)
sChat->currentCol++;
else
sChat->currentCol = 0;
break;
}
}
return FALSE;
} while (0);
return TRUE;
}
static void AppendTextToMessage(void)
{
int i;
const u8 *charsStr;
int strLength;
u8 *str;
u8 buffer[21];
if (sChat->currentPage != UNION_ROOM_KB_PAGE_REGISTER)
{
// Going to append a single character
charsStr = sUnionRoomKeyboardText[sChat->currentPage][sChat->currentRow];
for (i = 0; i < sChat->currentCol; i++)
{
if (*charsStr == CHAR_EXTRA_SYMBOL)
charsStr++;
charsStr++;
}
strLength = 1;
}
else
{
// Going to append registered text string
u8 *tempStr = StringCopy(buffer, sChat->registeredTexts[sChat->currentRow]);
tempStr[0] = CHAR_SPACE;
tempStr[1] = EOS;
charsStr = buffer;
strLength = StringLength_Multibyte(buffer);
}
sChat->lastBufferCursorPos = sChat->bufferCursorPos;
if (!charsStr)
return;
str = GetEndOfMessagePtr();
while (--strLength != -1 && sChat->bufferCursorPos < MAX_MESSAGE_LENGTH)
{
if (*charsStr == CHAR_EXTRA_SYMBOL)
{
*str = *charsStr;
charsStr++;
str++;
}
*str = *charsStr;
charsStr++;
str++;
sChat->bufferCursorPos++;
}
*str = EOS;
}
static void DeleteLastMessageCharacter(void)
{
sChat->lastBufferCursorPos = sChat->bufferCursorPos;
if (sChat->bufferCursorPos)
{
u8 *str = GetLastCharOfMessagePtr();
*str = EOS;
sChat->bufferCursorPos--;
}
}
static void SwitchCaseOfLastMessageCharacter(void)
{
u8 *str;
u8 character;
sChat->lastBufferCursorPos = sChat->bufferCursorPos - 1;
str = GetLastCharOfMessagePtr();
if (*str != CHAR_EXTRA_SYMBOL)
{
character = sCaseToggleTable[*str];
if (character)
*str = character;
}
}
static bool32 ChatMessageIsNotEmpty(void)
{
if (sChat->bufferCursorPos)
return TRUE;
else
return FALSE;
}
static void RegisterTextAtRow(void)
{
u8 *src = GetLimitedMessageStartPtr();
StringCopy(sChat->registeredTexts[sChat->currentRow], src);
sChat->changedRegisteredTexts = TRUE;
}
static void ResetMessageEntryBuffer(void)
{
sChat->messageEntryBuffer[0] = EOS;
sChat->lastBufferCursorPos = 15;
sChat->bufferCursorPos = 0;
}
static void SaveRegisteredTexts(void)
{
int i;
for (i = 0; i < UNION_ROOM_KB_ROW_COUNT; i++)
StringCopy(gSaveBlock1Ptr->registeredTexts[i], sChat->registeredTexts[i]);
}
static u8 *GetRegisteredTextByRow(int row)
{
return sChat->registeredTexts[row];
}
static u8 *GetEndOfMessagePtr(void)
{
u8 *str = sChat->messageEntryBuffer;
while (*str != EOS)
str++;
return str;
}
static u8 *GetLastCharOfMessagePtr(void)
{
u8 *currChar = sChat->messageEntryBuffer;
u8 *lastChar = currChar;
while (*currChar != EOS)
{
lastChar = currChar;
if (*currChar == CHAR_EXTRA_SYMBOL)
currChar++;
currChar++;
}
return lastChar;
}
static u16 GetNumOverflowCharsInMessage(void)
{
u8 *str;
u32 i, numChars, strLength;
strLength = StringLength_Multibyte(sChat->messageEntryBuffer);
str = sChat->messageEntryBuffer;
numChars = 0;
if (strLength > 10)
{
strLength -= 10;
for (i = 0; i < strLength; i++)
{
if (*str == CHAR_EXTRA_SYMBOL)
str++;
str++;
numChars++;
}
}
return numChars;
}
static void PrepareSendBuffer_Null(u8 *buffer)
{
buffer[0] = CHAT_MESSAGE_NONE;
}
static void PrepareSendBuffer_Join(u8 *buffer)
{
buffer[0] = CHAT_MESSAGE_JOIN;
StringCopy(&buffer[1], gSaveBlock2Ptr->playerName);
buffer[1 + (PLAYER_NAME_LENGTH + 1)] = sChat->multiplayerId;
}
static void PrepareSendBuffer_Chat(u8 *buffer)
{
buffer[0] = CHAT_MESSAGE_CHAT;
StringCopy(&buffer[1], gSaveBlock2Ptr->playerName);
StringCopy(&buffer[1 + (PLAYER_NAME_LENGTH + 1)], sChat->messageEntryBuffer);
}
static void PrepareSendBuffer_Leave(u8 *buffer)
{
buffer[0] = CHAT_MESSAGE_LEAVE;
StringCopy(&buffer[1], gSaveBlock2Ptr->playerName);
buffer[1 + (PLAYER_NAME_LENGTH + 1)] = sChat->multiplayerId;
RfuSetNormalDisconnectMode();
}
static void PrepareSendBuffer_Drop(u8 *buffer)
{
buffer[0] = CHAT_MESSAGE_DROP;
StringCopy(&buffer[1], gSaveBlock2Ptr->playerName);
buffer[1 + (PLAYER_NAME_LENGTH + 1)] = sChat->multiplayerId;
}
static void PrepareSendBuffer_Disband(u8 *buffer)
{
buffer[0] = CHAT_MESSAGE_DISBAND;
StringCopy(&buffer[1], gSaveBlock2Ptr->playerName);
buffer[1 + (PLAYER_NAME_LENGTH + 1)] = sChat->multiplayerId;
}
static bool32 ProcessReceivedChatMessage(u8 *dest, u8 *recvMessage)
{
u8 *tempStr;
u8 cmd = *recvMessage;
u8 *name = recvMessage + 1;
recvMessage = name;
recvMessage += PLAYER_NAME_LENGTH + 1;
switch (cmd)
{
case CHAT_MESSAGE_JOIN:
if (sChat->multiplayerId != name[PLAYER_NAME_LENGTH + 1])
{
DynamicPlaceholderTextUtil_Reset();
DynamicPlaceholderTextUtil_SetPlaceholderPtr(0, name);
DynamicPlaceholderTextUtil_ExpandPlaceholders(dest, gText_F700JoinedChat);
return TRUE;
}
break;
case CHAT_MESSAGE_CHAT:
tempStr = StringCopy(dest, name);
*(tempStr++) = EXT_CTRL_CODE_BEGIN;
*(tempStr++) = EXT_CTRL_CODE_CLEAR_TO;
*(tempStr++) = 42;
*(tempStr++) = CHAR_COLON;
StringCopy(tempStr, recvMessage);
return TRUE;
case CHAT_MESSAGE_DISBAND:
StringCopy(sChat->hostName, name);
// fall through
case CHAT_MESSAGE_LEAVE:
if (sChat->multiplayerId != *recvMessage)
{
DynamicPlaceholderTextUtil_Reset();
DynamicPlaceholderTextUtil_SetPlaceholderPtr(0, name);
DynamicPlaceholderTextUtil_ExpandPlaceholders(dest, gText_F700LeftChat);
return TRUE;
}
break;
}
return FALSE;
}
static u8 GetCurrentKeyboardPage(void)
{
return sChat->currentPage;
}
static void GetCurrentKeyboardColAndRow(u8 *col, u8 *row)
{
*col = sChat->currentCol;
*row = sChat->currentRow;
}
static u8 *GetMessageEntryBuffer(void)
{
return sChat->messageEntryBuffer;
}
static int GetLengthOfMessageEntry(void)
{
u8 *str = GetMessageEntryBuffer();
return StringLength_Multibyte(str);
}
static void GetBufferSelectionRegion(u32 *x, u32 *width)
{
int diff = sChat->bufferCursorPos - sChat->lastBufferCursorPos;
if (diff < 0)
{
diff *= -1;
*x = sChat->bufferCursorPos;
}
else
{
*x = sChat->lastBufferCursorPos;
}
*width = diff;
}
static u8 *GetLimitedMessageStartPtr(void)
{
int i;
u16 numChars = GetNumOverflowCharsInMessage();
u8 *str = sChat->messageEntryBuffer;
for (i = 0; i < numChars; i++)
{
if (*str == CHAR_EXTRA_SYMBOL)
*str++;
str++;
}
return str;
}
static u16 GetLimitedMessageStartPos(void)
{
u16 count;
u32 i;
u16 numChars = GetNumOverflowCharsInMessage();
u8 *str = sChat->messageEntryBuffer;
for (count = 0, i = 0; i < numChars; count++, i++)
{
if (*str == CHAR_EXTRA_SYMBOL)
str++;
str++;
}
return count;
}
static u8 *GetLastReceivedMessage(void)
{
return sChat->receivedMessage;
}
static u8 GetReceivedPlayerIndex(void)
{
return sChat->receivedPlayerIndex;
}
static int GetTextEntryCursorPosition(void)
{
return sChat->bufferCursorPos;
}
static int GetShouldShowCaseToggleIcon(void)
{
u8 *str = GetLastCharOfMessagePtr();
u32 character = *str;
if (character > EOS || sCaseToggleTable[character] == character || sCaseToggleTable[character] == CHAR_SPACE)
return 3; // Don't show
else
return 0; // Show
}
static u8 *GetChatHostName(void)
{
return sChat->hostName;
}
void InitUnionRoomChatRegisteredTexts(void)
{
StringCopy(gSaveBlock1Ptr->registeredTexts[0], gText_Hello);
StringCopy(gSaveBlock1Ptr->registeredTexts[1], gText_Pokemon2);
StringCopy(gSaveBlock1Ptr->registeredTexts[2], gText_Trade);
StringCopy(gSaveBlock1Ptr->registeredTexts[3], gText_Battle);
StringCopy(gSaveBlock1Ptr->registeredTexts[4], gText_Lets);
StringCopy(gSaveBlock1Ptr->registeredTexts[5], gText_Ok);
StringCopy(gSaveBlock1Ptr->registeredTexts[6], gText_Sorry);
StringCopy(gSaveBlock1Ptr->registeredTexts[7], gText_YaySmileEmoji);
StringCopy(gSaveBlock1Ptr->registeredTexts[8], gText_ThankYou);
StringCopy(gSaveBlock1Ptr->registeredTexts[9], gText_ByeBye);
}
#define tState data[0]
#define tI data[1]
#define tCurrLinkPlayer data[2]
#define tBlockReceivedStatus data[3]
#define tLinkPlayerCount data[4]
#define tNextState data[5]
static void Task_ReceiveChatMessage(u8 taskId)
{
u8 *buffer;
s16 *data = gTasks[taskId].data;
switch (tState)
{
case 0:
if (!gReceivedRemoteLinkPlayers)
{
DestroyTask(taskId);
return;
}
tState = 1;
// fall through
case 1:
tLinkPlayerCount = GetLinkPlayerCount();
if (sChat->linkPlayerCount != tLinkPlayerCount)
{
tState = 2;
sChat->linkPlayerCount = tLinkPlayerCount;
return;
}
tBlockReceivedStatus = GetBlockReceivedStatus();
if (!tBlockReceivedStatus && Rfu_IsPlayerExchangeActive())
return;
tI = 0;
tState = 3;
// fall through
case 3:
for (; tI < 5 && ((tBlockReceivedStatus >> tI) & 1) == 0; tI++)
;
if (tI == 5)
{
tState = 1;
return;
}
tCurrLinkPlayer = tI;
ResetBlockReceivedFlag(tCurrLinkPlayer);
buffer = (u8 *)gBlockRecvBuffer[tI];
switch (buffer[0])
{
default:
case CHAT_MESSAGE_CHAT: tNextState = 3; break;
case CHAT_MESSAGE_JOIN: tNextState = 3; break;
case CHAT_MESSAGE_LEAVE: tNextState = 4; break;
case CHAT_MESSAGE_DROP: tNextState = 5; break;
case CHAT_MESSAGE_DISBAND: tNextState = 6; break;
}
if (ProcessReceivedChatMessage(sChat->receivedMessage, (u8 *)gBlockRecvBuffer[tI]))
{
sChat->receivedPlayerIndex = tI;
StartDisplaySubtask(CHATDISPLAY_FUNC_SCROLL_CHAT, 2);
tState = 7;
}
else
{
tState = tNextState;
}
tI++;
break;
case 7:
if (!IsDisplaySubtaskActive(2))
tState = tNextState;
break;
case 4:
if (!sChat->multiplayerId && tCurrLinkPlayer)
{
if (GetLinkPlayerCount() == 2)
{
Rfu_StopPartnerSearch();
sChat->exitType = CHAT_EXIT_ONLY_LEADER;
DestroyTask(taskId);
return;
}
Rfu_DisconnectPlayerById(tCurrLinkPlayer);
}
tState = 3;
break;
case 5:
if (sChat->multiplayerId)
sChat->exitType = CHAT_EXIT_DROPPED;
DestroyTask(taskId);
break;
case 6:
sChat->exitType = CHAT_EXIT_DISBANDED;
DestroyTask(taskId);
break;
case 2:
if (!Rfu_IsPlayerExchangeActive())
{
if (!sChat->multiplayerId)
SetUnionRoomChatPlayerData(sChat->linkPlayerCount);
tState = 1;
}
break;
}
}
#undef tNextState
#undef tLinkPlayerCount
#undef tBlockReceivedStatus
#undef tCurrLinkPlayer
#undef tI
#undef tState
static bool8 TryAllocDisplay(void)
{
sDisplay = Alloc(sizeof(*sDisplay));
if (sDisplay && TryAllocSprites())
{
ResetBgsAndClearDma3BusyFlags(0);
InitBgsFromTemplates(0, sBgTemplates, ARRAY_COUNT(sBgTemplates));
InitWindows(sWinTemplates);
ResetTempTileDataBuffers();
InitScanlineEffect();
InitDisplay(sDisplay);
ResetDisplaySubtasks();
StartDisplaySubtask(CHATDISPLAY_FUNC_LOAD_GFX, 0);
return TRUE;
}
else
{
return FALSE;
}
}
static bool32 IsDisplaySubtask0Active(void)
{
return IsDisplaySubtaskActive(0);
}
static void FreeDisplay(void)
{
FreeSprites();
if (sDisplay)
FREE_AND_SET_NULL(sDisplay);
FreeAllWindowBuffers();
gScanlineEffect.state = 3;
}
static void InitDisplay(struct UnionRoomChatDisplay *display)
{
display->yesNoMenuWindowId = WINDOW_NONE;
display->messageWindowId = WINDOW_NONE;
display->currLine = 0;
}
static void ResetDisplaySubtasks(void)
{
int i;
if (!sDisplay)
return;
for (i = 0; i < (int)ARRAY_COUNT(sDisplay->subtasks); i++)
{
sDisplay->subtasks[i].callback = Display_Dummy;
sDisplay->subtasks[i].active = FALSE;
sDisplay->subtasks[i].state = 0;
}
}
static void RunDisplaySubtasks(void)
{
int i;
if (!sDisplay)
return;
for (i = 0; i < (int)ARRAY_COUNT(sDisplay->subtasks); i++)
{
sDisplay->subtasks[i].active =
sDisplay->subtasks[i].callback(&sDisplay->subtasks[i].state);
}
}
static void StartDisplaySubtask(u16 subtaskId, u8 assignId)
{
u32 i;
sDisplay->subtasks[assignId].callback = Display_Dummy;
for (i = 0; i < ARRAY_COUNT(sDisplaySubtasks); i++)
{
if (sDisplaySubtasks[i].idx == subtaskId)
{
sDisplay->subtasks[assignId].callback = sDisplaySubtasks[i].callback;
sDisplay->subtasks[assignId].active = TRUE;
sDisplay->subtasks[assignId].state = 0;
break;
}
}
}
static bool8 IsDisplaySubtaskActive(u8 id)
{
return sDisplay->subtasks[id].active;
}
static bool32 Display_LoadGfx(u8 *state)
{
if (FreeTempTileDataBuffersIfPossible() == TRUE)
return TRUE;
switch (*state)
{
case 0:
ResetGpuBgState();
SetBgTilemapBuffers();
break;
case 1:
ClearBg0();
break;
case 2:
LoadChatWindowBorderGfx();
break;
case 3:
LoadChatWindowGfx();
break;
case 4:
LoadChatUnkPalette();
break;
case 5:
LoadChatMessagesWindow();
LoadKeyboardWindow();
LoadKeyboardSwapWindow();
LoadTextEntryWindow();
break;
case 6:
if (!IsDma3ManagerBusyWithBgCopy())
{
CreateKeyboardCursorSprite();
CreateTextEntrySprites();
CreateRButtonSprites();
}
break;
default:
return FALSE;
}
(*state)++;
return TRUE;
}
static bool32 Display_ShowKeyboardSwapMenu(u8 *state)
{
switch (*state)
{
case 0:
ShowKeyboardSwapMenu();
CopyWindowToVram(3, COPYWIN_FULL);
break;
case 1:
return IsDma3ManagerBusyWithBgCopy();
}
(*state)++;
return TRUE;
}
static bool32 Display_HideKeyboardSwapMenu(u8 *state)
{
switch (*state)
{
case 0:
HideKeyboardSwapMenu();
CopyWindowToVram(3, COPYWIN_FULL);
break;
case 1:
return IsDma3ManagerBusyWithBgCopy();
}
(*state)++;
return TRUE;
}
static bool32 Display_SwitchPages(u8 *state)
{
switch (*state)
{
case 0:
SetKeyboardCursorInvisibility(TRUE);
if (SlideKeyboardPageOut())
return TRUE;
PrintCurrentKeyboardPage();
CopyWindowToVram(2, COPYWIN_GFX);
break;
case 1:
if (IsDma3ManagerBusyWithBgCopy())
return TRUE;
break;
case 2:
if (SlideKeyboardPageIn())
return TRUE;
MoveKeyboardCursor();
SetKeyboardCursorInvisibility(FALSE);
UpdateRButtonLabel();
return FALSE;
}
(*state)++;
return TRUE;
}
static bool32 Display_MoveKeyboardCursor(u8 *state)
{
MoveKeyboardCursor();
return FALSE;
}
static bool32 Display_AskQuitChatting(u8 *state)
{
switch (*state)
{
case 0:
AddStdMessageWindow(STDMESSAGE_QUIT_CHATTING, 0);
AddYesNoMenuAt(23, 11, 1);
CopyWindowToVram(sDisplay->messageWindowId, COPYWIN_FULL);
break;
case 1:
return IsDma3ManagerBusyWithBgCopy();
}
(*state)++;
return TRUE;
}
static bool32 Display_DestroyYesNoDialog(u8 *state)
{
switch (*state)
{
case 0:
HideStdMessageWindow();
HideYesNoMenuWindow();
CopyBgTilemapBufferToVram(0);
break;
case 1:
if (IsDma3ManagerBusyWithBgCopy())
return TRUE;
DestroyStdMessageWindow();
DestroyYesNoMenuWindow();
return FALSE;
}
(*state)++;
return TRUE;
}
static bool32 Display_UpdateMessageBuffer(u8 *state)
{
u32 x, width;
u8 *str;
switch (*state)
{
case 0:
GetBufferSelectionRegion(&x, &width);
FillTextEntryWindow(x, width, 0);
str = GetMessageEntryBuffer();
DrawTextEntryMessage(0, str, 3, 1, 2);
CopyWindowToVram(1, COPYWIN_GFX);
break;
case 1:
if (!IsDma3ManagerBusyWithBgCopy())
{
UpdateRButtonLabel();
return FALSE;
}
return TRUE;
}
(*state)++;
return TRUE;
}
static bool32 Display_AskRegisterText(u8 *state)
{
u16 x;
u8 *str;
u16 length;
switch (*state)
{
case 0:
x = GetLimitedMessageStartPos();
str = GetLimitedMessageStartPtr();
length = StringLength_Multibyte(str);
FillTextEntryWindow(x, length, PIXEL_FILL(6));
DrawTextEntryMessage(x, str, 0, 4, 5);
CopyWindowToVram(1, COPYWIN_GFX);
break;
case 1:
if (!IsDma3ManagerBusyWithBgCopy())
{
AddStdMessageWindow(STDMESSAGE_REGISTER_WHERE, 16);
CopyWindowToVram(sDisplay->messageWindowId, COPYWIN_FULL);
}
else
{
return TRUE;
}
break;
case 2:
if (!IsDma3ManagerBusyWithBgCopy())
SetRegisteredTextPalette(TRUE);
else
return TRUE;
break;
case 3:
return FALSE;
}
(*state)++;
return TRUE;
}
static bool32 Display_CancelRegister(u8 *state)
{
u16 x;
u8 *str;
u16 length;
switch (*state)
{
case 0:
x = GetLimitedMessageStartPos();
str = GetLimitedMessageStartPtr();
length = StringLength_Multibyte(str);
FillTextEntryWindow(x, length, PIXEL_FILL(0));
DrawTextEntryMessage(x, str, 3, 1, 2);
CopyWindowToVram(1, COPYWIN_GFX);
break;
case 1:
if (!IsDma3ManagerBusyWithBgCopy())
{
HideStdMessageWindow();
CopyWindowToVram(sDisplay->messageWindowId, COPYWIN_FULL);
}
else
{
return TRUE;
}
break;
case 2:
if (!IsDma3ManagerBusyWithBgCopy())
{
SetRegisteredTextPalette(FALSE);
DestroyStdMessageWindow();
}
else
{
return TRUE;
}
break;
case 3:
return FALSE;
}
(*state)++;
return TRUE;
}
static bool32 Display_ReturnToKeyboard(u8 *state)
{
switch (*state)
{
case 0:
PrintCurrentKeyboardPage();
CopyWindowToVram(2, COPYWIN_GFX);
(*state)++;
break;
case 1:
if (IsDma3ManagerBusyWithBgCopy())
return TRUE;
else
return FALSE;
}
return TRUE;
}
static bool32 Display_ScrollChat(u8 *state)
{
u16 row;
u8 *str;
u8 colorIdx;
switch (*state)
{
case 0:
row = sDisplay->currLine;
str = GetLastReceivedMessage();
colorIdx = GetReceivedPlayerIndex();
PrintChatMessage(row, str, colorIdx);
CopyWindowToVram(0, COPYWIN_GFX);
break;
case 1:
if (IsDma3ManagerBusyWithBgCopy())
return TRUE;
if (sDisplay->currLine < 9)
{
sDisplay->currLine++;
*state = 4;
return FALSE;
}
else
{
sDisplay->scrollCount = 0;
(*state)++;
}
// fall through
case 2:
ScrollWindow(0, 0, 5, PIXEL_FILL(1));
CopyWindowToVram(0, COPYWIN_GFX);
sDisplay->scrollCount++;
(*state)++;
// fall through
case 3:
if (IsDma3ManagerBusyWithBgCopy())
return TRUE;
if (sDisplay->scrollCount < 3)
{
(*state)--;
return TRUE;
}
break;
case 4:
return FALSE;
default:
return TRUE;
}
(*state)++;
return TRUE;
}
static bool32 Display_AnimateKeyboardCursor(u8 *state)
{
switch (*state)
{
case 0:
StartKeyboardCursorAnim();
(*state)++;
break;
case 1:
return TryKeyboardCursorReopen();
}
return TRUE;
}
static bool32 Display_PrintInputText(u8 *state)
{
switch (*state)
{
case 0:
AddStdMessageWindow(STDMESSAGE_INPUT_TEXT, 16);
CopyWindowToVram(sDisplay->messageWindowId, COPYWIN_FULL);
(*state)++;
break;
case 1:
return IsDma3ManagerBusyWithBgCopy();
}
return TRUE;
}
static bool32 Display_PrintExitingChat(u8 *state)
{
switch (*state)
{
case 0:
AddStdMessageWindow(STDMESSAGE_EXITING_CHAT, 0);
CopyWindowToVram(sDisplay->messageWindowId, COPYWIN_FULL);
(*state)++;
break;
case 1:
return IsDma3ManagerBusyWithBgCopy();
}
return TRUE;
}
static bool32 Display_PrintLeaderLeft(u8 *state)
{
u8 *str;
switch (*state)
{
case 0:
DynamicPlaceholderTextUtil_Reset();
str = GetChatHostName();
DynamicPlaceholderTextUtil_SetPlaceholderPtr(0, str);
AddStdMessageWindow(STDMESSAGE_LEADER_LEFT, 0);
CopyWindowToVram(sDisplay->messageWindowId, COPYWIN_FULL);
(*state)++;
break;
case 1:
return IsDma3ManagerBusyWithBgCopy();
}
return TRUE;
}
static bool32 Display_AskSave(u8 *state)
{
switch (*state)
{
case 0:
AddStdMessageWindow(STDMESSAGE_ASK_SAVE, 0);
AddYesNoMenuAt(23, 10, 1);
CopyWindowToVram(sDisplay->messageWindowId, COPYWIN_FULL);
(*state)++;
break;
case 1:
return IsDma3ManagerBusyWithBgCopy();
}
return TRUE;
}
static bool32 Display_AskOverwriteSave(u8 *state)
{
switch (*state)
{
case 0:
AddStdMessageWindow(STDMESSAGE_ASK_OVERWRITE, 0);
AddYesNoMenuAt(23, 10, 1);
CopyWindowToVram(sDisplay->messageWindowId, COPYWIN_FULL);
(*state)++;
break;
case 1:
return IsDma3ManagerBusyWithBgCopy();
}
return TRUE;
}
static bool32 Display_PrintSavingDontTurnOff(u8 *state)
{
switch (*state)
{
case 0:
AddStdMessageWindow(STDMESSAGE_SAVING_NO_OFF, 0);
CopyWindowToVram(sDisplay->messageWindowId, COPYWIN_FULL);
(*state)++;
break;
case 1:
return IsDma3ManagerBusyWithBgCopy();
}
return TRUE;
}
static bool32 Display_PrintSavedTheGame(u8 *state)
{
switch (*state)
{
case 0:
DynamicPlaceholderTextUtil_Reset();
DynamicPlaceholderTextUtil_SetPlaceholderPtr(0, gSaveBlock2Ptr->playerName);
AddStdMessageWindow(STDMESSAGE_SAVED_THE_GAME, 0);
CopyWindowToVram(sDisplay->messageWindowId, COPYWIN_FULL);
(*state)++;
break;
case 1:
return IsDma3ManagerBusyWithBgCopy();
}
return TRUE;
}
static bool32 Display_AskConfirmLeaderLeave(u8 *state)
{
switch (*state)
{
case 0:
AddStdMessageWindow(STDMESSAGE_WARN_LEADER_LEAVE, 0);
AddYesNoMenuAt(23, 10, 1);
CopyWindowToVram(sDisplay->messageWindowId, COPYWIN_FULL);
(*state)++;
break;
case 1:
return IsDma3ManagerBusyWithBgCopy();
}
return TRUE;
}
static bool32 Display_Dummy(u8 *state)
{
return FALSE;
}
static void AddYesNoMenuAt(u8 left, u8 top, u8 initialCursorPos)
{
struct WindowTemplate template;
template.bg = 0;
template.tilemapLeft = left;
template.tilemapTop = top;
template.width = 6;
template.height = 4;
template.paletteNum = 14;
template.baseBlock = 0x52;
sDisplay->yesNoMenuWindowId = AddWindow(&template);
if (sDisplay->yesNoMenuWindowId != WINDOW_NONE)
{
FillWindowPixelBuffer(sDisplay->yesNoMenuWindowId, PIXEL_FILL(1));
PutWindowTilemap(sDisplay->yesNoMenuWindowId);
AddTextPrinterParameterized(sDisplay->yesNoMenuWindowId, FONT_NORMAL, gText_Yes, 8, 1, TEXT_SKIP_DRAW, NULL);
AddTextPrinterParameterized(sDisplay->yesNoMenuWindowId, FONT_NORMAL, gText_No, 8, 17, TEXT_SKIP_DRAW, NULL);
DrawTextBorderOuter(sDisplay->yesNoMenuWindowId, 1, 13);
InitMenuInUpperLeftCornerNormal(sDisplay->yesNoMenuWindowId, 2, initialCursorPos);
}
}
static void HideYesNoMenuWindow(void)
{
if (sDisplay->yesNoMenuWindowId != WINDOW_NONE)
{
ClearStdWindowAndFrameToTransparent(sDisplay->yesNoMenuWindowId, FALSE);
ClearWindowTilemap(sDisplay->yesNoMenuWindowId);
}
}
static void DestroyYesNoMenuWindow(void)
{
if (sDisplay->yesNoMenuWindowId != WINDOW_NONE)
{
RemoveWindow(sDisplay->yesNoMenuWindowId);
sDisplay->yesNoMenuWindowId = WINDOW_NONE;
}
}
static s8 ProcessMenuInput(void)
{
return Menu_ProcessInput();
}
static void AddStdMessageWindow(int msgId, u16 bg0vofs)
{
const u8 *str;
int windowId;
struct WindowTemplate template;
template.bg = 0;
template.tilemapLeft = 8;
template.tilemapTop = 16;
template.width = 21;
template.height = 4;
template.paletteNum = 14;
template.baseBlock = 0x6A;
if (sDisplayStdMessages[msgId].useWiderBox)
{
template.tilemapLeft -= 7;
template.width += 7;
}
sDisplay->messageWindowId = AddWindow(&template);
windowId = sDisplay->messageWindowId;
if (sDisplay->messageWindowId == WINDOW_NONE)
return;
if (sDisplayStdMessages[msgId].hasPlaceholders)
{
DynamicPlaceholderTextUtil_ExpandPlaceholders(sDisplay->expandedPlaceholdersBuffer, sDisplayStdMessages[msgId].text);
str = sDisplay->expandedPlaceholdersBuffer;
}
else
{
str = sDisplayStdMessages[msgId].text;
}
ChangeBgY(0, bg0vofs * 256, BG_COORD_SET);
FillWindowPixelBuffer(windowId, PIXEL_FILL(1));
PutWindowTilemap(windowId);
if (sDisplayStdMessages[msgId].boxType == 1)
{
DrawTextBorderInner(windowId, 0xA, 2);
AddTextPrinterParameterized5(
windowId,
FONT_NORMAL,
str,
sDisplayStdMessages[msgId].x + 8,
sDisplayStdMessages[msgId].y + 8,
TEXT_SKIP_DRAW,
NULL,
sDisplayStdMessages[msgId].letterSpacing,
sDisplayStdMessages[msgId].lineSpacing);
}
else
{
DrawTextBorderOuter(windowId, 0xA, 2);
AddTextPrinterParameterized5(
windowId,
FONT_NORMAL,
str,
sDisplayStdMessages[msgId].x,
sDisplayStdMessages[msgId].y,
TEXT_SKIP_DRAW,
NULL,
sDisplayStdMessages[msgId].letterSpacing,
sDisplayStdMessages[msgId].lineSpacing);
}
sDisplay->messageWindowId = windowId;
}
static void HideStdMessageWindow(void)
{
if (sDisplay->messageWindowId != WINDOW_NONE)
{
ClearStdWindowAndFrameToTransparent(sDisplay->messageWindowId, FALSE);
ClearWindowTilemap(sDisplay->messageWindowId);
}
ChangeBgY(0, 0, BG_COORD_SET);
}
static void DestroyStdMessageWindow(void)
{
if (sDisplay->messageWindowId != WINDOW_NONE)
{
RemoveWindow(sDisplay->messageWindowId);
sDisplay->messageWindowId = WINDOW_NONE;
}
}
static void FillTextEntryWindow(u16 x, u16 width, u8 fillValue)
{
FillWindowPixelRect(1, fillValue, x * 8, 1, width * 8, 14);
}
static void DrawTextEntryMessage(u16 x, u8 *str, u8 bgColor, u8 fgColor, u8 shadowColor)
{
u8 color[3];
u8 strBuffer[35];
if (bgColor != TEXT_COLOR_TRANSPARENT)
FillTextEntryWindow(x, GetTextEntryCursorPosition() - x, bgColor);
color[0] = bgColor;
color[1] = fgColor;
color[2] = shadowColor;
strBuffer[0] = EXT_CTRL_CODE_BEGIN;
strBuffer[1] = EXT_CTRL_CODE_MIN_LETTER_SPACING;
strBuffer[2] = 8;
StringCopy(&strBuffer[3], str);
AddTextPrinterParameterized3(1, FONT_SHORT, x * 8, 1, color, TEXT_SKIP_DRAW, strBuffer);
}
static void PrintCurrentKeyboardPage(void)
{
u8 page;
int i;
u16 left;
u16 top;
u8 color[3];
u8 str[45];
u8 *str2;
FillWindowPixelBuffer(2, PIXEL_FILL(15));
page = GetCurrentKeyboardPage();
color[0] = TEXT_COLOR_TRANSPARENT;
color[1] = TEXT_DYNAMIC_COLOR_5;
color[2] = TEXT_DYNAMIC_COLOR_4;
if (page != UNION_ROOM_KB_PAGE_REGISTER)
{
str[0] = EXT_CTRL_CODE_BEGIN;
str[1] = EXT_CTRL_CODE_MIN_LETTER_SPACING;
str[2] = 8;
if (page == UNION_ROOM_KB_PAGE_EMOJI)
left = 6;
else
left = 8;
for (i = 0, top = 0; i < UNION_ROOM_KB_ROW_COUNT; i++, top += 12)
{
if (!sUnionRoomKeyboardText[page][i])
return;
StringCopy(&str[3], sUnionRoomKeyboardText[page][i]);
AddTextPrinterParameterized3(2, FONT_SMALL, left, top, color, TEXT_SKIP_DRAW, str);
}
}
else
{
left = 4;
for (i = 0, top = 0; i < UNION_ROOM_KB_ROW_COUNT; i++, top += 12)
{
str2 = GetRegisteredTextByRow(i);
if (GetStringWidth(FONT_SMALL, str2, 0) <= 40)
{
AddTextPrinterParameterized3(2, FONT_SMALL, left, top, color, TEXT_SKIP_DRAW, str2);
}
else
{
int length = StringLength_Multibyte(str2);
do
{
length--;
StringCopyN_Multibyte(str, str2, length);
} while (GetStringWidth(FONT_SMALL, str, 0) > 35);
AddTextPrinterParameterized3(2, FONT_SMALL, left, top, color, TEXT_SKIP_DRAW, str);
AddTextPrinterParameterized3(2, FONT_SMALL, left + 35, top, color, TEXT_SKIP_DRAW, sText_Ellipsis);
}
}
}
}
#define KEYBOARD_HOFS_END 56
static bool32 SlideKeyboardPageOut(void)
{
if (sDisplay->bg1hofs < KEYBOARD_HOFS_END)
{
sDisplay->bg1hofs += 12;
if (sDisplay->bg1hofs >= KEYBOARD_HOFS_END)
sDisplay->bg1hofs = KEYBOARD_HOFS_END;
if (sDisplay->bg1hofs < KEYBOARD_HOFS_END)
{
// Still sliding
UpdateSlidingKeyboard(sDisplay->bg1hofs);
return TRUE;
}
}
FinishSlidingKeyboard(sDisplay->bg1hofs);
return FALSE;
}
static bool32 SlideKeyboardPageIn(void)
{
if (sDisplay->bg1hofs > 0)
{
sDisplay->bg1hofs -= 12;
if (sDisplay->bg1hofs <= 0)
sDisplay->bg1hofs = 0;
if (sDisplay->bg1hofs > 0)
{
// Still sliding
UpdateSlidingKeyboard(sDisplay->bg1hofs);
return TRUE;
}
}
FinishSlidingKeyboard(sDisplay->bg1hofs);
return FALSE;
}
static void ShowKeyboardSwapMenu(void)
{
FillWindowPixelBuffer(3, PIXEL_FILL(1));
DrawTextBorderOuter(3, 1, 13);
PrintMenuActionTextsAtPos(3, FONT_SHORT, 8, 1, 14, 5, sKeyboardPageTitleTexts);
InitMenuNormal(3, FONT_SHORT, 0, 1, 14, 5, GetCurrentKeyboardPage());
PutWindowTilemap(3);
}
static void HideKeyboardSwapMenu(void)
{
ClearStdWindowAndFrameToTransparent(3, FALSE);
ClearWindowTilemap(3);
}
static void PrintChatMessage(u16 row, u8 *str, u8 colorIdx)
{
// colorIdx: 0 = gray, 1 = red, 2 = green, 3 = blue
u8 color[3];
color[0] = TEXT_COLOR_WHITE;
color[1] = colorIdx * 2 + 2;
color[2] = colorIdx * 2 + 3;
FillWindowPixelRect(0, PIXEL_FILL(1), 0, row * 15, 168, 15);
AddTextPrinterParameterized3(0, FONT_SHORT, 0, row * 15 + 1, color, TEXT_SKIP_DRAW, str);
}
static void ResetGpuBgState(void)
{
ChangeBgX(0, 0, BG_COORD_SET);
ChangeBgY(0, 0, BG_COORD_SET);
ChangeBgX(1, 0, BG_COORD_SET);
ChangeBgY(1, 0, BG_COORD_SET);
ChangeBgX(2, 0, BG_COORD_SET);
ChangeBgY(2, 0, BG_COORD_SET);
ChangeBgX(3, 0, BG_COORD_SET);
ChangeBgY(3, 0, BG_COORD_SET);
ShowBg(0);
ShowBg(1);
ShowBg(2);
ShowBg(3);
SetGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON | DISPCNT_OBJ_1D_MAP);
SetGpuReg(REG_OFFSET_BLDCNT, 0);
ClearGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_WIN0_ON | DISPCNT_WIN1_ON | DISPCNT_OBJWIN_ON);
SetGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_WIN0_ON);
SetGpuReg(REG_OFFSET_WIN0H, WIN_RANGE(64, DISPLAY_WIDTH));
SetGpuReg(REG_OFFSET_WIN0V, WIN_RANGE(0, DISPLAY_HEIGHT - 16));
SetGpuReg(REG_OFFSET_WININ, WININ_WIN0_BG0 | WININ_WIN0_BG2 | WININ_WIN0_BG3
| WININ_WIN0_OBJ | WININ_WIN0_CLR);
SetGpuReg(REG_OFFSET_WINOUT, WINOUT_WIN01_BG_ALL | WINOUT_WIN01_OBJ | WINOUT_WIN01_CLR);
}
static void SetBgTilemapBuffers(void)
{
SetBgTilemapBuffer(0, sDisplay->bg0Buffer);
SetBgTilemapBuffer(1, sDisplay->bg1Buffer);
SetBgTilemapBuffer(3, sDisplay->bg3Buffer);
SetBgTilemapBuffer(2, sDisplay->bg2Buffer);
}
static void ClearBg0(void)
{
RequestDma3Fill(0, (void *)BG_CHAR_ADDR(0), 0x20, 1);
FillBgTilemapBufferRect_Palette0(0, 0, 0, 0, 32, 32);
CopyBgTilemapBufferToVram(0);
}
static void LoadChatWindowBorderGfx(void)
{
LoadPalette(gUnionRoomChat_Window_Pal2, 0x70, 0x20);
LoadPalette(gUnionRoomChat_Window_Pal1, 0xC0, 0x20);
DecompressAndCopyTileDataToVram(1, gUnionRoomChat_Border_Gfx, 0, 0, 0);
CopyToBgTilemapBuffer(1, gUnionRoomChat_Border_Tilemap, 0, 0);
CopyBgTilemapBufferToVram(1);
}
static void LoadChatWindowGfx(void)
{
u8 *ptr;
LoadPalette(gUnionRoomChat_Background_Pal, 0, 0x20);
ptr = DecompressAndCopyTileDataToVram(2, gUnionRoomChat_Background_Gfx, 0, 0, 0);
if (ptr)
{
CpuFastCopy(&ptr[0x220], sDisplay->unk2128, sizeof(sDisplay->unk2128));
CpuFastCopy(&ptr[0x420], sDisplay->unk2148, sizeof(sDisplay->unk2148));
}
CopyToBgTilemapBuffer(2, gUnionRoomChat_Background_Tilemap, 0, 0);
CopyBgTilemapBufferToVram(2);
}
static void LoadChatUnkPalette(void)
{
LoadPalette(sUnk_Palette1, 0x80, sizeof(sUnk_Palette1));
RequestDma3Fill(0, (void *)BG_CHAR_ADDR(1) + 0x20, 0x20, 1);
}
static void LoadChatMessagesWindow(void)
{
LoadPalette(sUnk_Palette2, 0xF0, sizeof(sUnk_Palette2));
PutWindowTilemap(0);
FillWindowPixelBuffer(0, PIXEL_FILL(1));
CopyWindowToVram(0, COPYWIN_FULL);
}
static void LoadKeyboardWindow(void)
{
PutWindowTilemap(2);
PrintCurrentKeyboardPage();
CopyWindowToVram(2, COPYWIN_FULL);
}
static void LoadTextEntryWindow(void)
{
int i;
u8 unused[2];
unused[0] = 0;
unused[1] = 0xFF;
for (i = 0; i < MAX_MESSAGE_LENGTH; i++)
BlitBitmapToWindow(1, sDisplay->unk2128, i * 8, 0, 8, 16);
FillWindowPixelBuffer(1, PIXEL_FILL(0));
PutWindowTilemap(1);
CopyWindowToVram(1, COPYWIN_FULL);
}
static void LoadKeyboardSwapWindow(void)
{
FillWindowPixelBuffer(3, PIXEL_FILL(1));
LoadUserWindowBorderGfx(3, 1, 0xD0);
LoadUserWindowBorderGfx_(3, 0xA, 0x20);
LoadPalette(gStandardMenuPalette, 0xE0, 0x20);
}
static void InitScanlineEffect(void)
{
struct ScanlineEffectParams params;
params.dmaControl = SCANLINE_EFFECT_DMACNT_16BIT;
params.dmaDest = &REG_BG1HOFS;
params.initState = 1;
params.unused9 = 0;
sDisplay->bg1hofs = 0;
CpuFastFill(0, gScanlineEffectRegBuffers, sizeof(gScanlineEffectRegBuffers));
ScanlineEffect_SetParams(params);
}
static void UpdateSlidingKeyboard(s16 bg1hofs)
{
CpuFill16(bg1hofs, gScanlineEffectRegBuffers[gScanlineEffect.srcBuffer], 0x120);
CpuFill16(0, gScanlineEffectRegBuffers[gScanlineEffect.srcBuffer] + 0x90, 0x20);
}
static void FinishSlidingKeyboard(s16 bg1hofs)
{
CpuFill16(bg1hofs, gScanlineEffectRegBuffers[0], 0x120);
CpuFill16(0, gScanlineEffectRegBuffers[0] + 0x90, 0x20);
CpuFill16(bg1hofs, gScanlineEffectRegBuffers[0] + 0x3C0, 0x120);
CpuFill16(0, gScanlineEffectRegBuffers[0] + 0x450, 0x20);
}
static bool32 TryAllocSprites(void)
{
u32 i;
for (i = 0; i < ARRAY_COUNT(sSpriteSheets); i++)
LoadCompressedSpriteSheet(&sSpriteSheets[i]);
LoadSpritePalette(&sSpritePalette);
sSprites = Alloc(sizeof(*sSprites));
if (!sSprites)
return FALSE;
return TRUE;
}
static void FreeSprites(void)
{
if (sSprites)
Free(sSprites);
}
static void CreateKeyboardCursorSprite(void)
{
u8 spriteId = CreateSprite(&sSpriteTemplate_KeyboardCursor, 10, 24, 0);
sSprites->keyboardCursor = &gSprites[spriteId];
}
static void SetKeyboardCursorInvisibility(bool32 invisible)
{
sSprites->keyboardCursor->invisible = invisible;
}
static void MoveKeyboardCursor(void)
{
u8 x, y;
u8 page = GetCurrentKeyboardPage();
GetCurrentKeyboardColAndRow(&x, &y);
if (page != UNION_ROOM_KB_PAGE_REGISTER)
{
StartSpriteAnim(sSprites->keyboardCursor, 0);
sSprites->keyboardCursor->x = x * 8 + 10;
sSprites->keyboardCursor->y = y * 12 + 24;
}
else
{
StartSpriteAnim(sSprites->keyboardCursor, 2);
sSprites->keyboardCursor->x = 24;
sSprites->keyboardCursor->y = y * 12 + 24;
}
}
static void SetRegisteredTextPalette(bool32 registering)
{
const u16 *palette = &sUnionRoomChatInterfacePal[registering * 2 + 1];
u8 index = IndexOfSpritePaletteTag(PALTAG_INTERFACE);
LoadPalette(palette, index * 16 + 0x101, 4);
}
static void StartKeyboardCursorAnim(void)
{
if (GetCurrentKeyboardPage() != UNION_ROOM_KB_PAGE_REGISTER)
StartSpriteAnim(sSprites->keyboardCursor, 1);
else
StartSpriteAnim(sSprites->keyboardCursor, 3);
sSprites->cursorBlinkTimer = 0;
}
static bool32 TryKeyboardCursorReopen(void)
{
if (sSprites->cursorBlinkTimer > 3)
return FALSE;
if (++sSprites->cursorBlinkTimer > 3)
{
if (GetCurrentKeyboardPage() != UNION_ROOM_KB_PAGE_REGISTER)
StartSpriteAnim(sSprites->keyboardCursor, 0);
else
StartSpriteAnim(sSprites->keyboardCursor, 2);
return FALSE;
}
return TRUE;
}
static void CreateTextEntrySprites(void)
{
u8 spriteId = CreateSprite(&sSpriteTemplate_TextEntryCursor, 76, 152, 2);
sSprites->textEntryCursor = &gSprites[spriteId];
spriteId = CreateSprite(&sSpriteTemplate_TextEntryArrow, 64, 152, 1);
sSprites->textEntryArrow = &gSprites[spriteId];
}
static void SpriteCB_TextEntryCursor(struct Sprite *sprite)
{
int pos = GetTextEntryCursorPosition();
if (pos == MAX_MESSAGE_LENGTH)
{
sprite->invisible = TRUE;
}
else
{
sprite->invisible = FALSE;
sprite->x = pos * 8 + 76;
}
}
static void SpriteCB_TextEntryArrow(struct Sprite *sprite)
{
if (++sprite->data[0] > 4)
{
sprite->data[0] = 0;
if (++sprite->x2 > 4)
sprite->x2 = 0;
}
}
static void CreateRButtonSprites(void)
{
u8 spriteId = CreateSprite(&sSpriteTemplate_RButtonIcon, 8, 152, 3);
sSprites->rButtonIcon = &gSprites[spriteId];
spriteId = CreateSprite(&sSpriteTemplate_RButtonLabels, 32, 152, 4);
sSprites->rButtonLabel = &gSprites[spriteId];
sSprites->rButtonLabel->invisible = TRUE;
}
static void UpdateRButtonLabel(void)
{
if (GetCurrentKeyboardPage() == UNION_ROOM_KB_PAGE_REGISTER)
{
if (GetLengthOfMessageEntry() != 0)
{
// REGISTER
sSprites->rButtonLabel->invisible = FALSE;
StartSpriteAnim(sSprites->rButtonLabel, 3);
}
else
{
sSprites->rButtonLabel->invisible = TRUE;
}
}
else
{
int anim = GetShouldShowCaseToggleIcon();
if (anim == 3)
{
sSprites->rButtonLabel->invisible = TRUE;
}
else
{
// A <--> a (toggle case)
sSprites->rButtonLabel->invisible = FALSE;
StartSpriteAnim(sSprites->rButtonLabel, anim);
}
}
}