pokeemerald/src/easy_chat.c
2022-09-16 20:24:26 -03:00

5846 lines
162 KiB
C

#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/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 = FALSE,
.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 = FALSE,
.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 = FALSE,
.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 = FALSE,
.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 = FALSE,
.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 = FALSE,
.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].bravoTrainerTower.words;
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 = Alloc(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)
{
TRY_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);
}
// "<author>'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)
{
TRY_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 = &currentPhrase[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)
{
TRY_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);
}