diff options
Diffstat (limited to 'src/easy_chat.c')
-rw-r--r-- | src/easy_chat.c | 742 |
1 files changed, 742 insertions, 0 deletions
diff --git a/src/easy_chat.c b/src/easy_chat.c new file mode 100644 index 000000000..fd96f6a30 --- /dev/null +++ b/src/easy_chat.c @@ -0,0 +1,742 @@ +#include "global.h" +#include "malloc.h" +#include "bg.h" +#include "data.h" +#include "decompress.h" +#include "dynamic_placeholder_text_util.h" +#include "easy_chat.h" +#include "event_data.h" +#include "field_message_box.h" +#include "field_weather.h" +#include "gpu_regs.h" +#include "graphics.h" +#include "main.h" +#include "mevent.h" +#include "menu.h" +#include "mail.h" +#include "overworld.h" +#include "palette.h" +#include "pokedex.h" +#include "random.h" +#include "sound.h" +#include "string_util.h" +#include "strings.h" +#include "task.h" +#include "text_window.h" +#include "window.h" +#include "constants/easy_chat.h" +#include "constants/map_objects.h" +#include "constants/flags.h" +#include "constants/songs.h" +#include "constants/species.h" + +struct Unk203A120 +{ + u16 numGroups; + u16 groups[EC_NUM_GROUPS]; + u16 alphabeticalGroups[27]; + u16 alphabeticalWordsByGroup[27][270]; + u8 filler3958[0x2C]; + u16 allWords[270]; + u16 totalWords; +}; /*size = 0x3BA4*/ + +static EWRAM_DATA struct Unk203A120 * sEasyChatSelectionData = NULL; + +static bool8 EC_IsNationalPokedexEnabled(void); +static u16 GetRandomECPokemon(void); +static void PopulateECGroups(void); +static void PopulateAlphabeticalGroups(void); +static u16 GetUnlockedWordsInECGroup(u16); +static u16 GetUnlockedWordsInAlphabeticalGroup(u16); +static bool8 UnlockedECMonOrMove(u16, u8); +static bool32 EC_IsDeoxys(u16 species); +static bool8 IsWordUnlocked(u16 word); + +#include "data/easy_chat/easy_chat_groups.h" +#include "data/easy_chat/easy_chat_words_by_letter.h" + +static const u8 *const sEasyChatGroupNamePointers[] = { + [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_2] = gEasyChatGroupName_Pokemon2, +}; + +static const u16 sDefaultProfileWords[] = { + EC_WORD_I_AM, + EC_WORD_A, + EC_WORD_POKEMON, + EC_WORD_FRIEND, +}; + +static const u16 sDefaultBattleStartWords[] = { + EC_WORD_ARE, + EC_WORD_YOU, + EC_WORD_READY, + EC_WORD_QUES, + EC_WORD_HERE_I_COME, + EC_WORD_EXCL, +}; + +static const u16 sDeoxysValue[] = { + SPECIES_DEOXYS, +}; + +static bool8 IsECGroupUnlocked(u8 groupId) +{ + switch (groupId) + { + case EC_GROUP_TRENDY_SAYING: + return FALSE; + case EC_GROUP_EVENTS: + case EC_GROUP_MOVE_1: + case EC_GROUP_MOVE_2: + return FlagGet(FLAG_SYS_GAME_CLEAR); + case EC_GROUP_POKEMON: + return EC_IsNationalPokedexEnabled(); + default: + return TRUE; + } +} + +static u16 EasyChat_GetNumWordsInGroup(u8 groupId) +{ + if (groupId == EC_GROUP_POKEMON) + return GetNationalPokedexCount(FLAG_GET_SEEN); + + if (IsECGroupUnlocked(groupId)) + return sEasyChatGroups[groupId].numEnabledWords; + + return 0; +} + +static bool8 IsECWordInvalid(u16 easyChatWord) +{ + u16 i; + u8 groupId; + u32 index; + u16 numWords; + const u16 *list; + if (easyChatWord == EC_WORD_UNDEFINED) + return FALSE; + + groupId = EC_GROUP(easyChatWord); + index = EC_INDEX(easyChatWord); + if (groupId >= EC_NUM_GROUPS) + return TRUE; + + numWords = sEasyChatGroups[groupId].numWords; + switch (groupId) + { + case EC_GROUP_POKEMON: + case EC_GROUP_POKEMON_2: + case EC_GROUP_MOVE_1: + case EC_GROUP_MOVE_2: + list = sEasyChatGroups[groupId].wordData.valueList; + for (i = 0; i < numWords; i++) + { + if (index == list[i]) + return FALSE; + } + return TRUE; + default: + if (index >= numWords) + return TRUE; + else + return FALSE; + } +} + +static const u8 *GetEasyChatWord(u8 groupId, u16 index) +{ + switch (groupId) + { + case EC_GROUP_POKEMON: + case EC_GROUP_POKEMON_2: + return gSpeciesNames[index]; + case EC_GROUP_MOVE_1: + case EC_GROUP_MOVE_2: + return gMoveNames[index]; + default: + return sEasyChatGroups[groupId].wordData.words[index].text; + } +} + +u8 *CopyEasyChatWord(u8 *dest, u16 easyChatWord) +{ + u8 *resultStr; + if (IsECWordInvalid(easyChatWord)) + { + resultStr = StringCopy(dest, gText_ThreeQuestionMarks); + } + else if (easyChatWord != EC_WORD_UNDEFINED) + { + 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_WORD_UNDEFINED) + { + *dest = CHAR_SPACE; + dest++; + } + + src++; + } + + dest = CopyEasyChatWord(dest, *(src++)); + *dest = CHAR_NEWLINE; + dest++; + } + + dest--; + *dest = EOS; + return dest; +} + +static u16 GetEasyChatWordStringLength(u16 easyChatWord) +{ + if (easyChatWord == EC_WORD_UNDEFINED) + return 0; + + if (IsECWordInvalid(easyChatWord)) + { + return StringLength(gText_ThreeQuestionMarks); + } + else + { + u16 index = EC_INDEX(easyChatWord); + u8 groupId = EC_GROUP(easyChatWord); + return StringLength(GetEasyChatWord(groupId, index)); + } +} + +bool8 EC_DoesEasyChatStringFitOnLine(const u16 *easyChatWords, u8 columns, u8 rows, u16 maxLength) +{ + u8 i, j; + + for (i = 0; i < rows; i++) + { + u16 totalLength = columns - 1; + for (j = 0; j < columns; j++) + totalLength += GetEasyChatWordStringLength(*(easyChatWords++)); + + if (totalLength > maxLength) + return TRUE; + } + + return FALSE; +} + +static u16 GetRandomWordFromGroup(u16 groupId) +{ + u16 index = Random() % sEasyChatGroups[groupId].numWords; + if (groupId == EC_GROUP_POKEMON_2 + || groupId == EC_GROUP_POKEMON + || groupId == EC_GROUP_MOVE_1 + || groupId == EC_GROUP_MOVE_2) + { + index = sEasyChatGroups[groupId].wordData.valueList[index]; + } + + return EC_WORD(groupId, index); +} + +static u16 GetRandomWordFromAnyGroup(u16 groupId) +{ + if (!IsECGroupUnlocked(groupId)) + return EC_WORD_UNDEFINED; + + if (groupId == EC_GROUP_POKEMON) + return GetRandomECPokemon(); + + return GetRandomWordFromGroup(groupId); +} + +void Special_BufferEasyChatMessage(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 (EC_DoesEasyChatStringFitOnLine(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); +} + +void BufferRandomHobbyOrLifestyleString(void) +{ + int groupId = Random() & 1 ? EC_GROUP_HOBBIES : EC_GROUP_LIFESTYLE; + u16 easyChatWord = GetRandomWordFromAnyGroup(groupId); + CopyEasyChatWord(gStringVar2, easyChatWord); +} + +static bool8 IsTrendySayingUnlocked(u8 additionalPhraseId) +{ + int byteOffset = additionalPhraseId / 8; + int shift = additionalPhraseId % 8; + return (gSaveBlock1Ptr->additionalPhrases[byteOffset] >> shift) & 1; +} + +void EnableRareWord(u8 additionalPhraseId) +{ + if (additionalPhraseId < 33) + { + int byteOffset = additionalPhraseId / 8; + int shift = additionalPhraseId % 8; + gSaveBlock1Ptr->additionalPhrases[byteOffset] |= 1 << shift; + } +} + +static u8 GetNumUnlockedTrendySayings(void) +{ + u8 i; + u8 numAdditionalPhrasesUnlocked; + + for (i = 0, numAdditionalPhrasesUnlocked = 0; i < 33; i++) + { + if (IsTrendySayingUnlocked(i)) + numAdditionalPhrasesUnlocked++; + } + + return numAdditionalPhrasesUnlocked; +} + +static u16 UnlockRandomTrendySaying(void) +{ + u16 i; + u16 additionalPhraseId; + u8 numAdditionalPhrasesUnlocked = GetNumUnlockedTrendySayings(); + if (numAdditionalPhrasesUnlocked == 33) + return EC_WORD_UNDEFINED; + + additionalPhraseId = Random() % (33 - numAdditionalPhrasesUnlocked); + for (i = 0; i < 33; i++) + { + if (!IsTrendySayingUnlocked(i)) + { + if (additionalPhraseId) + { + additionalPhraseId--; + } + else + { + EnableRareWord(i); + return EC_WORD(EC_GROUP_TRENDY_SAYING, i); + } + } + } + + return EC_WORD_UNDEFINED; +} + +static u16 GetRandomUnlockedTrendySaying(void) +{ + u16 i; + u16 additionalPhraseId = GetNumUnlockedTrendySayings(); + if (additionalPhraseId == 0) + return EC_WORD_UNDEFINED; + + additionalPhraseId = Random() % additionalPhraseId; + for (i = 0; i < 33; i++) + { + if (IsTrendySayingUnlocked(i)) + { + if (additionalPhraseId) + additionalPhraseId--; + else + return EC_WORD(EC_GROUP_TRENDY_SAYING, i); + } + } + + return EC_WORD_UNDEFINED; +} + +static bool8 EC_IsNationalPokedexEnabled(void) +{ + return IsNationalPokedexEnabled(); +} + +static u16 GetRandomECPokemon(void) +{ + u16 i; + u16 numWords; + const u16 *species; + u16 index = EasyChat_GetNumWordsInGroup(EC_GROUP_POKEMON_2); + if (index == 0) + return EC_WORD_UNDEFINED; + + index = Random() % index; + species = sEasyChatGroups[EC_GROUP_POKEMON_2].wordData.valueList; + numWords = sEasyChatGroups[EC_GROUP_POKEMON_2].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_2, *species); + } + + species++; + } + + return EC_WORD_UNDEFINED; +} + +void InitEasyChatPhrases(void) +{ + u16 i, j; + + for (i = 0; i < 4; i++) + gSaveBlock1Ptr->easyChatProfile[i] = sDefaultProfileWords[i]; + + for (i = 0; i < 6; i++) + gSaveBlock1Ptr->easyChatBattleStart[i] = sDefaultBattleStartWords[i]; + + for (i = 0; i < 6; i++) + { + gSaveBlock1Ptr->easyChatBattleWon[i] = EC_WORD_UNDEFINED; + gSaveBlock1Ptr->easyChatBattleLost[i] = EC_WORD_UNDEFINED; + } + + for (i = 0; i < MAIL_COUNT; i++) + { + for (j = 0; j < MAIL_WORDS_COUNT; j++) + gSaveBlock1Ptr->mail[i].words[j] = EC_WORD_UNDEFINED; + } + + // 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; +} + +void ResetSomeMEventECBuffer_3120_338(void) +{ + s32 i; + u16 *ptr = sub_8143DA8(); + for (i = 0; i < 4; i++) + ptr[i] = EC_WORD_UNDEFINED; +} + +bool8 InitEasyChatSelection(void) +{ + sEasyChatSelectionData = Alloc(sizeof(*sEasyChatSelectionData)); + if (sEasyChatSelectionData == NULL) + return FALSE; + + PopulateECGroups(); + PopulateAlphabeticalGroups(); + return TRUE; +} + +void DestroyEasyChatSelectionData(void) +{ + if (sEasyChatSelectionData != NULL) + Free(sEasyChatSelectionData); +} + +static void PopulateECGroups(void) +{ + int i; + + sEasyChatSelectionData->numGroups = 0; + if (GetNationalPokedexCount(FLAG_GET_SEEN)) + sEasyChatSelectionData->groups[sEasyChatSelectionData->numGroups++] = EC_GROUP_POKEMON; + + for (i = EC_GROUP_TRAINER; i <= EC_GROUP_ADJECTIVES; i++) + sEasyChatSelectionData->groups[sEasyChatSelectionData->numGroups++] = i; + + if (FlagGet(FLAG_SYS_GAME_CLEAR)) + { + sEasyChatSelectionData->groups[sEasyChatSelectionData->numGroups++] = EC_GROUP_EVENTS; + sEasyChatSelectionData->groups[sEasyChatSelectionData->numGroups++] = EC_GROUP_MOVE_1; + sEasyChatSelectionData->groups[sEasyChatSelectionData->numGroups++] = EC_GROUP_MOVE_2; + } + + if (IsNationalPokedexEnabled()) + sEasyChatSelectionData->groups[sEasyChatSelectionData->numGroups++] = EC_GROUP_POKEMON_2; +} + +u8 GetNumDisplayableGroups(void) +{ + return sEasyChatSelectionData->numGroups; +} + +u8 GetSelectedGroupByIndex(u8 index) +{ + if (index >= sEasyChatSelectionData->numGroups) + return EC_NUM_GROUPS; + else + return sEasyChatSelectionData->groups[index]; +} + +static u8 *unref_sub_80BDF6C(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; +} + +const u8 *GetEasyChatWordGroupName(u8 groupId) +{ + return sEasyChatGroupNamePointers[groupId]; +} + +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 PopulateAlphabeticalGroups(void) +{ + static int i; + static int j; + static int k; + static int index; + static int numWords; + static int numToProcess; + static const u16 *words; + + for (i = 0; i < 27; i++) + { + numWords = sEasyChatWordsByLetterPointers[i].numWords; + words = sEasyChatWordsByLetterPointers[i].words; + sEasyChatSelectionData->alphabeticalGroups[i] = 0; + index = 0; + for (j = 0; j < numWords; ) + { + if (*words == EC_WORD_UNDEFINED) + { + words++; + numToProcess = *words++; + j += 2; + } + else + { + numToProcess = 1; + } + + for (k = 0; k < numToProcess; k++) + { + if (IsWordUnlocked(words[k])) + { + sEasyChatSelectionData->alphabeticalWordsByGroup[i][index++] = words[k]; + sEasyChatSelectionData->alphabeticalGroups[i]++; + break; + } + } + + words += numToProcess; + j += numToProcess; + } + } +} + +void GetUnlockedECWords(bool32 isAlphabetical, u16 groupId) +{ + if (!isAlphabetical) + sEasyChatSelectionData->totalWords = GetUnlockedWordsInECGroup(groupId); + else + sEasyChatSelectionData->totalWords = GetUnlockedWordsInAlphabeticalGroup(groupId); +} + +u16 GetDisplayedWordByIndex(u16 index) +{ + if (index >= sEasyChatSelectionData->totalWords) + return EC_WORD_UNDEFINED; + else + return sEasyChatSelectionData->allWords[index]; +} + +u16 GetNumDisplayedWords(void) +{ + return sEasyChatSelectionData->totalWords; +} + +static u16 GetUnlockedWordsInECGroup(u16 groupId) +{ + u16 i; + u16 totalWords; + const u16 *list; + const struct EasyChatWordInfo * wordInfo; + u16 numWords = sEasyChatGroups[groupId].numWords; + + if (groupId == EC_GROUP_POKEMON_2 || groupId == EC_GROUP_POKEMON + || groupId == EC_GROUP_MOVE_1 || groupId == EC_GROUP_MOVE_2) + { + list = sEasyChatGroups[groupId].wordData.valueList; + for (i = 0, totalWords = 0; i < numWords; i++) + { + if (UnlockedECMonOrMove(list[i], groupId)) + sEasyChatSelectionData->allWords[totalWords++] = EC_WORD(groupId, list[i]); + } + + return totalWords; + } + else + { + wordInfo = sEasyChatGroups[groupId].wordData.words; + for (i = 0, totalWords = 0; i < numWords; i++) + { + u16 alphabeticalOrder = wordInfo[i].alphabeticalOrder; + if (UnlockedECMonOrMove(alphabeticalOrder, groupId)) + sEasyChatSelectionData->allWords[totalWords++] = EC_WORD(groupId, alphabeticalOrder); + } + + return totalWords; + } +} + +static u16 GetUnlockedWordsInAlphabeticalGroup(u16 alphabeticalGroup) +{ + u16 i; + u16 totalWords; + + for (i = 0, totalWords = 0; i < sEasyChatSelectionData->alphabeticalGroups[alphabeticalGroup]; i++) + sEasyChatSelectionData->allWords[totalWords++] = sEasyChatSelectionData->alphabeticalWordsByGroup[alphabeticalGroup][i]; + + return totalWords; +} + +static bool8 IsGroupSelectable(u8 groupIdx) +{ + int i; + for (i = 0; i < sEasyChatSelectionData->numGroups; i++) + { + if (sEasyChatSelectionData->groups[i] == groupIdx) + return TRUE; + } + + return FALSE; +} + +static bool8 UnlockedECMonOrMove(u16 wordIndex, u8 groupId) +{ + switch (groupId) + { + case EC_GROUP_POKEMON: + return GetSetPokedexFlag(SpeciesToNationalPokedexNum(wordIndex), FLAG_GET_SEEN); + case EC_GROUP_POKEMON_2: + if (EC_IsDeoxys(wordIndex)) + return GetSetPokedexFlag(SpeciesToNationalPokedexNum(wordIndex), FLAG_GET_SEEN); + return TRUE; + case EC_GROUP_MOVE_1: + case EC_GROUP_MOVE_2: + return TRUE; + default: + return sEasyChatGroups[groupId].wordData.words[wordIndex].enabled; + } +} + +static bool32 EC_IsDeoxys(u16 species) +{ + u32 i; + for (i = 0; i < ARRAY_COUNT(sDeoxysValue); i++) + { + if (sDeoxysValue[i] == species) + return TRUE; + } + + return FALSE; +} + +static bool8 IsWordUnlocked(u16 easyChatWord) +{ + u8 groupId = EC_GROUP(easyChatWord); + u32 index = EC_INDEX(easyChatWord); + if (!IsGroupSelectable(groupId)) + return FALSE; + else + return UnlockedECMonOrMove(index, groupId); +} |