#include "global.h" #include "malloc.h" #include "bard_music.h" #include "bg.h" #include "data.h" #include "decompress.h" #include "dewford_trend.h" #include "dynamic_placeholder_text_util.h" #include "easy_chat.h" #include "event_data.h" #include "event_object_movement.h" #include "field_message_box.h" #include "field_weather.h" #include "gpu_regs.h" #include "graphics.h" #include "international_string_util.h" #include "main.h" #include "mystery_gift.h" #include "menu.h" #include "overworld.h" #include "palette.h" #include "pokedex.h" #include "random.h" #include "sound.h" #include "string_util.h" #include "strings.h" #include "task.h" #include "text_window.h" #include "window.h" #include "constants/easy_chat.h" #include "constants/event_objects.h" #include "constants/lilycove_lady.h" #include "constants/mauville_old_man.h" #include "constants/songs.h" #include "constants/rgb.h" static EWRAM_DATA struct EasyChatScreen *sEasyChatScreen = NULL; static EWRAM_DATA struct EasyChatScreenControl *sScreenControl = NULL; static EWRAM_DATA struct EasyChatScreenWordData *sWordData = NULL; static void Task_InitEasyChatScreen(u8); static void CB2_EasyChatScreen(void); static bool8 InitEasyChatScreen(u8); static void Task_EasyChatScreen(u8); static void ExitEasyChatScreen(MainCallback); static bool32 IsFuncIdForQuizLadyScreen(u16); static void EnterQuizLadyScreen(u16); static bool8 InitEasyChatScreenStruct(u8, u16 *, u8); static void FreeEasyChatScreenStruct(void); static u16 HandleEasyChatInput(void); static u16 HandleEasyChatInput_Phrase(void); static u16 HandleEasyChatInput_MainScreenButtons(void); static u16 HandleEasyChatInput_Keyboard(void); static u16 HandleEasyChatInput_WordSelect(void); static u16 HandleEasyChatInput_ExitPrompt(void); static u16 HandleEasyChatInput_ConfirmWordsYesNo(void); static u16 HandleEasyChatInput_DeleteAllYesNo(void); static u16 HandleEasyChatInput_QuizQuestion(void); static u16 HandleEasyChatInput_WaitForMsg(void); static u16 HandleEasyChatInput_StartConfirmLyrics(void); static u16 HandleEasyChatInput_ConfirmLyricsYesNo(void); static u16 StartConfirmExitPrompt(void); static u16 TryConfirmWords(void); static u8 GetEasyChatScreenFrameId(void); static u8 GetEachChatScreenTemplateId(u8); static void GetQuizTitle(u8 *); static void ClearUnusedField(void); static bool8 InitEasyChatScreenControl(void); static bool8 LoadEasyChatScreen(void); static void FreeEasyChatScreenControl(void); static void StartEasyChatFunction(u16); static bool8 RunEasyChatFunction(void); static bool8 InitEasyChatScreenWordData(void); static void FreeEasyChatScreenWordData(void); static u8 GetNumUnlockedEasyChatGroups(void); static int FooterHasFourOptions(void); static int DoDeleteAllButton(void); static int DoQuizButton(void); static int ExitKeyboardToMainScreen(void); static int SelectKeyboardGroup(void); static int StartSwitchKeyboardMode(void); static int DeleteSelectedWord(void); static u16 MoveKeyboardCursor(int); static u16 MoveWordSelectCursor(u32); static int SelectNewWord(void); static u8 GetEasyChatBackupState(void); static void SaveCurrentPhrase(void); static void SetSpecialEasyChatResult(void); static bool32 GetEasyChatCompleted(void); static void ResetCurrentPhrase(void); static void ResetCurrentPhraseToSaved(void); static int IsQuizQuestionEmpty(void); static int IsQuizAnswerEmpty(void); static bool32 IsCurrentPhraseFull(void); static bool32 IsCurrentPhraseEmpty(void); static u16 GetSelectedGroupIndex(void); static u8 GetUnlockedEasyChatGroupId(u8); static void SetSelectedWordGroup(bool32, u16); static int GetSelectedAlphabetGroupId(void); static u16 GetNumWordsInSelectedGroup(void); static void SetSelectedWord(u16); static u16 GetSelectedWordIndex(void); static u16 GetWordFromSelectedGroup(u16); static bool32 DummyWordCheck(int); static u16 GetWordIndexToReplace(void); static int MoveKeyboardCursor_GroupNames(u32); static int MoveKeyboardCursor_Alphabet(u32); static int MoveKeyboardCursor_ButtonWindow(u32); static void ReduceToValidKeyboardColumn(void); static void SetKeyboardCursorInButtonWindow(void); static bool8 IsSelectedKeyboardIndexInvalid(void); static void SetKeyboardCursorToLastColumn(void); static u8 GetLastAlphabetColumn(u8); static void ReduceToValidWordSelectColumn(void); static bool8 IsSelectedWordIndexInvalid(void); static int DidPlayerInputMysteryGiftPhrase(void); static u16 DidPlayerInputABerryMasterWifePhrase(void); static bool8 InitEasyChatScreenControl_(void); static void LoadEasyChatPalettes(void); static void InitEasyChatBgs(void); static void AdjustBgTilemapForFooter(void); static void BufferFrameTilemap(u16 *); static void AddPhraseWindow(void); static void AddMainScreenButtonWindow(void); static void PrintTitle(void); static void PrintInitialInstructions(void); static void PrintCurrentPhrase(void); static void DrawLowerWindow(void); static void LoadEasyChatGfx(void); static void CreateMainCursorSprite(void); static void SpriteCB_Cursor(struct Sprite *); static void SetWindowDimensions(u8, u8, u8, u8); static void CreateScrollIndicatorSprites(void); static void CreateStartSelectButtonSprites(void); static void TryAddInterviewObjectEvents(void); static bool8 ReprintPhrase(void); static bool8 UpdateMainCursor(void); static bool8 UpdateMainCursorOnButtons(void); static bool8 ShowConfirmDeleteAllPrompt(void); static bool8 ShowConfirmExitPrompt(void); static bool8 ShowConfirmPrompt(void); static bool8 ClosePrompt(void); static bool8 ClosePromptAfterDeleteAll(void); static bool8 OpenKeyboard(void); static bool8 CloseKeyboard(void); static bool8 OpenWordSelect(void); static bool8 CloseWordSelect(void); static bool8 ShowConfirmLyricsPrompt(void); static bool8 ReturnToKeyboard(void); static bool8 UpdateKeyboardCursor(void); static bool8 GroupNamesScrollDown(void); static bool8 GroupNamesScrollUp(void); static bool8 UpdateWordSelectCursor(void); static bool8 WordSelectScrollUp(void); static bool8 WordSelectScrollDown(void); static bool8 WordSelectPageScrollUp(void); static bool8 WordSelectPageScrollDown(void); static bool8 SwitchKeyboardMode(void); static bool8 ShowCreateQuizMsg(void); static bool8 ShowSelectAnswerMsg(void); static bool8 ShowSongTooShortMsg(void); static bool8 ShowCantDeleteLyricsMsg(void); static bool8 ShowCombineTwoWordsMsg(void); static bool8 ShowCantExitMsg(void); static void SetMainCursorPos(u8, u8); static int GetFooterOptionXOffset(int); static void StopMainCursorAnim(void); static void PrintEasyChatStdMessage(u8); static void CreateEasyChatYesNoMenu(u8); static void StartMainCursorAnim(void); static void PrintKeyboardText(void); static void InitLowerWindowAnim(int); static void CreateSideWindowSprites(void); static bool8 ShowSideWindow(void); static void CreateRectangleCursorSprites(void); static void SetScrollIndicatorXPos(bool32); static bool8 UpdateLowerWindowAnim(void); static void UpdateScrollIndicatorsVisibility(void); static void DestroyRectangleCursorSprites(void); static void HideModeWindow(void); static void HideScrollIndicators(void); static void SetModeWindowToTransition(void); static bool8 DestroySideWindowSprites(void); static bool8 IsModeWindowAnimActive(void); static void UpdateModeWindowAnim(void); static void UpdateRectangleCursorPos(void); static void InitLowerWindowScroll(s16, u8); static bool8 UpdateLowerWindowScroll(void); static void ClearWordSelectWindow(void); static void InitLowerWindowText(u32); static void CreateWordSelectCursorSprite(void); static void UpdateStartSelectButtonsVisibility(void); static void DestroyWordSelectCursorSprite(void); static void HideStartSelectButtons(void); static void UpdateWordSelectCursorPos(void); static void PrintWordSelectNextRowDown(void); static void PrintWordSelectNextRowUp(void); static int GetLowerWindowScrollOffset(void); static void PrintWordSelectRowsPageDown(void); static void PrintWordSelectRowsPageUp(void); static void PrintEasyChatTextWithColors(u8, u8, const u8 *, u8, u8, u8, u8, u8, u8); static void ResetLowerWindowScroll(void); static void PrintKeyboardGroupNames(void); static void PrintKeyboardAlphabet(void); static void PrintInitialWordSelectText(void); static const u8 *GetEasyChatWordGroupName(u8); static void PrintWordSelectText(u8, u8); static void EraseWordSelectRows(u8, u8); static void DrawLowerWindowFrame(u8); static void BufferLowerWindowFrame(int, int, int, int); static void SetRectangleCursorPos_GroupMode(s8, s8); static void SetRectangleCursorPos_AlphabetMode(s8, s8); static void SpriteCB_WordSelectCursor(struct Sprite *); static void SetWordSelectCursorPos(u8, u8); static bool8 EasyChatIsNationalPokedexEnabled(void); static u16 GetRandomUnlockedEasyChatPokemon(void); static void SetUnlockedEasyChatGroups(void); static void SetUnlockedWordsByAlphabet(void); static u8 *CopyEasyChatWordPadded(u8 *, u16, u16); static u8 IsEasyChatWordUnlocked(u16); static u16 SetSelectedWordGroup_GroupMode(u16); static u16 SetSelectedWordGroup_AlphabetMode(u16); static bool8 IsEasyChatIndexAndGroupUnlocked(u16, u8); static int IsRestrictedWordSpecies(u16); static void DoQuizAnswerEasyChatScreen(void); static void DoQuizQuestionEasyChatScreen(void); static void DoQuizSetAnswerEasyChatScreen(void); static void DoQuizSetQuestionEasyChatScreen(void); #define PALTAG_TRIANGLE_CURSOR 0 #define PALTAG_RECTANGLE_CURSOR 1 #define PALTAG_MISC_UI 2 #define PALTAG_RS_INTERVIEW_FRAME 3 #define GFXTAG_TRIANGLE_CURSOR 0 #define GFXTAG_RECTANGLE_CURSOR 1 #define GFXTAG_SCROLL_INDICATOR 2 #define GFXTAG_START_SELECT_BUTTONS 3 #define GFXTAG_MODE_WINDOW 4 #define GFXTAG_RS_INTERVIEW_FRAME 5 #define GFXTAG_BUTTON_WINDOW 6 // State values for sEasyChatScreen->inputState // Control which input handler to use in HandleEasyChatInput enum { INPUTSTATE_PHRASE, INPUTSTATE_MAIN_SCREEN_BUTTONS, INPUTSTATE_KEYBOARD, INPUTSTATE_WORD_SELECT, INPUTSTATE_EXIT_PROMPT, INPUTSTATE_DELETE_ALL_YES_NO, INPUTSTATE_CONFIRM_WORDS_YES_NO, INPUTSTATE_QUIZ_QUESTION, INPUTSTATE_WAIT_FOR_MSG, INPUTSTATE_START_CONFIRM_LYRICS, INPUTSTATE_CONFIRM_LYRICS_YES_NO, }; // Task states for the 'main' task, Task_EasyChatScreen enum { MAINSTATE_FADE_IN, MAINSTATE_HANDLE_INPUT, MAINSTATE_RUN_FUNC, MAINSTATE_TO_QUIZ_LADY, MAINSTATE_EXIT, MAINSTATE_WAIT_FADE_IN, }; // IDs provided to PrintEasyChatStdMessage to print a standard message enum { MSG_INSTRUCTIONS, MSG_CONFIRM_DELETE, MSG_CONFIRM_EXIT, MSG_CONFIRM, MSG_CREATE_QUIZ, MSG_SELECT_ANSWER, MSG_SONG_TOO_SHORT, MSG_CANT_DELETE_LYRICS, MSG_COMBINE_TWO_WORDS, MSG_CANT_QUIT, }; // IDs for supplementary Easy Chat functions // Returned by the input handler functions, and run // in the main task (MAINSTATE_RUN_FUNC) enum { ECFUNC_NONE, ECFUNC_REPRINT_PHRASE, ECFUNC_UPDATE_MAIN_CURSOR, ECFUNC_UPDATE_MAIN_CURSOR_ON_BUTTONS, ECFUNC_PROMPT_DELETE_ALL, ECFUNC_PROMPT_EXIT, ECFUNC_PROMPT_CONFIRM, ECFUNC_CLOSE_PROMPT, ECFUNC_CLOSE_PROMPT_AFTER_DELETE, ECFUNC_OPEN_KEYBOARD, ECFUNC_CLOSE_KEYBOARD, ECFUNC_OPEN_WORD_SELECT, ECFUNC_CLOSE_WORD_SELECT, ECFUNC_PROMPT_CONFIRM_LYRICS, ECFUNC_RETURN_TO_KEYBOARD, ECFUNC_UPDATE_KEYBOARD_CURSOR, ECFUNC_GROUP_NAMES_SCROLL_DOWN, ECFUNC_GROUP_NAMES_SCROLL_UP, ECFUNC_UPDATE_WORD_SELECT_CURSOR, ECFUNC_WORD_SELECT_SCROLL_UP, ECFUNC_WORD_SELECT_SCROLL_DOWN, ECFUNC_WORD_SELECT_PAGE_UP, ECFUNC_WORD_SELECT_PAGE_DOWN, ECFUNC_SWITCH_KEYBOARD_MODE, ECFUNC_EXIT, ECFUNC_QUIZ_QUESTION, ECFUNC_QUIZ_ANSWER, ECFUNC_SET_QUIZ_QUESTION, ECFUNC_SET_QUIZ_ANSWER, ECFUNC_MSG_CREATE_QUIZ, ECFUNC_MSG_SELECT_ANSWER, ECFUNC_MSG_SONG_TOO_SHORT, ECFUNC_MSG_CANT_DELETE_LYRICS, ECFUNC_MSG_COMBINE_TWO_WORDS, ECFUNC_MSG_CANT_EXIT, }; // IDs for InitLowerWindowText enum { TEXT_GROUPS, TEXT_ALPHABET, TEXT_WORD_SELECT, }; #define NUM_ALPHABET_ROWS 4 #define NUM_GROUP_NAME_ROWS 4 #define NUM_WORD_SELECT_ROWS 4 #define NUM_BUTTON_ROWS 3 #define NUM_ALPHABET_COLUMNS 7 #define NUM_GROUP_NAME_COLUMNS 2 #define NUM_WORD_SELECT_COLUMNS 2 enum { FRAMEID_GENERAL_2x2, FRAMEID_GENERAL_2x3, FRAMEID_MAIL, FRAMEID_COMBINE_TWO_WORDS, FRAMEID_INTERVIEW_SHOW_PERSON, FRAMEID_INTERVIEW, FRAMEID_QUIZ_ANSWER, FRAMEID_QUIZ_QUESTION, FRAMEID_QUIZ_SET_QUESTION, }; // IDs for the footer row of buttons on the main screen enum { FOOTER_NORMAL, FOOTER_QUIZ, FOOTER_ANSWER, NUM_FOOTER_TYPES }; enum { INPUT_RIGHT, INPUT_LEFT, INPUT_UP, INPUT_DOWN, INPUT_START, INPUT_SELECT, }; // Types of animations for the lower window (keyboard/word select), given to InitLowerWindowAnim enum { WINANIM_OPEN_KEYBOARD, WINANIM_CLOSE_KEYBOARD, WINANIM_OPEN_WORD_SELECT, WINANIM_CLOSE_WORD_SELECT, WINANIM_RETURN_TO_KEYBOARD, WINANIM_KEYBOARD_SWITCH_OUT, WINANIM_KEYBOARD_SWITCH_IN, }; // Values for text frame tilemap #define FRAME_OFFSET_ORANGE 0x1000 // Orange frame, for phrase text #define FRAME_OFFSET_GREEN 0x4000 // Green frame, for keyboard/word select #define FRAME_TILE_TRANSPARENT 0x0 #define FRAME_TILE_TOP_L_CORNER 0x1 #define FRAME_TILE_TOP_EDGE 0x2 #define FRAME_TILE_TOP_R_CORNER 0x3 #define FRAME_TILE_L_EDGE 0x5 #define FRAME_TILE_R_EDGE 0x7 #define FRAME_TILE_BOTTOM_L_CORNER 0x9 #define FRAME_TILE_BOTTOM_EDGE 0xA #define FRAME_TILE_BOTTOM_R_CORNER 0xB struct { u16 funcId; MainCallback callback; } static const sQuizLadyEasyChatScreens[] = { { .funcId = ECFUNC_QUIZ_ANSWER, .callback = DoQuizAnswerEasyChatScreen, }, { .funcId = ECFUNC_QUIZ_QUESTION, .callback = DoQuizQuestionEasyChatScreen, }, { .funcId = ECFUNC_SET_QUIZ_ANSWER, .callback = DoQuizSetAnswerEasyChatScreen, }, { .funcId = ECFUNC_SET_QUIZ_QUESTION, .callback = DoQuizSetQuestionEasyChatScreen, }, }; static const struct EasyChatScreenTemplate sEasyChatScreenTemplates[] = { { .type = EASY_CHAT_TYPE_PROFILE, .numColumns = 2, .numRows = 2, .frameId = FRAMEID_GENERAL_2x2, .fourFooterOptions = FALSE, .titleText = gText_Profile, .instructionsText1 = gText_CombineFourWordsOrPhrases, .instructionsText2 = gText_AndMakeYourProfile, .confirmText1 = gText_YourProfile, .confirmText2 = gText_IsAsShownOkay, }, { .type = EASY_CHAT_TYPE_BATTLE_START, .numColumns = 2, .numRows = 3, .frameId = FRAMEID_GENERAL_2x3, .fourFooterOptions = FALSE, .titleText = gText_AtTheBattlesStart, .instructionsText1 = gText_CombineSixWordsOrPhrases, .instructionsText2 = gText_AndMakeAMessage, .confirmText1 = gText_YourFeelingAtTheBattlesStart, .confirmText2 = gText_IsAsShownOkay, }, { .type = EASY_CHAT_TYPE_BATTLE_WON, .numColumns = 2, .numRows = 3, .frameId = FRAMEID_GENERAL_2x3, .fourFooterOptions = FALSE, .titleText = gText_UponWinningABattle, .instructionsText1 = gText_CombineSixWordsOrPhrases, .instructionsText2 = gText_AndMakeAMessage, .confirmText1 = gText_WhatYouSayIfYouWin, .confirmText2 = gText_IsAsShownOkay, }, { .type = EASY_CHAT_TYPE_BATTLE_LOST, .numColumns = 2, .numRows = 3, .frameId = FRAMEID_GENERAL_2x3, .fourFooterOptions = FALSE, .titleText = gText_UponLosingABattle, .instructionsText1 = gText_CombineSixWordsOrPhrases, .instructionsText2 = gText_AndMakeAMessage, .confirmText1 = gText_WhatYouSayIfYouLose, .confirmText2 = gText_IsAsShownOkay, }, { .type = EASY_CHAT_TYPE_MAIL, .numColumns = 2, .numRows = 5, .frameId = FRAMEID_MAIL, .fourFooterOptions = FALSE, .titleText = NULL, .instructionsText1 = gText_CombineNineWordsOrPhrases, .instructionsText2 = gText_AndMakeAMessage2, .confirmText1 = gText_TheMailMessage, .confirmText2 = gText_IsAsShownOkay, }, { .type = EASY_CHAT_TYPE_INTERVIEW, .numColumns = 2, .numRows = 2, .frameId = FRAMEID_INTERVIEW, .fourFooterOptions = FALSE, .titleText = gText_Interview, .instructionsText1 = gText_CombineFourWordsOrPhrases, .instructionsText2 = gText_LetsReplyToTheInterview, .confirmText1 = gText_TheAnswer, .confirmText2 = gText_IsAsShownOkay, }, { .type = EASY_CHAT_TYPE_BARD_SONG, .numColumns = 2, .numRows = 3, .frameId = FRAMEID_GENERAL_2x3, .fourFooterOptions = FALSE, .titleText = gText_TheBardsSong, .instructionsText1 = gText_ChangeJustOneWordOrPhrase, .instructionsText2 = gText_AndImproveTheBardsSong, .confirmText1 = gText_TheBardsSong2, .confirmText2 = gText_IsAsShownOkay, }, { .type = EASY_CHAT_TYPE_FAN_CLUB, .numColumns = 1, .numRows = 1, .frameId = FRAMEID_INTERVIEW_SHOW_PERSON, .fourFooterOptions = FALSE, .titleText = gText_Interview, .instructionsText1 = gText_FindWordsThatDescribeYour, .instructionsText2 = gText_FeelingsRightNow, .confirmText1 = gText_TheAnswer, .confirmText2 = gText_IsAsShownOkay, }, { .type = EASY_CHAT_TYPE_TRENDY_PHRASE, .numColumns = 2, .numRows = 1, .frameId = FRAMEID_COMBINE_TWO_WORDS, .fourFooterOptions = FALSE, .titleText = gText_WhatsHipAndHappening, .instructionsText1 = gText_CombineTwoWordsOrPhrases, .instructionsText2 = gText_AndMakeATrendySaying, .confirmText1 = gText_TheTrendySaying, .confirmText2 = gText_IsAsShownOkay, }, { .type = EASY_CHAT_TYPE_QUIZ_QUESTION, .numColumns = 2, .numRows = 5, .frameId = FRAMEID_QUIZ_QUESTION, .fourFooterOptions = TRUE, .titleText = NULL, .instructionsText1 = gText_AfterYouHaveReadTheQuiz, .instructionsText2 = gText_QuestionPressTheAButton, .confirmText1 = NULL, .confirmText2 = NULL, }, { .type = EASY_CHAT_TYPE_QUIZ_ANSWER, .numColumns = 1, .numRows = 1, .frameId = FRAMEID_QUIZ_ANSWER, .fourFooterOptions = TRUE, .titleText = gText_TheQuizAnswerIs, .instructionsText1 = gText_OutOfTheListedChoices, .instructionsText2 = gText_SelectTheAnswerToTheQuiz, .confirmText1 = gText_TheAnswerColon, .confirmText2 = gText_IsAsShownOkay, }, { .type = EASY_CHAT_TYPE_QUIZ_SET_QUESTION, .numColumns = 2, .numRows = 5, .frameId = FRAMEID_QUIZ_SET_QUESTION, .fourFooterOptions = TRUE, .titleText = NULL, .instructionsText1 = gText_CombineNineWordsOrPhrases, .instructionsText2 = gText_AndCreateAQuiz, .confirmText1 = gText_IsThisQuizOK, .confirmText2 = NULL, }, { .type = EASY_CHAT_TYPE_QUIZ_SET_ANSWER, .numColumns = 1, .numRows = 1, .frameId = FRAMEID_QUIZ_ANSWER, .fourFooterOptions = TRUE, .titleText = gText_TheQuizAnswerIs, .instructionsText1 = gText_PickAWordOrPhraseAnd, .instructionsText2 = gText_SetTheQuizAnswer, .confirmText1 = gText_IsThisQuizOK, .confirmText2 = NULL, }, { .type = EASY_CHAT_TYPE_BARD_SONG, .numColumns = 2, .numRows = 3, .frameId = FRAMEID_GENERAL_2x3, .fourFooterOptions = FALSE, .titleText = gText_TheBardsSong, .instructionsText1 = gText_ChangeJustOneWordOrPhrase, .instructionsText2 = gText_AndImproveTheBardsSong, .confirmText1 = gText_TheBardsSong2, .confirmText2 = gText_IsAsShownOkay, }, { .type = EASY_CHAT_TYPE_APPRENTICE, .numColumns = 2, .numRows = 3, .frameId = FRAMEID_GENERAL_2x3, .fourFooterOptions = FALSE, .titleText = gText_ApprenticesPhrase, .instructionsText1 = gText_FindWordsWhichFit, .instructionsText2 = gText_TheTrainersImage, .confirmText1 = gText_ApprenticePhrase, .confirmText2 = gText_IsAsShownOkay, }, { .type = EASY_CHAT_TYPE_GOOD_SAYING, .numColumns = 2, .numRows = 1, .frameId = FRAMEID_COMBINE_TWO_WORDS, .fourFooterOptions = FALSE, .titleText = gText_GoodSaying, .instructionsText1 = gText_CombineTwoWordsOrPhrases2, .instructionsText2 = gText_ToTeachHerAGoodSaying, .confirmText1 = gText_TheAnswer, .confirmText2 = gText_IsAsShownOkay, }, { .type = EASY_CHAT_TYPE_GABBY_AND_TY, .numColumns = 1, .numRows = 1, .frameId = FRAMEID_INTERVIEW_SHOW_PERSON, .fourFooterOptions = FALSE, .titleText = gText_Interview, .instructionsText1 = gText_FindWordsThatDescribeYour, .instructionsText2 = gText_FeelingsRightNow, .confirmText1 = gText_TheAnswer, .confirmText2 = gText_IsAsShownOkay, }, { .type = EASY_CHAT_TYPE_BATTLE_TOWER_INTERVIEW, .numColumns = 1, .numRows = 1, .frameId = FRAMEID_INTERVIEW_SHOW_PERSON, .fourFooterOptions = FALSE, .titleText = gText_Interview, .instructionsText1 = gText_FindWordsThatDescribeYour, .instructionsText2 = gText_FeelingsRightNow, .confirmText1 = gText_TheAnswer, .confirmText2 = gText_IsAsShownOkay, }, { .type = EASY_CHAT_TYPE_CONTEST_INTERVIEW, .numColumns = 1, .numRows = 1, .frameId = FRAMEID_INTERVIEW_SHOW_PERSON, .fourFooterOptions = FALSE, .titleText = gText_Interview, .instructionsText1 = gText_FindWordsThatDescribeYour, .instructionsText2 = gText_FeelingsRightNow, .confirmText1 = gText_TheAnswer, .confirmText2 = gText_IsAsShownOkay, }, { .type = EASY_CHAT_TYPE_FAN_QUESTION, .numColumns = 1, .numRows = 1, .frameId = FRAMEID_INTERVIEW_SHOW_PERSON, .fourFooterOptions = FALSE, .titleText = gText_FansQuestion, .instructionsText1 = gText_FindWordsWhichFit, .instructionsText2 = gText_TheTrainersImage, .confirmText1 = gText_TheImage, .confirmText2 = gText_IsAsShownOkay, }, { .type = EASY_CHAT_TYPE_QUESTIONNAIRE, .numColumns = 2, .numRows = 2, .frameId = FRAMEID_GENERAL_2x2, .fourFooterOptions = FALSE, .titleText = gText_Questionnaire, .instructionsText1 = gText_CombineFourWordsOrPhrases, .instructionsText2 = gText_AndFillOutTheQuestionnaire, .confirmText1 = gText_TheAnswer, .confirmText2 = gText_IsAsShownOkay, }, }; // IDs are used indirectly as indexes into gEasyChatWordsByLetterPointers // 0 is 'Others', 1-26 are the letters A-Z // This array maps the group IDs to the alphabet keyboard static const u8 sAlphabetGroupIdMap[NUM_ALPHABET_ROWS][NUM_ALPHABET_COLUMNS] = { { 1, 2, 3, 4, 5, 6, 0}, { 7, 8, 9, 10, 11, 12, 0}, {13, 14, 15, 16, 17, 18, 19}, {20, 21, 22, 23, 24, 25, 26}, }; static const u16 sMysteryGiftPhrase[NUM_QUESTIONNAIRE_WORDS] = { EC_WORD_LINK, EC_WORD_TOGETHER, EC_WORD_WITH, EC_WORD_ALL, }; static const u16 sBerryMasterWifePhrases[][2] = { [PHRASE_GREAT_BATTLE - 1] = {EC_WORD_GREAT, EC_WORD_BATTLE}, [PHRASE_CHALLENGE_CONTEST - 1] = {EC_WORD_CHALLENGE, EC_WORD_CONTEST}, [PHRASE_OVERWHELMING_LATIAS - 1] = {EC_WORD_OVERWHELMING, EC_POKEMON(LATIAS)}, [PHRASE_COOL_LATIOS - 1] = {EC_WORD_COOL, EC_POKEMON(LATIOS)}, [PHRASE_SUPER_HUSTLE - 1] = {EC_WORD_SUPER, EC_WORD_HUSTLE}, }; static const u16 sTriangleCursor_Pal[] = INCBIN_U16("graphics/easy_chat/triangle_cursor.gbapal"); static const u32 sTriangleCursor_Gfx[] = INCBIN_U32("graphics/easy_chat/triangle_cursor.4bpp"); static const u32 sScrollIndicator_Gfx[] = INCBIN_U32("graphics/easy_chat/scroll_indicator.4bpp"); static const u32 sStartSelectButtons_Gfx[] = INCBIN_U32("graphics/easy_chat/start_select_buttons.4bpp"); // In Ruby/Sapphire Easy Chat screens had a black background, and when the player & interviewer were present // on screen the interview_frame gfx was shown behind them. // In Emerald all Easy Chat screens have a filled background, so these gfx go unused static const u16 sRSInterviewFrame_Pal[] = INCBIN_U16("graphics/easy_chat/interview_frame.gbapal"); static const u32 sRSInterviewFrame_Gfx[] = INCBIN_U32("graphics/easy_chat/interview_frame.4bpp.lz"); static const u16 sTextInputFrameOrange_Pal[] = INCBIN_U16("graphics/easy_chat/text_input_frame_orange.gbapal"); static const u16 sTextInputFrameGreen_Pal[] = INCBIN_U16("graphics/easy_chat/text_input_frame_green.gbapal"); static const u32 sTextInputFrame_Gfx[] = INCBIN_U32("graphics/easy_chat/text_input_frame.4bpp.lz"); static const u16 sTitleText_Pal[] = INCBIN_U16("graphics/easy_chat/title_text.gbapal"); static const u16 sText_Pal[] = INCBIN_U16("graphics/easy_chat/text.gbapal"); static const struct EasyChatPhraseFrameDimensions sPhraseFrameDimensions[] = { [FRAMEID_GENERAL_2x2] = { .left = 3, .top = 4, .width = 24, .height = 4, .footerId = FOOTER_NORMAL, }, [FRAMEID_GENERAL_2x3] = { .left = 3, .top = 3, .width = 24, .height = 6, .footerId = FOOTER_NORMAL, }, [FRAMEID_MAIL] = { .left = 3, .top = 0, .width = 24, .height = 10, .footerId = FOOTER_NORMAL, }, [FRAMEID_COMBINE_TWO_WORDS] = { .left = 3, .top = 5, .width = 24, .height = 2, .footerId = FOOTER_NORMAL, }, [FRAMEID_INTERVIEW_SHOW_PERSON] = { .left = 16, .top = 5, .width = 12, .height = 2, .footerId = FOOTER_NORMAL, }, [FRAMEID_INTERVIEW] = { .left = 3, .top = 4, .width = 24, .height = 4, .footerId = FOOTER_NORMAL, }, [FRAMEID_QUIZ_ANSWER] = { .left = 9, .top = 4, .width = 12, .height = 2, .footerId = FOOTER_QUIZ, }, [FRAMEID_QUIZ_QUESTION] = { .left = 5, .top = 3, .width = 20, .height = 10, .footerId = NUM_FOOTER_TYPES, }, [FRAMEID_QUIZ_SET_QUESTION] = { .left = 3, .top = 0, .width = 24, .height = 10, .footerId = FOOTER_ANSWER, }, }; static const struct BgTemplate sEasyChatBgTemplates[] = { { .bg = 0, .charBaseIndex = 0, .mapBaseIndex = 28, .screenSize = 0, .paletteMode = 0, .priority = 0, .baseTile = 0, }, { .bg = 1, .charBaseIndex = 3, .mapBaseIndex = 29, .screenSize = 0, .paletteMode = 0, .priority = 1, .baseTile = 0, }, { .bg = 2, .charBaseIndex = 0, .mapBaseIndex = 30, .screenSize = 0, .paletteMode = 0, .priority = 2, .baseTile = 0x80, }, { .bg = 3, .charBaseIndex = 2, .mapBaseIndex = 31, .screenSize = 0, .paletteMode = 0, .priority = 3, .baseTile = 0, }, }; static const struct WindowTemplate sEasyChatWindowTemplates[] = { { .bg = 1, .tilemapLeft = 6, .tilemapTop = 0, .width = 18, .height = 2, .paletteNum = 10, .baseBlock = 0x10, }, { .bg = 0, .tilemapLeft = 3, .tilemapTop = 15, .width = 24, .height = 4, .paletteNum = 15, .baseBlock = 0xA, }, { .bg = 2, .tilemapLeft = 1, .tilemapTop = 0, .width = 28, .height = 32, .paletteNum = 3, .baseBlock = 0, }, DUMMY_WIN_TEMPLATE, }; static const struct WindowTemplate sEasyChatYesNoWindowTemplate = { .bg = 0, .tilemapLeft = 22, .tilemapTop = 9, .width = 5, .height = 4, .paletteNum = 15, .baseBlock = 0x6A, }; static const u8 sText_Clear17[] = _("{CLEAR 17}"); static const u8 *const sEasyChatKeyboardAlphabet[NUM_ALPHABET_ROWS] = { gText_EasyChatKeyboard_ABCDEFothers, gText_EasyChatKeyboard_GHIJKL, gText_EasyChatKeyboard_MNOPQRS, gText_EasyChatKeyboard_TUVWXYZ, }; static const struct SpriteSheet sSpriteSheets[] = { { .data = sTriangleCursor_Gfx, .size = 0x20, .tag = GFXTAG_TRIANGLE_CURSOR }, { .data = sScrollIndicator_Gfx, .size = 0x100, .tag = GFXTAG_SCROLL_INDICATOR }, { .data = sStartSelectButtons_Gfx, .size = 0x100, .tag = GFXTAG_START_SELECT_BUTTONS }, {0} }; static const struct SpritePalette sSpritePalettes[] = { { .data = sTriangleCursor_Pal, .tag = PALTAG_TRIANGLE_CURSOR, }, { .data = gEasyChatRectangleCursor_Pal, .tag = PALTAG_RECTANGLE_CURSOR, }, { .data = gEasyChatButtonWindow_Pal, .tag = PALTAG_MISC_UI, // The palette is generated from the button window but used for various parts of the UI }, { .data = sRSInterviewFrame_Pal, .tag = PALTAG_RS_INTERVIEW_FRAME, }, {0} }; static const struct CompressedSpriteSheet sCompressedSpriteSheets[] = { { .data = sRSInterviewFrame_Gfx, .size = 0x800, .tag = GFXTAG_RS_INTERVIEW_FRAME, }, { .data = gEasyChatRectangleCursor_Gfx, .size = 0x1000, .tag = GFXTAG_RECTANGLE_CURSOR, }, { .data = gEasyChatButtonWindow_Gfx, .size = 0x800, .tag = GFXTAG_BUTTON_WINDOW, }, { .data = gEasyChatMode_Gfx, .size = 0x1000, .tag = GFXTAG_MODE_WINDOW, }, }; static const u8 sAlphabetKeyboardColumnOffsets[NUM_ALPHABET_COLUMNS] = {0, 12, 24, 56, 68, 80, 92}; static const struct OamData sOamData_TriangleCursor = { .y = 0, .affineMode = ST_OAM_AFFINE_OFF, .objMode = ST_OAM_OBJ_NORMAL, .mosaic = 0, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(8x8), .x = 0, .matrixNum = 0, .size = SPRITE_SIZE(8x8), .tileNum = 0, .priority = 3, .paletteNum = 0, .affineParam = 0, }; static const struct SpriteTemplate sSpriteTemplate_TriangleCursor = { .tileTag = PALTAG_TRIANGLE_CURSOR, .paletteTag = GFXTAG_TRIANGLE_CURSOR, .oam = &sOamData_TriangleCursor, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCB_Cursor, }; static const struct OamData sOamData_RectangleCursor = { .y = 0, .affineMode = ST_OAM_AFFINE_OFF, .objMode = ST_OAM_OBJ_NORMAL, .mosaic = 0, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(64x32), .x = 0, .matrixNum = 0, .size = SPRITE_SIZE(64x32), .tileNum = 0, .priority = 1, .paletteNum = 0, .affineParam = 0, }; static const union AnimCmd sAnim_RectangleCursor_OnGroup[] = { ANIMCMD_FRAME(0, 0), ANIMCMD_END, }; static const union AnimCmd sAnim_RectangleCursor_OnButton[] = { ANIMCMD_FRAME(32, 0), ANIMCMD_END, }; static const union AnimCmd sAnim_RectangleCursor_OnOthers[] = { ANIMCMD_FRAME(64, 0), ANIMCMD_END, }; static const union AnimCmd sAnim_RectangleCursor_OnLetter[] = { ANIMCMD_FRAME(96, 0), ANIMCMD_END, }; // Each anim changes the width of the rectangle cursor to fit what it should be selecting enum { RECTCURSOR_ANIM_ON_GROUP, RECTCURSOR_ANIM_ON_BUTTON, RECTCURSOR_ANIM_ON_OTHERS, RECTCURSOR_ANIM_ON_LETTER, }; static const union AnimCmd *const sAnims_RectangleCursor[] = { [RECTCURSOR_ANIM_ON_GROUP] = sAnim_RectangleCursor_OnGroup, [RECTCURSOR_ANIM_ON_BUTTON] = sAnim_RectangleCursor_OnButton, [RECTCURSOR_ANIM_ON_OTHERS] = sAnim_RectangleCursor_OnOthers, [RECTCURSOR_ANIM_ON_LETTER] = sAnim_RectangleCursor_OnLetter, }; static const struct SpriteTemplate sSpriteTemplate_RectangleCursor = { .tileTag = GFXTAG_RECTANGLE_CURSOR, .paletteTag = PALTAG_RECTANGLE_CURSOR, .oam = &sOamData_RectangleCursor, .anims = sAnims_RectangleCursor, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCB_Cursor, }; static const struct OamData sOamData_ModeWindow = { .y = 0, .affineMode = ST_OAM_AFFINE_OFF, .objMode = ST_OAM_OBJ_NORMAL, .mosaic = 0, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(64x32), .x = 0, .matrixNum = 0, .size = SPRITE_SIZE(64x32), .tileNum = 0, .priority = 1, .paletteNum = 0, .affineParam = 0, }; static const union AnimCmd sAnim_ModeWindow_Hidden[] = { ANIMCMD_FRAME(96, 0), ANIMCMD_END, }; static const union AnimCmd sAnim_ModeWindow_ToGroup[] = { ANIMCMD_FRAME(64, 4), // Transition frame ANIMCMD_FRAME(32, 4), // 'Group' frame ANIMCMD_END, }; static const union AnimCmd sAnim_ModeWindow_ToAlphabet[] = { ANIMCMD_FRAME(64, 4), // Transition frame ANIMCMD_FRAME(0, 4), // 'A-Z' frame ANIMCMD_END, }; static const union AnimCmd sAnim_ModeWindow_ToHidden[] = { ANIMCMD_FRAME(64, 4), // Transition frame ANIMCMD_FRAME(96, 0), // Hidden frame ANIMCMD_END, }; static const union AnimCmd sAnim_ModeWindow_Transition[] = { ANIMCMD_FRAME(64, 4), ANIMCMD_END, }; enum { MODEWINDOW_ANIM_HIDDEN, MODEWINDOW_ANIM_TO_GROUP, MODEWINDOW_ANIM_TO_ALPHABET, MODEWINDOW_ANIM_TO_HIDDEN, MODEWINDOW_ANIM_TRANSITION, }; static const union AnimCmd *const sAnims_ModeWindow[] = { [MODEWINDOW_ANIM_HIDDEN] = sAnim_ModeWindow_Hidden, [MODEWINDOW_ANIM_TO_GROUP] = sAnim_ModeWindow_ToGroup, [MODEWINDOW_ANIM_TO_ALPHABET] = sAnim_ModeWindow_ToAlphabet, [MODEWINDOW_ANIM_TO_HIDDEN] = sAnim_ModeWindow_ToHidden, [MODEWINDOW_ANIM_TRANSITION] = sAnim_ModeWindow_Transition, }; static const struct SpriteTemplate sSpriteTemplate_ModeWindow = { .tileTag = GFXTAG_MODE_WINDOW, .paletteTag = PALTAG_MISC_UI, .oam = &sOamData_ModeWindow, .anims = sAnims_ModeWindow, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy, }; static const struct OamData sOamData_ButtonWindow = { .y = 0, .affineMode = ST_OAM_AFFINE_OFF, .objMode = ST_OAM_OBJ_NORMAL, .mosaic = 0, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(64x64), .x = 0, .matrixNum = 0, .size = SPRITE_SIZE(64x64), .tileNum = 0, .priority = 3, .paletteNum = 0, .affineParam = 0, }; static const struct SpriteTemplate sSpriteTemplate_ButtonWindow = { .tileTag = GFXTAG_BUTTON_WINDOW, .paletteTag = PALTAG_MISC_UI, .oam = &sOamData_ButtonWindow, .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy, }; static const struct OamData sOamData_StartSelectButton = { .y = 0, .affineMode = ST_OAM_AFFINE_OFF, .objMode = ST_OAM_OBJ_NORMAL, .mosaic = 0, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(32x8), .x = 0, .matrixNum = 0, .size = SPRITE_SIZE(32x8), .tileNum = 0, .priority = 1, .paletteNum = 0, .affineParam = 0, }; static const struct OamData sOamData_ScrollIndicator = { .y = 0, .affineMode = ST_OAM_AFFINE_OFF, .objMode = ST_OAM_OBJ_NORMAL, .mosaic = 0, .bpp = ST_OAM_4BPP, .shape = SPRITE_SHAPE(16x16), .x = 0, .matrixNum = 0, .size = SPRITE_SIZE(16x16), .tileNum = 0, .priority = 1, .paletteNum = 0, .affineParam = 0, }; static const union AnimCmd sAnim_Frame0[] = { ANIMCMD_FRAME(0, 0), ANIMCMD_END, }; static const union AnimCmd sAnim_Frame1[] = { ANIMCMD_FRAME(4, 0), ANIMCMD_END, }; // Frame0 is Start button, Frame1 is Select button, both are identical for the scroll indicators static const union AnimCmd *const sAnims_TwoFrame[] = { sAnim_Frame0, sAnim_Frame1, }; static const struct SpriteTemplate sSpriteTemplate_StartSelectButton = { .tileTag = GFXTAG_START_SELECT_BUTTONS, .paletteTag = PALTAG_MISC_UI, .oam = &sOamData_StartSelectButton, .anims = sAnims_TwoFrame, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy, }; static const struct SpriteTemplate sSpriteTemplate_ScrollIndicator = { .tileTag = GFXTAG_SCROLL_INDICATOR, .paletteTag = PALTAG_MISC_UI, .oam = &sOamData_ScrollIndicator, .anims = sAnims_TwoFrame, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, .callback = SpriteCallbackDummy, }; static const u8 sFooterOptionXOffsets[NUM_FOOTER_TYPES][4] = { [FOOTER_NORMAL] = {16, 111, 196, 0}, [FOOTER_QUIZ] = {16, 78, 130, 160}, [FOOTER_ANSWER] = {16, 80, 134, 170}, }; static const u8 *const sFooterTextOptions[NUM_FOOTER_TYPES][4] = { [FOOTER_NORMAL] = {gText_DelAll, gText_Cancel5, gText_Ok2, NULL}, [FOOTER_QUIZ] = {gText_DelAll, gText_Cancel5, gText_Ok2, gText_Quiz}, [FOOTER_ANSWER] = {gText_DelAll, gText_Cancel5, gText_Ok2, gText_Answer}, }; #include "data/easy_chat/easy_chat_groups.h" #include "data/easy_chat/easy_chat_words_by_letter.h" static const u8 *const sEasyChatGroupNamePointers[EC_NUM_GROUPS] = { [EC_GROUP_POKEMON] = gEasyChatGroupName_Pokemon, [EC_GROUP_TRAINER] = gEasyChatGroupName_Trainer, [EC_GROUP_STATUS] = gEasyChatGroupName_Status, [EC_GROUP_BATTLE] = gEasyChatGroupName_Battle, [EC_GROUP_GREETINGS] = gEasyChatGroupName_Greetings, [EC_GROUP_PEOPLE] = gEasyChatGroupName_People, [EC_GROUP_VOICES] = gEasyChatGroupName_Voices, [EC_GROUP_SPEECH] = gEasyChatGroupName_Speech, [EC_GROUP_ENDINGS] = gEasyChatGroupName_Endings, [EC_GROUP_FEELINGS] = gEasyChatGroupName_Feelings, [EC_GROUP_CONDITIONS] = gEasyChatGroupName_Conditions, [EC_GROUP_ACTIONS] = gEasyChatGroupName_Actions, [EC_GROUP_LIFESTYLE] = gEasyChatGroupName_Lifestyle, [EC_GROUP_HOBBIES] = gEasyChatGroupName_Hobbies, [EC_GROUP_TIME] = gEasyChatGroupName_Time, [EC_GROUP_MISC] = gEasyChatGroupName_Misc, [EC_GROUP_ADJECTIVES] = gEasyChatGroupName_Adjectives, [EC_GROUP_EVENTS] = gEasyChatGroupName_Events, [EC_GROUP_MOVE_1] = gEasyChatGroupName_Move1, [EC_GROUP_MOVE_2] = gEasyChatGroupName_Move2, [EC_GROUP_TRENDY_SAYING] = gEasyChatGroupName_TrendySaying, [EC_GROUP_POKEMON_NATIONAL] = gEasyChatGroupName_Pokemon2, }; static const u16 sDefaultProfileWords[EASY_CHAT_BATTLE_WORDS_COUNT - 2] = { EC_WORD_I_AM, EC_WORD_A, EC_WORD_POKEMON, EC_WORD_FRIEND, }; static const u16 sDefaultBattleStartWords[EASY_CHAT_BATTLE_WORDS_COUNT] = { EC_WORD_ARE, EC_WORD_YOU, EC_WORD_READY, EC_WORD_QUES, EC_WORD_HERE_I_COME, EC_WORD_EXCL, }; static const u16 sDefaultBattleWonWords[EASY_CHAT_BATTLE_WORDS_COUNT] = { EC_WORD_YAY, EC_WORD_YAY, EC_WORD_EXCL_EXCL, EC_WORD_I_VE, EC_WORD_WON, EC_WORD_EXCL_EXCL, }; static const u16 sDefaultBattleLostWords[EASY_CHAT_BATTLE_WORDS_COUNT] = { EC_WORD_TOO, EC_WORD_BAD, EC_WORD_ELLIPSIS, EC_WORD_WE, EC_WORD_LOST, EC_WORD_ELLIPSIS, }; static const u16 sRestrictedWordSpecies[] = { SPECIES_DEOXYS, }; // In addition to the task defines below, these two elements // have their indexes used explicitly because they are 4-byte // pointers, and occupy the next data element as well. // SetWordTaskArg/GetWordTaskArg use these defines to // read the pointer from the two elements #define TASKIDX_WORDS 2 #define TASKIDX_EXIT_CALLBACK 4 #define tState data[0] #define tType data[1] #define tWords data[TASKIDX_WORDS] // Occupies 2 and 3 #define tExitCallback data[TASKIDX_EXIT_CALLBACK] // Occupies 4 and 5 #define tFuncId data[6] #define tPersonType data[7] void DoEasyChatScreen(u8 type, u16 *words, MainCallback exitCallback, u8 displayedPersonType) { u8 taskId; ResetTasks(); taskId = CreateTask(Task_InitEasyChatScreen, 0); gTasks[taskId].tType = type; gTasks[taskId].tPersonType = displayedPersonType; SetWordTaskArg(taskId, TASKIDX_WORDS, (u32)words); SetWordTaskArg(taskId, TASKIDX_EXIT_CALLBACK, (u32)exitCallback); SetMainCallback2(CB2_EasyChatScreen); } static void CB2_EasyChatScreen(void) { RunTasks(); AnimateSprites(); BuildOamBuffer(); UpdatePaletteFade(); } static void VBlankCB_EasyChatScreen(void) { TransferPlttBuffer(); LoadOam(); ProcessSpriteCopyRequests(); } static void StartEasyChatScreen(u8 taskId, TaskFunc taskFunc) { gTasks[taskId].func = taskFunc; gTasks[taskId].tState = MAINSTATE_FADE_IN; } static void Task_InitEasyChatScreen(u8 taskId) { if (!IsOverworldLinkActive()) { while (InitEasyChatScreen(taskId)); } else { if (InitEasyChatScreen(taskId) == TRUE) return; } StartEasyChatScreen(taskId, Task_EasyChatScreen); } // After loading, this is the 'main' Easy Chat task static void Task_EasyChatScreen(u8 taskId) { u16 funcId; s16 *data; data = gTasks[taskId].data; switch (tState) { case MAINSTATE_FADE_IN: SetVBlankCallback(VBlankCB_EasyChatScreen); BlendPalettes(PALETTES_ALL, 16, 0); BeginNormalPaletteFade(PALETTES_ALL, -1, 16, 0, RGB_BLACK); tState = MAINSTATE_WAIT_FADE_IN; break; case MAINSTATE_HANDLE_INPUT: funcId = HandleEasyChatInput(); if (IsFuncIdForQuizLadyScreen(funcId)) { // Fade to Quiz Lady screen BeginNormalPaletteFade(PALETTES_ALL, -2, 0, 16, RGB_BLACK); tState = MAINSTATE_TO_QUIZ_LADY; tFuncId = funcId; } else if (funcId == ECFUNC_EXIT) { // Fade and exit Easy Chat BeginNormalPaletteFade(PALETTES_ALL, -1, 0, 16, RGB_BLACK); tState = MAINSTATE_EXIT; } else if (funcId != ECFUNC_NONE) { PlaySE(SE_SELECT); StartEasyChatFunction(funcId); tState++; // MAINSTATE_RUN_FUNC } break; case MAINSTATE_RUN_FUNC: if (!RunEasyChatFunction()) tState = MAINSTATE_HANDLE_INPUT; break; case MAINSTATE_TO_QUIZ_LADY: if (!gPaletteFade.active) EnterQuizLadyScreen(tFuncId); break; case MAINSTATE_EXIT: if (!gPaletteFade.active) ExitEasyChatScreen((MainCallback)GetWordTaskArg(taskId, TASKIDX_EXIT_CALLBACK)); break; case MAINSTATE_WAIT_FADE_IN: if (!gPaletteFade.active) tState = MAINSTATE_HANDLE_INPUT; break; } } // Returns TRUE if still initializing, FALSE when finished // If an allocation fails it will switch to the exit callback static bool8 InitEasyChatScreen(u8 taskId) { s16 *data; data = gTasks[taskId].data; switch (tState) { case 0: SetVBlankCallback(NULL); ResetSpriteData(); FreeAllSpritePalettes(); ResetPaletteFade(); break; case 1: if (!InitEasyChatScreenWordData()) { // Alloc failed, exit ExitEasyChatScreen((MainCallback)GetWordTaskArg(taskId, TASKIDX_EXIT_CALLBACK)); } break; case 2: if (!InitEasyChatScreenStruct(tType, (u16 *)GetWordTaskArg(taskId, TASKIDX_WORDS), tPersonType)) { // Alloc failed, exit ExitEasyChatScreen((MainCallback)GetWordTaskArg(taskId, TASKIDX_EXIT_CALLBACK)); } break; case 3: if (!InitEasyChatScreenControl()) { // Alloc failed, exit ExitEasyChatScreen((MainCallback)GetWordTaskArg(taskId, TASKIDX_EXIT_CALLBACK)); } break; case 4: if (LoadEasyChatScreen()) { return TRUE; } break; default: return FALSE; } tState++; return TRUE; } static void ExitEasyChatScreen(MainCallback callback) { FreeEasyChatScreenControl(); FreeEasyChatScreenStruct(); FreeEasyChatScreenWordData(); FreeAllWindowBuffers(); SetMainCallback2(callback); } void ShowEasyChatScreen(void) { int i; u16 *words; struct MauvilleManBard *bard; u8 displayedPersonType = EASY_CHAT_PERSON_DISPLAY_NONE; switch (gSpecialVar_0x8004) { case EASY_CHAT_TYPE_PROFILE: words = gSaveBlock1Ptr->easyChatProfile; break; case EASY_CHAT_TYPE_BATTLE_START: words = gSaveBlock1Ptr->easyChatBattleStart; break; case EASY_CHAT_TYPE_BATTLE_WON: words = gSaveBlock1Ptr->easyChatBattleWon; break; case EASY_CHAT_TYPE_BATTLE_LOST: words = gSaveBlock1Ptr->easyChatBattleLost; break; case EASY_CHAT_TYPE_MAIL: words = gSaveBlock1Ptr->mail[gSpecialVar_0x8005].words; break; case EASY_CHAT_TYPE_BARD_SONG: bard = &gSaveBlock1Ptr->oldMan.bard; for (i = 0; i < BARD_SONG_LENGTH; i ++) bard->temporaryLyrics[i] = bard->songLyrics[i]; words = bard->temporaryLyrics; break; case EASY_CHAT_TYPE_INTERVIEW: words = gSaveBlock1Ptr->tvShows[gSpecialVar_0x8005].bravoTrainer.words; displayedPersonType = gSpecialVar_0x8006; break; case EASY_CHAT_TYPE_FAN_CLUB: words = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8005].fanclubOpinions.words[gSpecialVar_0x8006]; displayedPersonType = EASY_CHAT_PERSON_REPORTER_FEMALE; break; case EASY_CHAT_TYPE_DUMMY_SHOW: words = gSaveBlock1Ptr->tvShows[gSpecialVar_0x8005].dummy.words; displayedPersonType = EASY_CHAT_PERSON_REPORTER_MALE; break; case EASY_CHAT_TYPE_TRENDY_PHRASE: words = (u16 *)gStringVar3; words[0] = gSaveBlock1Ptr->dewfordTrends[0].words[0]; words[1] = gSaveBlock1Ptr->dewfordTrends[0].words[1]; break; case EASY_CHAT_TYPE_GABBY_AND_TY: words = gSaveBlock1Ptr->gabbyAndTyData.quote; *words = EC_EMPTY_WORD; displayedPersonType = EASY_CHAT_PERSON_REPORTER_FEMALE; break; case EASY_CHAT_TYPE_CONTEST_INTERVIEW: words = &gSaveBlock1Ptr->tvShows[gSpecialVar_0x8005].bravoTrainer.words[gSpecialVar_0x8006]; displayedPersonType = EASY_CHAT_PERSON_REPORTER_MALE; break; case EASY_CHAT_TYPE_BATTLE_TOWER_INTERVIEW: words = gSaveBlock1Ptr->tvShows[gSpecialVar_0x8005].fanclubOpinions.words18; displayedPersonType = EASY_CHAT_PERSON_REPORTER_FEMALE; break; case EASY_CHAT_TYPE_GOOD_SAYING: words = (u16 *)gStringVar3; InitializeEasyChatWordArray(words, 2); break; case EASY_CHAT_TYPE_FAN_QUESTION: words = gSaveBlock1Ptr->tvShows[gSpecialVar_0x8005].fanClubSpecial.words; words[0] = EC_EMPTY_WORD; displayedPersonType = EASY_CHAT_PERSON_BOY; break; case EASY_CHAT_TYPE_QUIZ_ANSWER: words = &gSaveBlock1Ptr->lilycoveLady.quiz.playerAnswer; break; case EASY_CHAT_TYPE_QUIZ_QUESTION: return; case EASY_CHAT_TYPE_QUIZ_SET_QUESTION: words = gSaveBlock1Ptr->lilycoveLady.quiz.question; break; case EASY_CHAT_TYPE_QUIZ_SET_ANSWER: words = &gSaveBlock1Ptr->lilycoveLady.quiz.correctAnswer; break; case EASY_CHAT_TYPE_APPRENTICE: words = gSaveBlock2Ptr->apprentices[0].speechWon; break; case EASY_CHAT_TYPE_QUESTIONNAIRE: words = GetQuestionnaireWordsPtr(); break; default: return; } CleanupOverworldWindowsAndTilemaps(); DoEasyChatScreen(gSpecialVar_0x8004, words, CB2_ReturnToFieldContinueScript, displayedPersonType); } static void CB2_QuizLadyQuestion(void) { LilycoveLady *lilycoveLady; UpdatePaletteFade(); switch (gMain.state) { case 0: FadeScreen(FADE_TO_BLACK, 0); break; case 1: if (!gPaletteFade.active) { lilycoveLady = &gSaveBlock1Ptr->lilycoveLady; lilycoveLady->quiz.playerAnswer = EC_EMPTY_WORD; CleanupOverworldWindowsAndTilemaps(); DoQuizQuestionEasyChatScreen(); } return; } gMain.state ++; } void QuizLadyShowQuizQuestion(void) { SetMainCallback2(CB2_QuizLadyQuestion); } static int GetQuizLadyScreenByFuncId(u16 funcId) { int i; for (i = 0; i < ARRAY_COUNT(sQuizLadyEasyChatScreens); i ++) { if (funcId == sQuizLadyEasyChatScreens[i].funcId) return i; } return -1; } static bool32 IsFuncIdForQuizLadyScreen(u16 funcId) { return GetQuizLadyScreenByFuncId(funcId) == -1 ? FALSE : TRUE; } static void EnterQuizLadyScreen(u16 funcId) { int i; i = GetQuizLadyScreenByFuncId(funcId); ResetTasks(); ExitEasyChatScreen(sQuizLadyEasyChatScreens[i].callback); } static void DoQuizAnswerEasyChatScreen(void) { DoEasyChatScreen( EASY_CHAT_TYPE_QUIZ_ANSWER, &gSaveBlock1Ptr->lilycoveLady.quiz.playerAnswer, CB2_ReturnToFieldContinueScript, EASY_CHAT_PERSON_DISPLAY_NONE); } static void DoQuizQuestionEasyChatScreen(void) { DoEasyChatScreen(EASY_CHAT_TYPE_QUIZ_QUESTION, gSaveBlock1Ptr->lilycoveLady.quiz.question, CB2_ReturnToFieldContinueScript, EASY_CHAT_PERSON_DISPLAY_NONE); } static void DoQuizSetAnswerEasyChatScreen(void) { DoEasyChatScreen(EASY_CHAT_TYPE_QUIZ_SET_ANSWER, &gSaveBlock1Ptr->lilycoveLady.quiz.correctAnswer, CB2_ReturnToFieldContinueScript, EASY_CHAT_PERSON_DISPLAY_NONE); } static void DoQuizSetQuestionEasyChatScreen(void) { DoEasyChatScreen(EASY_CHAT_TYPE_QUIZ_SET_QUESTION, gSaveBlock1Ptr->lilycoveLady.quiz.question, CB2_ReturnToFieldContinueScript, EASY_CHAT_PERSON_DISPLAY_NONE); } static bool8 InitEasyChatScreenStruct(u8 type, u16 *words, u8 displayedPersonType) { u8 templateId; int i; sEasyChatScreen = malloc(sizeof(*sEasyChatScreen)); if (sEasyChatScreen == NULL) return FALSE; sEasyChatScreen->type = type; sEasyChatScreen->savedPhrase = words; sEasyChatScreen->mainCursorColumn = 0; sEasyChatScreen->mainCursorRow = 0; sEasyChatScreen->inAlphabetMode = FALSE; sEasyChatScreen->displayedPersonType = displayedPersonType; sEasyChatScreen->unused = 0; templateId = GetEachChatScreenTemplateId(type); if (type == EASY_CHAT_TYPE_QUIZ_QUESTION) { GetQuizTitle(sEasyChatScreen->quizTitle); sEasyChatScreen->titleText = sEasyChatScreen->quizTitle; sEasyChatScreen->inputState = INPUTSTATE_QUIZ_QUESTION; } else { sEasyChatScreen->inputState = INPUTSTATE_PHRASE; sEasyChatScreen->titleText = sEasyChatScreenTemplates[templateId].titleText; } sEasyChatScreen->numColumns = sEasyChatScreenTemplates[templateId].numColumns; sEasyChatScreen->numRows = sEasyChatScreenTemplates[templateId].numRows; sEasyChatScreen->maxWords = sEasyChatScreen->numColumns * sEasyChatScreen->numRows; sEasyChatScreen->templateId = templateId; if (sEasyChatScreen->maxWords > ARRAY_COUNT(sEasyChatScreen->currentPhrase)) sEasyChatScreen->maxWords = ARRAY_COUNT(sEasyChatScreen->currentPhrase); if (words != NULL) { // Phrase starts with words filled in, copy to current phrase CpuCopy16(words, sEasyChatScreen->currentPhrase, sEasyChatScreen->maxWords * sizeof(u16)); } else { // Phrase starts with no words, fill with empty words and save for (i = 0; i < sEasyChatScreen->maxWords; i ++) sEasyChatScreen->currentPhrase[i] = EC_EMPTY_WORD; sEasyChatScreen->savedPhrase = sEasyChatScreen->currentPhrase; } sEasyChatScreen->keyboardLastRow = (GetNumUnlockedEasyChatGroups() - 1) / 2 + 1; return TRUE; } static void FreeEasyChatScreenStruct(void) { if (sEasyChatScreen != NULL) FREE_AND_SET_NULL(sEasyChatScreen); } // Returns the function ID of the action to take as a result of player's input. // If no action is needed, returns ECFUNC_NONE static u16 HandleEasyChatInput(void) { switch (sEasyChatScreen->inputState) { case INPUTSTATE_PHRASE: return HandleEasyChatInput_Phrase(); case INPUTSTATE_MAIN_SCREEN_BUTTONS: return HandleEasyChatInput_MainScreenButtons(); case INPUTSTATE_KEYBOARD: return HandleEasyChatInput_Keyboard(); case INPUTSTATE_WORD_SELECT: return HandleEasyChatInput_WordSelect(); case INPUTSTATE_EXIT_PROMPT: return HandleEasyChatInput_ExitPrompt(); case INPUTSTATE_DELETE_ALL_YES_NO: return HandleEasyChatInput_DeleteAllYesNo(); case INPUTSTATE_CONFIRM_WORDS_YES_NO: return HandleEasyChatInput_ConfirmWordsYesNo(); case INPUTSTATE_QUIZ_QUESTION: return HandleEasyChatInput_QuizQuestion(); case INPUTSTATE_WAIT_FOR_MSG: return HandleEasyChatInput_WaitForMsg(); case INPUTSTATE_START_CONFIRM_LYRICS: return HandleEasyChatInput_StartConfirmLyrics(); case INPUTSTATE_CONFIRM_LYRICS_YES_NO: return HandleEasyChatInput_ConfirmLyricsYesNo(); } return ECFUNC_NONE; } static bool32 IsCurrentFrame2x5(void) { switch (GetEasyChatScreenFrameId()) { case FRAMEID_MAIL: case FRAMEID_QUIZ_QUESTION: case FRAMEID_QUIZ_SET_QUESTION: return TRUE; } return FALSE; } // Handles main screen input while cursor is on a word in the phrase static u16 HandleEasyChatInput_Phrase(void) { do { if (JOY_NEW(A_BUTTON)) { ClearUnusedField(); sEasyChatScreen->inputState = INPUTSTATE_KEYBOARD; sEasyChatScreen->keyboardColumn = 0; sEasyChatScreen->keyboardRow = 0; sEasyChatScreen->keyboardScrollOffset = 0; return ECFUNC_OPEN_KEYBOARD; } else if (JOY_NEW(B_BUTTON)) { return StartConfirmExitPrompt(); } else if (JOY_NEW(START_BUTTON)) { return TryConfirmWords(); } else if (JOY_NEW(DPAD_UP)) { sEasyChatScreen->mainCursorRow--; break; } else if (JOY_NEW(DPAD_LEFT)) { sEasyChatScreen->mainCursorColumn--; break; } else if (JOY_NEW(DPAD_DOWN)) { sEasyChatScreen->mainCursorRow++; break; } else if (JOY_NEW(DPAD_RIGHT)) { sEasyChatScreen->mainCursorColumn++; break; } return ECFUNC_NONE; } while (0); // Handle D-Pad input // Wrap row if (sEasyChatScreen->mainCursorRow < 0) sEasyChatScreen->mainCursorRow = sEasyChatScreenTemplates[sEasyChatScreen->templateId].numRows; if (sEasyChatScreen->mainCursorRow > sEasyChatScreenTemplates[sEasyChatScreen->templateId].numRows) sEasyChatScreen->mainCursorRow = 0; if (sEasyChatScreen->mainCursorRow == sEasyChatScreenTemplates[sEasyChatScreen->templateId].numRows) { // Moved onto bottom row (buttons) if (sEasyChatScreen->mainCursorColumn > 2) sEasyChatScreen->mainCursorColumn = 2; sEasyChatScreen->inputState = INPUTSTATE_MAIN_SCREEN_BUTTONS; return ECFUNC_UPDATE_MAIN_CURSOR_ON_BUTTONS; } // Wrap column if (sEasyChatScreen->mainCursorColumn < 0) sEasyChatScreen->mainCursorColumn = sEasyChatScreenTemplates[sEasyChatScreen->templateId].numColumns - 1; if (sEasyChatScreen->mainCursorColumn >= sEasyChatScreenTemplates[sEasyChatScreen->templateId].numColumns) sEasyChatScreen->mainCursorColumn = 0; // All 2x5 phrases are only 9 words long, exclude the bottom right (10th) position if (IsCurrentFrame2x5() && sEasyChatScreen->mainCursorColumn == 1 && sEasyChatScreen->mainCursorRow == 4) sEasyChatScreen->mainCursorColumn = 0; return ECFUNC_UPDATE_MAIN_CURSOR; } // Handles main screen input while cursor is below the phrase on one of the buttons, e.g. Del. All or Cancel static u16 HandleEasyChatInput_MainScreenButtons(void) { do { if (JOY_NEW(A_BUTTON)) { switch (sEasyChatScreen->mainCursorColumn) { case 0: // Del. All button return DoDeleteAllButton(); case 1: // Cancel button return StartConfirmExitPrompt(); case 2: // OK button return TryConfirmWords(); case 3: // Quiz/Answer button return DoQuizButton(); } } if (JOY_NEW(B_BUTTON)) { return StartConfirmExitPrompt(); } else if (JOY_NEW(START_BUTTON)) { return TryConfirmWords(); } else if (JOY_NEW(DPAD_UP)) { sEasyChatScreen->mainCursorRow--; break; } else if (JOY_NEW(DPAD_LEFT)) { sEasyChatScreen->mainCursorColumn--; break; } else if (JOY_NEW(DPAD_DOWN)) { sEasyChatScreen->mainCursorRow = 0; break; } else if (JOY_NEW(DPAD_RIGHT)) { sEasyChatScreen->mainCursorColumn++; break; } return ECFUNC_NONE; } while (0); if (sEasyChatScreen->mainCursorRow == sEasyChatScreenTemplates[sEasyChatScreen->templateId].numRows) { int numFooterColumns = FooterHasFourOptions() ? 4 : 3; if (sEasyChatScreen->mainCursorColumn < 0) sEasyChatScreen->mainCursorColumn = numFooterColumns - 1; if (sEasyChatScreen->mainCursorColumn >= numFooterColumns) sEasyChatScreen->mainCursorColumn = 0; return ECFUNC_UPDATE_MAIN_CURSOR_ON_BUTTONS; } if (sEasyChatScreen->mainCursorColumn >= sEasyChatScreenTemplates[sEasyChatScreen->templateId].numColumns) sEasyChatScreen->mainCursorColumn = sEasyChatScreenTemplates[sEasyChatScreen->templateId].numColumns - 1; // All 2x5 phrases are only 9 words long, exclude the bottom right (10th) position if (IsCurrentFrame2x5() && sEasyChatScreen->mainCursorColumn == 1 && sEasyChatScreen->mainCursorRow == 4) sEasyChatScreen->mainCursorColumn = 0; sEasyChatScreen->inputState = INPUTSTATE_PHRASE; return ECFUNC_UPDATE_MAIN_CURSOR; } static u16 HandleEasyChatInput_Keyboard(void) { if (JOY_NEW(B_BUTTON)) return ExitKeyboardToMainScreen(); if (JOY_NEW(A_BUTTON)) { if (sEasyChatScreen->keyboardColumn != -1) return SelectKeyboardGroup(); // Cursor is in button window switch (sEasyChatScreen->keyboardRow) { case 0: // Mode button return StartSwitchKeyboardMode(); case 1: // Delete button return DeleteSelectedWord(); case 2: // Cancel button return ExitKeyboardToMainScreen(); } } if (JOY_NEW(SELECT_BUTTON)) return StartSwitchKeyboardMode(); if (JOY_REPEAT(DPAD_UP)) return MoveKeyboardCursor(INPUT_UP); if (JOY_REPEAT(DPAD_DOWN)) return MoveKeyboardCursor(INPUT_DOWN); if (JOY_REPEAT(DPAD_LEFT)) return MoveKeyboardCursor(INPUT_LEFT); if (JOY_REPEAT(DPAD_RIGHT)) return MoveKeyboardCursor(INPUT_RIGHT); return ECFUNC_NONE; } // Input handling for the lower window after a word group has been selected static u16 HandleEasyChatInput_WordSelect(void) { if (JOY_NEW(B_BUTTON)) { sEasyChatScreen->inputState = INPUTSTATE_KEYBOARD; return ECFUNC_RETURN_TO_KEYBOARD; } if (JOY_NEW(A_BUTTON)) return SelectNewWord(); if (JOY_NEW(START_BUTTON)) return MoveWordSelectCursor(INPUT_START); if (JOY_NEW(SELECT_BUTTON)) return MoveWordSelectCursor(INPUT_SELECT); if (JOY_REPEAT(DPAD_UP)) return MoveWordSelectCursor(INPUT_UP); if (JOY_REPEAT(DPAD_DOWN)) return MoveWordSelectCursor(INPUT_DOWN); if (JOY_REPEAT(DPAD_LEFT)) return MoveWordSelectCursor(INPUT_LEFT); if (JOY_REPEAT(DPAD_RIGHT)) return MoveWordSelectCursor(INPUT_RIGHT); return ECFUNC_NONE; } static u16 HandleEasyChatInput_ExitPrompt(void) { switch (Menu_ProcessInputNoWrapClearOnChoose()) { case MENU_B_PRESSED: case 1: // No (Continue) sEasyChatScreen->inputState = GetEasyChatBackupState(); return ECFUNC_CLOSE_PROMPT; case 0: // Yes (Exit) gSpecialVar_Result = 0; if (sEasyChatScreen->type == EASY_CHAT_TYPE_QUIZ_SET_QUESTION || sEasyChatScreen->type == EASY_CHAT_TYPE_QUIZ_SET_ANSWER) SaveCurrentPhrase(); return ECFUNC_EXIT; default: return ECFUNC_NONE; } } static u16 HandleEasyChatInput_ConfirmWordsYesNo(void) { switch (Menu_ProcessInputNoWrapClearOnChoose()) { case MENU_B_PRESSED: case 1: // No sEasyChatScreen->inputState = GetEasyChatBackupState(); return ECFUNC_CLOSE_PROMPT; case 0: // Yes SetSpecialEasyChatResult(); gSpecialVar_Result = GetEasyChatCompleted(); SaveCurrentPhrase(); return ECFUNC_EXIT; default: return ECFUNC_NONE; } } static u16 HandleEasyChatInput_DeleteAllYesNo(void) { switch (Menu_ProcessInputNoWrapClearOnChoose()) { case MENU_B_PRESSED: case 1: // No sEasyChatScreen->inputState = INPUTSTATE_MAIN_SCREEN_BUTTONS; return ECFUNC_CLOSE_PROMPT; case 0: // Yes ResetCurrentPhrase(); sEasyChatScreen->inputState = INPUTSTATE_MAIN_SCREEN_BUTTONS; return ECFUNC_CLOSE_PROMPT_AFTER_DELETE; default: return ECFUNC_NONE; } } static u16 HandleEasyChatInput_QuizQuestion(void) { if (JOY_NEW(A_BUTTON)) return ECFUNC_QUIZ_ANSWER; if (JOY_NEW(B_BUTTON)) return StartConfirmExitPrompt(); return ECFUNC_NONE; } // A message has been printed. Wait for player to // press A or B, then return to previous state static u16 HandleEasyChatInput_WaitForMsg(void) { if (JOY_NEW(A_BUTTON | B_BUTTON)) { sEasyChatScreen->inputState = GetEasyChatBackupState(); return ECFUNC_CLOSE_PROMPT; } return ECFUNC_NONE; } // Odd, could have been skipped. Just passes to HandleEasyChatInput_ConfirmLyricsYesNo static u16 HandleEasyChatInput_StartConfirmLyrics(void) { sEasyChatScreen->inputState = INPUTSTATE_CONFIRM_LYRICS_YES_NO; return ECFUNC_PROMPT_CONFIRM; } static u16 HandleEasyChatInput_ConfirmLyricsYesNo(void) { switch (Menu_ProcessInputNoWrapClearOnChoose()) { case MENU_B_PRESSED: case 1: // No ResetCurrentPhraseToSaved(); sEasyChatScreen->inputStateBackup = INPUTSTATE_PHRASE; sEasyChatScreen->inputState = INPUTSTATE_WAIT_FOR_MSG; return ECFUNC_MSG_SONG_TOO_SHORT; case 0: // Yes gSpecialVar_Result = GetEasyChatCompleted(); SaveCurrentPhrase(); return ECFUNC_EXIT; default: return ECFUNC_NONE; } } static u16 StartConfirmExitPrompt(void) { if (sEasyChatScreen->type == EASY_CHAT_TYPE_APPRENTICE || sEasyChatScreen->type == EASY_CHAT_TYPE_CONTEST_INTERVIEW) { sEasyChatScreen->inputStateBackup = sEasyChatScreen->inputState; sEasyChatScreen->inputState = INPUTSTATE_WAIT_FOR_MSG; return ECFUNC_MSG_CANT_EXIT; } else { sEasyChatScreen->inputStateBackup = sEasyChatScreen->inputState; sEasyChatScreen->inputState = INPUTSTATE_EXIT_PROMPT; return ECFUNC_PROMPT_EXIT; } } static int DoDeleteAllButton(void) { sEasyChatScreen->inputStateBackup = sEasyChatScreen->inputState; if (sEasyChatScreen->type != EASY_CHAT_TYPE_BARD_SONG) { // Show Delete yes/no sEasyChatScreen->inputState = INPUTSTATE_DELETE_ALL_YES_NO; return ECFUNC_PROMPT_DELETE_ALL; } else { // Cannot delete lyrics when setting Bard's song sEasyChatScreen->inputStateBackup = sEasyChatScreen->inputState; sEasyChatScreen->inputState = INPUTSTATE_WAIT_FOR_MSG; return ECFUNC_MSG_CANT_DELETE_LYRICS; } } static u16 TryConfirmWords(void) { sEasyChatScreen->inputStateBackup = sEasyChatScreen->inputState; if (sEasyChatScreen->type == EASY_CHAT_TYPE_QUIZ_SET_QUESTION) { if (IsQuizQuestionEmpty()) { sEasyChatScreen->inputState = INPUTSTATE_WAIT_FOR_MSG; return ECFUNC_MSG_CREATE_QUIZ; } if (IsQuizAnswerEmpty()) { sEasyChatScreen->inputState = INPUTSTATE_WAIT_FOR_MSG; return ECFUNC_MSG_SELECT_ANSWER; } sEasyChatScreen->inputState = INPUTSTATE_CONFIRM_WORDS_YES_NO; return ECFUNC_PROMPT_CONFIRM; } else if (sEasyChatScreen->type == EASY_CHAT_TYPE_QUIZ_SET_ANSWER) { if (IsQuizAnswerEmpty()) { sEasyChatScreen->inputState = INPUTSTATE_WAIT_FOR_MSG; return ECFUNC_MSG_SELECT_ANSWER; } if (IsQuizQuestionEmpty()) { sEasyChatScreen->inputState = INPUTSTATE_WAIT_FOR_MSG; return ECFUNC_MSG_CREATE_QUIZ; } sEasyChatScreen->inputState = INPUTSTATE_CONFIRM_WORDS_YES_NO; return ECFUNC_PROMPT_CONFIRM; } else if (sEasyChatScreen->type == EASY_CHAT_TYPE_TRENDY_PHRASE || sEasyChatScreen->type == EASY_CHAT_TYPE_GOOD_SAYING) { if (!IsCurrentPhraseFull()) { sEasyChatScreen->inputState = INPUTSTATE_WAIT_FOR_MSG; return ECFUNC_MSG_COMBINE_TWO_WORDS; } sEasyChatScreen->inputState = INPUTSTATE_CONFIRM_WORDS_YES_NO; return ECFUNC_PROMPT_CONFIRM; } else if (sEasyChatScreen->type == EASY_CHAT_TYPE_APPRENTICE || sEasyChatScreen->type == EASY_CHAT_TYPE_CONTEST_INTERVIEW) { if (IsCurrentPhraseEmpty()) { sEasyChatScreen->inputState = INPUTSTATE_WAIT_FOR_MSG; return ECFUNC_MSG_CANT_EXIT; } sEasyChatScreen->inputState = INPUTSTATE_CONFIRM_WORDS_YES_NO; return ECFUNC_PROMPT_CONFIRM; } else if (sEasyChatScreen->type == EASY_CHAT_TYPE_QUESTIONNAIRE) { sEasyChatScreen->inputState = INPUTSTATE_CONFIRM_WORDS_YES_NO; return ECFUNC_PROMPT_CONFIRM; } else { if (IsCurrentPhraseEmpty() == TRUE || !GetEasyChatCompleted()) { sEasyChatScreen->inputState = INPUTSTATE_EXIT_PROMPT; return ECFUNC_PROMPT_EXIT; } sEasyChatScreen->inputState = INPUTSTATE_CONFIRM_WORDS_YES_NO; return ECFUNC_PROMPT_CONFIRM; } } static int DoQuizButton(void) { sEasyChatScreen->inputStateBackup = sEasyChatScreen->inputState; switch (sEasyChatScreen->type) { case EASY_CHAT_TYPE_QUIZ_ANSWER: return ECFUNC_QUIZ_QUESTION; case EASY_CHAT_TYPE_QUIZ_SET_QUESTION: SaveCurrentPhrase(); return ECFUNC_SET_QUIZ_ANSWER; case EASY_CHAT_TYPE_QUIZ_SET_ANSWER: SaveCurrentPhrase(); return ECFUNC_SET_QUIZ_QUESTION; default: return ECFUNC_NONE; } } static u8 GetEasyChatBackupState(void) { return sEasyChatScreen->inputStateBackup; } static int SelectKeyboardGroup(void) { u16 numWords; if (!sEasyChatScreen->inAlphabetMode) { u8 groupId = GetUnlockedEasyChatGroupId(GetSelectedGroupIndex()); SetSelectedWordGroup(FALSE, groupId); } else { SetSelectedWordGroup(TRUE, GetSelectedAlphabetGroupId()); } numWords = GetNumWordsInSelectedGroup(); if (numWords == 0) return ECFUNC_NONE; sEasyChatScreen->wordSelectLastRow = (numWords - 1) / 2; sEasyChatScreen->wordSelectScrollOffset = 0; sEasyChatScreen->wordSelectColumn = 0; sEasyChatScreen->wordSelectRow = 0; sEasyChatScreen->inputState = INPUTSTATE_WORD_SELECT; return ECFUNC_OPEN_WORD_SELECT; } static int ExitKeyboardToMainScreen(void) { sEasyChatScreen->inputState = INPUTSTATE_PHRASE; return ECFUNC_CLOSE_KEYBOARD; } static int StartSwitchKeyboardMode(void) { sEasyChatScreen->keyboardColumn = 0; sEasyChatScreen->keyboardRow = 0; sEasyChatScreen->keyboardScrollOffset = 0; if (!sEasyChatScreen->inAlphabetMode) sEasyChatScreen->inAlphabetMode = TRUE; else sEasyChatScreen->inAlphabetMode = FALSE; return ECFUNC_SWITCH_KEYBOARD_MODE; } static int DeleteSelectedWord(void) { if (sEasyChatScreen->type == EASY_CHAT_TYPE_BARD_SONG) { PlaySE(SE_FAILURE); return ECFUNC_NONE; } else { SetSelectedWord(EC_EMPTY_WORD); return ECFUNC_REPRINT_PHRASE; } } static int SelectNewWord(void) { u16 easyChatWord = GetWordFromSelectedGroup(GetSelectedWordIndex()); if (DummyWordCheck(easyChatWord)) { // Never reached. Would disallow selecting certain words PlaySE(SE_FAILURE); return ECFUNC_NONE; } else { SetSelectedWord(easyChatWord); if (sEasyChatScreen->type != EASY_CHAT_TYPE_BARD_SONG) { sEasyChatScreen->inputState = INPUTSTATE_PHRASE; return ECFUNC_CLOSE_WORD_SELECT; } else { sEasyChatScreen->inputState = INPUTSTATE_START_CONFIRM_LYRICS; return ECFUNC_PROMPT_CONFIRM_LYRICS; } } } static void SaveCurrentPhrase(void) { int i; for (i = 0; i < sEasyChatScreen->maxWords; i++) sEasyChatScreen->savedPhrase[i] = sEasyChatScreen->currentPhrase[i]; } static void ResetCurrentPhrase(void) { int i; for (i = 0; i < sEasyChatScreen->maxWords; i++) sEasyChatScreen->currentPhrase[i] = EC_EMPTY_WORD; } static void ResetCurrentPhraseToSaved(void) { int i; for (i = 0; i < sEasyChatScreen->maxWords; i++) sEasyChatScreen->currentPhrase[i] = sEasyChatScreen->savedPhrase[i]; } static void SetSelectedWord(u16 easyChatWord) { u16 index = GetWordIndexToReplace(); sEasyChatScreen->currentPhrase[index] = easyChatWord; } // Compare current phrase to the original saved phrase static bool8 DidPhraseChange(void) { u16 i; for (i = 0; i < sEasyChatScreen->maxWords; i++) { if (sEasyChatScreen->currentPhrase[i] != sEasyChatScreen->savedPhrase[i]) return TRUE; } return FALSE; } // 'Completed' if the phrase was changed, or in the case of making a quiz, the question and answer were filled out static bool32 GetEasyChatCompleted(void) { if (sEasyChatScreen->type == EASY_CHAT_TYPE_QUIZ_SET_QUESTION || sEasyChatScreen->type == EASY_CHAT_TYPE_QUIZ_SET_ANSWER) { if (IsQuizQuestionEmpty()) return FALSE; if (IsQuizAnswerEmpty()) return FALSE; return TRUE; } else { return DidPhraseChange(); } } static u16 MoveKeyboardCursor(int input) { if (sEasyChatScreen->keyboardColumn != -1) { if (!sEasyChatScreen->inAlphabetMode) return MoveKeyboardCursor_GroupNames(input); else return MoveKeyboardCursor_Alphabet(input); } else { return MoveKeyboardCursor_ButtonWindow(input); } } static int MoveKeyboardCursor_GroupNames(u32 input) { switch (input) { case INPUT_UP: if (sEasyChatScreen->keyboardRow != -sEasyChatScreen->keyboardScrollOffset) { if (sEasyChatScreen->keyboardRow) { sEasyChatScreen->keyboardRow--; return ECFUNC_UPDATE_KEYBOARD_CURSOR; } else { sEasyChatScreen->keyboardScrollOffset--; return ECFUNC_GROUP_NAMES_SCROLL_UP; } } break; case INPUT_DOWN: if (sEasyChatScreen->keyboardRow + sEasyChatScreen->keyboardScrollOffset < sEasyChatScreen->keyboardLastRow - 1) { int funcId; if (sEasyChatScreen->keyboardRow < NUM_GROUP_NAME_ROWS - 1) { sEasyChatScreen->keyboardRow++; funcId = ECFUNC_UPDATE_KEYBOARD_CURSOR; } else { sEasyChatScreen->keyboardScrollOffset++; funcId = ECFUNC_GROUP_NAMES_SCROLL_DOWN; } ReduceToValidKeyboardColumn(); return funcId; } break; case INPUT_LEFT: if (sEasyChatScreen->keyboardColumn) sEasyChatScreen->keyboardColumn--; else SetKeyboardCursorInButtonWindow(); return ECFUNC_UPDATE_KEYBOARD_CURSOR; case INPUT_RIGHT: if (sEasyChatScreen->keyboardColumn < 1) { sEasyChatScreen->keyboardColumn++; if (IsSelectedKeyboardIndexInvalid()) SetKeyboardCursorInButtonWindow(); } else { SetKeyboardCursorInButtonWindow(); } return ECFUNC_UPDATE_KEYBOARD_CURSOR; } return ECFUNC_NONE; } static int MoveKeyboardCursor_Alphabet(u32 input) { switch (input) { case INPUT_UP: if (sEasyChatScreen->keyboardRow > 0) sEasyChatScreen->keyboardRow--; else sEasyChatScreen->keyboardRow = NUM_ALPHABET_ROWS - 1; ReduceToValidKeyboardColumn(); return ECFUNC_UPDATE_KEYBOARD_CURSOR; case INPUT_DOWN: if (sEasyChatScreen->keyboardRow < NUM_ALPHABET_ROWS - 1) sEasyChatScreen->keyboardRow++; else sEasyChatScreen->keyboardRow = 0; ReduceToValidKeyboardColumn(); return ECFUNC_UPDATE_KEYBOARD_CURSOR; case INPUT_RIGHT: sEasyChatScreen->keyboardColumn++; if (IsSelectedKeyboardIndexInvalid()) SetKeyboardCursorInButtonWindow(); return ECFUNC_UPDATE_KEYBOARD_CURSOR; case INPUT_LEFT: sEasyChatScreen->keyboardColumn--; if (sEasyChatScreen->keyboardColumn < 0) SetKeyboardCursorInButtonWindow(); return ECFUNC_UPDATE_KEYBOARD_CURSOR; } return ECFUNC_NONE; } static int MoveKeyboardCursor_ButtonWindow(u32 input) { switch (input) { case INPUT_UP: if (sEasyChatScreen->keyboardRow) sEasyChatScreen->keyboardRow--; else sEasyChatScreen->keyboardRow = NUM_BUTTON_ROWS - 1; return ECFUNC_UPDATE_KEYBOARD_CURSOR; case INPUT_DOWN: if (sEasyChatScreen->keyboardRow < NUM_BUTTON_ROWS - 1) sEasyChatScreen->keyboardRow++; else sEasyChatScreen->keyboardRow = 0; return ECFUNC_UPDATE_KEYBOARD_CURSOR; case INPUT_LEFT: sEasyChatScreen->keyboardRow++; SetKeyboardCursorToLastColumn(); return ECFUNC_UPDATE_KEYBOARD_CURSOR; case INPUT_RIGHT: sEasyChatScreen->keyboardColumn = 0; sEasyChatScreen->keyboardRow++; return ECFUNC_UPDATE_KEYBOARD_CURSOR; } return ECFUNC_NONE; } static void SetKeyboardCursorInButtonWindow(void) { sEasyChatScreen->keyboardColumn = -1; if (sEasyChatScreen->keyboardRow) sEasyChatScreen->keyboardRow--; } static void SetKeyboardCursorToLastColumn(void) { if (!sEasyChatScreen->inAlphabetMode) { sEasyChatScreen->keyboardColumn = 1; ReduceToValidKeyboardColumn(); } else { sEasyChatScreen->keyboardColumn = GetLastAlphabetColumn(sEasyChatScreen->keyboardRow); } } static u16 MoveWordSelectCursor(u32 input) { u16 funcId; switch (input) { case INPUT_UP: if (sEasyChatScreen->wordSelectRow + sEasyChatScreen->wordSelectScrollOffset > 0) { if (sEasyChatScreen->wordSelectRow > 0) { sEasyChatScreen->wordSelectRow--; funcId = ECFUNC_UPDATE_WORD_SELECT_CURSOR; } else { sEasyChatScreen->wordSelectScrollOffset--; funcId = ECFUNC_WORD_SELECT_SCROLL_UP; } ReduceToValidWordSelectColumn(); return funcId; } break; case INPUT_DOWN: if (sEasyChatScreen->wordSelectRow + sEasyChatScreen->wordSelectScrollOffset < sEasyChatScreen->wordSelectLastRow) { if (sEasyChatScreen->wordSelectRow < NUM_WORD_SELECT_ROWS - 1) { sEasyChatScreen->wordSelectRow++; funcId = ECFUNC_UPDATE_WORD_SELECT_CURSOR; } else { sEasyChatScreen->wordSelectScrollOffset++; funcId = ECFUNC_WORD_SELECT_SCROLL_DOWN; } ReduceToValidWordSelectColumn(); return funcId; } break; case INPUT_LEFT: if (sEasyChatScreen->wordSelectColumn > 0) sEasyChatScreen->wordSelectColumn--; else sEasyChatScreen->wordSelectColumn = 1; ReduceToValidWordSelectColumn(); return ECFUNC_UPDATE_WORD_SELECT_CURSOR; case INPUT_RIGHT: if (sEasyChatScreen->wordSelectColumn < 1) { sEasyChatScreen->wordSelectColumn++; if (IsSelectedWordIndexInvalid()) sEasyChatScreen->wordSelectColumn = 0; } else { sEasyChatScreen->wordSelectColumn = 0; } return ECFUNC_UPDATE_WORD_SELECT_CURSOR; case INPUT_START: // Page scroll up if (sEasyChatScreen->wordSelectScrollOffset) { if (sEasyChatScreen->wordSelectScrollOffset >= NUM_WORD_SELECT_ROWS) sEasyChatScreen->wordSelectScrollOffset -= NUM_WORD_SELECT_ROWS; else sEasyChatScreen->wordSelectScrollOffset = 0; return ECFUNC_WORD_SELECT_PAGE_UP; } break; case INPUT_SELECT: // Page scroll down if (sEasyChatScreen->wordSelectScrollOffset <= sEasyChatScreen->wordSelectLastRow - NUM_WORD_SELECT_ROWS) { sEasyChatScreen->wordSelectScrollOffset += NUM_WORD_SELECT_ROWS; if (sEasyChatScreen->wordSelectScrollOffset > sEasyChatScreen->wordSelectLastRow - NUM_WORD_SELECT_ROWS + 1) sEasyChatScreen->wordSelectScrollOffset = sEasyChatScreen->wordSelectLastRow - NUM_WORD_SELECT_ROWS + 1; ReduceToValidWordSelectColumn(); return ECFUNC_WORD_SELECT_PAGE_DOWN; } break; } return ECFUNC_NONE; } static u16 GetWordIndexToReplace(void) { return (sEasyChatScreen->mainCursorRow * sEasyChatScreen->numColumns) + sEasyChatScreen->mainCursorColumn; } static u16 GetSelectedGroupIndex(void) { return NUM_GROUP_NAME_COLUMNS * (sEasyChatScreen->keyboardRow + sEasyChatScreen->keyboardScrollOffset) + sEasyChatScreen->keyboardColumn; } static int GetSelectedAlphabetGroupId(void) { int column = (u8)sEasyChatScreen->keyboardColumn < NUM_ALPHABET_COLUMNS ? sEasyChatScreen->keyboardColumn : 0; int row = (u8)sEasyChatScreen->keyboardRow < NUM_ALPHABET_ROWS ? sEasyChatScreen->keyboardRow : 0; return sAlphabetGroupIdMap[row][column]; } static u16 GetSelectedWordIndex(void) { return NUM_WORD_SELECT_COLUMNS * (sEasyChatScreen->wordSelectRow + sEasyChatScreen->wordSelectScrollOffset) + sEasyChatScreen->wordSelectColumn; } // Get the index of the last column in the alphabet keyboard, depending on current row static u8 GetLastAlphabetColumn(u8 row) { switch (row) { case 0: default: return NUM_ALPHABET_COLUMNS - 1; case 1: return NUM_ALPHABET_COLUMNS - 2; // At 6 letters, only the 2nd row (index 1) has less than the max columns // The 3rd and 4th row have 7 letters, the 1st row has 6 letters and 'Others' } } static void ReduceToValidKeyboardColumn(void) { while (IsSelectedKeyboardIndexInvalid()) { if (sEasyChatScreen->keyboardColumn) sEasyChatScreen->keyboardColumn--; else break; } } static void ReduceToValidWordSelectColumn(void) { while (IsSelectedWordIndexInvalid()) { if (sEasyChatScreen->wordSelectColumn) sEasyChatScreen->wordSelectColumn--; else break; } } static bool8 IsSelectedKeyboardIndexInvalid(void) { if (!sEasyChatScreen->inAlphabetMode) return GetSelectedGroupIndex() >= GetNumUnlockedEasyChatGroups() ? TRUE : FALSE; else return sEasyChatScreen->keyboardColumn > GetLastAlphabetColumn(sEasyChatScreen->keyboardRow) ? TRUE : FALSE; } static bool8 IsSelectedWordIndexInvalid(void) { return GetSelectedWordIndex() >= GetNumWordsInSelectedGroup() ? TRUE : FALSE; } static int FooterHasFourOptions(void) { return sEasyChatScreenTemplates[sEasyChatScreen->templateId].fourFooterOptions; } static u8 GetEasyChatScreenType(void) { return sEasyChatScreen->type; } static u8 GetEasyChatScreenFrameId(void) { return sEasyChatScreenTemplates[sEasyChatScreen->templateId].frameId; } const u8 *GetTitleText(void) { return sEasyChatScreen->titleText; } static u16 *GetCurrentPhrase(void) { return sEasyChatScreen->currentPhrase; } static u8 GetNumRows(void) { return sEasyChatScreen->numRows; } static u8 GetNumColumns(void) { return sEasyChatScreen->numColumns; } static u8 GetMainCursorColumn(void) { return sEasyChatScreen->mainCursorColumn; } static u8 GetMainCursorRow(void) { return sEasyChatScreen->mainCursorRow; } static void GetEasyChatInstructionsText(const u8 **str1, const u8 **str2) { *str1 = sEasyChatScreenTemplates[sEasyChatScreen->templateId].instructionsText1; *str2 = sEasyChatScreenTemplates[sEasyChatScreen->templateId].instructionsText2; } static void GetEasyChatConfirmText(const u8 **str1, const u8 **str2) { *str1 = sEasyChatScreenTemplates[sEasyChatScreen->templateId].confirmText1; *str2 = sEasyChatScreenTemplates[sEasyChatScreen->templateId].confirmText2; } static void GetEasyChatConfirmExitText(const u8 **str1, const u8 **str2) { switch (sEasyChatScreen->type) { case EASY_CHAT_TYPE_MAIL: *str1 = gText_StopGivingPkmnMail; *str2 = NULL; break; case EASY_CHAT_TYPE_QUIZ_ANSWER: case EASY_CHAT_TYPE_QUIZ_QUESTION: *str1 = gText_LikeToQuitQuiz; *str2 = gText_ChallengeQuestionMark; break; default: *str1 = gText_QuitEditing; *str2 = NULL; break; } } static void GetEasyChatConfirmDeletionText(const u8 **str1, const u8 **str2) { *str1 = gText_AllTextBeingEditedWill; *str2 = gText_BeDeletedThatOkay; } static void GetKeyboardCursorColAndRow(u8 *column, u8 *row) { *column = sEasyChatScreen->keyboardColumn; *row = sEasyChatScreen->keyboardRow; } static bool8 GetInAlphabetMode(void) { return sEasyChatScreen->inAlphabetMode; } static u8 GetKeyboardScrollOffset(void) { return sEasyChatScreen->keyboardScrollOffset; } static void GetWordSelectColAndRow(u8 *column, u8 *row) { *column = sEasyChatScreen->wordSelectColumn; *row = sEasyChatScreen->wordSelectRow; } static u8 GetWordSelectScrollOffset(void) { return sEasyChatScreen->wordSelectScrollOffset; } static u8 GetWordSelectLastRow(void) { return sEasyChatScreen->wordSelectLastRow; } static u8 UnusedDummy(void) { return FALSE; } static bool32 CanScrollUp(void) { switch (sEasyChatScreen->inputState) { case INPUTSTATE_KEYBOARD: if (!sEasyChatScreen->inAlphabetMode && sEasyChatScreen->keyboardScrollOffset) return TRUE; break; case INPUTSTATE_WORD_SELECT: if (sEasyChatScreen->wordSelectScrollOffset) return TRUE; break; } return FALSE; } static bool32 CanScrollDown(void) { switch (sEasyChatScreen->inputState) { case INPUTSTATE_KEYBOARD: if (!sEasyChatScreen->inAlphabetMode && sEasyChatScreen->keyboardScrollOffset + NUM_GROUP_NAME_ROWS <= sEasyChatScreen->keyboardLastRow - 1) return TRUE; break; case INPUTSTATE_WORD_SELECT: if (sEasyChatScreen->wordSelectScrollOffset + NUM_WORD_SELECT_ROWS <= sEasyChatScreen->wordSelectLastRow) return TRUE; break; } return FALSE; } static int FooterHasFourOptions_(void) { return FooterHasFourOptions(); } static bool8 IsPhraseDifferentThanPlayerInput(const u16 *phrase, u8 phraseLength) { u8 i; for (i = 0; i < phraseLength; i++) { if (phrase[i] != sEasyChatScreen->currentPhrase[i]) return TRUE; } return FALSE; } static u8 GetDisplayedPersonType(void) { return sEasyChatScreen->displayedPersonType; } static u8 GetEachChatScreenTemplateId(u8 type) { u32 i; for (i = 0; i < ARRAY_COUNT(sEasyChatScreenTemplates); i++) { if (sEasyChatScreenTemplates[i].type == type) return i; } return 0; } static bool32 IsCurrentPhraseEmpty(void) { int i; for (i = 0; i < sEasyChatScreen->maxWords; i++) { if (sEasyChatScreen->currentPhrase[i] != EC_EMPTY_WORD) return FALSE; } return TRUE; } static bool32 IsCurrentPhraseFull(void) { int i; for (i = 0; i < sEasyChatScreen->maxWords; i++) { if (sEasyChatScreen->currentPhrase[i] == EC_EMPTY_WORD) return FALSE; } return TRUE; } static int IsQuizQuestionEmpty(void) { int i; struct SaveBlock1 *saveBlock1; if (sEasyChatScreen->type == EASY_CHAT_TYPE_QUIZ_SET_QUESTION) return IsCurrentPhraseEmpty(); saveBlock1 = gSaveBlock1Ptr; for (i = 0; i < QUIZ_QUESTION_LEN; i++) { if (saveBlock1->lilycoveLady.quiz.question[i] != EC_EMPTY_WORD) return FALSE; } return TRUE; } static int IsQuizAnswerEmpty(void) { struct LilycoveLadyQuiz *quiz; if (sEasyChatScreen->type == EASY_CHAT_TYPE_QUIZ_SET_ANSWER) return IsCurrentPhraseEmpty(); quiz = &gSaveBlock1Ptr->lilycoveLady.quiz; return quiz->correctAnswer == EC_EMPTY_WORD ? TRUE : FALSE; } static void GetQuizTitle(u8 *dst) { u8 name[32]; struct SaveBlock1 *saveBlock1 = gSaveBlock1Ptr; DynamicPlaceholderTextUtil_Reset(); // Buffer author's name if (StringLength(saveBlock1->lilycoveLady.quiz.playerName) != 0) { TVShowConvertInternationalString(name, saveBlock1->lilycoveLady.quiz.playerName, saveBlock1->lilycoveLady.quiz.language); DynamicPlaceholderTextUtil_SetPlaceholderPtr(0, name); } else { DynamicPlaceholderTextUtil_SetPlaceholderPtr(0, gText_Lady); } // "'s Quiz" DynamicPlaceholderTextUtil_ExpandPlaceholders(dst, gText_F700sQuiz); } static void BufferCurrentPhraseToStringVar2(void) { int i; u16 *phrase; u8 *str; phrase = sEasyChatScreen->currentPhrase; str = gStringVar2; i = 0; while (i < sEasyChatScreen->maxWords) { str = CopyEasyChatWordPadded(str, *phrase, 0); *str = 0; str++; phrase++; i++; } str--; str[0] = EOS; } static void SetSpecialEasyChatResult(void) { switch (sEasyChatScreen->type) { case EASY_CHAT_TYPE_PROFILE: FlagSet(FLAG_SYS_CHAT_USED); break; case EASY_CHAT_TYPE_QUESTIONNAIRE: if (DidPlayerInputMysteryGiftPhrase()) gSpecialVar_0x8004 = 2; else gSpecialVar_0x8004 = 0; break; case EASY_CHAT_TYPE_TRENDY_PHRASE: BufferCurrentPhraseToStringVar2(); gSpecialVar_0x8004 = TrySetTrendyPhrase(sEasyChatScreen->currentPhrase); break; case EASY_CHAT_TYPE_GOOD_SAYING: gSpecialVar_0x8004 = DidPlayerInputABerryMasterWifePhrase(); break; } } static int DidPlayerInputMysteryGiftPhrase(void) { return !IsPhraseDifferentThanPlayerInput(sMysteryGiftPhrase, ARRAY_COUNT(sMysteryGiftPhrase)); } static u16 DidPlayerInputABerryMasterWifePhrase(void) { int i; for (i = 0; i < (int)ARRAY_COUNT(sBerryMasterWifePhrases); i++) { if (!IsPhraseDifferentThanPlayerInput(sBerryMasterWifePhrases[i], ARRAY_COUNT(*sBerryMasterWifePhrases))) return i + 1; } return 0; } static void ClearUnusedField(void) { sEasyChatScreen->unused = 0; } static bool32 DummyWordCheck(int easyChatWord) { return FALSE; } static bool8 InitEasyChatScreenControl(void) { if (!InitEasyChatScreenControl_()) return FALSE; else return TRUE; } static bool8 LoadEasyChatScreen(void) { switch (sScreenControl->funcState) { case 0: ResetBgsAndClearDma3BusyFlags(0); InitBgsFromTemplates(0, sEasyChatBgTemplates, ARRAY_COUNT(sEasyChatBgTemplates)); SetBgTilemapBuffer(3, sScreenControl->bg3TilemapBuffer); SetBgTilemapBuffer(1, sScreenControl->bg1TilemapBuffer); InitWindows(sEasyChatWindowTemplates); DeactivateAllTextPrinters(); LoadEasyChatPalettes(); InitEasyChatBgs(); CpuFastFill(0, (void *)OAM, OAM_SIZE); break; case 1: DecompressAndLoadBgGfxUsingHeap(3, gEasyChatWindow_Gfx, 0, 0, 0); CopyToBgTilemapBuffer(3, gEasyChatWindow_Tilemap, 0, 0); AdjustBgTilemapForFooter(); BufferFrameTilemap(sScreenControl->bg1TilemapBuffer); AddPhraseWindow(); AddMainScreenButtonWindow(); CopyBgTilemapBufferToVram(3); break; case 2: DecompressAndLoadBgGfxUsingHeap(1, sTextInputFrame_Gfx, 0, 0, 0); CopyBgTilemapBufferToVram(1); break; case 3: PrintTitle(); PrintInitialInstructions(); PrintCurrentPhrase(); DrawLowerWindow(); break; case 4: LoadEasyChatGfx(); if (GetEasyChatScreenType() != EASY_CHAT_TYPE_QUIZ_QUESTION) CreateMainCursorSprite(); break; case 5: if (IsDma3ManagerBusyWithBgCopy()) { return TRUE; } else { SetWindowDimensions(0, 0, 0, 0); SetGpuReg(REG_OFFSET_WININ, WININ_WIN0_BG_ALL | WININ_WIN0_OBJ | WININ_WIN0_CLR); SetGpuReg(REG_OFFSET_WINOUT, WINOUT_WIN01_BG0 | WINOUT_WIN01_BG1 | WINOUT_WIN01_BG3 | WINOUT_WIN01_OBJ | WINOUT_WIN01_CLR); ShowBg(3); ShowBg(1); ShowBg(2); ShowBg(0); CreateScrollIndicatorSprites(); CreateStartSelectButtonSprites(); TryAddInterviewObjectEvents(); } break; default: return FALSE; } sScreenControl->funcState++; return TRUE; } static void FreeEasyChatScreenControl(void) { if (sScreenControl) FREE_AND_SET_NULL(sScreenControl); } static void StartEasyChatFunction(u16 funcId) { sScreenControl->currentFuncId = funcId; sScreenControl->funcState = 0; RunEasyChatFunction(); } // Returns FALSE when called function has finished static bool8 RunEasyChatFunction(void) { switch (sScreenControl->currentFuncId) { case ECFUNC_NONE: return FALSE; case ECFUNC_REPRINT_PHRASE: return ReprintPhrase(); case ECFUNC_UPDATE_MAIN_CURSOR: return UpdateMainCursor(); case ECFUNC_UPDATE_MAIN_CURSOR_ON_BUTTONS: return UpdateMainCursorOnButtons(); case ECFUNC_PROMPT_DELETE_ALL: return ShowConfirmDeleteAllPrompt(); case ECFUNC_PROMPT_EXIT: return ShowConfirmExitPrompt(); case ECFUNC_PROMPT_CONFIRM: return ShowConfirmPrompt(); case ECFUNC_CLOSE_PROMPT: return ClosePrompt(); case ECFUNC_CLOSE_PROMPT_AFTER_DELETE: return ClosePromptAfterDeleteAll(); case ECFUNC_OPEN_KEYBOARD: return OpenKeyboard(); case ECFUNC_CLOSE_KEYBOARD: return CloseKeyboard(); case ECFUNC_OPEN_WORD_SELECT: return OpenWordSelect(); case ECFUNC_CLOSE_WORD_SELECT: return CloseWordSelect(); case ECFUNC_PROMPT_CONFIRM_LYRICS: return ShowConfirmLyricsPrompt(); case ECFUNC_RETURN_TO_KEYBOARD: return ReturnToKeyboard(); case ECFUNC_UPDATE_KEYBOARD_CURSOR: return UpdateKeyboardCursor(); case ECFUNC_GROUP_NAMES_SCROLL_DOWN: return GroupNamesScrollDown(); case ECFUNC_GROUP_NAMES_SCROLL_UP: return GroupNamesScrollUp(); case ECFUNC_UPDATE_WORD_SELECT_CURSOR: return UpdateWordSelectCursor(); case ECFUNC_WORD_SELECT_SCROLL_UP: return WordSelectScrollUp(); case ECFUNC_WORD_SELECT_SCROLL_DOWN: return WordSelectScrollDown(); case ECFUNC_WORD_SELECT_PAGE_UP: return WordSelectPageScrollUp(); case ECFUNC_WORD_SELECT_PAGE_DOWN: return WordSelectPageScrollDown(); case ECFUNC_SWITCH_KEYBOARD_MODE: return SwitchKeyboardMode(); case ECFUNC_EXIT: return FALSE; case ECFUNC_QUIZ_QUESTION: return FALSE; // The 4 quiz functions case ECFUNC_QUIZ_ANSWER: return FALSE; // 'finish' automatically case ECFUNC_SET_QUIZ_QUESTION: return FALSE; // because they switch to a case ECFUNC_SET_QUIZ_ANSWER: return FALSE; // callback in sQuizLadyEasyChatScreens case ECFUNC_MSG_CREATE_QUIZ: return ShowCreateQuizMsg(); case ECFUNC_MSG_SELECT_ANSWER: return ShowSelectAnswerMsg(); case ECFUNC_MSG_SONG_TOO_SHORT: return ShowSongTooShortMsg(); case ECFUNC_MSG_CANT_DELETE_LYRICS: return ShowCantDeleteLyricsMsg(); case ECFUNC_MSG_COMBINE_TWO_WORDS: return ShowCombineTwoWordsMsg(); case ECFUNC_MSG_CANT_EXIT: return ShowCantExitMsg(); default: return FALSE; } } // Only used to update the current phrase after a word deletion static bool8 ReprintPhrase(void) { switch (sScreenControl->funcState) { case 0: PrintCurrentPhrase(); sScreenControl->funcState++; break; case 1: return IsDma3ManagerBusyWithBgCopy(); } return TRUE; } static bool8 UpdateMainCursor(void) { u8 i; u16 *currentPhrase; u16 *ecWord; u8 frameId; u8 cursorColumn, cursorRow, numColumns; s16 x; int stringWidth; int trueStringWidth; u8 y; u8 str[64]; currentPhrase = GetCurrentPhrase(); frameId = GetEasyChatScreenFrameId(); cursorColumn = GetMainCursorColumn(); cursorRow = GetMainCursorRow(); numColumns = GetNumColumns(); ecWord = ¤tPhrase[cursorRow * numColumns]; x = 8 * sPhraseFrameDimensions[frameId].left + 13; for (i = 0; i < cursorColumn; i++) { if (*ecWord == EC_EMPTY_WORD) { stringWidth = 72; } else { CopyEasyChatWord(str, *ecWord); stringWidth = GetStringWidth(FONT_NORMAL, str, 0); } trueStringWidth = stringWidth + 17; x += trueStringWidth; ecWord++; } y = 8 * (sPhraseFrameDimensions[frameId].top + cursorRow * 2); SetMainCursorPos(x, y + 8); return FALSE; } static bool8 UpdateMainCursorOnButtons(void) { u8 xOffset = GetFooterOptionXOffset(GetMainCursorColumn()); SetMainCursorPos(xOffset, 96); return FALSE; } static bool8 ShowConfirmExitPrompt(void) { switch (sScreenControl->funcState) { case 0: StopMainCursorAnim(); PrintEasyChatStdMessage(MSG_CONFIRM_EXIT); CreateEasyChatYesNoMenu(1); sScreenControl->funcState++; break; case 1: return IsDma3ManagerBusyWithBgCopy(); } return TRUE; } static bool8 ShowConfirmPrompt(void) { switch (sScreenControl->funcState) { case 0: StopMainCursorAnim(); PrintEasyChatStdMessage(MSG_CONFIRM); CreateEasyChatYesNoMenu(0); sScreenControl->funcState++; break; case 1: return IsDma3ManagerBusyWithBgCopy(); } return TRUE; } static bool8 ShowConfirmDeleteAllPrompt(void) { switch (sScreenControl->funcState) { case 0: StopMainCursorAnim(); PrintEasyChatStdMessage(MSG_CONFIRM_DELETE); CreateEasyChatYesNoMenu(1); sScreenControl->funcState++; break; case 1: return IsDma3ManagerBusyWithBgCopy(); } return TRUE; } static bool8 ClosePrompt(void) { switch (sScreenControl->funcState) { case 0: StartMainCursorAnim(); PrintEasyChatStdMessage(MSG_INSTRUCTIONS); PrintCurrentPhrase(); ShowBg(0); sScreenControl->funcState++; break; case 1: return IsDma3ManagerBusyWithBgCopy(); } return TRUE; } static bool8 ClosePromptAfterDeleteAll(void) { switch (sScreenControl->funcState) { case 0: StartMainCursorAnim(); PrintEasyChatStdMessage(MSG_INSTRUCTIONS); PrintCurrentPhrase(); sScreenControl->funcState++; // Fall through case 1: return IsDma3ManagerBusyWithBgCopy(); } return TRUE; } static bool8 OpenKeyboard(void) { switch (sScreenControl->funcState) { case 0: StopMainCursorAnim(); HideBg(0); SetWindowDimensions(0, 0, 0, 0); PrintKeyboardText(); sScreenControl->funcState++; break; case 1: if (!IsDma3ManagerBusyWithBgCopy()) { InitLowerWindowAnim(WINANIM_OPEN_KEYBOARD); sScreenControl->funcState++; } break; case 2: if (!IsDma3ManagerBusyWithBgCopy() && !UpdateLowerWindowAnim()) sScreenControl->funcState++; break; case 3: if (!IsDma3ManagerBusyWithBgCopy()) { CreateSideWindowSprites(); sScreenControl->funcState++; } break; case 4: if (!ShowSideWindow()) { CreateRectangleCursorSprites(); SetScrollIndicatorXPos(FALSE); UpdateScrollIndicatorsVisibility(); sScreenControl->funcState++; return FALSE; } break; default: return FALSE; } return TRUE; } static bool8 CloseKeyboard(void) { switch (sScreenControl->funcState) { case 0: DestroyRectangleCursorSprites(); HideModeWindow(); HideScrollIndicators(); sScreenControl->funcState++; break; case 1: if (DestroySideWindowSprites() == TRUE) break; InitLowerWindowAnim(WINANIM_CLOSE_KEYBOARD); sScreenControl->funcState++; // Fall through case 2: if (!UpdateLowerWindowAnim()) sScreenControl->funcState++; break; case 3: if (!IsDma3ManagerBusyWithBgCopy()) { StartMainCursorAnim(); ShowBg(0); sScreenControl->funcState++; } break; case 4: return FALSE; } return TRUE; } static bool8 SwitchKeyboardMode(void) { switch (sScreenControl->funcState) { case 0: DestroyRectangleCursorSprites(); HideScrollIndicators(); SetModeWindowToTransition(); InitLowerWindowAnim(WINANIM_KEYBOARD_SWITCH_OUT); sScreenControl->funcState++; break; case 1: if (!UpdateLowerWindowAnim() && !IsModeWindowAnimActive()) { PrintKeyboardText(); sScreenControl->funcState++; } break; case 2: if (!IsDma3ManagerBusyWithBgCopy()) { InitLowerWindowAnim(WINANIM_KEYBOARD_SWITCH_IN); UpdateModeWindowAnim(); sScreenControl->funcState++; } break; case 3: if (!UpdateLowerWindowAnim() && !IsModeWindowAnimActive()) { UpdateScrollIndicatorsVisibility(); CreateRectangleCursorSprites(); sScreenControl->funcState++; return FALSE; } break; case 4: return FALSE; } return TRUE; } static bool8 UpdateKeyboardCursor(void) { UpdateRectangleCursorPos(); return FALSE; } static bool8 GroupNamesScrollDown(void) { switch (sScreenControl->funcState) { case 0: InitLowerWindowScroll(1, 4); sScreenControl->funcState++; // Fall through case 1: if (!UpdateLowerWindowScroll()) { UpdateRectangleCursorPos(); UpdateScrollIndicatorsVisibility(); return FALSE; } break; } return TRUE; } static bool8 GroupNamesScrollUp(void) { switch (sScreenControl->funcState) { case 0: InitLowerWindowScroll(-1, 4); sScreenControl->funcState++; // Fall through case 1: if (!UpdateLowerWindowScroll()) { UpdateScrollIndicatorsVisibility(); sScreenControl->funcState++; return FALSE; } break; case 2: return FALSE; } return TRUE; } static bool8 OpenWordSelect(void) { switch (sScreenControl->funcState) { case 0: DestroyRectangleCursorSprites(); HideModeWindow(); HideScrollIndicators(); sScreenControl->funcState++; break; case 1: if (!DestroySideWindowSprites()) { ClearWordSelectWindow(); sScreenControl->funcState++; } break; case 2: if (!IsDma3ManagerBusyWithBgCopy()) { InitLowerWindowAnim(WINANIM_OPEN_WORD_SELECT); sScreenControl->funcState++; } break; case 3: if (!UpdateLowerWindowAnim()) { InitLowerWindowText(TEXT_WORD_SELECT); sScreenControl->funcState++; } break; case 4: if (!IsDma3ManagerBusyWithBgCopy()) { CreateWordSelectCursorSprite(); SetScrollIndicatorXPos(TRUE); UpdateScrollIndicatorsVisibility(); UpdateStartSelectButtonsVisibility(); sScreenControl->funcState++; return FALSE; } break; case 5: return FALSE; } return TRUE; } static bool8 CloseWordSelect(void) { switch (sScreenControl->funcState) { case 0: PrintCurrentPhrase(); sScreenControl->funcState++; break; case 1: DestroyWordSelectCursorSprite(); HideScrollIndicators(); HideStartSelectButtons(); ClearWordSelectWindow(); sScreenControl->funcState++; break; case 2: if (!IsDma3ManagerBusyWithBgCopy()) { InitLowerWindowAnim(WINANIM_CLOSE_WORD_SELECT); sScreenControl->funcState++; } break; case 3: if (!UpdateLowerWindowAnim()) { ShowBg(0); sScreenControl->funcState++; } break; case 4: if (!IsDma3ManagerBusyWithBgCopy()) { StartMainCursorAnim(); sScreenControl->funcState++; return FALSE; } break; case 5: return FALSE; } return TRUE; } static bool8 ShowConfirmLyricsPrompt(void) { switch (sScreenControl->funcState) { case 0: PrintCurrentPhrase(); sScreenControl->funcState++; break; case 1: DestroyWordSelectCursorSprite(); HideScrollIndicators(); HideStartSelectButtons(); ClearWordSelectWindow(); sScreenControl->funcState++; break; case 2: if (!IsDma3ManagerBusyWithBgCopy()) { InitLowerWindowAnim(WINANIM_CLOSE_WORD_SELECT); sScreenControl->funcState++; } break; case 3: if (!UpdateLowerWindowAnim()) { PrintEasyChatStdMessage(MSG_CONFIRM); sScreenControl->funcState++; } break; case 4: if (!IsDma3ManagerBusyWithBgCopy()) { ShowBg(0); sScreenControl->funcState++; } break; case 5: if (!IsDma3ManagerBusyWithBgCopy()) { StartMainCursorAnim(); sScreenControl->funcState++; return FALSE; } break; case 6: return FALSE; } return TRUE; } static bool8 ReturnToKeyboard(void) { switch (sScreenControl->funcState) { case 0: DestroyWordSelectCursorSprite(); HideScrollIndicators(); HideStartSelectButtons(); ClearWordSelectWindow(); sScreenControl->funcState++; break; case 1: if (!IsDma3ManagerBusyWithBgCopy()) { InitLowerWindowAnim(WINANIM_RETURN_TO_KEYBOARD); sScreenControl->funcState++; } break; case 2: if (!UpdateLowerWindowAnim()) { PrintKeyboardText(); sScreenControl->funcState++; } break; case 3: if (!IsDma3ManagerBusyWithBgCopy()) { CreateSideWindowSprites(); sScreenControl->funcState++; } break; case 4: if (!ShowSideWindow()) { CreateRectangleCursorSprites(); SetScrollIndicatorXPos(FALSE); UpdateScrollIndicatorsVisibility(); sScreenControl->funcState++; return FALSE; } break; } return TRUE; } static bool8 UpdateWordSelectCursor(void) { UpdateWordSelectCursorPos(); return FALSE; } static bool8 WordSelectScrollDown(void) { switch (sScreenControl->funcState) { case 0: PrintWordSelectNextRowDown(); sScreenControl->funcState++; break; case 1: if (!IsDma3ManagerBusyWithBgCopy()) { InitLowerWindowScroll(1, 4); sScreenControl->funcState++; } break; case 2: if (!UpdateLowerWindowScroll()) { UpdateWordSelectCursorPos(); UpdateScrollIndicatorsVisibility(); UpdateStartSelectButtonsVisibility(); sScreenControl->funcState++; return FALSE; } break; case 3: return FALSE; } return TRUE; } static bool8 WordSelectScrollUp(void) { switch (sScreenControl->funcState) { case 0: PrintWordSelectNextRowUp(); sScreenControl->funcState++; break; case 1: if (!IsDma3ManagerBusyWithBgCopy()) { InitLowerWindowScroll(-1, 4); sScreenControl->funcState++; } break; case 2: if (!UpdateLowerWindowScroll()) { UpdateScrollIndicatorsVisibility(); UpdateStartSelectButtonsVisibility(); sScreenControl->funcState++; return FALSE; } break; case 3: return FALSE; } return TRUE; } static bool8 WordSelectPageScrollDown(void) { switch (sScreenControl->funcState) { case 0: PrintWordSelectRowsPageDown(); sScreenControl->funcState++; break; case 1: if (!IsDma3ManagerBusyWithBgCopy()) { s16 scrollChange = GetWordSelectScrollOffset() - GetLowerWindowScrollOffset(); InitLowerWindowScroll(scrollChange, 8); sScreenControl->funcState++; } break; case 2: if (!UpdateLowerWindowScroll()) { UpdateWordSelectCursorPos(); UpdateScrollIndicatorsVisibility(); UpdateStartSelectButtonsVisibility(); sScreenControl->funcState++; return FALSE; } break; case 3: return FALSE; } return TRUE; } static bool8 WordSelectPageScrollUp(void) { switch (sScreenControl->funcState) { case 0: PrintWordSelectRowsPageUp(); sScreenControl->funcState++; break; case 1: if (!IsDma3ManagerBusyWithBgCopy()) { s16 scrollChange = GetWordSelectScrollOffset() - GetLowerWindowScrollOffset(); InitLowerWindowScroll(scrollChange, 8); sScreenControl->funcState++; } break; case 2: if (!UpdateLowerWindowScroll()) { UpdateScrollIndicatorsVisibility(); UpdateStartSelectButtonsVisibility(); sScreenControl->funcState++; return FALSE; } break; case 3: return FALSE; } return TRUE; } static bool8 ShowCreateQuizMsg(void) { switch (sScreenControl->funcState) { case 0: StopMainCursorAnim(); PrintEasyChatStdMessage(MSG_CREATE_QUIZ); sScreenControl->funcState++; break; case 1: return IsDma3ManagerBusyWithBgCopy(); } return TRUE; } static bool8 ShowSelectAnswerMsg(void) { switch (sScreenControl->funcState) { case 0: StopMainCursorAnim(); PrintEasyChatStdMessage(MSG_SELECT_ANSWER); sScreenControl->funcState++; break; case 1: return IsDma3ManagerBusyWithBgCopy(); } return TRUE; } static bool8 ShowSongTooShortMsg(void) { switch (sScreenControl->funcState) { case 0: StopMainCursorAnim(); PrintEasyChatStdMessage(MSG_SONG_TOO_SHORT); sScreenControl->funcState++; break; case 1: return IsDma3ManagerBusyWithBgCopy(); } return TRUE; } static bool8 ShowCantDeleteLyricsMsg(void) { switch (sScreenControl->funcState) { case 0: StopMainCursorAnim(); PrintEasyChatStdMessage(MSG_CANT_DELETE_LYRICS); sScreenControl->funcState++; break; case 1: return IsDma3ManagerBusyWithBgCopy(); } return TRUE; } static bool8 ShowCombineTwoWordsMsg(void) { switch (sScreenControl->funcState) { case 0: StopMainCursorAnim(); PrintEasyChatStdMessage(MSG_COMBINE_TWO_WORDS); sScreenControl->funcState++; break; case 1: return IsDma3ManagerBusyWithBgCopy(); } return TRUE; } static bool8 ShowCantExitMsg(void) { switch (sScreenControl->funcState) { case 0: StopMainCursorAnim(); PrintEasyChatStdMessage(MSG_CANT_QUIT); sScreenControl->funcState++; break; case 1: return IsDma3ManagerBusyWithBgCopy(); } return TRUE; } static bool8 InitEasyChatScreenControl_(void) { sScreenControl = Alloc(sizeof(*sScreenControl)); if (!sScreenControl) return FALSE; sScreenControl->funcState = 0; sScreenControl->mainCursorSprite = NULL; sScreenControl->rectangleCursorSpriteRight = NULL; sScreenControl->rectangleCursorSpriteLeft = NULL; sScreenControl->wordSelectCursorSprite = NULL; sScreenControl->buttonWindowSprite = NULL; sScreenControl->modeWindowSprite = NULL; sScreenControl->scrollIndicatorUpSprite = NULL; sScreenControl->scrollIndicatorDownSprite = NULL; sScreenControl->startButtonSprite = NULL; sScreenControl->selectButtonSprite = NULL; sScreenControl->fourFooterOptions = FooterHasFourOptions_(); return TRUE; } static void InitEasyChatBgs(void) { ChangeBgX(3, 0, BG_COORD_SET); ChangeBgY(3, 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(0, 0, BG_COORD_SET); ChangeBgY(0, 0, BG_COORD_SET); SetGpuReg(REG_OFFSET_DISPCNT, DISPCNT_MODE_0 | DISPCNT_OBJ_1D_MAP | DISPCNT_OBJ_ON | DISPCNT_WIN0_ON); } static void LoadEasyChatPalettes(void) { ResetPaletteFade(); LoadPalette(gEasyChatMode_Pal, 0, 32); LoadPalette(sTextInputFrameOrange_Pal, 1 * 16, 32); LoadPalette(sTextInputFrameGreen_Pal, 4 * 16, 32); LoadPalette(sTitleText_Pal, 10 * 16, 8); LoadPalette(sText_Pal, 11 * 16, 12); LoadPalette(sText_Pal, 15 * 16, 12); LoadPalette(sText_Pal, 3 * 16, 12); } static void PrintTitle(void) { int xOffset; const u8 *titleText = GetTitleText(); if (!titleText) return; xOffset = GetStringCenterAlignXOffset(FONT_NORMAL, titleText, 144); FillWindowPixelBuffer(0, PIXEL_FILL(0)); PrintEasyChatTextWithColors(0, FONT_NORMAL, titleText, xOffset, 1, TEXT_SKIP_DRAW, TEXT_COLOR_TRANSPARENT, TEXT_COLOR_DARK_GRAY, TEXT_COLOR_LIGHT_GRAY); PutWindowTilemap(0); CopyWindowToVram(0, COPYWIN_FULL); } static void PrintEasyChatText(u8 windowId, u8 fontId, const u8 *str, u8 x, u8 y, u8 speed, void (*callback)(struct TextPrinterTemplate *, u16)) { AddTextPrinterParameterized(windowId, fontId, str, x, y, speed, callback); } static void PrintEasyChatTextWithColors(u8 windowId, u8 fontId, const u8 *str, u8 left, u8 top, u8 speed, u8 bg, u8 fg, u8 shadow) { u8 color[3]; color[0] = bg; color[1] = fg; color[2] = shadow; AddTextPrinterParameterized3(windowId, fontId, left, top, color, speed, str); } static void PrintInitialInstructions(void) { FillBgTilemapBufferRect(0, 0, 0, 0, 32, 20, 17); LoadUserWindowBorderGfx(1, 1, 0xE0); DrawTextBorderOuter(1, 1, 14); PrintEasyChatStdMessage(MSG_INSTRUCTIONS); PutWindowTilemap(1); CopyBgTilemapBufferToVram(0); } static void PrintEasyChatStdMessage(u8 msgId) { const u8 *text2 = NULL; const u8 *text1 = NULL; switch (msgId) { case MSG_INSTRUCTIONS: GetEasyChatInstructionsText(&text1, &text2); break; case MSG_CONFIRM_EXIT: GetEasyChatConfirmExitText(&text1, &text2); break; case MSG_CONFIRM: GetEasyChatConfirmText(&text1, &text2); break; case MSG_CONFIRM_DELETE: GetEasyChatConfirmDeletionText(&text1, &text2); break; case MSG_CREATE_QUIZ: text1 = gText_CreateAQuiz; break; case MSG_SELECT_ANSWER: text1 = gText_SelectTheAnswer; break; case MSG_SONG_TOO_SHORT: text1 = gText_OnlyOnePhrase; text2 = gText_OriginalSongWillBeUsed; break; case MSG_CANT_DELETE_LYRICS: text1 = gText_LyricsCantBeDeleted; break; case MSG_COMBINE_TWO_WORDS: text1 = gText_CombineTwoWordsOrPhrases3; break; case MSG_CANT_QUIT: text1 = gText_YouCannotQuitHere; text2 = gText_SectionMustBeCompleted; break; } FillWindowPixelBuffer(1, PIXEL_FILL(1)); if (text1) PrintEasyChatText(1, FONT_NORMAL, text1, 0, 1, TEXT_SKIP_DRAW, 0); if (text2) PrintEasyChatText(1, FONT_NORMAL, text2, 0, 17, TEXT_SKIP_DRAW, 0); CopyWindowToVram(1, COPYWIN_FULL); } static void CreateEasyChatYesNoMenu(u8 initialCursorPos) { CreateYesNoMenu(&sEasyChatYesNoWindowTemplate, 1, 14, initialCursorPos); } static void AddPhraseWindow(void) { u8 frameId; struct WindowTemplate template; frameId = GetEasyChatScreenFrameId(); template.bg = 3; template.tilemapLeft = sPhraseFrameDimensions[frameId].left; template.tilemapTop = sPhraseFrameDimensions[frameId].top; template.width = sPhraseFrameDimensions[frameId].width; template.height = sPhraseFrameDimensions[frameId].height; template.paletteNum = 11; template.baseBlock = 0x6C; sScreenControl->windowId = AddWindow(&template); PutWindowTilemap(sScreenControl->windowId); } static void PrintCurrentPhrase(void) { u8 strClear[4]; u16 *currentPhrase; u8 numColumns, numRows; u8 *str; int frameId; bool32 isQuizQuestion; int i, j, k; currentPhrase = GetCurrentPhrase(); numColumns = GetNumColumns(); numRows = GetNumRows(); frameId = GetEasyChatScreenFrameId(); isQuizQuestion = FALSE; if (frameId == FRAMEID_QUIZ_QUESTION) isQuizQuestion = TRUE; FillWindowPixelBuffer(sScreenControl->windowId, PIXEL_FILL(1)); for (i = 0; i < numRows; i++) { memcpy(strClear, sText_Clear17, sizeof(sText_Clear17)); if (isQuizQuestion) strClear[2] = 6; str = sScreenControl->phrasePrintBuffer; sScreenControl->phrasePrintBuffer[0] = EOS; str = StringAppend(str, strClear); for (j = 0; j < numColumns; j++) { if (*currentPhrase != EC_EMPTY_WORD) { str = CopyEasyChatWord(str, *currentPhrase); currentPhrase++; } else { currentPhrase++; if (!isQuizQuestion) { str = WriteColorChangeControlCode(str, 0, 4); for (k = 0; k < 12; k++) { *str = CHAR_HYPHEN; str++; } str = WriteColorChangeControlCode(str, 0, 2); } } if (isQuizQuestion) strClear[2] = 3; str = StringAppend(str, strClear); if (frameId == FRAMEID_MAIL || frameId == FRAMEID_QUIZ_QUESTION || frameId == FRAMEID_QUIZ_SET_QUESTION) { // Is 2x5 frame, end on 9th word if (j == 0 && i == 4) break; } } *str = EOS; PrintEasyChatText(sScreenControl->windowId, FONT_NORMAL, sScreenControl->phrasePrintBuffer, 0, i * 16 + 1, TEXT_SKIP_DRAW, 0); } CopyWindowToVram(sScreenControl->windowId, COPYWIN_FULL); } static void BufferFrameTilemap(u16 *tilemap) { u8 frameId; int right, bottom; int x, y; frameId = GetEasyChatScreenFrameId(); CpuFastFill(0, tilemap, BG_SCREEN_SIZE); if (frameId == FRAMEID_MAIL || frameId == FRAMEID_QUIZ_SET_QUESTION) { // These frames fill the screen, no need to draw top/bottom edges right = sPhraseFrameDimensions[frameId].left + sPhraseFrameDimensions[frameId].width; bottom = sPhraseFrameDimensions[frameId].top + sPhraseFrameDimensions[frameId].height; // Draw middle section for (y = sPhraseFrameDimensions[frameId].top; y < bottom; y++) { x = sPhraseFrameDimensions[frameId].left - 1; tilemap[y * 32 + x] = FRAME_OFFSET_ORANGE + FRAME_TILE_L_EDGE; x++; for (; x < right; x++) tilemap[y * 32 + x] = FRAME_OFFSET_ORANGE + FRAME_TILE_TRANSPARENT; tilemap[y* 32 + x] = FRAME_OFFSET_ORANGE + FRAME_TILE_R_EDGE; } } else { y = sPhraseFrameDimensions[frameId].top - 1; x = sPhraseFrameDimensions[frameId].left - 1; right = sPhraseFrameDimensions[frameId].left + sPhraseFrameDimensions[frameId].width; bottom = sPhraseFrameDimensions[frameId].top + sPhraseFrameDimensions[frameId].height; // Draw top edge tilemap[y * 32 + x] = FRAME_OFFSET_ORANGE + FRAME_TILE_TOP_L_CORNER; x++; for (; x < right; x++) tilemap[y * 32 + x] = FRAME_OFFSET_ORANGE + FRAME_TILE_TOP_EDGE; tilemap[y * 32 + x] = FRAME_OFFSET_ORANGE + FRAME_TILE_TOP_R_CORNER; y++; // Draw middle section for (; y < bottom; y++) { x = sPhraseFrameDimensions[frameId].left - 1; tilemap[y * 32 + x] = FRAME_OFFSET_ORANGE + FRAME_TILE_L_EDGE; x++; for (; x < right; x++) tilemap[y * 32 + x] = FRAME_OFFSET_ORANGE + FRAME_TILE_TRANSPARENT; tilemap[y* 32 + x] = FRAME_OFFSET_ORANGE + FRAME_TILE_R_EDGE; } // Draw bottom edge x = sPhraseFrameDimensions[frameId].left - 1; tilemap[y * 32 + x] = FRAME_OFFSET_ORANGE + FRAME_TILE_BOTTOM_L_CORNER; x++; for (; x < right; x++) tilemap[y * 32 + x] = FRAME_OFFSET_ORANGE + FRAME_TILE_BOTTOM_EDGE; tilemap[y * 32 + x] = FRAME_OFFSET_ORANGE + FRAME_TILE_BOTTOM_R_CORNER; } } static void AdjustBgTilemapForFooter(void) { u8 frameId; u16 *tilemap; tilemap = GetBgTilemapBuffer(3); frameId = GetEasyChatScreenFrameId(); switch (sPhraseFrameDimensions[frameId].footerId) { case FOOTER_ANSWER: tilemap += 0x2A0; CopyToBgTilemapBufferRect(3, tilemap, 0, 11, 32, 2); break; case FOOTER_QUIZ: tilemap += 0x300; CopyToBgTilemapBufferRect(3, tilemap, 0, 11, 32, 2); break; case NUM_FOOTER_TYPES: CopyToBgTilemapBufferRect(3, tilemap, 0, 10, 32, 4); break; } } static void DrawLowerWindow(void) { PutWindowTilemap(2); CopyBgTilemapBufferToVram(2); } static void InitLowerWindowText(u32 whichText) { ResetLowerWindowScroll(); FillWindowPixelBuffer(2, PIXEL_FILL(1)); switch (whichText) { case TEXT_GROUPS: PrintKeyboardGroupNames(); break; case TEXT_ALPHABET: PrintKeyboardAlphabet(); break; case TEXT_WORD_SELECT: PrintInitialWordSelectText(); break; } CopyWindowToVram(2, COPYWIN_GFX); } static void PrintKeyboardText(void) { if (!GetInAlphabetMode()) InitLowerWindowText(TEXT_GROUPS); else InitLowerWindowText(TEXT_ALPHABET); } static void PrintKeyboardGroupNames(void) { int i; int x, y; i = 0; y = 97; while (1) { for (x = 0; x < 2; x++) { u8 groupId = GetUnlockedEasyChatGroupId(i++); if (groupId == EC_NUM_GROUPS) { InitLowerWindowScroll(GetKeyboardScrollOffset(), 0); return; } PrintEasyChatText(2, FONT_NORMAL, GetEasyChatWordGroupName(groupId), x * 84 + 10, y, TEXT_SKIP_DRAW, NULL); } y += 16; } } static void PrintKeyboardAlphabet(void) { u32 i; for (i = 0; i < ARRAY_COUNT(sEasyChatKeyboardAlphabet); i++) PrintEasyChatText(2, FONT_NORMAL, sEasyChatKeyboardAlphabet[i], 10, 97 + i * 16, TEXT_SKIP_DRAW, NULL); } static void PrintInitialWordSelectText(void) { PrintWordSelectText(0, NUM_WORD_SELECT_ROWS); } static void PrintWordSelectNextRowDown(void) { u8 wordScroll = GetWordSelectScrollOffset() + NUM_WORD_SELECT_ROWS - 1; EraseWordSelectRows(wordScroll, 1); PrintWordSelectText(wordScroll, 1); } static void PrintWordSelectNextRowUp(void) { u8 wordScroll = GetWordSelectScrollOffset(); EraseWordSelectRows(wordScroll, 1); PrintWordSelectText(wordScroll, 1); } static void PrintWordSelectRowsPageDown(void) { u8 wordScroll = GetWordSelectScrollOffset(); u8 maxScroll = wordScroll + NUM_WORD_SELECT_ROWS; u8 maxRows = GetWordSelectLastRow() + 1; if (maxScroll > maxRows) maxScroll = maxRows; if (wordScroll < maxScroll) { u8 numRows = maxScroll - wordScroll; EraseWordSelectRows(wordScroll, numRows); PrintWordSelectText(wordScroll, numRows); } } static void PrintWordSelectRowsPageUp(void) { u8 wordScroll = GetWordSelectScrollOffset(); u8 windowScroll = GetLowerWindowScrollOffset(); if (wordScroll < windowScroll) { u8 numRows = windowScroll - wordScroll; EraseWordSelectRows(wordScroll, numRows); PrintWordSelectText(wordScroll, numRows); } } // Print the easy chat words available for selection in // the currently selected group and at the given offset and row static void PrintWordSelectText(u8 scrollOffset, u8 numRows) { int i, j; u16 easyChatWord; int y; int wordIndex; wordIndex = scrollOffset * NUM_WORD_SELECT_COLUMNS; y = (scrollOffset * 16 + 96) & 0xFF; y++; for (i = 0; i < numRows; i++) { for (j = 0; j < 2; j++) { easyChatWord = GetWordFromSelectedGroup(wordIndex++); if (easyChatWord != EC_EMPTY_WORD) { CopyEasyChatWordPadded(sScreenControl->wordSelectPrintBuffer, easyChatWord, 0); if (!DummyWordCheck(easyChatWord)) PrintEasyChatText(2, FONT_NORMAL, sScreenControl->wordSelectPrintBuffer, (j * 13 + 3) * 8, y, TEXT_SKIP_DRAW, NULL); else // Never reached PrintEasyChatTextWithColors(2, FONT_NORMAL, sScreenControl->wordSelectPrintBuffer, (j * 13 + 3) * 8, y, TEXT_SKIP_DRAW, TEXT_COLOR_WHITE, TEXT_COLOR_LIGHT_RED, TEXT_COLOR_LIGHT_GRAY); } } y += 16; } CopyWindowToVram(2, COPYWIN_GFX); } static void EraseWordSelectRows(u8 scrollOffset, u8 numRows) { int y; int var0; int var1; int var2; y = (scrollOffset * 16 + 96) & 0xFF; var2 = numRows * 16; var0 = y + var2; if (var0 > 255) { var1 = var0 - 256; var2 = 256 - y; } else { var1 = 0; } FillWindowPixelRect(2, PIXEL_FILL(1), 0, y, 224, var2); if (var1) FillWindowPixelRect(2, PIXEL_FILL(1), 0, 0, 224, var1); } static void ClearWordSelectWindow(void) { FillWindowPixelBuffer(2, PIXEL_FILL(1)); CopyWindowToVram(2, COPYWIN_GFX); } static void InitLowerWindowAnim(int winAnimType) { switch (winAnimType) { case WINANIM_OPEN_KEYBOARD: sScreenControl->curWindowAnimState = 0; sScreenControl->destWindowAnimState = 10; break; case WINANIM_CLOSE_KEYBOARD: sScreenControl->curWindowAnimState = 9; sScreenControl->destWindowAnimState = 0; break; case WINANIM_OPEN_WORD_SELECT: sScreenControl->curWindowAnimState = 11; sScreenControl->destWindowAnimState = 17; break; case WINANIM_CLOSE_WORD_SELECT: sScreenControl->curWindowAnimState = 17; sScreenControl->destWindowAnimState = 0; break; case WINANIM_RETURN_TO_KEYBOARD: sScreenControl->curWindowAnimState = 17; sScreenControl->destWindowAnimState = 10; break; case WINANIM_KEYBOARD_SWITCH_OUT: sScreenControl->curWindowAnimState = 18; sScreenControl->destWindowAnimState = 22; break; case WINANIM_KEYBOARD_SWITCH_IN: sScreenControl->curWindowAnimState = 22; sScreenControl->destWindowAnimState = 18; break; } sScreenControl->windowAnimStateDir = sScreenControl->curWindowAnimState < sScreenControl->destWindowAnimState ? 1 : -1; } // Returns FALSE if the anim is finished static bool8 UpdateLowerWindowAnim(void) { u8 curState, destState; if (sScreenControl->curWindowAnimState == sScreenControl->destWindowAnimState) return FALSE; sScreenControl->curWindowAnimState += sScreenControl->windowAnimStateDir; DrawLowerWindowFrame(sScreenControl->curWindowAnimState); curState = sScreenControl->curWindowAnimState; destState = sScreenControl->destWindowAnimState; return (curState ^ destState) > 0; } // States in this function are used incrementally with differing start/end cases // to draw the lower window and create the appearance that it's opening/closing/animating. // See InitLowerWindowAnim static void DrawLowerWindowFrame(u8 type) { FillBgTilemapBufferRect_Palette0(1, 0, 0, 10, 30, 10); switch (type) { case 0: // Closed break; case 1: BufferLowerWindowFrame(11, 14, 3, 2); break; case 2: BufferLowerWindowFrame(9, 14, 7, 2); break; case 3: BufferLowerWindowFrame(7, 14, 11, 2); break; case 4: BufferLowerWindowFrame(5, 14, 15, 2); break; case 5: BufferLowerWindowFrame(3, 14, 19, 2); break; case 6: BufferLowerWindowFrame(1, 14, 23, 2); break; case 7: BufferLowerWindowFrame(1, 13, 23, 4); break; case 8: BufferLowerWindowFrame(1, 12, 23, 6); break; case 9: BufferLowerWindowFrame(1, 11, 23, 8); break; case 10: BufferLowerWindowFrame(1, 10, 23, 10); break; case 11: BufferLowerWindowFrame(1, 10, 24, 10); break; case 12: BufferLowerWindowFrame(1, 10, 25, 10); break; case 13: BufferLowerWindowFrame(1, 10, 26, 10); break; case 14: BufferLowerWindowFrame(1, 10, 27, 10); break; case 15: BufferLowerWindowFrame(1, 10, 28, 10); break; case 16: BufferLowerWindowFrame(1, 10, 29, 10); break; case 17: BufferLowerWindowFrame(0, 10, 30, 10); break; case 18: BufferLowerWindowFrame(1, 10, 23, 10); break; case 19: BufferLowerWindowFrame(1, 11, 23, 8); break; case 20: BufferLowerWindowFrame(1, 12, 23, 6); break; case 21: BufferLowerWindowFrame(1, 13, 23, 4); break; case 22: BufferLowerWindowFrame(1, 14, 23, 2); break; } CopyBgTilemapBufferToVram(1); } static void BufferLowerWindowFrame(int left, int top, int width, int height) { u16 *tilemap; int right; int bottom; int x, y; tilemap = sScreenControl->bg1TilemapBuffer; right = left + width - 1; bottom = top + height - 1; x = left; y = top; // Draw top edge tilemap[y * 32 + x] = FRAME_OFFSET_GREEN + FRAME_TILE_TOP_L_CORNER; x++; for (; x < right; x++) tilemap[y * 32 + x] = FRAME_OFFSET_GREEN + FRAME_TILE_TOP_EDGE; tilemap[y * 32 + x] = FRAME_OFFSET_GREEN + FRAME_TILE_TOP_R_CORNER; y++; // Draw middle section for (; y < bottom; y++) { tilemap[y * 32 + left] = FRAME_OFFSET_GREEN + FRAME_TILE_L_EDGE; x = left + 1; for (; x < right; x++) tilemap[y * 32 + x] = FRAME_OFFSET_GREEN + FRAME_TILE_TRANSPARENT; tilemap[y * 32 + x] = FRAME_OFFSET_GREEN + FRAME_TILE_R_EDGE; } // Draw bottom edge tilemap[y * 32 + left] = FRAME_OFFSET_GREEN + FRAME_TILE_BOTTOM_L_CORNER; x = left + 1; for (; x < right; x++) tilemap[y * 32 + x] = FRAME_OFFSET_GREEN + FRAME_TILE_BOTTOM_EDGE; tilemap[y * 32 + x] = FRAME_OFFSET_GREEN + FRAME_TILE_BOTTOM_R_CORNER; SetWindowDimensions((left + 1) * 8, (top + 1) * 8, (width - 2) * 8, (height - 2) * 8); } static void ResetLowerWindowScroll(void) { ChangeBgY(2, 0x800, BG_COORD_SET); sScreenControl->scrollOffset = 0; } static void InitLowerWindowScroll(s16 scrollChange, u8 speed) { int bgY; s16 yChange; bgY = GetBgY(2); sScreenControl->scrollOffset += scrollChange; yChange = scrollChange * 16; bgY += yChange * 256; if (speed) { sScreenControl->scrollDest = bgY; sScreenControl->scrollSpeed = speed * 256; if (yChange < 0) sScreenControl->scrollSpeed = -sScreenControl->scrollSpeed; } else { ChangeBgY(2, bgY, BG_COORD_SET); } } static bool8 UpdateLowerWindowScroll(void) { int bgY; bgY = GetBgY(2); if (bgY == sScreenControl->scrollDest) { return FALSE; } else { ChangeBgY(2, sScreenControl->scrollSpeed, BG_COORD_ADD); return TRUE; } } static int GetLowerWindowScrollOffset(void) { return sScreenControl->scrollOffset; } static void SetWindowDimensions(u8 left, u8 top, u8 width, u8 height) { u16 horizontalDimensions = WIN_RANGE(left, left + width); u16 verticalDimensions = WIN_RANGE(top, top + height); SetGpuReg(REG_OFFSET_WIN0H, horizontalDimensions); SetGpuReg(REG_OFFSET_WIN0V, verticalDimensions); } static void LoadEasyChatGfx(void) { u32 i; LoadSpriteSheets(sSpriteSheets); LoadSpritePalettes(sSpritePalettes); for (i = 0; i < ARRAY_COUNT(sCompressedSpriteSheets); i++) LoadCompressedSpriteSheet(&sCompressedSpriteSheets[i]); } #define sDelayTimer data[0] #define sAnimateCursor data[1] static void CreateMainCursorSprite(void) { u8 frameId = GetEasyChatScreenFrameId(); int x = sPhraseFrameDimensions[frameId].left * 8 + 13; int y = sPhraseFrameDimensions[frameId].top * 8 + 8; u8 spriteId = CreateSprite(&sSpriteTemplate_TriangleCursor, x, y, 2); sScreenControl->mainCursorSprite = &gSprites[spriteId]; gSprites[spriteId].sAnimateCursor = TRUE; } static void SpriteCB_Cursor(struct Sprite *sprite) { if (sprite->sAnimateCursor) { if (++sprite->sDelayTimer > 2) { sprite->sDelayTimer = 0; if (++sprite->x2 > 0) sprite->x2 = -6; } } } static void SetMainCursorPos(u8 x, u8 y) { sScreenControl->mainCursorSprite->x = x; sScreenControl->mainCursorSprite->y = y; sScreenControl->mainCursorSprite->x2 = 0; sScreenControl->mainCursorSprite->sDelayTimer = 0; } static void StopMainCursorAnim(void) { sScreenControl->mainCursorSprite->sDelayTimer = 0; sScreenControl->mainCursorSprite->sAnimateCursor = FALSE; sScreenControl->mainCursorSprite->x2 = 0; } static void StartMainCursorAnim(void) { sScreenControl->mainCursorSprite->sAnimateCursor = TRUE; } static void CreateRectangleCursorSprites(void) { u8 spriteId = CreateSprite(&sSpriteTemplate_RectangleCursor, 0, 0, 3); sScreenControl->rectangleCursorSpriteRight = &gSprites[spriteId]; sScreenControl->rectangleCursorSpriteRight->x2 = 32; spriteId = CreateSprite(&sSpriteTemplate_RectangleCursor, 0, 0, 3); sScreenControl->rectangleCursorSpriteLeft = &gSprites[spriteId]; sScreenControl->rectangleCursorSpriteLeft->x2 = -32; sScreenControl->rectangleCursorSpriteRight->hFlip = TRUE; UpdateRectangleCursorPos(); } static void DestroyRectangleCursorSprites(void) { DestroySprite(sScreenControl->rectangleCursorSpriteRight); sScreenControl->rectangleCursorSpriteRight = NULL; DestroySprite(sScreenControl->rectangleCursorSpriteLeft); sScreenControl->rectangleCursorSpriteLeft = NULL; } static void UpdateRectangleCursorPos(void) { s8 column; s8 row; if (sScreenControl->rectangleCursorSpriteRight && sScreenControl->rectangleCursorSpriteLeft) { GetKeyboardCursorColAndRow(&column, &row); if (!GetInAlphabetMode()) SetRectangleCursorPos_GroupMode(column, row); else SetRectangleCursorPos_AlphabetMode(column, row); } } static void SetRectangleCursorPos_GroupMode(s8 column, s8 row) { if (column != -1) { // In group name window StartSpriteAnim(sScreenControl->rectangleCursorSpriteRight, RECTCURSOR_ANIM_ON_GROUP); sScreenControl->rectangleCursorSpriteRight->x = column * 84 + 58; sScreenControl->rectangleCursorSpriteRight->y = row * 16 + 96; StartSpriteAnim(sScreenControl->rectangleCursorSpriteLeft, RECTCURSOR_ANIM_ON_GROUP); sScreenControl->rectangleCursorSpriteLeft->x = column * 84 + 58; sScreenControl->rectangleCursorSpriteLeft->y = row * 16 + 96; } else { // In button window StartSpriteAnim(sScreenControl->rectangleCursorSpriteRight, RECTCURSOR_ANIM_ON_BUTTON); sScreenControl->rectangleCursorSpriteRight->x = 216; sScreenControl->rectangleCursorSpriteRight->y = row * 16 + 112; StartSpriteAnim(sScreenControl->rectangleCursorSpriteLeft, RECTCURSOR_ANIM_ON_BUTTON); sScreenControl->rectangleCursorSpriteLeft->x = 216; sScreenControl->rectangleCursorSpriteLeft->y = row * 16 + 112; } } static void SetRectangleCursorPos_AlphabetMode(s8 column, s8 row) { int anim; int x, y; if (column != -1) { y = row * 16 + 96; x = 32; if (column == NUM_ALPHABET_COLUMNS - 1 && row == 0) { // Cursor is on 'Others' x = 158; anim = RECTCURSOR_ANIM_ON_OTHERS; } else { // Cursor is on a letter x += sAlphabetKeyboardColumnOffsets[(u8)column < NUM_ALPHABET_COLUMNS ? column : 0]; anim = RECTCURSOR_ANIM_ON_LETTER; } StartSpriteAnim(sScreenControl->rectangleCursorSpriteRight, anim); sScreenControl->rectangleCursorSpriteRight->x = x; sScreenControl->rectangleCursorSpriteRight->y = y; StartSpriteAnim(sScreenControl->rectangleCursorSpriteLeft, anim); sScreenControl->rectangleCursorSpriteLeft->x = x; sScreenControl->rectangleCursorSpriteLeft->y = y; } else { // In button window StartSpriteAnim(sScreenControl->rectangleCursorSpriteRight, RECTCURSOR_ANIM_ON_BUTTON); sScreenControl->rectangleCursorSpriteRight->x = 216; sScreenControl->rectangleCursorSpriteRight->y = row * 16 + 112; StartSpriteAnim(sScreenControl->rectangleCursorSpriteLeft, RECTCURSOR_ANIM_ON_BUTTON); sScreenControl->rectangleCursorSpriteLeft->x = 216; sScreenControl->rectangleCursorSpriteLeft->y = row * 16 + 112; } } // Cursor for selecting a new word // Identical in appearance to the 'main' cursor static void CreateWordSelectCursorSprite(void) { u8 spriteId = CreateSprite(&sSpriteTemplate_TriangleCursor, 0, 0, 4); sScreenControl->wordSelectCursorSprite = &gSprites[spriteId]; sScreenControl->wordSelectCursorSprite->callback = SpriteCB_WordSelectCursor; sScreenControl->wordSelectCursorSprite->oam.priority = 2; UpdateWordSelectCursorPos(); } static void SpriteCB_WordSelectCursor(struct Sprite *sprite) { if (++sprite->sDelayTimer > 2) { sprite->sDelayTimer = 0; if (++sprite->x2 > 0) sprite->x2 = -6; } } static void UpdateWordSelectCursorPos(void) { s8 column, row, x, y; GetWordSelectColAndRow(&column, &row); x = column * 13; x = x * 8 + 28; y = row * 16 + 96; SetWordSelectCursorPos(x, y); } static void SetWordSelectCursorPos(u8 x, u8 y) { if (sScreenControl->wordSelectCursorSprite) { sScreenControl->wordSelectCursorSprite->x = x; sScreenControl->wordSelectCursorSprite->y = y; sScreenControl->wordSelectCursorSprite->x2 = 0; sScreenControl->wordSelectCursorSprite->sDelayTimer = 0; } } static void DestroyWordSelectCursorSprite(void) { if (sScreenControl->wordSelectCursorSprite) { DestroySprite(sScreenControl->wordSelectCursorSprite); sScreenControl->wordSelectCursorSprite = NULL; } } static void CreateSideWindowSprites(void) { u8 spriteId = CreateSprite(&sSpriteTemplate_ButtonWindow, 208, 128, 6); sScreenControl->buttonWindowSprite = &gSprites[spriteId]; sScreenControl->buttonWindowSprite->x2 = -64; spriteId = CreateSprite(&sSpriteTemplate_ModeWindow, 208, 80, 5); sScreenControl->modeWindowSprite = &gSprites[spriteId]; sScreenControl->modeWindowState = 0; } static bool8 ShowSideWindow(void) { switch (sScreenControl->modeWindowState) { default: return FALSE; case 0: // Slide button window on sScreenControl->buttonWindowSprite->x2 += 8; if (sScreenControl->buttonWindowSprite->x2 >= 0) { sScreenControl->buttonWindowSprite->x2 = 0; // Set mode window anim if (!GetInAlphabetMode()) StartSpriteAnim(sScreenControl->modeWindowSprite, MODEWINDOW_ANIM_TO_GROUP); else StartSpriteAnim(sScreenControl->modeWindowSprite, MODEWINDOW_ANIM_TO_ALPHABET); sScreenControl->modeWindowState++; } break; case 1: if (sScreenControl->modeWindowSprite->animEnded) { sScreenControl->modeWindowState = 2; return FALSE; } } return TRUE; } static void HideModeWindow(void) { sScreenControl->modeWindowState = 0; StartSpriteAnim(sScreenControl->modeWindowSprite, MODEWINDOW_ANIM_TO_HIDDEN); } static bool8 DestroySideWindowSprites(void) { switch (sScreenControl->modeWindowState) { default: return FALSE; case 0: if (sScreenControl->modeWindowSprite->animEnded) sScreenControl->modeWindowState = 1; break; case 1: sScreenControl->buttonWindowSprite->x2 -= 8; if (sScreenControl->buttonWindowSprite->x2 <= -64) { DestroySprite(sScreenControl->modeWindowSprite); DestroySprite(sScreenControl->buttonWindowSprite); sScreenControl->modeWindowSprite = NULL; sScreenControl->buttonWindowSprite = NULL; sScreenControl->modeWindowState++; return FALSE; } } return TRUE; } static void SetModeWindowToTransition(void) { StartSpriteAnim(sScreenControl->modeWindowSprite, MODEWINDOW_ANIM_TRANSITION); } static void UpdateModeWindowAnim(void) { if (!GetInAlphabetMode()) StartSpriteAnim(sScreenControl->modeWindowSprite, MODEWINDOW_ANIM_TO_GROUP); else StartSpriteAnim(sScreenControl->modeWindowSprite, MODEWINDOW_ANIM_TO_ALPHABET); } static bool8 IsModeWindowAnimActive(void) { return !sScreenControl->modeWindowSprite->animEnded; } static void CreateScrollIndicatorSprites(void) { u8 spriteId = CreateSprite(&sSpriteTemplate_ScrollIndicator, 96, 80, 0); if (spriteId != MAX_SPRITES) sScreenControl->scrollIndicatorUpSprite = &gSprites[spriteId]; spriteId = CreateSprite(&sSpriteTemplate_ScrollIndicator, 96, 156, 0); if (spriteId != MAX_SPRITES) { sScreenControl->scrollIndicatorDownSprite = &gSprites[spriteId]; sScreenControl->scrollIndicatorDownSprite->vFlip = TRUE; } HideScrollIndicators(); } static void UpdateScrollIndicatorsVisibility(void) { sScreenControl->scrollIndicatorUpSprite->invisible = !CanScrollUp(); sScreenControl->scrollIndicatorDownSprite->invisible = !CanScrollDown(); } static void HideScrollIndicators(void) { sScreenControl->scrollIndicatorUpSprite->invisible = TRUE; sScreenControl->scrollIndicatorDownSprite->invisible = TRUE; } static void SetScrollIndicatorXPos(bool32 inWordSelect) { if (!inWordSelect) { // Keyboard (only relevant for group mode, can't scroll in alphabet mode) sScreenControl->scrollIndicatorUpSprite->x = 96; sScreenControl->scrollIndicatorDownSprite->x = 96; } else { // Word select sScreenControl->scrollIndicatorUpSprite->x = 120; sScreenControl->scrollIndicatorDownSprite->x = 120; } } // The Start/Select buttons are used as page scroll indicators static void CreateStartSelectButtonSprites(void) { u8 spriteId = CreateSprite(&sSpriteTemplate_StartSelectButton, 220, 84, 1); if (spriteId != MAX_SPRITES) sScreenControl->startButtonSprite = &gSprites[spriteId]; spriteId = CreateSprite(&sSpriteTemplate_StartSelectButton, 220, 156, 1); if (spriteId != MAX_SPRITES) { sScreenControl->selectButtonSprite = &gSprites[spriteId]; StartSpriteAnim(sScreenControl->selectButtonSprite, 1); } HideStartSelectButtons(); } static void UpdateStartSelectButtonsVisibility(void) { sScreenControl->startButtonSprite->invisible = !CanScrollUp(); sScreenControl->selectButtonSprite->invisible = !CanScrollDown(); } static void HideStartSelectButtons(void) { sScreenControl->startButtonSprite->invisible = TRUE; sScreenControl->selectButtonSprite->invisible = TRUE; } static void TryAddInterviewObjectEvents(void) { int graphicsId; u8 spriteId; switch (GetDisplayedPersonType()) { case EASY_CHAT_PERSON_REPORTER_MALE: graphicsId = OBJ_EVENT_GFX_REPORTER_M; break; case EASY_CHAT_PERSON_REPORTER_FEMALE: graphicsId = OBJ_EVENT_GFX_REPORTER_F; break; case EASY_CHAT_PERSON_BOY: graphicsId = OBJ_EVENT_GFX_BOY_1; break; default: return; } if (GetEasyChatScreenFrameId() != FRAMEID_INTERVIEW_SHOW_PERSON) return; // Add object for reporter/interviewing fan (facing left) spriteId = CreateObjectGraphicsSprite(graphicsId, SpriteCallbackDummy, 76, 40, 0); if (spriteId != MAX_SPRITES) { gSprites[spriteId].oam.priority = 0; StartSpriteAnim(&gSprites[spriteId], 2); } // Add object for player (facing right) spriteId = CreateObjectGraphicsSprite( gSaveBlock2Ptr->playerGender == MALE ? OBJ_EVENT_GFX_RIVAL_BRENDAN_NORMAL : OBJ_EVENT_GFX_RIVAL_MAY_NORMAL, SpriteCallbackDummy, 52, 40, 0); if (spriteId != MAX_SPRITES) { gSprites[spriteId].oam.priority = 0; StartSpriteAnim(&gSprites[spriteId], 3); } } int GetFooterIndex(void) { u8 frameId = GetEasyChatScreenFrameId(); switch (sPhraseFrameDimensions[frameId].footerId) { case FOOTER_QUIZ: return FOOTER_QUIZ; case FOOTER_ANSWER: return FOOTER_ANSWER; case FOOTER_NORMAL: return FOOTER_NORMAL; default: return NUM_FOOTER_TYPES; } } static int GetFooterOptionXOffset(int option) { int footerIndex = GetFooterIndex(); if (footerIndex < NUM_FOOTER_TYPES) return sFooterOptionXOffsets[footerIndex][option] + 4; else return 0; } static void AddMainScreenButtonWindow(void) { int i; u16 windowId; struct WindowTemplate template; int footerIndex = GetFooterIndex(); if (footerIndex == NUM_FOOTER_TYPES) return; template.bg = 3; template.tilemapLeft = 1; template.tilemapTop = 11; template.width = 28; template.height = 2; template.paletteNum = 11; template.baseBlock = 0x34; windowId = AddWindow(&template); FillWindowPixelBuffer(windowId, PIXEL_FILL(1)); for (i = 0; i < (int)ARRAY_COUNT(sFooterTextOptions[0]); i++) { const u8 *str = sFooterTextOptions[footerIndex][i]; if (str) { int x = sFooterOptionXOffsets[footerIndex][i]; PrintEasyChatText(windowId, FONT_NORMAL, str, x, 1, 0, NULL); } } PutWindowTilemap(windowId); } static bool8 IsEasyChatGroupUnlocked(u8 groupId) { switch (groupId) { case EC_GROUP_TRENDY_SAYING: return FlagGet(FLAG_SYS_HIPSTER_MEET); case EC_GROUP_EVENTS: case EC_GROUP_MOVE_1: case EC_GROUP_MOVE_2: return FlagGet(FLAG_SYS_GAME_CLEAR); case EC_GROUP_POKEMON_NATIONAL: return EasyChatIsNationalPokedexEnabled(); default: return TRUE; } } u16 EasyChat_GetNumWordsInGroup(u8 groupId) { if (groupId == EC_GROUP_POKEMON) return GetNationalPokedexCount(FLAG_GET_SEEN); if (IsEasyChatGroupUnlocked(groupId)) return gEasyChatGroups[groupId].numEnabledWords; return 0; } static bool8 IsEasyChatWordInvalid(u16 easyChatWord) { u16 i; u8 groupId; u32 index; u16 numWords; const u16 *list; if (easyChatWord == EC_EMPTY_WORD) return FALSE; groupId = EC_GROUP(easyChatWord); index = EC_INDEX(easyChatWord); if (groupId >= EC_NUM_GROUPS) return TRUE; numWords = gEasyChatGroups[groupId].numWords; switch (groupId) { case EC_GROUP_POKEMON: case EC_GROUP_POKEMON_NATIONAL: case EC_GROUP_MOVE_1: case EC_GROUP_MOVE_2: list = gEasyChatGroups[groupId].wordData.valueList; for (i = 0; i < numWords; i++) { if (index == list[i]) return FALSE; } return TRUE; } if (index >= numWords) return TRUE; else return FALSE; } bool8 IsBardWordInvalid(u16 easyChatWord) { int numWordsInGroup; u8 groupId = EC_GROUP(easyChatWord); u32 index = EC_INDEX(easyChatWord); if (groupId >= EC_NUM_GROUPS) return TRUE; switch (groupId) { case EC_GROUP_POKEMON: case EC_GROUP_POKEMON_NATIONAL: numWordsInGroup = gNumBardWords_Species; break; case EC_GROUP_MOVE_1: case EC_GROUP_MOVE_2: numWordsInGroup = gNumBardWords_Moves; break; default: numWordsInGroup = gEasyChatGroups[groupId].numWords; break; } if (numWordsInGroup <= index) return TRUE; else return FALSE; } static const u8 *GetEasyChatWord(u8 groupId, u16 index) { switch (groupId) { case EC_GROUP_POKEMON: case EC_GROUP_POKEMON_NATIONAL: return gSpeciesNames[index]; case EC_GROUP_MOVE_1: case EC_GROUP_MOVE_2: return gMoveNames[index]; default: return gEasyChatGroups[groupId].wordData.words[index].text; } } u8 *CopyEasyChatWord(u8 *dest, u16 easyChatWord) { u8 *resultStr; if (IsEasyChatWordInvalid(easyChatWord)) { resultStr = StringCopy(dest, gText_ThreeQuestionMarks); } else if (easyChatWord != EC_EMPTY_WORD) { u16 index = EC_INDEX(easyChatWord); u8 groupId = EC_GROUP(easyChatWord); resultStr = StringCopy(dest, GetEasyChatWord(groupId, index)); } else { *dest = EOS; resultStr = dest; } return resultStr; } u8 *ConvertEasyChatWordsToString(u8 *dest, const u16 *src, u16 columns, u16 rows) { u16 i, j; u16 numColumns = columns - 1; for (i = 0; i < rows; i++) { for (j = 0; j < numColumns; j++) { dest = CopyEasyChatWord(dest, *src); if (*src != EC_EMPTY_WORD) { *dest = CHAR_SPACE; dest++; } src++; } dest = CopyEasyChatWord(dest, *(src++)); *dest = CHAR_NEWLINE; dest++; } dest--; *dest = EOS; return dest; } static u8 *UnusedConvertEasyChatWordsToString(u8 *dest, const u16 *src, u16 columns, u16 rows) { u16 i, j, k; u16 numColumns; int notEmpty, lineNumber; numColumns = columns; lineNumber = 0; columns--; for (i = 0; i < rows; i++) { const u16 *str = src; notEmpty = FALSE; for (j = 0; j < numColumns; j++) { if (str[j] != EC_EMPTY_WORD) notEmpty = TRUE; } if (!notEmpty) { src += numColumns; continue; } for (k = 0; k < columns; k++) { dest = CopyEasyChatWord(dest, *src); if (*src != EC_EMPTY_WORD) { *dest = CHAR_SPACE; dest++; } src++; } dest = CopyEasyChatWord(dest, *(src++)); if (lineNumber == 0) *dest = CHAR_NEWLINE; else *dest = CHAR_PROMPT_SCROLL; dest++; lineNumber++; } dest--; *dest = EOS; return dest; } static u16 GetEasyChatWordStringLength(u16 easyChatWord) { if (easyChatWord == EC_EMPTY_WORD) return 0; if (IsEasyChatWordInvalid(easyChatWord)) { return StringLength(gText_ThreeQuestionMarks); } else { u16 index = EC_INDEX(easyChatWord); u8 groupId = EC_GROUP(easyChatWord); return StringLength(GetEasyChatWord(groupId, index)); } } static bool8 CanPhraseFitInXRowsYCols(const u16 *easyChatWords, u8 numRows, u8 numColumns, u16 maxLength) { u8 i, j; for (i = 0; i < numColumns; i++) { u16 totalLength = numRows - 1; for (j = 0; j < numRows; j++) totalLength += GetEasyChatWordStringLength(*(easyChatWords++)); if (totalLength > maxLength) return TRUE; } return FALSE; } u16 GetRandomEasyChatWordFromGroup(u16 groupId) { u16 index = Random() % gEasyChatGroups[groupId].numWords; if (groupId == EC_GROUP_POKEMON || groupId == EC_GROUP_POKEMON_NATIONAL || groupId == EC_GROUP_MOVE_1 || groupId == EC_GROUP_MOVE_2) { index = gEasyChatGroups[groupId].wordData.valueList[index]; } return EC_WORD(groupId, index); } u16 GetRandomEasyChatWordFromUnlockedGroup(u16 groupId) { if (!IsEasyChatGroupUnlocked(groupId)) return EC_EMPTY_WORD; if (groupId == EC_GROUP_POKEMON) return GetRandomUnlockedEasyChatPokemon(); return GetRandomEasyChatWordFromGroup(groupId); } void ShowEasyChatProfile(void) { u16 *easyChatWords; int columns, rows; switch (gSpecialVar_0x8004) { case 0: easyChatWords = gSaveBlock1Ptr->easyChatProfile; columns = 2; rows = 2; break; case 1: easyChatWords = gSaveBlock1Ptr->easyChatBattleStart; if (CanPhraseFitInXRowsYCols(gSaveBlock1Ptr->easyChatBattleStart, 3, 2, 18)) { columns = 2; rows = 3; } else { columns = 3; rows = 2; } break; case 2: easyChatWords = gSaveBlock1Ptr->easyChatBattleWon; columns = 3; rows = 2; break; case 3: easyChatWords = gSaveBlock1Ptr->easyChatBattleLost; columns = 3; rows = 2; break; default: return; } ConvertEasyChatWordsToString(gStringVar4, easyChatWords, columns, rows); ShowFieldAutoScrollMessage(gStringVar4); } // The phrase that a man in Dewford Hall suggests has a "deep link" to the current trendy phrase void BufferDeepLinkPhrase(void) { int groupId = Random() & 1 ? EC_GROUP_HOBBIES : EC_GROUP_LIFESTYLE; u16 easyChatWord = GetRandomEasyChatWordFromUnlockedGroup(groupId); CopyEasyChatWord(gStringVar2, easyChatWord); } static bool8 IsAdditionalPhraseUnlocked(u8 additionalPhraseId) { int byteOffset = additionalPhraseId / 8; int shift = additionalPhraseId % 8; return (gSaveBlock1Ptr->additionalPhrases[byteOffset] >> shift) & 1; } void UnlockAdditionalPhrase(u8 additionalPhraseId) { if (additionalPhraseId < NUM_ADDITIONAL_PHRASES) { int byteOffset = additionalPhraseId / 8; int shift = additionalPhraseId % 8; gSaveBlock1Ptr->additionalPhrases[byteOffset] |= 1 << shift; } } static u8 GetNumAdditionalPhrasesUnlocked(void) { u8 i; u8 numAdditionalPhrasesUnlocked; for (i = 0, numAdditionalPhrasesUnlocked = 0; i < NUM_ADDITIONAL_PHRASES; i++) { if (IsAdditionalPhraseUnlocked(i)) numAdditionalPhrasesUnlocked++; } return numAdditionalPhrasesUnlocked; } u16 GetNewHipsterPhraseToTeach(void) { u16 i; u16 additionalPhraseId; u8 numAdditionalPhrasesUnlocked = GetNumAdditionalPhrasesUnlocked(); if (numAdditionalPhrasesUnlocked == NUM_ADDITIONAL_PHRASES) return EC_EMPTY_WORD; additionalPhraseId = Random() % (NUM_ADDITIONAL_PHRASES - numAdditionalPhrasesUnlocked); for (i = 0; i < NUM_ADDITIONAL_PHRASES; i++) { if (!IsAdditionalPhraseUnlocked(i)) { if (additionalPhraseId) { additionalPhraseId--; } else { UnlockAdditionalPhrase(i); return EC_WORD(EC_GROUP_TRENDY_SAYING, i); } } } return EC_EMPTY_WORD; } // Unused u16 GetRandomTaughtHipsterPhrase(void) { u16 i; u16 additionalPhraseId = GetNumAdditionalPhrasesUnlocked(); if (additionalPhraseId == 0) return EC_EMPTY_WORD; additionalPhraseId = Random() % additionalPhraseId; for (i = 0; i < NUM_ADDITIONAL_PHRASES; i++) { if (IsAdditionalPhraseUnlocked(i)) { if (additionalPhraseId) additionalPhraseId--; else return EC_WORD(EC_GROUP_TRENDY_SAYING, i); } } return EC_EMPTY_WORD; } static bool8 EasyChatIsNationalPokedexEnabled(void) { return IsNationalPokedexEnabled(); } static u16 GetRandomUnlockedEasyChatPokemon(void) { u16 i; u16 numWords; const u16 *species; u16 index = EasyChat_GetNumWordsInGroup(EC_GROUP_POKEMON); if (index == 0) return EC_EMPTY_WORD; index = Random() % index; species = gEasyChatGroups[EC_GROUP_POKEMON].wordData.valueList; numWords = gEasyChatGroups[EC_GROUP_POKEMON].numWords; for (i = 0; i < numWords; i++) { u16 dexNum = SpeciesToNationalPokedexNum(*species); if (GetSetPokedexFlag(dexNum, FLAG_GET_SEEN)) { if (index) index--; else return EC_WORD(EC_GROUP_POKEMON, *species); } species++; } return EC_EMPTY_WORD; } void InitEasyChatPhrases(void) { u16 i, j; for (i = 0; i < ARRAY_COUNT(sDefaultProfileWords); i++) gSaveBlock1Ptr->easyChatProfile[i] = sDefaultProfileWords[i]; for (i = 0; i < EASY_CHAT_BATTLE_WORDS_COUNT; i++) gSaveBlock1Ptr->easyChatBattleStart[i] = sDefaultBattleStartWords[i]; for (i = 0; i < EASY_CHAT_BATTLE_WORDS_COUNT; i++) gSaveBlock1Ptr->easyChatBattleWon[i] = sDefaultBattleWonWords[i]; for (i = 0; i < EASY_CHAT_BATTLE_WORDS_COUNT; i++) gSaveBlock1Ptr->easyChatBattleLost[i] = sDefaultBattleLostWords[i]; for (i = 0; i < MAIL_COUNT; i++) { for (j = 0; j < MAIL_WORDS_COUNT; j++) gSaveBlock1Ptr->mail[i].words[j] = EC_EMPTY_WORD; } #ifndef UBFIX // BUG: This is supposed to clear 64 bits, but this loop is clearing 64 bytes. // However, this bug has no resulting effect on gameplay because only the // Mauville old man data is corrupted, which is initialized directly after // this function is called when starting a new game. for (i = 0; i < 64; i++) gSaveBlock1Ptr->additionalPhrases[i] = 0; #else for (i = 0; i < ARRAY_COUNT(gSaveBlock1Ptr->additionalPhrases); i++) gSaveBlock1Ptr->additionalPhrases[i] = 0; #endif } static bool8 InitEasyChatScreenWordData(void) { sWordData = Alloc(sizeof(*sWordData)); if (!sWordData) return FALSE; SetUnlockedEasyChatGroups(); SetUnlockedWordsByAlphabet(); return TRUE; } static void FreeEasyChatScreenWordData(void) { if (sWordData) FREE_AND_SET_NULL(sWordData); } static void SetUnlockedEasyChatGroups(void) { int i; sWordData->numUnlockedGroups = 0; if (GetNationalPokedexCount(FLAG_GET_SEEN)) sWordData->unlockedGroupIds[sWordData->numUnlockedGroups++] = EC_GROUP_POKEMON; // These groups are unlocked automatically for (i = EC_GROUP_TRAINER; i <= EC_GROUP_ADJECTIVES; i++) sWordData->unlockedGroupIds[sWordData->numUnlockedGroups++] = i; if (FlagGet(FLAG_SYS_GAME_CLEAR)) { sWordData->unlockedGroupIds[sWordData->numUnlockedGroups++] = EC_GROUP_EVENTS; sWordData->unlockedGroupIds[sWordData->numUnlockedGroups++] = EC_GROUP_MOVE_1; sWordData->unlockedGroupIds[sWordData->numUnlockedGroups++] = EC_GROUP_MOVE_2; } if (FlagGet(FLAG_SYS_HIPSTER_MEET)) sWordData->unlockedGroupIds[sWordData->numUnlockedGroups++] = EC_GROUP_TRENDY_SAYING; if (IsNationalPokedexEnabled()) sWordData->unlockedGroupIds[sWordData->numUnlockedGroups++] = EC_GROUP_POKEMON_NATIONAL; } static u8 GetNumUnlockedEasyChatGroups(void) { return sWordData->numUnlockedGroups; } static u8 GetUnlockedEasyChatGroupId(u8 index) { if (index >= sWordData->numUnlockedGroups) return EC_NUM_GROUPS; else return sWordData->unlockedGroupIds[index]; } // Unused static u8 *BufferEasyChatWordGroupName(u8 *dest, u8 groupId, u16 totalChars) { u16 i; u8 *str = StringCopy(dest, sEasyChatGroupNamePointers[groupId]); for (i = str - dest; i < totalChars; i++) { *str = CHAR_SPACE; str++; } *str = EOS; return str; } static const u8 *GetEasyChatWordGroupName(u8 groupId) { return sEasyChatGroupNamePointers[groupId]; } static u8 *CopyEasyChatWordPadded(u8 *dest, u16 easyChatWord, u16 totalChars) { u16 i; u8 *str = CopyEasyChatWord(dest, easyChatWord); for (i = str - dest; i < totalChars; i++) { *str = CHAR_SPACE; str++; } *str = EOS; return str; } static void SetUnlockedWordsByAlphabet(void) { int i, j, k; int numWords; const u16 *words; u16 numToProcess; int index; for (i = 0; i < EC_NUM_ALPHABET_GROUPS; i++) { numWords = gEasyChatWordsByLetterPointers[i].numWords; words = gEasyChatWordsByLetterPointers[i].words; sWordData->numUnlockedAlphabetWords[i] = 0; index = 0; for (j = 0; j < numWords; j++) { if (*words == EC_EMPTY_WORD) { words++; numToProcess = *words; words++; j += 1 + numToProcess; } else { numToProcess = 1; } for (k = 0; k < numToProcess; k++) { if (IsEasyChatWordUnlocked(words[k])) { sWordData->unlockedAlphabetWords[i][index++] = words[k]; sWordData->numUnlockedAlphabetWords[i]++; break; } } words += numToProcess; } } } static void SetSelectedWordGroup(bool32 inAlphabetMode, u16 groupId) { if (!inAlphabetMode) sWordData->numSelectedGroupWords = SetSelectedWordGroup_GroupMode(groupId); else sWordData->numSelectedGroupWords = SetSelectedWordGroup_AlphabetMode(groupId); } static u16 GetWordFromSelectedGroup(u16 index) { if (index >= sWordData->numSelectedGroupWords) return EC_EMPTY_WORD; else return sWordData->selectedGroupWords[index]; } static u16 GetNumWordsInSelectedGroup(void) { return sWordData->numSelectedGroupWords; } static u16 SetSelectedWordGroup_GroupMode(u16 groupId) { u32 i; int totalWords; const u16 *list; const struct EasyChatWordInfo *wordInfo; u16 numWords = gEasyChatGroups[groupId].numWords; if (groupId == EC_GROUP_POKEMON || groupId == EC_GROUP_POKEMON_NATIONAL || groupId == EC_GROUP_MOVE_1 || groupId == EC_GROUP_MOVE_2) { list = gEasyChatGroups[groupId].wordData.valueList; for (i = 0, totalWords = 0; i < numWords; i++) { if (IsEasyChatIndexAndGroupUnlocked(list[i], groupId)) sWordData->selectedGroupWords[totalWords++] = EC_WORD(groupId, list[i]); } return totalWords; } else { wordInfo = gEasyChatGroups[groupId].wordData.words; for (i = 0, totalWords = 0; i < numWords; i++) { u16 alphabeticalOrder = wordInfo[i].alphabeticalOrder; if (IsEasyChatIndexAndGroupUnlocked(alphabeticalOrder, groupId)) sWordData->selectedGroupWords[totalWords++] = EC_WORD(groupId, alphabeticalOrder); } return totalWords; } } static u16 SetSelectedWordGroup_AlphabetMode(u16 groupId) { u16 i; u16 totalWords; for (i = 0, totalWords = 0; i < sWordData->numUnlockedAlphabetWords[groupId]; i++) sWordData->selectedGroupWords[totalWords++] = sWordData->unlockedAlphabetWords[groupId][i]; return totalWords; } static bool8 IsEasyChatGroupUnlocked2(u8 groupId) { int i; for (i = 0; i < sWordData->numUnlockedGroups; i++) { if (sWordData->unlockedGroupIds[i] == groupId) return TRUE; } return FALSE; } static bool8 IsEasyChatIndexAndGroupUnlocked(u16 wordIndex, u8 groupId) { switch (groupId) { case EC_GROUP_POKEMON: return GetSetPokedexFlag(SpeciesToNationalPokedexNum(wordIndex), FLAG_GET_SEEN); case EC_GROUP_POKEMON_NATIONAL: if (IsRestrictedWordSpecies(wordIndex)) GetSetPokedexFlag(SpeciesToNationalPokedexNum(wordIndex), FLAG_GET_SEEN); return TRUE; case EC_GROUP_MOVE_1: case EC_GROUP_MOVE_2: return TRUE; case EC_GROUP_TRENDY_SAYING: return IsAdditionalPhraseUnlocked(wordIndex); default: return gEasyChatGroups[groupId].wordData.words[wordIndex].enabled; } } // Pokémon words in EC_GROUP_POKEMON_NATIONAL are always allowed (assuming the group is unlocked) // unless they are in this group. If they are in this group (just Deoxys), they must also have been seen. static int IsRestrictedWordSpecies(u16 species) { u32 i; for (i = 0; i < ARRAY_COUNT(sRestrictedWordSpecies); i++) { if (sRestrictedWordSpecies[i] == species) return TRUE; } return FALSE; } static u8 IsEasyChatWordUnlocked(u16 easyChatWord) { u8 groupId = EC_GROUP(easyChatWord); u32 index = EC_INDEX(easyChatWord); if (!IsEasyChatGroupUnlocked2(groupId)) return FALSE; else return IsEasyChatIndexAndGroupUnlocked(index, groupId); } void InitializeEasyChatWordArray(u16 *words, u16 length) { u16 i; for (i = length - 1; i != EC_EMPTY_WORD; i--) *(words++) = EC_EMPTY_WORD; } void InitQuestionnaireWords(void) { int i; u16 *words = GetQuestionnaireWordsPtr(); for (i = 0; i < NUM_QUESTIONNAIRE_WORDS; i++) words[i] = EC_EMPTY_WORD; } bool32 IsEasyChatAnswerUnlocked(int easyChatWord) { int groupId = EC_GROUP(easyChatWord); int mask = EC_MASK_GROUP; int index = EC_INDEX(easyChatWord); if (!IsEasyChatGroupUnlocked(groupId & mask)) return FALSE; else return IsEasyChatIndexAndGroupUnlocked(index, groupId & mask); }