#include "d/lyt/meter/d_lyt_meter_heart.h" #include "common.h" #include "d/a/d_a_player.h" #include "d/d_sc_game.h" #include "d/lyt/d2d.h" #include "d/lyt/d_lyt_game_over.h" #include "d/snd/d_snd_small_effect_mgr.h" #include "nw4r/math/math_types.h" #include "toBeSorted/d_emitter.h" #include "toBeSorted/event_manager.h" #include "toBeSorted/file_manager.h" static const d2d::LytBrlanMapping brlanMap[] = { { "heartAll_00_heat.brlan", "G_heart_00"}, { "heartAll_00_heat.brlan", "G_heart_01"}, { "heartAll_00_heat.brlan", "G_heart_02"}, { "heartAll_00_heat.brlan", "G_heart_03"}, { "heartAll_00_heat.brlan", "G_heart_04"}, { "heartAll_00_heat.brlan", "G_heart_05"}, { "heartAll_00_heat.brlan", "G_heart_06"}, { "heartAll_00_heat.brlan", "G_heart_07"}, { "heartAll_00_heat.brlan", "G_heart_08"}, { "heartAll_00_heat.brlan", "G_heart_09"}, { "heartAll_00_heat.brlan", "G_heart_10"}, { "heartAll_00_heat.brlan", "G_heart_11"}, { "heartAll_00_heat.brlan", "G_heart_12"}, { "heartAll_00_heat.brlan", "G_heart_13"}, { "heartAll_00_heat.brlan", "G_heart_14"}, { "heartAll_00_heat.brlan", "G_heart_15"}, { "heartAll_00_heat.brlan", "G_heart_16"}, { "heartAll_00_heat.brlan", "G_heart_17"}, { "heartAll_00_heat.brlan", "G_heart_18"}, { "heartAll_00_heat.brlan", "G_heart_19"}, {"heartAll_00_heartMain.brlan", "G_heartMain_00"}, { "heartAll_00_drink.brlan", "G_drink_00"}, { "heartAll_00_mLoop.brlan", "G_mLoop_00"}, { "heartAll_00_mCaution.brlan", "G_mCaution_00"}, { "heartAll_00_loop.brlan", "G_loop_00"}, { "heartAll_00_danger.brlan", "G_danger_00"}, }; #define HEART_ANIM_HEAT_OFFSET 0 #define HEART_ANIM_MAIN 20 #define HEART_ANIM_DRINK 21 #define HEART_ANIM_MLOOP 22 #define HEART_ANIM_MCAUTION 23 #define HEART_ANIM_LOOP 24 #define HEART_ANIM_DANGER 25 #define HEART_NUM_ANIMS 26 #define HEART_NUM_HEARTS 20 static const char *sPaneNames[] = { "N_heart_00", "N_heart_01", "N_heart_02", "N_heart_03", "N_heart_04", "N_heart_05", "N_heart_06", "N_heart_07", "N_heart_08", "N_heart_09", "N_heart_10", "N_heart_11", "N_heart_12", "N_heart_13", "N_heart_14", "N_heart_15", "N_heart_16", "N_heart_17", "N_heart_18", "N_heart_19", }; bool dLytMeterHeart_c::build(d2d::ResAccIf_c *resAcc) { mLyt.setResAcc(resAcc); mLyt.build("heartAll_00.brlyt", nullptr); for (int i = 0; i < HEART_NUM_ANIMS; i++) { mAnm[i].init(brlanMap[i].mFile, resAcc, mLyt.getLayout(), brlanMap[i].mName); mAnm[i].bind(false); if (i <= HEART_NUM_HEARTS) { mAnm[i].setAnimEnable(true); } else { mAnm[i].setAnimEnable(false); } } for (int i = 0; i < HEART_NUM_HEARTS; i++) { mpHeartPanes[i] = mLyt.findPane(sPaneNames[i]); } mpAlphaPane = mLyt.findPane("N_heartAlpha_00"); mpMainPane = mLyt.findPane("N_heartMain_00"); mCurrentHealth = 0; mCurrentHealthCapacity = 0; mStoredHealth = 0; mStoredHealthCapacity = 0; mHealthCapacityIncreaseDelayTimer = 0; mEffectHeartIdx = -1; field_0x78C = 0; field_0x790 = 0; field_0x794 = 0; field_0x797 = 0; mIsLoop = false; mIsDanger = false; field_0x79A = 0; mCurrentHealth = getCurrentHealth(); mCurrentHealthCapacity = getCurrentHealthCapacity(); mStoredHealth = mCurrentHealth; mStoredHealthCapacity = mCurrentHealthCapacity; field_0x794 = 1; realizeHeartsState(); field_0x795 = 1; mLyt.calc(); return true; } bool dLytMeterHeart_c::remove() { return true; } bool dLytMeterHeart_c::execute() { if (field_0x78C > 0) { field_0x78C--; } if (field_0x790 > 0) { field_0x790--; } executeInternal(); if (mIsLoop) { if (!mAnm[HEART_ANIM_LOOP].isEnabled()) { mAnm[HEART_ANIM_LOOP].setFrame(0.0f); mAnm[HEART_ANIM_LOOP].setAnimEnable(true); } } else { if (mAnm[HEART_ANIM_LOOP].isEnabled()) { mAnm[HEART_ANIM_LOOP].setFrame(0.0f); mLyt.calc(); mAnm[HEART_ANIM_LOOP].setAnimEnable(false); } } if (mIsDanger) { if (dAcPy_c::GetLink2()->getField_0x4564() == 1.0f) { if (!mAnm[HEART_ANIM_DANGER].isEnabled()) { mAnm[HEART_ANIM_DANGER].setAnimEnable(true); } mAnm[HEART_ANIM_DANGER].setFrame(0.0f); } } else { if (mAnm[HEART_ANIM_DANGER].isEnabled()) { mAnm[HEART_ANIM_DANGER].setToEnd(); mLyt.getLayout()->Animate(0); mLyt.calc(); mAnm[HEART_ANIM_DANGER].setAnimEnable(false); } } f32 drinkFrame = 0.0f; bool hasGuardianPotion = false; bool hasExpiringGuardianPotion = false; if (FileManager::GetInstance()->hasGuardianPotionPlus()) { drinkFrame = 2.0f; hasGuardianPotion = true; } else if (FileManager::GetInstance()->hasGuardianPotionNormal()) { hasGuardianPotion = true; drinkFrame = 1.0f; } if ((FileManager::GetInstance()->hasGuardianPotionNormal() && FileManager::GetInstance()->getGuardianPotionTimer() < 600) || (FileManager::GetInstance()->hasGuardianPotionPlus() && FileManager::GetInstance()->getGuardianPotionPlusTimer() < 600)) { hasExpiringGuardianPotion = true; } if (hasGuardianPotion) { if (!mAnm[HEART_ANIM_MLOOP].isEnabled()) { mAnm[HEART_ANIM_MLOOP].setFrame(0.0f); mAnm[HEART_ANIM_MLOOP].setAnimEnable(true); } } else if (mAnm[HEART_ANIM_MLOOP].getFrame() < 1.0f) { mAnm[HEART_ANIM_MLOOP].setFrame(0.0f); mLyt.getLayout()->Animate(0); mAnm[HEART_ANIM_MLOOP].setAnimEnable(false); } if (hasGuardianPotion && hasExpiringGuardianPotion) { if (!mAnm[HEART_ANIM_MCAUTION].isEnabled()) { mAnm[HEART_ANIM_MCAUTION].setFrame(0.0f); mAnm[HEART_ANIM_MCAUTION].setAnimEnable(true); } } else if (mAnm[HEART_ANIM_MCAUTION].getFrame() < 1.0f) { mAnm[HEART_ANIM_MCAUTION].setFrame(0.0f); mLyt.getLayout()->Animate(0); mAnm[HEART_ANIM_MCAUTION].setAnimEnable(false); } mAnm[HEART_ANIM_DRINK].setFrame(drinkFrame); mAnm[HEART_ANIM_DRINK].setAnimEnable(true); for (int i = 0; i < HEART_NUM_ANIMS; i++) { if (i > HEART_NUM_HEARTS + 1 && mAnm[i].isEnabled()) { mAnm[i].play(); } } if (mIsDanger && mAnm[HEART_ANIM_DANGER].isEndReached()) { mAnm[HEART_ANIM_DANGER].setToEnd(); mAnm[HEART_ANIM_DANGER].setAnimEnable(false); } mLyt.calc(); return true; } s32 dLytMeterHeart_c::getNumDisplayedHearts() const { return mStoredHealthCapacity / 4; } s32 dLytMeterHeart_c::getDisplayedHealthCapacity() const { return getNumDisplayedHearts() * 4; } void dLytMeterHeart_c::realizeHeartsState() { s32 hearts = getNumDisplayedHearts(); (void)getDisplayedHealthCapacity(); s32 health = mStoredHealth; s32 healthCapacity = mStoredHealthCapacity; s32 numFilledHearts = health / 4; s32 partialHeartFill = health % 4; // Wrong s32 displayedFilledHearts = healthCapacity / 4; s32 mNumTotalHeartsIncludingPartial = (getCurrentHealthCapacity() + 3) / 4; if (displayedFilledHearts < mNumTotalHeartsIncludingPartial) { mNumTotalHeartsIncludingPartial = displayedFilledHearts; } mIsLoop = false; mIsDanger = false; for (int i = 0; i < HEART_NUM_HEARTS; i++) { if (i < mNumTotalHeartsIncludingPartial) { mpHeartPanes[i]->SetVisible(true); if (i < displayedFilledHearts) { if (i < hearts) { if (numFilledHearts == 0 && partialHeartFill == 0) { if (i == 0) { mpMainPane->SetVisible(false); } mAnm[i + HEART_ANIM_HEAT_OFFSET].setFrame(1.0f); } else if (i < numFilledHearts) { mAnm[i + HEART_ANIM_HEAT_OFFSET].setFrame(0.0f); if (i == numFilledHearts - 1 && partialHeartFill == 0) { mAnm[i + HEART_ANIM_HEAT_OFFSET].setFrame(2.0f); mpMainPane->SetVisible(true); mpMainPane->SetTranslate(mpHeartPanes[i]->GetTranslate()); mAnm[HEART_ANIM_MAIN].setFrame(4.0f); if (dAcPy_c::GetLink2() != nullptr && dAcPy_c::GetLink2()->hasLessThanQuarterHealth(false)) { mIsDanger = true; } else { mIsLoop = true; } } else { mAnm[i + HEART_ANIM_HEAT_OFFSET].setFrame(0.0f); } } else if (i == numFilledHearts && partialHeartFill != 0) { mAnm[i + HEART_ANIM_HEAT_OFFSET].setFrame(2.0f); mpMainPane->SetVisible(true); mpMainPane->SetTranslate(mpHeartPanes[i]->GetTranslate()); mAnm[HEART_ANIM_MAIN].setFrame(partialHeartFill); if (dAcPy_c::GetLink2() != nullptr && dAcPy_c::GetLink2()->hasLessThanQuarterHealth(false)) { mIsDanger = true; } else { mIsLoop = true; } } else { mAnm[i + HEART_ANIM_HEAT_OFFSET].setFrame(1.0f); } } else { mAnm[i + HEART_ANIM_HEAT_OFFSET].setFrame(1.0f); } } else { mAnm[i + HEART_ANIM_HEAT_OFFSET].setFrame(1.0f); } } else { mpHeartPanes[i]->SetVisible(false); } } } void dLytMeterHeart_c::executeInternal() { if (mEffectHeartIdx >= 0) { nw4r::math::MTX34 mtx = mpHeartPanes[mEffectHeartIdx]->GetGlobalMtx(); mVec3_c pos(mtx._03, mtx._13, 0.0f); dJEffManager_c::spawnUIEffect(PARTICLE_RESOURCE_ID_MAPPING_972_, pos, nullptr, nullptr, nullptr, nullptr); dSndSmallEffectMgr_c::GetInstance()->playSound(SE_S_HEART_ADD); mEffectHeartIdx = -1; } // Redundant if (!field_0x794) { mCurrentHealth = getCurrentHealth(); mCurrentHealthCapacity = getCurrentHealthCapacity(); field_0x794 = 1; } mCurrentHealthCapacity = getCurrentHealthCapacity(); mCurrentHealth = getCurrentHealth(); if (field_0x796) { for (int i = 0; i < HEART_NUM_HEARTS; i++) { mAnm[i + HEART_ANIM_HEAT_OFFSET].setFrame(1.0f); } mLyt.calc(); field_0x796 = 0; } bool isDanger = dAcPy_c::GetLink2()->hasLessThanQuarterHealth(0); bool healthDifferent = mStoredHealth != mCurrentHealth; bool healthCapacityDifferent = mStoredHealthCapacity != mCurrentHealthCapacity; if (healthCapacityDifferent && field_0x790 < 20) { field_0x790 = 20; } if (healthDifferent || field_0x797 != isDanger) { s32 current = mCurrentHealth; if (mStoredHealth + 1 <= current) { mStoredHealth = mStoredHealth + 1; if (mpAlphaPane->IsVisible() && mpAlphaPane->GetGlobalAlpha() != 0 && mStoredHealth % 4 == 0 && (!(dScGame_c::currentSpawnInfo.stageName == "F406") || dScGame_c::currentSpawnInfo.layer != 13) && dLytGameOverMgr_c::GetInstance() == nullptr) { dSndSmallEffectMgr_c::GetInstance()->playSound(SE_S_HP_GAUGE_UP); } } else { if (mStoredHealth - 1 >= current) { mStoredHealth = mStoredHealth - 1; if (EventManager::isInEvent() && !strcmp(EventManager::getCurrentEventName(), ("MoleF202Start")) && mStoredHealth % 4 == 0) { dSndSmallEffectMgr_c::GetInstance()->playSound(SE_S_HP_GAUGE_DOWN); } } } realizeHeartsState(); if (healthDifferent && field_0x78C < 10) { field_0x78C = 10; } field_0x797 = isDanger; } else if (healthCapacityDifferent) { if (mStoredHealthCapacity + 1 <= mCurrentHealthCapacity) { if ((mStoredHealthCapacity + 1) % 4 == 0) { if (mHealthCapacityIncreaseDelayTimer < 15) { mHealthCapacityIncreaseDelayTimer += 1; return; } mHealthCapacityIncreaseDelayTimer = 0; } mStoredHealthCapacity++; if (mStoredHealthCapacity % 4 == 0) { field_0x79A = 1; } } else { if (mStoredHealthCapacity - 1 >= mCurrentHealthCapacity) { mStoredHealthCapacity = mStoredHealthCapacity - 1; } } if (field_0x79A && mStoredHealth > mCurrentHealthCapacity - 3) { s32 numDisplayed = getNumDisplayedHearts(); if (numDisplayed >= 1 && numDisplayed <= 20) { mEffectHeartIdx = numDisplayed - 1; field_0x79A = 0; } } realizeHeartsState(); if (field_0x78C < 20) { field_0x78C = 20; } } else if (field_0x79A) { field_0x79A = 0; } } u8 dLytMeterHeart_c::getCurrentHealth() const { return FileManager::GetInstance()->getCurrentHealth(); } u8 dLytMeterHeart_c::getCurrentHealthCapacity() const { return dAcPy_c::getCurrentHealthCapacity(); }