diff options
Diffstat (limited to 'src/union_room_chat.c')
-rw-r--r-- | src/union_room_chat.c | 1452 |
1 files changed, 1452 insertions, 0 deletions
diff --git a/src/union_room_chat.c b/src/union_room_chat.c new file mode 100644 index 000000000..167da514a --- /dev/null +++ b/src/union_room_chat.c @@ -0,0 +1,1452 @@ +#include "global.h" +#include "gflib.h" +#include "dynamic_placeholder_text_util.h" +#include "help_system.h" +#include "link.h" +#include "link_rfu.h" +#include "load_save.h" +#include "menu.h" +#include "overworld.h" +#include "quest_log.h" +#include "save.h" +#include "scanline_effect.h" +#include "strings.h" +#include "task.h" +#include "union_room_chat.h" +#include "union_room_chat_display.h" +#include "data_8479668.h" +#include "constants/songs.h" + +#define MESSAGE_BUFFER_NCHAR 15 + +#define CHAT_MESSAGE_0 0 +#define CHAT_MESSAGE_CHAT 1 +#define CHAT_MESSAGE_JOIN 2 +#define CHAT_MESSAGE_LEAVE 3 +#define CHAT_MESSAGE_DROP 4 +#define CHAT_MESSAGE_DISBAND 5 + +#define CHATENTRYROUTINE_JOIN 0 +#define CHATNETRYROUTINE_HANDLE_INPUT 1 +#define CHATENTRYROUTINE_SWITCH 2 +#define CHATENTRYROUTINE_ASKQUITCHATTING 3 +#define CHATENTRYROUTINE_SEND 4 +#define CHATENTRYROUTINE_REGISTER 5 +#define CHATENTRYROUTINE_EXITCHAT 6 +#define CHATENTRYROUTINE_DROP 7 +#define CHATENTRYROUTINE_DISBANDED 8 +#define CHATENTRYROUTINE_SAVEANDEXIT 9 + +#define CHATEXIT_NONE 0 +#define CHATEXIT_LEADER_LAST 1 +#define CHATEXIT_DROPPED 2 +#define CHATEXIT_DISBANDED 3 + +struct UnionRoomChat +{ + u8 filler0[0x4]; + u16 routineNo; + u16 routineState; + u8 filler8[0x2]; + u16 exitDelayTimer; + u8 fillerC[0x1]; + u8 linkPlayerCount; + u8 handleInputTask; + u8 receiveMessagesTask; + u8 currentPage; + u8 currentCol; + u8 currentRow; + u8 multiplayerId; + u8 lastBufferCursorPos; + u8 bufferCursorPos; + u8 receivedPlayerIndex; + u8 exitType; + bool8 changedRegisteredTexts; + u8 afterSaveTimer; + u8 messageEntryBuffer[2 * MESSAGE_BUFFER_NCHAR + 1]; + u8 receivedMessage[0x40]; + u8 hostName[0x40]; + u8 registeredTexts[UNION_ROOM_KB_ROW_COUNT][21]; + u8 filler18B[0x5]; + u8 sendMessageBuffer[0x28]; +}; + +static EWRAM_DATA struct UnionRoomChat * sWork = NULL; + +static void InitChatWork(struct UnionRoomChat * unionRoomChat); +static void CB2_LoadInterface(void); +static void VBlankCB_UnionRoomChatMain(void); +static void CB2_UnionRoomChatMain(void); +static void Task_HandlePlayerInput(u8 taskId); +static void ChatEntryRoutine_Join(void); +static void ChatEntryRoutine_HandleInput(void); +static void ChatEntryRoutine_Switch(void); +static void ChatEntryRoutine_AskQuitChatting(void); +static void ChatEntryRoutine_ExitChat(void); +static void ChatEntryRoutine_Drop(void); +static void ChatEntryRoutine_Disbanded(void); +static void ChatEntryRoutine_SendMessage(void); +static void ChatEntryRoutine_Register(void); +static void ChatEntryRoutine_SaveAndExit(void); +static void GoToRoutine(u16 routineNo); +static bool32 TypeChatMessage_HandleDPad(void); +static void AppendCharacterToChatMessageBuffer(void); +static void DeleteLastCharacterOfChatMessageBuffer(void); +static void ToggleCaseOfLastCharacterInChatMessageBuffer(void); +static bool32 ChatMsgHasAtLeastOneCharcter(void); +static void RegisterTextAtRow(void); +static void ResetMessageEntryBuffer(void); +static void SaveRegisteredTextsToSB1(void); +static u8 *GetEndOfUnk1A(void); +static u8 *GetPtrToLastCharOfUnk1A(void); +static void PrepareSendBuffer_Null(u8 *ptr); +static void PrepareSendBuffer_Join(u8 *ptr); +static void PrepareSendBuffer_Chat(u8 *ptr); +static void PrepareSendBuffer_Leave(u8 *ptr); +static void PrepareSendBuffer_Drop(u8 *ptr); +static void PrepareSendBuffer_Disband(u8 *ptr); +static void Task_ReceiveChatMessage(u8 taskId); + +static void (*const sChatEntryRoutines[])(void) = { + [CHATENTRYROUTINE_JOIN] = ChatEntryRoutine_Join, + [CHATNETRYROUTINE_HANDLE_INPUT] = ChatEntryRoutine_HandleInput, + [CHATENTRYROUTINE_SWITCH] = ChatEntryRoutine_Switch, + [CHATENTRYROUTINE_ASKQUITCHATTING] = ChatEntryRoutine_AskQuitChatting, + [CHATENTRYROUTINE_SEND] = ChatEntryRoutine_SendMessage, + [CHATENTRYROUTINE_REGISTER] = ChatEntryRoutine_Register, + [CHATENTRYROUTINE_EXITCHAT] = ChatEntryRoutine_ExitChat, + [CHATENTRYROUTINE_DROP] = ChatEntryRoutine_Drop, + [CHATENTRYROUTINE_DISBANDED] = ChatEntryRoutine_Disbanded, + [CHATENTRYROUTINE_SAVEANDEXIT] = ChatEntryRoutine_SaveAndExit +}; + +static const u8 sKeyboardPageMaxRow[] = +{ + [UNION_ROOM_KB_PAGE_UPPER] = 9, + [UNION_ROOM_KB_PAGE_LOWER] = 9, + [UNION_ROOM_KB_PAGE_EMOJI] = 9, + 9 +}; + +static const u8 sCaseToggleTable[] = { + 0x00, 0x16, 0x17, 0x68, 0x19, 0x1A, 0x1B, 0x1C, + 0x1D, 0x1E, 0x00, 0x20, 0x21, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x27, 0x28, 0x29, 0x15, 0x01, 0x02, + 0x00, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x00, + 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, + 0x13, 0x14, 0x2A, 0x2B, 0x2C, 0x2D, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x36, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x53, 0x54, 0x55, 0x56, 0x00, + 0x00, 0x00, 0x6F, 0x5B, 0x5C, 0x5D, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x84, 0x85, 0x86, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, + 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, + 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, + 0xB8, 0xB9, 0xBA, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, + 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, + 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, + 0xEA, 0xEB, 0xEC, 0xED, 0xEE, 0xBB, 0xBC, 0xBD, + 0xBE, 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, + 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xCB, 0xCC, 0xCD, + 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xEF, + 0xF0, 0xF4, 0xF5, 0xF6, 0xF1, 0xF2, 0xF3, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +const u8 *const gUnionRoomKeyboardText[UNION_ROOM_KB_PAGE_COUNT][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 + } +}; + +void EnterUnionRoomChat(void) +{ + sWork = Alloc(sizeof(struct UnionRoomChat)); + InitChatWork(sWork); + gKeyRepeatStartDelay = 20; + HelpSystem_DisableToggleWithRButton(); + SetVBlankCallback(NULL); + SetMainCallback2(CB2_LoadInterface); +} + +static void InitChatWork(struct UnionRoomChat * unionRoomChat) +{ + int i; + + unionRoomChat->routineNo = CHATENTRYROUTINE_JOIN; + unionRoomChat->routineState = 0; + unionRoomChat->currentPage = UNION_ROOM_KB_PAGE_UPPER; + unionRoomChat->currentCol = 0; + unionRoomChat->currentRow = 0; + unionRoomChat->lastBufferCursorPos = 0; + unionRoomChat->bufferCursorPos = 0; + unionRoomChat->receivedPlayerIndex = 0; + unionRoomChat->messageEntryBuffer[0] = EOS; + unionRoomChat->linkPlayerCount = GetLinkPlayerCount(); + unionRoomChat->multiplayerId = GetMultiplayerId(); + unionRoomChat->exitType = 0; + unionRoomChat->changedRegisteredTexts = FALSE; + PrepareSendBuffer_Null(unionRoomChat->sendMessageBuffer); + for (i = 0; i < UNION_ROOM_KB_ROW_COUNT; i++) + StringCopy(unionRoomChat->registeredTexts[i], gSaveBlock1Ptr->registeredTexts[i]); +} + +static void FreeChatWork(void) +{ + DestroyTask(sWork->handleInputTask); + DestroyTask(sWork->receiveMessagesTask); + Free(sWork); +} + +static void CB2_LoadInterface(void) +{ + switch (gMain.state) + { + case 0: + ResetTasks(); + ResetSpriteData(); + FreeAllSpritePalettes(); + UnionRoomChat_TryAllocGraphicsWork(); + gMain.state++; + break; + case 1: + UnionRoomChat_RunDisplaySubtasks(); + if (!UnionRoomChat_RunDisplaySubtask0()) + { + BlendPalettes(0xFFFFFFFF, 16, RGB_BLACK); + BeginNormalPaletteFade(0xFFFFFFFF, -1, 16, 0, RGB_BLACK); + SetVBlankCallback(VBlankCB_UnionRoomChatMain); + gMain.state++; + } + break; + case 2: + UpdatePaletteFade(); + if (!gPaletteFade.active) + { + SetMainCallback2(CB2_UnionRoomChatMain); + SetQuestLogEvent(QL_EVENT_USED_UNION_ROOM_CHAT, NULL); + sWork->handleInputTask = CreateTask(Task_HandlePlayerInput, 8); + sWork->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(); + UnionRoomChat_RunDisplaySubtasks(); + AnimateSprites(); + BuildOamBuffer(); + UpdatePaletteFade(); +} + +static void Task_HandlePlayerInput(u8 taskId) +{ + switch (sWork->exitType) + { + case CHATEXIT_LEADER_LAST: + GoToRoutine(CHATENTRYROUTINE_EXITCHAT); + sWork->exitType = CHATEXIT_NONE; + break; + case CHATEXIT_DROPPED: + GoToRoutine(CHATENTRYROUTINE_DROP); + sWork->exitType = CHATEXIT_NONE; + break; + case CHATEXIT_DISBANDED: + GoToRoutine(CHATENTRYROUTINE_DISBANDED); + sWork->exitType = CHATEXIT_NONE; + break; + } + + sChatEntryRoutines[sWork->routineNo](); +} + +static void ChatEntryRoutine_Join(void) +{ + switch (sWork->routineState) + { + case 0: + PrepareSendBuffer_Join(sWork->sendMessageBuffer); + sWork->routineState++; + // fall through + case 1: + if (IsLinkTaskFinished() && !GetRfuUnkCE8()) + { + if (SendBlock(0, sWork->sendMessageBuffer, sizeof(sWork->sendMessageBuffer))) + sWork->routineState++; + } + break; + case 2: + if (IsLinkTaskFinished()) + GoToRoutine(CHATNETRYROUTINE_HANDLE_INPUT); + break; + } +} + +static void ChatEntryRoutine_HandleInput(void) +{ + bool8 var0, var1; + + switch (sWork->routineState) + { + case 0: + if (JOY_NEW(START_BUTTON)) + { + if (sWork->bufferCursorPos) + GoToRoutine(CHATENTRYROUTINE_SEND); + } + else if (JOY_NEW(SELECT_BUTTON)) + { + GoToRoutine(CHATENTRYROUTINE_SWITCH); + } + else if (JOY_REPT(B_BUTTON)) + { + if (sWork->bufferCursorPos) + { + DeleteLastCharacterOfChatMessageBuffer(); + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_PRINTMSG, 0); + sWork->routineState = 1; + } + else + { + GoToRoutine(CHATENTRYROUTINE_ASKQUITCHATTING); + } + } + else if (JOY_NEW(A_BUTTON)) + { + AppendCharacterToChatMessageBuffer(); + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_PRINTMSG, 0); + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_CURSORBLINK, 1); + sWork->routineState = 1; + } + else if (JOY_NEW(R_BUTTON)) + { + if (sWork->currentPage != UNION_ROOM_KB_PAGE_COUNT) + { + ToggleCaseOfLastCharacterInChatMessageBuffer(); + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_PRINTMSG, 0); + sWork->routineState = 1; + } + else + { + GoToRoutine(CHATENTRYROUTINE_REGISTER); + } + } + else if (TypeChatMessage_HandleDPad()) + { + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_MOVEKBCURSOR, 0); + sWork->routineState = 1; + } + break; + case 1: + var0 = RunDisplaySubtask(0); + var1 = RunDisplaySubtask(1); + if (!var0 && !var1) + sWork->routineState = 0; + break; + } +} + +static void ChatEntryRoutine_Switch(void) +{ + s16 input; + bool32 shouldSwitchPages; + + switch (sWork->routineState) + { + case 0: + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_SHOWKBSWAPMENU, 0); + sWork->routineState++; + break; + case 1: + if (!RunDisplaySubtask(0)) + sWork->routineState++; + break; + case 2: + input = Menu_ProcessInput(); + switch (input) + { + default: + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_HIDEKBSWAPMENU, 0); + shouldSwitchPages = TRUE; + if (sWork->currentPage == input || input > UNION_ROOM_KB_PAGE_COUNT) + shouldSwitchPages = FALSE; + break; + case MENU_NOTHING_CHOSEN: + if (JOY_NEW(SELECT_BUTTON)) + { + PlaySE(SE_SELECT); + Menu_MoveCursor(1); + } + return; + case MENU_B_PRESSED: + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_HIDEKBSWAPMENU, 0); + sWork->routineState = 3; + return; + } + + if (!shouldSwitchPages) + { + sWork->routineState = 3; + return; + } + + sWork->currentCol = 0; + sWork->currentRow = 0; + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_SWITCHPAGES, 1); + sWork->currentPage = input; + sWork->routineState = 4; + break; + case 3: + // Wait Return To Prev Page + if (!RunDisplaySubtask(0)) + GoToRoutine(CHATNETRYROUTINE_HANDLE_INPUT); + break; + case 4: + // Wait Page Switch + if (!RunDisplaySubtask(0) && !RunDisplaySubtask(1)) + GoToRoutine(CHATNETRYROUTINE_HANDLE_INPUT); + break; + } +} + +static void ChatEntryRoutine_AskQuitChatting(void) +{ + s8 input; + + switch (sWork->routineState) + { + case 0: + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_SHOWQUITCHATTINGDIALOG, 0); + sWork->routineState = 1; + break; + case 1: + if (!RunDisplaySubtask(0)) + sWork->routineState = 2; + break; + case 2: + input = UnionRoomChat_ProcessInput(); + switch (input) + { + case -1: + case 1: + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_DESTROYSTDMSGANDYESNO, 0); + sWork->routineState = 3; + break; + case 0: + if (sWork->multiplayerId == 0) + { + PrepareSendBuffer_Disband(sWork->sendMessageBuffer); + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_DESTROYSTDMSGANDYESNO, 0); + sWork->routineState = 9; + } + else + { + PrepareSendBuffer_Leave(sWork->sendMessageBuffer); + sWork->routineState = 4; + } + break; + } + break; + case 3: + if (!RunDisplaySubtask(0)) + GoToRoutine(CHATNETRYROUTINE_HANDLE_INPUT); + break; + case 9: + if (!RunDisplaySubtask(0)) + { + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_SHOWCONFIRMLEADERLEAVEDIALOG, 0); + sWork->routineState = 10; + } + break; + case 10: + if (!RunDisplaySubtask(0)) + sWork->routineState = 8; + break; + case 8: + input = UnionRoomChat_ProcessInput(); + switch (input) + { + case -1: + case 1: + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_DESTROYSTDMSGANDYESNO, 0); + sWork->routineState = 3; + break; + case 0: + sub_80FA4A8(); + PrepareSendBuffer_Disband(sWork->sendMessageBuffer); + sWork->routineState = 4; + break; + } + break; + case 4: + if (IsLinkTaskFinished() && !GetRfuUnkCE8() && SendBlock(0, sWork->sendMessageBuffer, sizeof(sWork->sendMessageBuffer))) + { + if (sWork->multiplayerId == 0) + sWork->routineState = 6; + else + sWork->routineState = 5; + } + break; + case 5: + if (gReceivedRemoteLinkPlayers == 0) + { + GoToRoutine(CHATENTRYROUTINE_SAVEANDEXIT); + } + break; + } +} + +static void ChatEntryRoutine_ExitChat(void) +{ + switch (sWork->routineState) + { + case 0: + if (!FuncIsActiveTask(Task_ReceiveChatMessage)) + { + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_DESTROYSTDMSGANDYESNO, 0); + sWork->routineState++; + } + break; + case 1: + if (!RunDisplaySubtask(0)) + { + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_PRINTEXITINGCHAT, 0); + sWork->routineState++; + } + break; + case 2: + if (!RunDisplaySubtask(0)) + { + PrepareSendBuffer_Drop(sWork->sendMessageBuffer); + sWork->routineState++; + } + break; + case 3: + if (IsLinkTaskFinished() && !GetRfuUnkCE8() && SendBlock(0, sWork->sendMessageBuffer, sizeof(sWork->sendMessageBuffer))) + sWork->routineState++; + break; + case 4: + if ((GetBlockReceivedStatus() & 1) && !GetRfuUnkCE8()) + sWork->routineState++; + break; + case 5: + if (IsLinkTaskFinished() && !GetRfuUnkCE8()) + { + Link_TryStartSend5FFF(); + sWork->exitDelayTimer = 0; + sWork->routineState++; + } + break; + case 6: + if (sWork->exitDelayTimer < 150) + sWork->exitDelayTimer++; + + if (!gReceivedRemoteLinkPlayers) + sWork->routineState++; + break; + case 7: + if (sWork->exitDelayTimer >= 150) + GoToRoutine(CHATENTRYROUTINE_SAVEANDEXIT); + else + sWork->exitDelayTimer++; + break; + } +} + +static void ChatEntryRoutine_Drop(void) +{ + switch (sWork->routineState) + { + case 0: + if (!FuncIsActiveTask(Task_ReceiveChatMessage)) + { + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_DESTROYSTDMSGANDYESNO, 0); + sWork->routineState++; + } + break; + case 1: + if (!RunDisplaySubtask(0) && IsLinkTaskFinished() && !GetRfuUnkCE8()) + { + Link_TryStartSend5FFF(); + sWork->exitDelayTimer = 0; + sWork->routineState++; + } + break; + case 2: + if (sWork->exitDelayTimer < 150) + sWork->exitDelayTimer++; + + if (!gReceivedRemoteLinkPlayers) + sWork->routineState++; + break; + case 3: + if (sWork->exitDelayTimer >= 150) + GoToRoutine(CHATENTRYROUTINE_SAVEANDEXIT); + else + sWork->exitDelayTimer++; + break; + } +} + +static void ChatEntryRoutine_Disbanded(void) +{ + switch (sWork->routineState) + { + case 0: + if (!FuncIsActiveTask(Task_ReceiveChatMessage)) + { + if (sWork->multiplayerId) + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_DESTROYSTDMSGANDYESNO, 0); + + sWork->routineState++; + } + break; + case 1: + if (!RunDisplaySubtask(0)) + { + if (sWork->multiplayerId != 0) + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_PRINTLEADERLEFT, 0); + + sWork->routineState++; + } + break; + case 2: + if (RunDisplaySubtask(0) != TRUE && IsLinkTaskFinished() && !GetRfuUnkCE8()) + { + Link_TryStartSend5FFF(); + sWork->exitDelayTimer = 0; + sWork->routineState++; + } + break; + case 3: + if (sWork->exitDelayTimer < 150) + sWork->exitDelayTimer++; + + if (!gReceivedRemoteLinkPlayers) + sWork->routineState++; + break; + case 4: + if (sWork->exitDelayTimer >= 150) + GoToRoutine(CHATENTRYROUTINE_SAVEANDEXIT); + else + sWork->exitDelayTimer++; + break; + } +} + +static void ChatEntryRoutine_SendMessage(void) +{ + switch (sWork->routineState) + { + case 0: + if (!gReceivedRemoteLinkPlayers) + { + GoToRoutine(CHATNETRYROUTINE_HANDLE_INPUT); + break; + } + + PrepareSendBuffer_Chat(sWork->sendMessageBuffer); + sWork->routineState++; + // fall through + case 1: + if (IsLinkTaskFinished() == TRUE && !GetRfuUnkCE8() && SendBlock(0, sWork->sendMessageBuffer, sizeof(sWork->sendMessageBuffer))) + sWork->routineState++; + break; + case 2: + ResetMessageEntryBuffer(); + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_PRINTMSG, 0); + sWork->routineState++; + break; + case 3: + if (!RunDisplaySubtask(0)) + sWork->routineState++; + break; + case 4: + if (IsLinkTaskFinished()) + GoToRoutine(CHATNETRYROUTINE_HANDLE_INPUT); + break; + } +} + +static void ChatEntryRoutine_Register(void) +{ + switch (sWork->routineState) + { + case 0: + if (ChatMsgHasAtLeastOneCharcter()) + { + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_PRINTREGISTERWHERE, 0); + sWork->routineState = 2; + } + else + { + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_PRINTINPUTTEXT, 0); + sWork->routineState = 5; + } + break; + case 1: + if (JOY_NEW(A_BUTTON)) + { + RegisterTextAtRow(); + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_RETURNTOKB, 0); + sWork->routineState = 3; + } + else if (JOY_NEW(B_BUTTON)) + { + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_CANCELREGISTER, 0); + sWork->routineState = 4; + } + else if (TypeChatMessage_HandleDPad()) + { + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_MOVEKBCURSOR, 0); + sWork->routineState = 2; + } + break; + case 2: + if (!RunDisplaySubtask(0)) + sWork->routineState = 1; + break; + case 3: + if (!RunDisplaySubtask(0)) + { + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_CANCELREGISTER, 0); + sWork->routineState = 4; + } + break; + case 4: + if (!RunDisplaySubtask(0)) + GoToRoutine(CHATNETRYROUTINE_HANDLE_INPUT); + break; + case 5: + if (!RunDisplaySubtask(0)) + sWork->routineState = 6; + break; + case 6: + if (JOY_NEW(A_BUTTON | B_BUTTON)) + { + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_DESTROYSTDMSGANDYESNO, 0); + sWork->routineState = 4; + } + break; + } +} + +static void ChatEntryRoutine_SaveAndExit(void) +{ + s8 input; + + switch (sWork->routineState) + { + case 0: + if (!sWork->changedRegisteredTexts) + { + sWork->routineState = 12; + } + else + { + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_DESTROYSTDMSGANDYESNO, 0); + sWork->routineState = 1; + } + break; + case 1: + if (!RunDisplaySubtask(0)) + { + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_ASKSAVE, 0); + sWork->routineState = 2; + } + break; + case 2: + input = UnionRoomChat_ProcessInput(); + switch (input) + { + case -1: + case 1: + sWork->routineState = 12; + break; + case 0: + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_DESTROYSTDMSGANDYESNO, 0); + sWork->routineState = 3; + break; + } + break; + case 3: + if (!RunDisplaySubtask(0)) + { + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_ASKOVERWRITESAVE, 0); + sWork->routineState = 4; + } + break; + case 4: + if (!RunDisplaySubtask(0)) + sWork->routineState = 5; + break; + case 5: + input = UnionRoomChat_ProcessInput(); + switch (input) + { + case -1: + case 1: + sWork->routineState = 12; + break; + case 0: + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_DESTROYSTDMSGANDYESNO, 0); + sWork->routineState = 6; + break; + } + break; + case 6: + if (!RunDisplaySubtask(0)) + { + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_PRINTSAVING, 0); + SaveRegisteredTextsToSB1(); + sWork->routineState = 7; + } + break; + case 7: + if (!RunDisplaySubtask(0)) + { + SetContinueGameWarpStatusToDynamicWarp(); + TrySavingData(SAVE_NORMAL); + sWork->routineState = 8; + } + break; + case 8: + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_PRINTSAVEDTHEGAME, 0); + sWork->routineState = 9; + break; + case 9: + if (!RunDisplaySubtask(0)) + { + PlaySE(SE_SAVE); + ClearContinueGameWarpStatus2(); + sWork->routineState = 10; + } + break; + case 10: + sWork->afterSaveTimer = 0; + sWork->routineState = 11; + break; + case 11: + sWork->afterSaveTimer++; + if (sWork->afterSaveTimer > 120) + sWork->routineState = 12; + break; + case 12: + BeginNormalPaletteFade(0xFFFFFFFF, -1, 0, 16, RGB_BLACK); + sWork->routineState = 13; + break; + case 13: + if (!gPaletteFade.active) + { + HelpSystem_EnableToggleWithRButton(); + UnionRoomChat_FreeGraphicsWork(); + FreeChatWork(); + SetMainCallback2(CB2_ReturnToField); + } + break; + } +} + +static void GoToRoutine(u16 routineNo) +{ + sWork->routineNo = routineNo; + sWork->routineState = 0; +} + +static bool32 TypeChatMessage_HandleDPad(void) +{ + do + { + if (JOY_REPT(DPAD_UP)) + { + if (sWork->currentRow > 0) + sWork->currentRow--; + else + sWork->currentRow = sKeyboardPageMaxRow[sWork->currentPage]; + + break; + } + if (JOY_REPT(DPAD_DOWN)) + { + if (sWork->currentRow < sKeyboardPageMaxRow[sWork->currentPage]) + { + sWork->currentRow++; + } + else + { + sWork->currentRow = 0; + } + + break; + } + if (sWork->currentPage != UNION_ROOM_KB_PAGE_COUNT) + { + if (JOY_REPT(DPAD_LEFT)) + { + if (sWork->currentCol > 0) + sWork->currentCol--; + else + sWork->currentCol = 4; + break; + } + if (JOY_REPT(DPAD_RIGHT)) + { + if (sWork->currentCol < 4) + sWork->currentCol++; + else + sWork->currentCol = 0; + break; + } + } + + return FALSE; + } while (0); + return TRUE; +} + +static void AppendCharacterToChatMessageBuffer(void) +{ + int i; + const u8 *charsStr; + int strLength; + u8 *str; + u8 buffer[21]; + + if (sWork->currentPage != UNION_ROOM_KB_PAGE_COUNT) + { + charsStr = gUnionRoomKeyboardText[sWork->currentPage][sWork->currentRow]; + for (i = 0; i < sWork->currentCol; i++) + { + if (*charsStr == CHAR_EXTRA_EMOJI) + charsStr++; + charsStr++; + } + + strLength = 1; + } + else + { + u8 *tempStr = StringCopy(buffer, sWork->registeredTexts[sWork->currentRow]); + tempStr[0] = CHAR_SPACE; + tempStr[1] = EOS; + charsStr = buffer; + strLength = StringLength_Multibyte(buffer); + } + + sWork->lastBufferCursorPos = sWork->bufferCursorPos; + if (!charsStr) + return; + + str = GetEndOfUnk1A(); + while (--strLength != -1 && sWork->bufferCursorPos < MESSAGE_BUFFER_NCHAR) + { + if (*charsStr == CHAR_EXTRA_EMOJI) + { + *str = *charsStr; + charsStr++; + str++; + } + + *str = *charsStr; + charsStr++; + str++; + + sWork->bufferCursorPos++; + } + + *str = EOS; +} + +static void DeleteLastCharacterOfChatMessageBuffer(void) +{ + sWork->lastBufferCursorPos = sWork->bufferCursorPos; + if (sWork->bufferCursorPos) + { + u8 *str = GetPtrToLastCharOfUnk1A(); + *str = EOS; + sWork->bufferCursorPos--; + } +} + +static void ToggleCaseOfLastCharacterInChatMessageBuffer(void) +{ + u8 *str; + u8 character; + + sWork->lastBufferCursorPos = sWork->bufferCursorPos - 1; + str = GetPtrToLastCharOfUnk1A(); + if (*str != CHAR_EXTRA_EMOJI) + { + character = sCaseToggleTable[*str]; + if (character) + *str = character; + } +} + +static bool32 ChatMsgHasAtLeastOneCharcter(void) +{ + if (sWork->bufferCursorPos) + return TRUE; + else + return FALSE; +} + +static void RegisterTextAtRow(void) +{ + u8 *src = UnionRoomChat_GetEndOfMessageEntryBuffer(); + StringCopy(sWork->registeredTexts[sWork->currentRow], src); + sWork->changedRegisteredTexts = TRUE; +} + +static void ResetMessageEntryBuffer(void) +{ + sWork->messageEntryBuffer[0] = EOS; + sWork->lastBufferCursorPos = MESSAGE_BUFFER_NCHAR; + sWork->bufferCursorPos = 0; +} + +static void SaveRegisteredTextsToSB1(void) +{ + int i; + for (i = 0; i < UNION_ROOM_KB_ROW_COUNT; i++) + StringCopy(gSaveBlock1Ptr->registeredTexts[i], sWork->registeredTexts[i]); +} + +u8 *UnionRoomChat_GetWorkRegisteredText(int arg0) +{ + return sWork->registeredTexts[arg0]; +} + +static u8 *GetEndOfUnk1A(void) +{ + u8 *str = sWork->messageEntryBuffer; + while (*str != EOS) + str++; + + return str; +} + +static u8 *GetPtrToLastCharOfUnk1A(void) +{ + u8 *str = sWork->messageEntryBuffer; + u8 *str2 = str; + while (*str != EOS) + { + str2 = str; + if (*str == CHAR_EXTRA_EMOJI) + str++; + str++; + } + + return str2; +} + +static u16 GetNumCharsInMessageEntryBuffer(void) +{ + u8 *str; + u32 i, numChars, strLength; + + strLength = StringLength_Multibyte(sWork->messageEntryBuffer); + str = sWork->messageEntryBuffer; + numChars = 0; + if (strLength > 10) + { + strLength -= 10; + for (i = 0; i < strLength; i++) + { + if (*str == CHAR_EXTRA_EMOJI) + str++; + + str++; + numChars++; + } + } + + return numChars; +} + +static void PrepareSendBuffer_Null(u8 *arg0) +{ + arg0[0] = CHAT_MESSAGE_0; +} + +static void PrepareSendBuffer_Join(u8 *arg0) +{ + arg0[0] = CHAT_MESSAGE_JOIN; + StringCopy(&arg0[1], gSaveBlock2Ptr->playerName); + arg0[1 + (PLAYER_NAME_LENGTH + 1)] = sWork->multiplayerId; +} + +static void PrepareSendBuffer_Chat(u8 *arg0) +{ + arg0[0] = CHAT_MESSAGE_CHAT; + StringCopy(&arg0[1], gSaveBlock2Ptr->playerName); + StringCopy(&arg0[1 + (PLAYER_NAME_LENGTH + 1)], sWork->messageEntryBuffer); +} + +static void PrepareSendBuffer_Leave(u8 *arg0) +{ + arg0[0] = CHAT_MESSAGE_LEAVE; + StringCopy(&arg0[1], gSaveBlock2Ptr->playerName); + arg0[1 + (PLAYER_NAME_LENGTH + 1)] = sWork->multiplayerId; + sub_80FB9D0(); +} + +static void PrepareSendBuffer_Drop(u8 *arg0) +{ + arg0[0] = CHAT_MESSAGE_DROP; + StringCopy(&arg0[1], gSaveBlock2Ptr->playerName); + arg0[1 + (PLAYER_NAME_LENGTH + 1)] = sWork->multiplayerId; +} + +static void PrepareSendBuffer_Disband(u8 *arg0) +{ + arg0[0] = CHAT_MESSAGE_DISBAND; + StringCopy(&arg0[1], gSaveBlock2Ptr->playerName); + arg0[1 + (PLAYER_NAME_LENGTH + 1)] = sWork->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 (sWork->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(sWork->hostName, name); + // fall through + case CHAT_MESSAGE_LEAVE: + if (sWork->multiplayerId != *recvMessage) + { + DynamicPlaceholderTextUtil_Reset(); + DynamicPlaceholderTextUtil_SetPlaceholderPtr(0, name); + DynamicPlaceholderTextUtil_ExpandPlaceholders(dest, gText_F700LeftChat); + return TRUE; + } + break; + } + + return FALSE; +} + +u8 GetCurrentKeyboardPage(void) +{ + return sWork->currentPage; +} + +void UnionRoomChat_GetCursorColAndRow(u8 *colp, u8 *rowp) +{ + *colp = sWork->currentCol; + *rowp = sWork->currentRow; +} + +u8 *UnionRoomChat_GetMessageEntryBuffer(void) +{ + return sWork->messageEntryBuffer; +} + +int UnionRoomChat_LenMessageEntryBuffer(void) +{ + u8 *str = UnionRoomChat_GetMessageEntryBuffer(); + return StringLength_Multibyte(str); +} + +void UnionRoomChat_GetBufferSelectionRegion(u32 *startp, u32 *diffp) +{ + int diff = sWork->bufferCursorPos - sWork->lastBufferCursorPos; + if (diff < 0) + { + diff *= -1; + *startp = sWork->bufferCursorPos; + } + else + { + *startp = sWork->lastBufferCursorPos; + } + + *diffp = diff; +} + +u8 *UnionRoomChat_GetEndOfMessageEntryBuffer(void) +{ + int i; + u16 numChars = GetNumCharsInMessageEntryBuffer(); + u8 *str = sWork->messageEntryBuffer; + for (i = 0; i < numChars; i++) + { + if (*str == CHAR_EXTRA_EMOJI) + str++; + + str++; + } + + return str; +} + +// Useless overhead +u16 UnionRoomChat_GetNumCharsInMessageEntryBuffer(void) +{ + u16 count; + u32 i; + u16 numChars = GetNumCharsInMessageEntryBuffer(); + u8 *str = sWork->messageEntryBuffer; + for (count = 0, i = 0; i < numChars; count++, i++) + { + if (*str == CHAR_EXTRA_EMOJI) + str++; + + str++; + } + + return count; +} + +u8 *UnionRoomChat_GetLastReceivedMessage(void) +{ + return sWork->receivedMessage; +} + +u16 UnionRoomChat_GetReceivedPlayerIndex(void) +{ + return sWork->receivedPlayerIndex; +} + +int UnionRoomChat_GetMessageEntryCursorPosition(void) +{ + return sWork->bufferCursorPos; +} + +// This probably does more in the Japanese titles. +int UnionRoomChat_GetWhetherShouldShowCaseToggleIcon(void) +{ + u8 *str = GetPtrToLastCharOfUnk1A(); + u32 character = *str; + if (character > 0xFF || sCaseToggleTable[character] == character || sCaseToggleTable[character] == 0) + return 3; + else + return 0; +} + +u8 *UnionRoomChat_GetNameOfPlayerWhoDisbandedChat(void) +{ + return sWork->hostName; +} + +void UnionRoomChat_InitializeRegisteredTexts(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 (sWork->linkPlayerCount != tLinkPlayerCount) + { + tState = 2; + sWork->linkPlayerCount = tLinkPlayerCount; + return; + } + + tBlockReceivedStatus = GetBlockReceivedStatus(); + if (!tBlockReceivedStatus && GetRfuUnkCE8()) + return; + + tI = 0; + tState = 3; + // fall through + case 3: + // Idle listen + 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(sWork->receivedMessage, (u8 *)gBlockRecvBuffer[tI])) + { + sWork->receivedPlayerIndex = tI; + UnionRoomChat_StartDisplaySubtask(CHATDISPLAYROUTINE_SCROLLCHAT, 2); + tState = 7; + } + else + { + tState = tNextState; + } + + tI++; + break; + case 7: + if (!RunDisplaySubtask(2)) + tState = tNextState; + break; + case 4: + // Someone is leaving + if (sWork->multiplayerId == 0 && tCurrLinkPlayer != 0) + { + // You're the leader, and the person who left is not you + if (GetLinkPlayerCount() == 2) + { + sub_80FA4A8(); + sWork->exitType = CHATEXIT_LEADER_LAST; + DestroyTask(taskId); + return; + } + + sub_80FBD6C(tCurrLinkPlayer); + } + + tState = 3; + break; + case 5: + // Person left + if (sWork->multiplayerId != 0) + sWork->exitType = CHATEXIT_DROPPED; + + DestroyTask(taskId); + break; + case 6: + // The leader disbanded the chat + sWork->exitType = CHATEXIT_DISBANDED; + DestroyTask(taskId); + break; + case 2: + if (!GetRfuUnkCE8()) + { + if (sWork->multiplayerId == 0) + sub_80FB030(sWork->linkPlayerCount); + + tState = 1; + } + break; + } +} + +#undef tNextState +#undef tLinkPlayerCount +#undef tBlockReceivedStatus +#undef tCurrLinkPlayer +#undef tI +#undef tState |