#include "global.h" #include "bard_music.h" #include "mauville_man.h" #include "easy_chat.h" #include "constants/easy_chat.h" #include "event_data.h" #include "field_message_box.h" #include "m4a.h" #include "menu.h" #include "overworld.h" #include "random.h" #include "script.h" #include "constants/event_objects.h" #include "constants/songs.h" #include "sound.h" #include "string_util.h" #include "strings.h" #include "task.h" #include "trader.h" #define MACRO1(a) (((a) % 4) + (((a) / 8) & 1)) extern struct MusicPlayerInfo gMPlayInfo_SE2; extern u16 gSpecialVar_Result; extern u16 gSpecialVar_0x8004; extern const u8 gTextStoryteller_Story1Title[]; extern const u8 gTextStoryteller_Story1Action[]; extern const u8 gTextStoryteller_Story1Text[]; extern const u8 gTextStoryteller_Story2Title[]; extern const u8 gTextStoryteller_Story2Action[]; extern const u8 gTextStoryteller_Story2Text[]; extern const u8 gTextStoryteller_Story3Title[]; extern const u8 gTextStoryteller_Story3Action[]; extern const u8 gTextStoryteller_Story3Text[]; extern const u8 gTextStoryteller_Story4Title[]; extern const u8 gTextStoryteller_Story4Action[]; extern const u8 gTextStoryteller_Story4Text[]; extern const u8 gTextStoryteller_Story5Title[]; extern const u8 gTextStoryteller_Story5Action[]; extern const u8 gTextStoryteller_Story5Text[]; extern const u8 gTextStoryteller_Story6Title[]; extern const u8 gTextStoryteller_Story6Action[]; extern const u8 gTextStoryteller_Story6Text[]; extern const u8 gTextStoryteller_Story7Title[]; extern const u8 gTextStoryteller_Story7Action[]; extern const u8 gTextStoryteller_Story7Text[]; extern const u8 gTextStoryteller_Story8Title[]; extern const u8 gTextStoryteller_Story8Action[]; extern const u8 gTextStoryteller_Story8Text[]; extern const u8 gTextStoryteller_Story9Title[]; extern const u8 gTextStoryteller_Story9Action[]; extern const u8 gTextStoryteller_Story9Text[]; extern const u8 gTextStoryteller_Story10Title[]; extern const u8 gTextStoryteller_Story10Action[]; extern const u8 gTextStoryteller_Story10Text[]; extern const u8 gTextStoryteller_Story11Title[]; extern const u8 gTextStoryteller_Story11Action[]; extern const u8 gTextStoryteller_Story11Text[]; extern const u8 gTextStoryteller_Story12Title[]; extern const u8 gTextStoryteller_Story12Action[]; extern const u8 gTextStoryteller_Story12Text[]; extern const u8 gTextStoryteller_Story13Title[]; extern const u8 gTextStoryteller_Story13Action[]; extern const u8 gTextStoryteller_Story13Text[]; extern const u8 gTextStoryteller_Story14Title[]; extern const u8 gTextStoryteller_Story14Action[]; extern const u8 gTextStoryteller_Story14Text[]; extern const u8 gTextStoryteller_Story15Title[]; extern const u8 gTextStoryteller_Story15Action[]; extern const u8 gTextStoryteller_Story15Text[]; extern const u8 gTextStoryteller_Story16Title[]; extern const u8 gTextStoryteller_Story16Action[]; extern const u8 gTextStoryteller_Story16Text[]; extern const u8 gTextStoryteller_Story17Title[]; extern const u8 gTextStoryteller_Story17Action[]; extern const u8 gTextStoryteller_Story17Text[]; extern const u8 gTextStoryteller_Story18Title[]; extern const u8 gTextStoryteller_Story18Action[]; extern const u8 gTextStoryteller_Story18Text[]; extern const u8 gTextStoryteller_Story19Title[]; extern const u8 gTextStoryteller_Story19Action[]; extern const u8 gTextStoryteller_Story19Text[]; extern const u8 gTextStoryteller_Story20Title[]; extern const u8 gTextStoryteller_Story20Action[]; extern const u8 gTextStoryteller_Story20Text[]; extern const u8 gTextStoryteller_Story21Title[]; extern const u8 gTextStoryteller_Story21Action[]; extern const u8 gTextStoryteller_Story21Text[]; extern const u8 gTextStoryteller_Story22Title[]; extern const u8 gTextStoryteller_Story22Action[]; extern const u8 gTextStoryteller_Story22Text[]; extern const u8 gTextStoryteller_Story23Title[]; extern const u8 gTextStoryteller_Story23Action[]; extern const u8 gTextStoryteller_Story23Text[]; extern const u8 gTextStoryteller_Story24Title[]; extern const u8 gTextStoryteller_Story24Action[]; extern const u8 gTextStoryteller_Story24Text[]; extern const u8 gTextStoryteller_Story25Title[]; extern const u8 gTextStoryteller_Story25Action[]; extern const u8 gTextStoryteller_Story25Text[]; extern const u8 gTextStoryteller_Story26Title[]; extern const u8 gTextStoryteller_Story26Action[]; extern const u8 gTextStoryteller_Story26Text[]; extern const u8 gTextStoryteller_Story27Title[]; extern const u8 gTextStoryteller_Story27Action[]; extern const u8 gTextStoryteller_Story27Text[]; extern const u8 gTextStoryteller_Story28Title[]; extern const u8 gTextStoryteller_Story28Action[]; extern const u8 gTextStoryteller_Story28Text[]; extern const u8 gTextStoryteller_Story29Title[]; extern const u8 gTextStoryteller_Story29Action[]; extern const u8 gTextStoryteller_Story29Text[]; extern const u8 gTextStoryteller_Story30Title[]; extern const u8 gTextStoryteller_Story30Action[]; extern const u8 gTextStoryteller_Story30Text[]; extern const u8 gTextStoryteller_Story31Title[]; extern const u8 gTextStoryteller_Story31Action[]; extern const u8 gTextStoryteller_Story31Text[]; extern const u8 gTextStoryteller_Story32Title[]; extern const u8 gTextStoryteller_Story32Action[]; extern const u8 gTextStoryteller_Story32Text[]; extern const u8 gTextStoryteller_Story33Title[]; extern const u8 gTextStoryteller_Story33Action[]; extern const u8 gTextStoryteller_Story33Text[]; extern const u8 gTextStoryteller_Story34Title[]; extern const u8 gTextStoryteller_Story34Action[]; extern const u8 gTextStoryteller_Story34Text[]; extern const u8 gTextStoryteller_Story35Title[]; extern const u8 gTextStoryteller_Story35Action[]; extern const u8 gTextStoryteller_Story35Text[]; extern const u8 gTextStoryteller_Story36Title[]; extern const u8 gTextStoryteller_Story36Action[]; extern const u8 gTextStoryteller_Story36Text[]; struct BardSong gUnknown_03005DA0; EWRAM_DATA static u16 gUnknown_020388BC = 0; // set but not used? static const u16 sDefaultBardSongLyrics[] = { #ifdef ENGLISH EC_WORD_SISTER, EC_WORD_EATS, EC_WORD_SWEETS, EC_WORD_VORACIOUS, EC_WORD_AND, EC_WORD_DROOLING, #else EC_WORD_SISTER, EC_WORD_MUST_BE, EC_WORD_SWEETS, EC_WORD_VORACIOUS, EC_WORD_DROOLING, EC_WORD_THICK, #endif }; static const u8 *const sGiddyAdjectives[] = { OtherText_SoPretty, OtherText_SoDarling, OtherText_SoRelaxed, OtherText_SoSunny, OtherText_SoDesirable, OtherText_SoExciting, OtherText_SoAmusing, OtherText_SoMagical, }; static const u8 *const sGiddyQuestions[] = { OtherText_WantVacationNicePlace, OtherText_BoughtCrayonsIsNice, OtherText_IfWeCouldFloat, OtherText_SandWashesAwayMakeSad, OtherText_WhatsBottomSeaLike, OtherText_SeeSettingSun, OtherText_LyingInGreenGrass, OtherText_SecretBasesWonderful, }; static void sub_80F7DC0(void); static void Task_BardSong(u8); static void StartBardSong(u8); static void StorytellerSetup(void); static void sub_80F8428(void); static void SetupBard(void) { u16 i; struct MauvilleManBard *bard = &gSaveBlock1.mauvilleMan.bard; bard->id = MAUVILLE_MAN_BARD; bard->hasChangedSong = FALSE; for (i = 0; i < 6; i++) bard->songLyrics[i] = sDefaultBardSongLyrics[i]; } static void SetupHipster(void) { struct MauvilleManHipster *hipster = &gSaveBlock1.mauvilleMan.hipster; hipster->id = MAUVILLE_MAN_HIPSTER; hipster->alreadySpoken = FALSE; } static void SetupStoryteller(void) { StorytellerSetup(); } static void SetupGiddy(void) { struct MauvilleManGiddy *giddy = &gSaveBlock1.mauvilleMan.giddy; giddy->id = MAUVILLE_MAN_GIDDY; giddy->taleCounter = 0; } static void SetupTrader(void) { TraderSetup(); } void SetupMauvilleOldMan(void) { u16 trainerId = (gSaveBlock2.playerTrainerId[1] << 8) | gSaveBlock2.playerTrainerId[0]; // Determine man based on the last digit of the player's trainer ID. switch ((trainerId % 10) / 2) { case MAUVILLE_MAN_BARD: SetupBard(); break; case MAUVILLE_MAN_HIPSTER: SetupHipster(); break; case MAUVILLE_MAN_TRADER: SetupTrader(); break; case MAUVILLE_MAN_STORYTELLER: SetupStoryteller(); break; case MAUVILLE_MAN_GIDDY: SetupGiddy(); break; } sub_80F83D0(); } #if DEBUG void debug_sub_810B32C(u8 a) { u8 i; u8 savedArr[8]; switch (a) { case 0: SetupBard(); break; case 1: for (i = 0; i < 8; i++) savedArr[i] = gSaveBlock1.unk2D8C[i]; SetupHipster(); for (i = 0; i < 8; i++) gSaveBlock1.unk2D8C[i] = savedArr[i]; break; case 2: SetupTrader(); break; case 3: SetupStoryteller(); break; case 4: SetupGiddy(); break; } sub_80F83D0(); } #endif u8 GetCurrentMauvilleOldMan(void) { struct MauvilleManCommon *common = &gSaveBlock1.mauvilleMan.common; return common->id; } void ScrSpecial_GetCurrentMauvilleMan(void) { gSpecialVar_Result = GetCurrentMauvilleOldMan(); } void ScrSpecial_HasBardSongBeenChanged(void) { u16 *scriptResult = &gSpecialVar_Result; // why?? struct MauvilleManBard *bard = &gSaveBlock1.mauvilleMan.bard; *scriptResult = bard->hasChangedSong; } void ScrSpecial_SaveBardSongLyrics(void) { u16 i; struct MauvilleManBard *bard = &gSaveBlock1.mauvilleMan.bard; StringCopy(bard->playerName, gSaveBlock2.playerName); for (i = 0; i < 4; i++) bard->playerTrainerId[i] = gSaveBlock2.playerTrainerId[i]; for (i = 0; i < 6; i++) bard->songLyrics[i] = bard->temporaryLyrics[i]; bard->hasChangedSong = TRUE; } // Copies lyrics into gStringVar4 void PrepareSongText(void) { struct MauvilleManBard *bard = &gSaveBlock1.mauvilleMan.bard; u16 specialVar = gSpecialVar_0x8004; // It's a bit odd to use this temp variable, but it seems needed to match. u16 *lyrics; u16 lineNum; u8 *wordEnd; u8 *str; lyrics = bard->temporaryLyrics; if (specialVar == 0) lyrics = bard->songLyrics; wordEnd = gStringVar4; str = wordEnd; // Put three words on each line for (lineNum = 0; lineNum < 2; lineNum++) { wordEnd = EasyChat_GetWordText(wordEnd, *(lyrics++)); while (wordEnd != str) { if (*str == CHAR_SPACE) *str = CHAR_SONG_WORD_SEPARATOR; str++; } str++; *(wordEnd++) = CHAR_SPACE; wordEnd = EasyChat_GetWordText(wordEnd, *(lyrics++)); while (wordEnd != str) { if (*str == CHAR_SPACE) *str = CHAR_SONG_WORD_SEPARATOR; str++; } str++; *(wordEnd++) = CHAR_NEWLINE; wordEnd = EasyChat_GetWordText(wordEnd, *(lyrics++)); while (wordEnd != str) { if (*str == CHAR_SPACE) *str = CHAR_SONG_WORD_SEPARATOR; str++; } if (lineNum == 0) { *(wordEnd++) = EXT_CTRL_CODE_BEGIN; *(wordEnd++) = 15; } } } void ScrSpecial_PlayBardSong(void) { StartBardSong(gSpecialVar_0x8004); Menu_DisplayDialogueFrame(); ScriptContext1_Stop(); } void ScrSpecial_GetHipsterSpokenFlag(void) { u16 *scriptResult = &gSpecialVar_Result; // again?? struct MauvilleManHipster *hipster = &gSaveBlock1.mauvilleMan.hipster; *scriptResult = hipster->alreadySpoken; } void ScrSpecial_SetHipsterSpokenFlag(void) { struct MauvilleManHipster *hipster = &gSaveBlock1.mauvilleMan.hipster; hipster->alreadySpoken = TRUE; } void ScrSpecial_HipsterTeachWord(void) { u16 var = sub_80EB8EC(); if (var == 0xFFFF) { gSpecialVar_Result = FALSE; } else { EasyChat_GetWordText(gStringVar1, var); gSpecialVar_Result = TRUE; } } void ScrSpecial_GiddyShouldTellAnotherTale(void) { struct MauvilleManGiddy *giddy = &gSaveBlock1.mauvilleMan.giddy; if (giddy->taleCounter == 10) { gSpecialVar_Result = FALSE; giddy->taleCounter = 0; } else { gSpecialVar_Result = TRUE; } } void ScrSpecial_GenerateGiddyLine(void) { struct MauvilleManGiddy *giddy = &gSaveBlock1.mauvilleMan.giddy; if (giddy->taleCounter == 0) sub_80F7DC0(); if (giddy->randomWords[giddy->taleCounter] != 0xFFFF) // is not the last element of the array? { u8 *stringPtr; u32 adjective = Random(); adjective %= 8; stringPtr = EasyChat_GetWordText(gStringVar4, giddy->randomWords[giddy->taleCounter]); stringPtr = StringCopy(stringPtr, gOtherText_Is); stringPtr = StringCopy(stringPtr, sGiddyAdjectives[adjective]); StringCopy(stringPtr, gOtherText_DontYouAgree); } else { StringCopy(gStringVar4, sGiddyQuestions[giddy->questionList[giddy->questionNum++]]); } if (!(Random() % 10)) giddy->taleCounter = 10; else giddy->taleCounter++; gSpecialVar_Result = TRUE; } static void sub_80F7DC0(void) { struct MauvilleManGiddy *giddy = &gSaveBlock1.mauvilleMan.giddy; u16 arr[][2] = { {EC_GROUP_POKEMON_1, 0}, {EC_GROUP_LIFESTYLE, 0}, {EC_GROUP_HOBBIES, 0}, {EC_GROUP_MOVE_1, 0}, {EC_GROUP_MOVE_2, 0}, {EC_GROUP_POKEMON_2, 0} }; u16 i; u16 r10; u16 r7; u16 r1; for (i = 0; i < 8; i++) { giddy->questionList[i] = i; } for (i = 0; i < 8; i++) { r1 = Random() % (i + 1); r7 = giddy->questionList[i]; giddy->questionList[i] = giddy->questionList[r1]; giddy->questionList[r1] = r7; } r10 = 0; for (i = 0; i < 6; i++) { arr[i][1] = sub_80EAE88(arr[i][0]); r10 += arr[i][1]; } giddy->questionNum = 0; r7 = 0; for (i = 0; i < 10; i++) { r1 = Random() % 10; if (r1 < 3 && r7 < 8) { giddy->randomWords[i] = 0xFFFF; r7++; } else { s16 r2 = Random() % r10; for (r1 = 0; i < 6; r1++) { if ((r2 -= arr[r1][1]) <= 0) break; } if (r1 == 6) r1 = 0; giddy->randomWords[i] = sub_80EB784(arr[r1][0]); } } } static void sub_80F7EFC(void) { struct MauvilleManBard *bard = &gSaveBlock1.mauvilleMan.bard; bard->hasChangedSong = FALSE; } static void sub_80F7F0C(void) { struct MauvilleManHipster *hipster = &gSaveBlock1.mauvilleMan.hipster; hipster->alreadySpoken = FALSE; } static void sub_80F7F18(void) { sub_8109A20(); } static void sub_80F7F24(void) { sub_80F8428(); } void sub_80F7F30(void) { switch (GetCurrentMauvilleOldMan()) { case MAUVILLE_MAN_BARD: sub_80F7EFC(); break; case MAUVILLE_MAN_HIPSTER: sub_80F7F0C(); break; case MAUVILLE_MAN_STORYTELLER: sub_80F7F24(); break; case MAUVILLE_MAN_TRADER: sub_80F7F18(); break; case MAUVILLE_MAN_GIDDY: break; } sub_80F83D0(); } #define tState data[0] #define tCharIndex data[3] #define tCurrWord data[4] #define tUseTemporaryLyrics data[5] static void StartBardSong(bool8 useTemporaryLyrics) { u8 taskId = CreateTask(Task_BardSong, 0x50); gTasks[taskId].tUseTemporaryLyrics = useTemporaryLyrics; } static void BardSing(struct Task *task, struct BardSong *song) { switch (task->tState) { case 0: // Initialize song { struct MauvilleManBard *bard = &gSaveBlock1.mauvilleMan.bard; u16 *lyrics; s32 i; // Copy lyrics if (gSpecialVar_0x8004 == 0) lyrics = bard->songLyrics; else lyrics = bard->temporaryLyrics; for (i = 0; i < 6; i++) song->lyrics[i] = lyrics[i]; // Clear phonemes for (i = 0; i < 6; i++) { song->phonemes[i].sound = 0xFFFF; song->phonemes[i].length = 0; song->phonemes[i].pitch = 0; song->phonemes[i].volume = 0; } song->currWord = 0; song->currPhoneme = 0; song->var04 = 0; } break; case 1: // Wait for BGM to end break; case 2: // Initialize word { u16 word = song->lyrics[song->currWord]; const struct BardSound *sounds = GetWordSounds(EC_GROUP(word), EC_INDEX(word)); song->var04 = 0; GetWordPhonemes(song, sounds, MACRO1(word)); } break; case 3: case 4: { struct BardPhoneme *phoneme = &song->phonemes[song->currPhoneme]; switch (song->state) { case 0: if (song->phonemeTimer == 0) // Timer has expired. Move to next phoneme { if (song->currPhoneme == 6 || phoneme->sound == 0xFF) { song->state = 0xFE; break; } song->phonemeTimer = phoneme->length; if (phoneme->sound <= 50) { u16 num = phoneme->sound / 3; m4aSongNumStart(249 + num * 3); } song->state = 1; } else { if (song->voiceInflection > 10) song->volume -= 2; if (song->voiceInflection & 1) song->pitch += 64; else song->pitch -= 64; m4aMPlayVolumeControl(&gMPlayInfo_SE2, 0xFFFF, song->volume); m4aMPlayPitchControl(&gMPlayInfo_SE2, 0xFFFF, song->pitch); song->voiceInflection++; } song->phonemeTimer--; break; case 1: song->currPhoneme++; song->state = 0; if (phoneme->sound <= 50) { song->volume = 0x100 + phoneme->volume * 16; m4aMPlayVolumeControl(&gMPlayInfo_SE2, 0xFFFF, song->volume); song->pitch = 0x200 + phoneme->pitch; m4aMPlayPitchControl(&gMPlayInfo_SE2, 0xFFFF, song->pitch); } break; case 0xFE: m4aMPlayStop(&gMPlayInfo_SE2); song->state = 0xFF; break; } } break; case 5: break; } } static void Task_BardSong(u8 taskId) { struct Task *task = &gTasks[taskId]; // r5 BardSing(task, &gUnknown_03005DA0); switch (task->tState) { case 0: // Initialize song PrepareSongText(); Text_InitWindowWithTemplate(gMenuWindowPtr, &gMenuTextWindowTemplate); Contest_StartTextPrinter(gMenuWindowPtr, gStringVar4, 2, 4, 15); task->data[1] = 0; task->data[2] = 0; task->tCharIndex = 0; task->tCurrWord = 0; FadeOutBGMTemporarily(4); task->tState = 1; break; case 1: // Wait for BGM to end if (IsBGMPausedOrStopped()) task->tState = 2; break; case 2: // Initialize word { struct MauvilleManBard *bard = &gSaveBlock1.mauvilleMan.bard; u8 *str = gStringVar4 + task->tCharIndex; u16 wordLen = 0; while (*str != CHAR_SPACE && *str != CHAR_NEWLINE && *str != EXT_CTRL_CODE_BEGIN && *str != EOS) { str++; wordLen++; } if (!task->tUseTemporaryLyrics) gUnknown_020388BC = MACRO1(bard->songLyrics[task->tCurrWord]); else gUnknown_020388BC = MACRO1(bard->temporaryLyrics[task->tCurrWord]); gUnknown_03005DA0.var04 /= wordLen; if (gUnknown_03005DA0.var04 <= 0) gUnknown_03005DA0.var04 = 1; task->tCurrWord++; if (task->data[2] == 0) { task->tState = 3; task->data[1] = 0; } else { task->tState = 5; task->data[1] = 0; } } break; case 5: if (task->data[2] == 0) task->tState = 3; else task->data[2]--; break; case 3: if (gStringVar4[task->tCharIndex] == EOS) { FadeInNewBGM(MUS_POKE_CENTER, 6); m4aMPlayFadeOutTemporarily(&gMPlayInfo_SE2, 2); EnableBothScriptContexts(); DestroyTask(taskId); } else if (gStringVar4[task->tCharIndex] == CHAR_SPACE) { Text_PrintWindowSimple(gMenuWindowPtr); task->tCharIndex++; task->tState = 2; task->data[2] = 0; } else if (gStringVar4[task->tCharIndex] == CHAR_NEWLINE) { task->tCharIndex++; task->tState = 2; task->data[2] = 0; } else if (gStringVar4[task->tCharIndex] == EXT_CTRL_CODE_BEGIN) { task->tCharIndex += 2; // skip over control codes task->tState = 2; task->data[2] = 8; } else if (gStringVar4[task->tCharIndex] == CHAR_SONG_WORD_SEPARATOR) { gStringVar4[task->tCharIndex] = CHAR_SPACE; // restore it back to a space Text_PrintWindowSimple(gMenuWindowPtr); task->tCharIndex++; task->data[2] = 0; } else { switch (task->data[1]) { case 0: Text_PrintWindowSimple(gMenuWindowPtr); task->data[1]++; break; case 1: task->data[1]++; break; case 2: task->tCharIndex++; task->data[1] = 0; task->data[2] = gUnknown_03005DA0.var04; task->tState = 4; break; } } break; case 4: task->data[2]--; if (task->data[2] == 0) task->tState = 3; break; } } void sub_80F83D0(void) { VarSet(VAR_OBJ_GFX_ID_0, OBJ_EVENT_GFX_BARD + GetCurrentMauvilleOldMan()); } struct Story { u8 stat; u8 minVal; const u8 *title; const u8 *action; const u8 *fullText; }; static const struct Story sStorytellerStories[] = { {0x32, 1, gTextStoryteller_Story1Title, gTextStoryteller_Story1Action, gTextStoryteller_Story1Text}, {0x02, 1, gTextStoryteller_Story2Title, gTextStoryteller_Story2Action, gTextStoryteller_Story2Text}, {0x03, 1, gTextStoryteller_Story3Title, gTextStoryteller_Story3Action, gTextStoryteller_Story3Text}, {0x04, 1, gTextStoryteller_Story4Title, gTextStoryteller_Story4Action, gTextStoryteller_Story4Text}, {0x06, 1, gTextStoryteller_Story5Title, gTextStoryteller_Story5Action, gTextStoryteller_Story5Text}, {0x09, 1, gTextStoryteller_Story6Title, gTextStoryteller_Story6Action, gTextStoryteller_Story6Text}, {0x0B, 1, gTextStoryteller_Story7Title, gTextStoryteller_Story7Action, gTextStoryteller_Story7Text}, {0x0C, 1, gTextStoryteller_Story8Title, gTextStoryteller_Story8Action, gTextStoryteller_Story8Text}, {0x0D, 1, gTextStoryteller_Story9Title, gTextStoryteller_Story9Action, gTextStoryteller_Story9Text}, {0x0E, 1, gTextStoryteller_Story10Title, gTextStoryteller_Story10Action, gTextStoryteller_Story10Text}, {0x0F, 1, gTextStoryteller_Story11Title, gTextStoryteller_Story11Action, gTextStoryteller_Story11Text}, {0x10, 1, gTextStoryteller_Story12Title, gTextStoryteller_Story12Action, gTextStoryteller_Story12Text}, {0x11, 1, gTextStoryteller_Story13Title, gTextStoryteller_Story13Action, gTextStoryteller_Story13Text}, {0x12, 1, gTextStoryteller_Story14Title, gTextStoryteller_Story14Action, gTextStoryteller_Story14Text}, {0x13, 1, gTextStoryteller_Story15Title, gTextStoryteller_Story15Action, gTextStoryteller_Story15Text}, {0x14, 1, gTextStoryteller_Story16Title, gTextStoryteller_Story16Action, gTextStoryteller_Story16Text}, {0x1A, 1, gTextStoryteller_Story17Title, gTextStoryteller_Story17Action, gTextStoryteller_Story17Text}, {0x1B, 1, gTextStoryteller_Story18Title, gTextStoryteller_Story18Action, gTextStoryteller_Story18Text}, {0x1C, 1, gTextStoryteller_Story19Title, gTextStoryteller_Story19Action, gTextStoryteller_Story19Text}, {0x1D, 2, gTextStoryteller_Story20Title, gTextStoryteller_Story20Action, gTextStoryteller_Story20Text}, {0x1E, 1, gTextStoryteller_Story21Title, gTextStoryteller_Story21Action, gTextStoryteller_Story21Text}, {0x21, 1, gTextStoryteller_Story22Title, gTextStoryteller_Story22Action, gTextStoryteller_Story22Text}, {0x24, 1, gTextStoryteller_Story23Title, gTextStoryteller_Story23Action, gTextStoryteller_Story23Text}, {0x25, 1, gTextStoryteller_Story24Title, gTextStoryteller_Story24Action, gTextStoryteller_Story24Text}, {0x26, 1, gTextStoryteller_Story25Title, gTextStoryteller_Story25Action, gTextStoryteller_Story25Text}, {0x27, 1, gTextStoryteller_Story26Title, gTextStoryteller_Story26Action, gTextStoryteller_Story26Text}, {0x28, 1, gTextStoryteller_Story27Title, gTextStoryteller_Story27Action, gTextStoryteller_Story27Text}, {0x29, 1, gTextStoryteller_Story28Title, gTextStoryteller_Story28Action, gTextStoryteller_Story28Text}, {0x2A, 1, gTextStoryteller_Story29Title, gTextStoryteller_Story29Action, gTextStoryteller_Story29Text}, {0x2B, 1, gTextStoryteller_Story30Title, gTextStoryteller_Story30Action, gTextStoryteller_Story30Text}, {0x2C, 1, gTextStoryteller_Story31Title, gTextStoryteller_Story31Action, gTextStoryteller_Story31Text}, {0x2D, 1, gTextStoryteller_Story32Title, gTextStoryteller_Story32Action, gTextStoryteller_Story32Text}, {0x2E, 1, gTextStoryteller_Story33Title, gTextStoryteller_Story33Action, gTextStoryteller_Story33Text}, {0x2F, 1, gTextStoryteller_Story34Title, gTextStoryteller_Story34Action, gTextStoryteller_Story34Text}, {0x30, 1, gTextStoryteller_Story35Title, gTextStoryteller_Story35Action, gTextStoryteller_Story35Text}, {0x31, 1, gTextStoryteller_Story36Title, gTextStoryteller_Story36Action, gTextStoryteller_Story36Text}, }; static void StorytellerSetup(void) { struct MauvilleManStoryteller *storyteller = &gSaveBlock1.mauvilleMan.storyteller; s32 i; storyteller->id = MAUVILLE_MAN_STORYTELLER; storyteller->alreadyRecorded = FALSE; for (i = 0; i < 4; i++) { storyteller->gameStatIDs[i] = 0; storyteller->trainerNames[0][i] = EOS; // Maybe they meant storyteller->trainerNames[i][0] instead? } } static void sub_80F8428(void) { struct MauvilleManStoryteller *storyteller = &gSaveBlock1.mauvilleMan.storyteller; storyteller->id = MAUVILLE_MAN_STORYTELLER; storyteller->alreadyRecorded = FALSE; } static u32 StorytellerGetGameStat(u8 stat) { if (stat == NUM_GAME_STATS) stat = 0; return GetGameStat(stat); } static const struct Story *GetStoryByStat(u32 stat) { s32 i; for (i = 0; i < 36; i++) { if (sStorytellerStories[i].stat == stat) return &sStorytellerStories[i]; } return &sStorytellerStories[35]; } static const u8 *GetStoryTitleByStat(u32 stat) { return GetStoryByStat(stat)->title; } static const u8 *GetStoryTextByStat(u32 stat) { return GetStoryByStat(stat)->fullText; } static const u8 *GetStoryActionByStat(u32 stat) { return GetStoryByStat(stat)->action; } static u8 GetFreeStorySlot(void) { u8 i; for (i = 0; i < 4; i++) { struct MauvilleManStoryteller *storyteller = &gSaveBlock1.mauvilleMan.storyteller; if (storyteller->gameStatIDs[i] == 0) break; } return i; } static u32 StorytellerGetRecordedTrainerStat(u32 trainer) { u8 *ptr = gSaveBlock1.mauvilleMan.storyteller.statValues[trainer]; return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24); } static void StorytellerSetRecordedTrainerStat(u32 trainer, u32 val) { u8 *ptr = gSaveBlock1.mauvilleMan.storyteller.statValues[trainer]; ptr[0] = val; ptr[1] = val >> 8; ptr[2] = val >> 16; ptr[3] = val >> 24; } static bool32 HasTrainerStatIncreased(u32 trainer) { struct MauvilleManStoryteller *storyteller = &gSaveBlock1.mauvilleMan.storyteller; if (StorytellerGetGameStat(storyteller->gameStatIDs[trainer]) > StorytellerGetRecordedTrainerStat(trainer)) return TRUE; else return FALSE; } static void GetStoryByStattellerPlayerName(u32 player, void *dst) { u8 *name = gSaveBlock1.mauvilleMan.storyteller.trainerNames[player]; memset(dst, EOS, 8); memcpy(dst, name, 7); } static void StorytellerSetPlayerName(u32 player, const u8 *src) { u8 *name = gSaveBlock1.mauvilleMan.storyteller.trainerNames[player]; u8 len = StringLength(src); memset(name, EOS, 7); StringCopyN(name, src, len); } static void StorytellerRecordNewStat(u32 player, u32 stat) { struct MauvilleManStoryteller *storyteller = &gSaveBlock1.mauvilleMan.storyteller; storyteller->gameStatIDs[player] = stat; StorytellerSetPlayerName(player, gSaveBlock2.playerName); StorytellerSetRecordedTrainerStat(player, StorytellerGetGameStat(stat)); ConvertIntToDecimalStringN(gStringVar1, StorytellerGetGameStat(stat), 0, 10); StringCopy(gStringVar2, GetStoryActionByStat(stat)); } static void ScrambleStatList(u8 *arr, s32 count) { s32 i; for (i = 0; i < count; i++) arr[i] = i; for (i = 0; i < count; i++) { u32 a = Random() % count; u32 b = Random() % count; u8 temp = arr[a]; arr[a] = arr[b]; arr[b] = temp; } } // What purpose does this struct even serve? Only the length field is used. static const struct {u32 length; struct MauvilleManStoryteller *unused1; u32 unused2;} sStorytellerStuff = { 36, &gSaveBlock1.mauvilleMan.storyteller, // unused 12, // unused }; static bool8 StorytellerInitializeRandomStat(void) { u8 arr[sStorytellerStuff.length]; s32 i; s32 j; ScrambleStatList(arr, 36); for (i = 0; i < 36; i++) { u8 stat = sStorytellerStories[arr[i]].stat; u8 minVal = sStorytellerStories[arr[i]].minVal; struct MauvilleManStoryteller *storyteller = &gSaveBlock1.mauvilleMan.storyteller; for (j = 0; j < 4; j++) { if (gSaveBlock1.mauvilleMan.storyteller.gameStatIDs[j] == stat) break; } if (j == 4 && StorytellerGetGameStat(stat) >= minVal) { storyteller->alreadyRecorded = TRUE; StorytellerRecordNewStat(GetFreeStorySlot(), stat); return TRUE; } } return FALSE; } static void StorytellerDisplayStory(u32 player) { struct MauvilleManStoryteller *storyteller = &gSaveBlock1.mauvilleMan.storyteller; u8 stat = storyteller->gameStatIDs[player]; ConvertIntToDecimalStringN(gStringVar1, StorytellerGetRecordedTrainerStat(player), 0, 10); StringCopy(gStringVar2, GetStoryActionByStat(stat)); GetStoryByStattellerPlayerName(player, gStringVar3); ShowFieldMessage(GetStoryTextByStat(stat)); } static void PrintStoryList(void) { s32 i; Menu_DrawStdWindowFrame(0, 0, 25, 4 + GetFreeStorySlot() * 2); for (i = 0; i < 4; i++) { struct MauvilleManStoryteller *storyteller = &gSaveBlock1.mauvilleMan.storyteller; u8 stat = storyteller->gameStatIDs[i]; if (stat == 0) break; Menu_PrintText(GetStoryTitleByStat(stat), 1, 2 + i * 2); } Menu_PrintText(gPCText_Cancel, 1, 2 + i * 2); } static u8 gUnknown_03000748; static void Task_StoryListMenu(u8 taskId) { struct Task *task = &gTasks[taskId]; s32 selection; switch (task->data[0]) { case 0: PrintStoryList(); InitMenu(0, 1, 2, GetFreeStorySlot() + 1, 0, 24); task->data[0]++; break; case 1: selection = Menu_ProcessInput(); if (selection == -2) break; if (selection == -1 || selection == GetFreeStorySlot()) { gSpecialVar_Result = 0; } else { gSpecialVar_Result = 1; gUnknown_03000748 = selection; } Menu_DestroyCursor(); Menu_EraseWindowRect(0, 0, 25, 12); DestroyTask(taskId); EnableBothScriptContexts(); break; } } // Sets gSpecialVar_Result to TRUE if player selected a story void ScrSpecial_StorytellerStoryListMenu(void) { CreateTask(Task_StoryListMenu, 0x50); } void ScrSpecial_StorytellerDisplayStory(void) { StorytellerDisplayStory(gUnknown_03000748); } u8 ScrSpecial_StorytellerGetFreeStorySlot(void) { return GetFreeStorySlot(); } // Returns TRUE if stat has increased bool8 ScrSpecial_StorytellerUpdateStat(void) { struct MauvilleManStoryteller *storyteller = &gSaveBlock1.mauvilleMan.storyteller; u8 r4 = storyteller->gameStatIDs[gUnknown_03000748]; if (HasTrainerStatIncreased(gUnknown_03000748) == TRUE) { StorytellerRecordNewStat(gUnknown_03000748, r4); return TRUE; } return FALSE; } bool8 ScrSpecial_HasStorytellerAlreadyRecorded(void) { struct MauvilleManStoryteller *storyteller = &gSaveBlock1.mauvilleMan.storyteller; if (storyteller->alreadyRecorded == FALSE) return FALSE; else return TRUE; } bool8 ScrSpecial_StorytellerInitializeRandomStat(void) { return StorytellerInitializeRandomStat(); }