#include "d/lyt/d_lyt_game_over.h" #include "common.h" #include "d/d_base.h" #include "d/d_fader.h" #include "d/d_lang.h" #include "d/d_message.h" #include "d/d_sc_game.h" #include "d/d_sc_title.h" #include "d/d_stage.h" #include "d/flag/sceneflag_manager.h" #include "d/lyt/d2d.h" #include "d/lyt/d_window.h" #include "d/snd/d_snd_bgm_mgr.h" #include "d/snd/d_snd_wzsound.h" #include "f/f_base.h" #include "nw4r/lyt/lyt_group.h" #include "toBeSorted/arc_managers/layout_arc_manager.h" #include "toBeSorted/file_manager.h" #include "toBeSorted/minigame_mgr.h" #include "toBeSorted/misc_actor.h" STATE_DEFINE(dLytGameOver_c, Init); STATE_DEFINE(dLytGameOver_c, In); STATE_DEFINE(dLytGameOver_c, Wait); STATE_DEFINE(dLytGameOver_c, Out); static const d2d::LytBrlanMapping brlanMap2[] = { {"gameOver_02_gameOver.brlan", "G_gameOver_00"}, { "gameOver_02_loop.brlan", "G_loop_00"}, { "gameOver_02_textOut.brlan", "G_textInOut_00"}, { "gameOver_02_textIn.brlan", "G_textInOut_00"}, { "gameOver_02_fade.brlan", "G_fade_00"}, }; static const d2d::LytBrlanMapping brlanMap3[] = { {"gameOver_03_gameOver.brlan", "G_gameOver_00"}, { "gameOver_03_loop.brlan", "G_loop_00"}, { "gameOver_03_textOut.brlan", "G_textInOut_00"}, { "gameOver_03_textIn.brlan", "G_textInOut_00"}, { "gameOver_03_fade.brlan", "G_fade_00"}, }; static const d2d::LytBrlanMapping brlanMap1[] = { {"gameOver_01_gameOver.brlan", "G_gameOver_00"}, { "gameOver_01_loop.brlan", "G_loop_00"}, { "gameOver_01_textOut.brlan", "G_textInOut_00"}, { "gameOver_01_textIn.brlan", "G_textInOut_00"}, { "gameOver_01_fade.brlan", "G_fade_00"}, }; #define GAME_OVER_ANIM_GAME_OVER 0 #define GAME_OVER_ANIM_LOOP 1 #define GAME_OVER_ANIM_TEXT_OUT 2 #define GAME_OVER_ANIM_TEXT_IN 3 #define GAME_OVER_ANIM_TEXT_FADE 4 #define GAME_OVER_NUM_ANIMS 5 static const char *sGroupName = "G_ref_00"; dLytGameOver_c::dLytGameOver_c() : mStateMgr(*this) { init(); } bool dLytGameOver_c::build() { u8 lang = getCurrentLanguage1(); s32 variant; if (lang == D_LANG_FR) { variant = 1; } else if (lang == D_LANG_ES) { variant = 2; } else { variant = 0; } if (variant == 0) { void *data = LayoutArcManager::GetInstance()->getLoadedData("gameOver_01"); mResAcc1.attach(data, ""); } else if (variant == 1) { void *data = LayoutArcManager::GetInstance()->getLoadedData("gameOver_02"); mResAcc1.attach(data, ""); } else { void *data = LayoutArcManager::GetInstance()->getLoadedData("gameOver_03"); mResAcc1.attach(data, ""); } void *data2 = LayoutArcManager::GetInstance()->getLoadedData("System2D"); mResAcc2.attach(data2, ""); switch (variant) { case 1: { mLyt.setResAcc(&mResAcc1); mLyt.build("gameOver_02.brlyt", nullptr); for (int i = 0; i < GAME_OVER_NUM_ANIMS; i++) { mAnm[i].init(brlanMap2[i].mFile, &mResAcc1, mLyt.getLayout(), brlanMap2[i].mName); } break; } case 2: { mLyt.setResAcc(&mResAcc1); mLyt.build("gameOver_03.brlyt", nullptr); for (int i = 0; i < GAME_OVER_NUM_ANIMS; i++) { mAnm[i].init(brlanMap3[i].mFile, &mResAcc1, mLyt.getLayout(), brlanMap3[i].mName); } break; } default: { mLyt.setResAcc(&mResAcc1); mLyt.build("gameOver_01.brlyt", nullptr); for (int i = 0; i < GAME_OVER_NUM_ANIMS; i++) { mAnm[i].init(brlanMap1[i].mFile, &mResAcc1, mLyt.getLayout(), brlanMap1[i].mName); } break; } } mLyt.setPriority(0x8A); for (int i = 0; i < 1; i++) { mSubpanes[i].mpLytPane = &mSelectBtn; mSelectBtn.build(&mResAcc2); mSubpaneList.PushBack(&mSubpanes[i]); } if (mLyt.getLayout()->GetGroupContainer() != nullptr) { nw4r::lyt::Group *g = mLyt.getLayout()->GetGroupContainer()->FindGroupByName(sGroupName); if (g != nullptr) { d2d::dSubPane::linkMeters(g, &mSubpaneList); } } mSelectBtn.setFlipBtnLayout(1); mSelectBtn.setSkipOutAnim(1); mSelectBtn.setInSound(2); mStateMgr.changeState(StateID_Init); field_0x1338 = true; return true; } bool dLytGameOver_c::remove() { for (d2d::SubPaneList::Iterator it = mSubpaneList.GetBeginIter(); it != mSubpaneList.GetEndIter(); ++it) { d2d::dSubPane *subPane = it->mpLytPane; if (subPane != nullptr) { nw4r::lyt::Pane *parent = subPane->getPane()->GetParent(); parent->RemoveChild(subPane->getPane()); } } for (int i = 0; i < 1; i++) { mSubpanes[i].mpLytPane->remove(); } mLyt.unbindAnims(); for (int i = 0; i < GAME_OVER_NUM_ANIMS; i++) { mAnm[i].remove(); } mResAcc2.detach(); mResAcc1.detach(); dBase_c::s_NextExecuteControlFlags &= ~dBase_c::BASE_PROP_0x200; dBase_c::s_DrawControlFlags &= ~dBase_c::BASE_PROP_0x200; return true; } bool dLytGameOver_c::execute() { mStateMgr.executeState(); mLyt.calc(); for (int i = 0; i < 1; i++) { mSubpanes[i].mpLytPane->execute(); } return true; } bool dLytGameOver_c::draw() { if (mIsVisible != true) { return true; } mLyt.addToDrawList(); return true; } bool dLytGameOver_c::requestIn(const char *label, s32 p1, s32 p2) { mLabel = label; mVariant = p1; mInType = p2; mInRequest = true; return true; } bool dLytGameOver_c::requestOut(s32 type) { if (!mStateMgr.getStateID()->isEqual(StateID_Wait)) { return false; } mOutType = type; mOutRequest = true; return true; } void dLytGameOver_c::initializeState_Init() { init(); } void dLytGameOver_c::executeState_Init() { if (mInRequest == true) { mInRequest = false; mStateMgr.changeState(StateID_In); } } void dLytGameOver_c::finalizeState_Init() {} void dLytGameOver_c::initializeState_In() { mStep = 0; mIsVisible = true; u32 anim = GAME_OVER_ANIM_TEXT_IN; if (mInType == 0) { anim = GAME_OVER_ANIM_GAME_OVER; } setAnm(anim); applyText(); field_0x1338 = 0; } void dLytGameOver_c::executeState_In() { u32 anim = GAME_OVER_ANIM_TEXT_IN; if (mInType == 0) { anim = GAME_OVER_ANIM_GAME_OVER; } d2d::AnmGroup_c &anm = mAnm[anim]; if (anm.isEndReached() == true) { stopAnm(anim); setAnm(GAME_OVER_ANIM_LOOP); mStateMgr.changeState(StateID_Wait); setupButtons(); } anm.play(); } void dLytGameOver_c::finalizeState_In() { mSelectBtn.getPane()->SetVisible(true); } void dLytGameOver_c::initializeState_Wait() { mStep = 0; } void dLytGameOver_c::executeState_Wait() { d2d::AnmGroup_c &anm = mAnm[GAME_OVER_ANIM_LOOP]; anm.play(); switch (mStep) { case 0: { if (mSelectBtn.getDecidedBtnIdx() >= 0) { mSelectedOption = mSelectBtn.getDecidedBtnIdx(); mStep += 1; mIsChangingState = true; } break; } case 1: { if (mStep == 1 && mOutRequest == true) { mIsChangingState = false; mOutRequest = false; mStateMgr.changeState(StateID_Out); } break; } } } void dLytGameOver_c::finalizeState_Wait() {} void dLytGameOver_c::initializeState_Out() { mStep = 0; if (mOutType == 0) { setAnm(GAME_OVER_ANIM_TEXT_OUT); } else { setAnm(GAME_OVER_ANIM_TEXT_FADE); } } void dLytGameOver_c::executeState_Out() { u32 anim = GAME_OVER_ANIM_TEXT_OUT; if (mOutType != 0) { anim = GAME_OVER_ANIM_TEXT_FADE; } d2d::AnmGroup_c &anm = mAnm[anim]; switch (mStep) { case 0: { if (anm.isEndReached() == true) { stopAnm(GAME_OVER_ANIM_TEXT_OUT); stopAnm(GAME_OVER_ANIM_LOOP); mIsVisible = false; mIsChangingState = true; mSelectBtn.getPane()->SetVisible(false); mStep = 1; } break; } case 1: { mStateMgr.changeState(StateID_Init); break; } } anm.play(); } void dLytGameOver_c::finalizeState_Out() {} void dLytGameOver_c::init() { mIsVisible = false; mIsChangingState = false; mInRequest = false; mOutRequest = false; mSelectedOption = 0; } void dLytGameOver_c::setAnm(int idx) { d2d::AnmGroup_c &anm = mAnm[idx]; anm.bind(false); anm.setFrame(0.0f); } void dLytGameOver_c::stopAnm(int idx) { d2d::AnmGroup_c &anm = mAnm[idx]; anm.unbind(); } void dLytGameOver_c::applyText() { const wchar_t *text = dMessage_c::getTextMessageByLabel(mLabel, true, nullptr, 0); mLyt.getTextBox("T_select_00")->setTextWithGlobalTextProcessor(text); mLyt.getTextBox("T_selectS_00")->setTextWithGlobalTextProcessor(text); dWindow_c *window = mLyt.getWindow("W_bgP_00"); window->UpdateSize(mLyt.getSizeBoxInWindow("W_bgP_00"), 32.0f); } void dLytGameOver_c::setupButtons() { s32 numOptions = 2; if (mVariant == 1) { numOptions = 3; } else if (mVariant == 2) { numOptions = 4; } for (int i = 0; i < numOptions; i++) { const wchar_t *buf = dMessage_c::getGlobalTagProcessor()->getOptionString(i); mSelectBtn.getSelectTextBox(i, 0)->setTextWithGlobalTextProcessor(buf); mSelectBtn.getSelectTextBox(i, 1)->setTextWithGlobalTextProcessor(buf); } mSelectBtn.setTagProcessor(dMessage_c::getGlobalTagProcessor()); u32 tmp = dMessage_c::getGlobalTagProcessor()->getCancelBtnIdx(); mSelectBtn.setCancelBtnIdx(tmp); mSelectBtn.setField_0x990(tmp); mSelectBtn.requestIn(numOptions, true); } SPECIAL_BASE_PROFILE(LYT_GAMEOVER_MGR, dLytGameOverMgr_c, fProfile::LYT_GAMEOVER_MGR, 0x02AC, 0x0201); STATE_DEFINE(dLytGameOverMgr_c, Init); STATE_DEFINE(dLytGameOverMgr_c, FadeOut); STATE_DEFINE(dLytGameOverMgr_c, Select); dLytGameOverMgr_c::dLytGameOverMgr_c() : mStateMgr(*this) { sInstance = this; init(); } dLytGameOverMgr_c::~dLytGameOverMgr_c() { sInstance = nullptr; } int dLytGameOverMgr_c::create() { mMain.build(); mStateMgr.changeState(StateID_Init); return SUCCEEDED; } int dLytGameOverMgr_c::doDelete() { mMain.remove(); return SUCCEEDED; } int dLytGameOverMgr_c::execute() { mStateMgr.executeState(); return SUCCEEDED; } int dLytGameOverMgr_c::draw() { mMain.draw(); return SUCCEEDED; } void dLytGameOverMgr_c::initializeState_Init() {} void dLytGameOverMgr_c::executeState_Init() { mStateMgr.changeState(StateID_FadeOut); } void dLytGameOverMgr_c::finalizeState_Init() {} void dLytGameOverMgr_c::initializeState_FadeOut() { dStage_c::GetInstance()->fadeOut(dFader_c::FADER_BLACK, 40); } void dLytGameOverMgr_c::executeState_FadeOut() { if (dStage_c::GetInstance()->isFadedOut() == true) { // "Do you want to continue adventuring? [1]Continue[2]Quit" if (mMain.requestIn("SYS_GAMEOVER_01", 0, 0)) { mMain.setField_0x1338(true); mStateMgr.changeState(StateID_Select); dSndBgmMgr_c::GetInstance()->playFanSound(FAN_GAMEOVER); } } } void dLytGameOverMgr_c::finalizeState_FadeOut() {} void dLytGameOverMgr_c::initializeState_Select() { mStep = 0; mTimer = 0; } void dLytGameOverMgr_c::executeState_Select() { // TODO - regswaps https://decomp.me/scratch/IPWjo // FileManager::GetInstance() causes even more regswaps FileManager *fileManager = FileManager::sInstance; // I'm not sure if all these were declared here - it'd make // sense for a switch statement... const char *stage; u8 entrance; u16 room; u8 layer; bool isSpecialMinigame; switch (mStep) { case 0: { if (mTimer < 20) { mTimer++; if (mTimer == 20) { dBase_c::s_NextExecuteControlFlags |= dBase_c::BASE_PROP_0x1; dBase_c::s_DrawControlFlags |= dBase_c::BASE_PROP_0x1; } } if (mMain.isChangingState() == true) { if (mMain.getSelectedOption() == 0) { mStep = 20; } else { mStep = 1; } } break; } case 1: case 10: { if (mMain.requestOut(0) == true) { mStep++; } break; } case 2: case 11: { if (mMain.isChangingState() == true) { mStep++; } break; } case 3: { // "Are you sure you want to quit? [1]Quit[2-]Cancel" if (mMain.requestIn("SYS_GAMEOVER_03", 0, 1)) { mStep++; } break; } case 4: { if (mMain.isChangingState() == true) { if (mMain.getSelectedOption() == 0) { mStep++; } else { mStep = 10; } } break; } case 5: case 20: { if (mMain.requestOut(1) == true) { mStep++; } break; } case 6: { dScTitle_c::loadTitleScreen(1); mStep = 100; // break; } case 12: { // "Do you want to continue adventuring? [1]Continue[2]Quit" if (mMain.requestIn("SYS_GAMEOVER_01", 0, 1)) { mStep = 0; } break; } case 21: { if (mMain.isChangingState() == true) { dScTitle_c::setLbl_80575787(true); dScGame_c::sPreventClearingSomeFlag = true; // Maybe an inline? Would have to use the inverse condition and then negate... isSpecialMinigame = false; if (MinigameManager::isInMinigameState(MinigameManager::TRIAL_TIME_ATTACK) || MinigameManager::isInMinigameState(MinigameManager::BOSS_RUSH)) { isSpecialMinigame = true; } if (mParams != 0 && !isSpecialMinigame) { dScGame_c::setReloaderType(1); dScGame_c::sCopyFileBToCurrentAfterRespawn = true; entrance = fileManager->getEntranceT1_FileB(); room = fileManager->getLoadRoomT1_FileB(); layer = fileManager->getForcedLayerT1_FileB(); // TODO document this logic if (dScGame_c::isCurrentStage("F403") && layer == 2) { layer = 7; } stage = fileManager->getAreaT1_FileB(); // TODO - this is weird, why is dScGame_c::sInstance being loaded first? // inlines? But we're using the fileManager from above for this call... // Probably the reason for the regswaps... dScGame_c::GetInstance()->triggerEntrance( stage, room, layer, entrance, fileManager->getNightT1_FileB() ); } else { dScGame_c::setReloaderType(3); entrance = fileManager->getEntranceT3(); room = fileManager->getLoadRoomT3(); layer = fileManager->getForcedLayerT3(); // TODO - document this logic if (dScGame_c::isCurrentStage("B400") && !isSpecialMinigame) { dScGame_c::setReloaderType(4); entrance = 8; room = 1; layer = 3; } stage = fileManager->getAreaT3(); // TODO - same as above dScGame_c::GetInstance()->triggerEntrance(stage, room, layer, entrance, fileManager->getNightT3()); } // TODO - document this logic if (dScGame_c::isCurrentStage("F401")) { if (fileManager->getCurrentHealth() < 24) { fileManager->setCurrentHealth(24); } } else { fileManager->setCurrentHealth(24); } // TODO - document this logic if (dScGame_c::isCurrentStage("F302")) { // "Minecart Escort related" SceneflagManager::sInstance->unsetTempflag_i(0x3F, 0); } restoreSkykeepPuzzleFromGlobal(); mStep = 100; } break; } case 100: { dBase_c::s_NextExecuteControlFlags &= ~dBase_c::BASE_PROP_0x1; dBase_c::s_DrawControlFlags &= ~dBase_c::BASE_PROP_0x1; dBase_c::s_NextExecuteControlFlags |= dBase_c::BASE_PROP_0x200; dBase_c::s_DrawControlFlags |= dBase_c::BASE_PROP_0x200; mStep++; break; } } mMain.execute(); } void dLytGameOverMgr_c::finalizeState_Select() {} void dLytGameOverMgr_c::init() { mStep = 0; mTimer = 0; }