From 027164e4b4baa4ac02ad8647b9977798c540297a Mon Sep 17 00:00:00 2001 From: Treeki Date: Mon, 25 Feb 2013 22:03:47 +0100 Subject: implement completion congratulations messages --- koopatlas.yaml | 6 + src/koopatlas/core.cpp | 81 ++++++++++++++ src/koopatlas/core.h | 4 + src/koopatlas/pathmanager.cpp | 255 +++++++++++++++++++++++++++++++++++++++++- src/koopatlas/pathmanager.h | 16 +++ 5 files changed, 359 insertions(+), 3 deletions(-) diff --git a/koopatlas.yaml b/koopatlas.yaml index 5c9c081..377e9eb 100644 --- a/koopatlas.yaml +++ b/koopatlas.yaml @@ -98,3 +98,9 @@ hooks: addr_pal: 0x800D52F0 data: '41820010' + - name: ResetAllCompletionCandidates + type: branch_insn + branch_type: b + src_addr_pal: 0x800B2FF0 + target_func: 'ResetAllCompletionCandidates(void)' + diff --git a/src/koopatlas/core.cpp b/src/koopatlas/core.cpp index 1772724..9666e53 100644 --- a/src/koopatlas/core.cpp +++ b/src/koopatlas/core.cpp @@ -10,6 +10,8 @@ dScKoopatlas_c *dScKoopatlas_c::instance = 0; CREATE_STATE_E(dScKoopatlas_c, Limbo); CREATE_STATE(dScKoopatlas_c, ContinueWait); CREATE_STATE_E(dScKoopatlas_c, Normal); +CREATE_STATE(dScKoopatlas_c, CompletionMsg); +CREATE_STATE_E(dScKoopatlas_c, CompletionMsgHideWait); CREATE_STATE_E(dScKoopatlas_c, CSMenu); CREATE_STATE_E(dScKoopatlas_c, TitleConfirmOpenWait); CREATE_STATE_E(dScKoopatlas_c, TitleConfirmSelect); @@ -499,6 +501,12 @@ void dScKoopatlas_c::endState_ContinueWait() { void dScKoopatlas_c::executeState_Normal() { + if (pathManager.completionMessagePending) { + OSReport("Going to set CompletionMsg\n"); + state.setState(&StateID_CompletionMsg); + return; + } + if (pathManager.doingThings()) return; @@ -1050,6 +1058,79 @@ void dScKoopatlas_c::showSaveWindow() { } +void dScKoopatlas_c::beginState_CompletionMsg() { + OSReport("CompletionMsg beginning with type %d\n", pathManager.completionMessageType); + static const int ynTypes[8] = { + /*NULL*/ -1, + /*COINS*/ 14, + /*EXITS*/ 7, + /*WORLD*/ 8, + /*COINS EXC W9*/ 9, + /*GLOBAL COINS*/ 11, + /*GLOBAL EXITS*/ 27, + /*EVERYTHING*/ 21 + }; + yesNoWindow->type = ynTypes[pathManager.completionMessageType]; + yesNoWindow->visible = true; + mustFixYesNoText = 10; // hacky shit +} + +void dScKoopatlas_c::endState_CompletionMsg() { + pathManager.completionMessagePending = false; + pathManager.completionMessageType = 0; +} + +void dScKoopatlas_c::executeState_CompletionMsg() { + // hacky shit + if (mustFixYesNoText > 0) { + mustFixYesNoText--; + + int type = pathManager.completionMessageType; + + if (type >= CMP_MSG_COINS && type <= CMP_MSG_WORLD) { + // get the stuff we need: base BMG string + static const int bmgs[4] = {-1, 65, 58, 59}; + const wchar_t *baseText = GetBMG()->findStringForMessageID(0, bmgs[type]); + + // title + int w = pathManager.completionMessageWorldNum; + int l = ((w == 5) || (w == 7)) ? 101 : 100; + dLevelInfo_c::entry_s *titleEntry = dLevelInfo_c::s_info.searchByDisplayNum(w, l); + const char *title = dLevelInfo_c::s_info.getNameForLevel(titleEntry); + + // assemble the string + wchar_t text[512]; + + wcscpy(text, baseText); + int pos = wcslen(text); + + text[pos++] = ' '; + + while (*title) + text[pos++] = *(title++); + + text[pos++] = '!'; + text[pos++] = 0; + + yesNoWindow->T_question_00->SetString(text); + yesNoWindow->T_questionS_00->SetString(text); + } + } + + if (!yesNoWindow->animationActive) { + if (Wiimote_TestButtons(GetActiveWiimote(), WPAD_A | WPAD_TWO)) { + yesNoWindow->close = true; + state.setState(&StateID_CompletionMsgHideWait); + } + } +} + +void dScKoopatlas_c::executeState_CompletionMsgHideWait() { + if (!yesNoWindow->visible) + state.setState(&StateID_Normal); +} + + void NewerMapDrawFunc() { Reset3DState(); SetCurrentCameraID(0); diff --git a/src/koopatlas/core.h b/src/koopatlas/core.h index 58030b7..f314b82 100644 --- a/src/koopatlas/core.h +++ b/src/koopatlas/core.h @@ -58,6 +58,8 @@ class dScKoopatlas_c : public dScene_c { DECLARE_STATE(Limbo); DECLARE_STATE(ContinueWait); DECLARE_STATE(Normal); + DECLARE_STATE(CompletionMsg); + DECLARE_STATE(CompletionMsgHideWait); DECLARE_STATE(CSMenu); DECLARE_STATE(TitleConfirmOpenWait); DECLARE_STATE(TitleConfirmSelect); @@ -118,6 +120,8 @@ class dScKoopatlas_c : public dScene_c { bool warpZoneHacks; + int mustFixYesNoText; + u32 iterateMapList(u32(*callback)(u32,const char *,int,int), u32 userData, int *ptrIndex = 0); const char *getMapNameForIndex(int index); int getIndexForMapName(const char *name); diff --git a/src/koopatlas/pathmanager.cpp b/src/koopatlas/pathmanager.cpp index cf3e0eb..8c31d96 100644 --- a/src/koopatlas/pathmanager.cpp +++ b/src/koopatlas/pathmanager.cpp @@ -8,6 +8,25 @@ extern "C" void PlaySoundWithFunctionB4(void *spc, nw4r::snd::SoundHandle *handle, int id, int unk); u8 MaybeFinishingLevel[2] = {0xFF,0xFF}; +u8 LastLevelPlayed[2] = {0xFF,0xFF}; +bool CanFinishCoins = false; +bool CanFinishExits = false; +bool CanFinishWorld = false; +bool CanFinishAlmostAllCoins = false; +bool CanFinishAllCoins = false; +bool CanFinishAllExits = false; +bool CanFinishEverything = false; +void ResetAllCompletionCandidates() { + MaybeFinishingLevel[0] = 0xFF; + LastLevelPlayed[0] = 0xFF; + CanFinishCoins = false; + CanFinishExits = false; + CanFinishWorld = false; + CanFinishAlmostAllCoins = false; + CanFinishAllCoins = false; + CanFinishAllExits = false; + CanFinishEverything = false; +} void dWMPathManager_c::setup() { dScKoopatlas_c *wm = dScKoopatlas_c::instance; @@ -147,13 +166,74 @@ void dWMPathManager_c::setup() { // did we just beat a level? if (MaybeFinishingLevel[0] != 0xFF) { - SaveBlock *save = GetSaveFile()->GetBlock(-1); if (save->CheckLevelCondition(MaybeFinishingLevel[0], MaybeFinishingLevel[1], COND_NORMAL)) { shouldRequestSave = true; } + } - MaybeFinishingLevel[0] = 0xFF; + // have we got any completions? + u32 conds = save->GetLevelCondition(LastLevelPlayed[0], LastLevelPlayed[1]); + dLevelInfo_c::entry_s *whatEntry = + dLevelInfo_c::s_info.searchBySlot(LastLevelPlayed[0], LastLevelPlayed[1]); + + // how many exits? + int exits = 0, maxExits = 0; + if (whatEntry->flags & 0x10) { + maxExits++; + if (conds & COND_NORMAL) + exits++; + } + if (whatEntry->flags & 0x20) { + maxExits++; + if (conds & COND_SECRET) + exits++; + } + + completionMessageWorldNum = whatEntry->displayWorld; + + // now do all the message checks + int flag = 0, totalFlag = 0; + if (CanFinishCoins) { + totalFlag |= 1; + if ((conds & COND_COIN_ALL) == COND_COIN_ALL) { + flag |= 1; + completionMessageType = CMP_MSG_COINS; + } + } + if (CanFinishExits) { + totalFlag |= 2; + if (exits == maxExits) { + flag |= 2; + completionMessageType = CMP_MSG_EXITS; + } + } + if (CanFinishWorld && flag == totalFlag) + completionMessageType = CMP_MSG_WORLD; + + if (CanFinishAlmostAllCoins) { + if ((conds & COND_COIN_ALL) == COND_COIN_ALL) + completionMessageType = CMP_MSG_GLOBAL_COINS_EXC_W9; + } + + int gFlag = 0, gTotalFlag = 0; + if (CanFinishAllCoins) { + gTotalFlag |= 1; + if ((conds & COND_COIN_ALL) == COND_COIN_ALL) { + gFlag |= 1; + completionMessageType = CMP_MSG_GLOBAL_COINS; + } + } + if (CanFinishAllExits) { + gTotalFlag |= 2; + if (exits == maxExits) { + gFlag |= 2; + completionMessageType = CMP_MSG_GLOBAL_EXITS; + } } + if (CanFinishEverything && gFlag == gTotalFlag) + completionMessageType = CMP_MSG_EVERYTHING; + + ResetAllCompletionCandidates(); if (wm->isAfterKamekCutscene) copyWorldDefToSave(wm->mapData.findWorldDef(1)); @@ -180,6 +260,135 @@ dWMPathManager_c::~dWMPathManager_c() { MaybeFinishingLevel[1] = enteredLevel->levelSlot; } } + + // Now, a fuckton of checks for the various possible things we can finish! + dLevelInfo_c *li = &dLevelInfo_c::s_info; + u32 theseConds = save->GetLevelCondition(enteredLevel->worldSlot, enteredLevel->levelSlot); + + int coinCount = 0, exitCount = 0; + int globalCoinCount = 0, globalCoinCountExcW9 = 0, globalExitCount = 0; + + int totalCoinCount = 0, totalExitCount = 0; + int totalGlobalCoinCount = 0, totalGlobalCoinCountExcW9 = 0, totalGlobalExitCount = 0; + + for (int sIdx = 0; sIdx < li->sectionCount(); sIdx++) { + dLevelInfo_c::section_s *sect = li->getSectionByIndex(sIdx); + + for (int lIdx = 0; lIdx < sect->levelCount; lIdx++) { + dLevelInfo_c::entry_s *entry = §->levels[lIdx]; + u32 entryConds = save->GetLevelCondition(entry->worldSlot, entry->levelSlot); + + // Only track actual levels + if (!(entry->flags & 2)) + continue; + + // Counts for this world + if (entry->displayWorld == enteredLevel->displayWorld) { + totalCoinCount++; + if ((entryConds & COND_COIN_ALL) == COND_COIN_ALL) + coinCount++; + + // Normal exit + if (entry->flags & 0x10) { + totalExitCount++; + if (entryConds & COND_NORMAL) + exitCount++; + } + + // Secret exit + if (entry->flags & 0x20) { + totalExitCount++; + if (entryConds & COND_SECRET) + exitCount++; + } + } + + // Counts for everywhere + totalGlobalCoinCount++; + if ((entryConds & COND_COIN_ALL) == COND_COIN_ALL) + globalCoinCount++; + + if (entry->displayWorld != 9) { + totalGlobalCoinCountExcW9++; + if (entryConds & COND_COIN_ALL) + globalCoinCountExcW9++; + } + + // Normal exit + if (entry->flags & 0x10) { + totalGlobalExitCount++; + if (entryConds & COND_NORMAL) + globalExitCount++; + } + + // Secret exit + if (entry->flags & 0x20) { + totalGlobalExitCount++; + if (entryConds & COND_SECRET) + globalExitCount++; + } + } + } + + // So.. are we candidates for any of these? + ResetAllCompletionCandidates(); + + LastLevelPlayed[0] = enteredLevel->worldSlot; + LastLevelPlayed[1] = enteredLevel->levelSlot; + + int everythingFlag = 0, gEverythingFlag = 0; + if (coinCount == totalCoinCount) + everythingFlag |= 1; + if (exitCount == globalExitCount) + everythingFlag |= 2; + if (globalCoinCount == totalGlobalCoinCount) + gEverythingFlag |= 1; + if (globalExitCount == totalGlobalExitCount) + gEverythingFlag |= 2; + + // Check if we could obtain every star coin + if ((theseConds & COND_COIN_ALL) != COND_COIN_ALL) { + if ((coinCount + 1) == totalCoinCount) { + CanFinishCoins = true; + everythingFlag |= 1; + } + if ((globalCoinCount + 1) == totalGlobalCoinCount) { + CanFinishAllCoins = true; + gEverythingFlag |= 1; + } + if ((globalCoinCountExcW9 + 1) == totalGlobalCoinCountExcW9) + CanFinishAlmostAllCoins = true; + } + + // Check if we could obtain every exit + int elExits = 0, elTotalExits = 0; + if (enteredLevel->flags & 0x10) { + elTotalExits++; + if (theseConds & COND_NORMAL) + elExits++; + } + if (enteredLevel->flags & 0x20) { + elTotalExits++; + if (theseConds & COND_SECRET) + elExits++; + } + + if ((elExits + 1) == elTotalExits) { + if ((exitCount + 1) == totalExitCount) { + CanFinishExits = true; + everythingFlag |= 2; + } + if ((globalExitCount + 1) == totalGlobalExitCount) { + CanFinishAllExits = true; + gEverythingFlag |= 2; + } + } + + // And could we obtain EVERYTHING? + if ((CanFinishCoins || CanFinishExits) && everythingFlag == 3) + CanFinishWorld = true; + if ((CanFinishAllCoins || CanFinishAllExits) && gEverythingFlag == 3) + CanFinishEverything = true; } if (penguinSlideSound.Exists()) @@ -382,7 +591,7 @@ bool dWMPathManager_c::evaluateUnlockCondition(u8 *&in, SaveBlock *save, int sta bool dWMPathManager_c::doingThings() { - if (isEnteringLevel || (waitAfterUnlock > 0) || + if (isEnteringLevel || (waitAfterUnlock > 0) || (completionAnimDelay > 0) || (waitAtStart > 0) || (waitForAfterDeathAnim > 0) || (countdownToFadeIn > 0) || (unlockingAlpha != -1)) return true; @@ -461,6 +670,46 @@ void dWMPathManager_c::execute() { return; } + if (completionAnimDelay > 0) { + completionAnimDelay--; + if (dmGladDuration > 0) { + dmGladDuration--; + if (dmGladDuration == 0) + daWMPlayer_c::instance->startAnimation(wait_select, 1.0f, 0.0f, 0.0f); + } + if (completionAnimDelay == 0) + completionMessagePending = true; + return; + } + + // just in case + if (completionMessagePending) + return; + + if (completionMessageType > 0) { + OSReport("We have a completion message type: %d\n", completionMessageType); + + int whichSound; + if (completionMessageType == CMP_MSG_GLOBAL_COINS) { + whichSound = STRM_BGM_STAR_COIN_CMPLT_ALL; + completionAnimDelay = 240; + } else if (completionMessageType == CMP_MSG_EVERYTHING) { + whichSound = STRM_BGM_ALL_CMPLT_5STARS; + completionAnimDelay = 216; + } else { + whichSound = STRM_BGM_STAR_COIN_CMPLT_WORLD; + completionAnimDelay = 138; + } + + nw4r::snd::SoundHandle something; + PlaySoundWithFunctionB4(SoundRelatedClass, &something, whichSound, 1); + + daWMPlayer_c::instance->startAnimation(coin_comp, 1.0f, 0.0f, 0.0f); + dmGladDuration = 154; + + return; + } + if (waitAtStart > 0) { waitAtStart--; if (waitAtStart == 0) { diff --git a/src/koopatlas/pathmanager.h b/src/koopatlas/pathmanager.h index 156ed7b..522db7f 100644 --- a/src/koopatlas/pathmanager.h +++ b/src/koopatlas/pathmanager.h @@ -15,6 +15,17 @@ extern "C" void *SoundRelatedClass; extern "C" void *MapSoundPlayer(void *SoundClass, int soundID, int unk); extern "C" bool SpawnEffect(const char*, int, Vec*, S16Vec*, Vec*); +enum CompletionMessageType { + CMP_MSG_NULL = 0, + CMP_MSG_COINS = 1, + CMP_MSG_EXITS = 2, + CMP_MSG_WORLD = 3, + CMP_MSG_GLOBAL_COINS_EXC_W9 = 4, + CMP_MSG_GLOBAL_COINS = 5, + CMP_MSG_GLOBAL_EXITS = 6, + CMP_MSG_EVERYTHING = 7 +}; + class dWMPathManager_c { public: void setup(); @@ -76,6 +87,11 @@ class dWMPathManager_c { public: bool shouldRequestSave; bool isEnteringLevel; + bool completionMessagePending; + int dmGladDuration; + int completionAnimDelay; + int completionMessageType; + int completionMessageWorldNum; bool calledEnteredNode; int levelStartWait; -- cgit v1.2.3