diff --git a/include/d/d_msg_scrn_3select.h b/include/d/d_msg_scrn_3select.h index de74919d4f..f70bbe5789 100644 --- a/include/d/d_msg_scrn_3select.h +++ b/include/d/d_msg_scrn_3select.h @@ -107,9 +107,6 @@ public: /* 0x115 */ u8 mDPDPoint; /* 0x116 */ u8 field_0x116; /* 0x117 */ u8 field_0x117; -#if TARGET_PC - bool mPresenting; -#endif }; // Size: 0x118 #endif /* MSG_SCRN_D_MSG_SCRN_3SELECT_H */ diff --git a/include/d/d_timer.h b/include/d/d_timer.h index 235b22a981..f73573e9d3 100644 --- a/include/d/d_timer.h +++ b/include/d/d_timer.h @@ -119,12 +119,6 @@ public: /* 0x3E0 */ u8 field_0x3E0; /* 0x3E1 */ u8 mTimerVisible; /* 0x3E2 */ u8 field_0x3e2; -#if TARGET_PC - bool mPresentStep = false; - bool mPresentOpening = false; - bool mPresentClosing = false; - bool mPresentTimerSlide = false; -#endif }; class dTimer_c : public msg_class { diff --git a/res/rml/prelaunch.rcss b/res/rml/prelaunch.rcss index f3110a4fec..18766ebb72 100644 --- a/res/rml/prelaunch.rcss +++ b/res/rml/prelaunch.rcss @@ -509,6 +509,10 @@ body.animate-in .intro-item { text-align: right; } + #menu-list button.mod-update-link { + justify-content: flex-end; + } + body.mirrored #menu-list { align-items: flex-start; } @@ -517,6 +521,10 @@ body.animate-in .intro-item { text-align: left; } + body.mirrored #menu-list button.mod-update-link { + justify-content: flex-start; + } + eyebrow { display: none; } diff --git a/res/rml/window.rcss b/res/rml/window.rcss index f820de9735..a819151ac7 100644 --- a/res/rml/window.rcss +++ b/res/rml/window.rcss @@ -333,21 +333,24 @@ select-button:disabled { cursor: default; } +select-button key, +select-button value { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + select-button key { font-family: var(--font-family-heading); font-weight: bold; font-size: var(--font-size-xl); text-transform: uppercase; - flex: 0 0 auto; - white-space: nowrap; + flex: 0 1 auto; } select-button value { - flex: 1 1 auto; - min-width: 0; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + flex: 1 0 20%; text-align: right; font-size: var(--font-size-2xl); } @@ -361,6 +364,16 @@ select-button input { font-size: var(--font-size-2xl); } +select-button icon { + display: none; + flex: 0 0 auto; +} + +select-button.has-icon icon, +select-button.group-button icon { + display: inline-block; +} + select-button.group-button icon { flex: 0 0 24dp; margin-left: auto; diff --git a/src/d/d_msg_class.cpp b/src/d/d_msg_class.cpp index e0f9584a9c..ee559f16d2 100644 --- a/src/d/d_msg_class.cpp +++ b/src/d/d_msg_class.cpp @@ -914,7 +914,7 @@ void jmessage_tReference::addCharAlpha() { } void jmessage_tReference::addCharAllAlphaRate() { - dusk::vdt::advance_toward_frame(mCharAllAlphaRate, 1.0f, mAddCharAllAlphaRate); + mCharAllAlphaRate = std::min(mCharAllAlphaRate + mAddCharAllAlphaRate, 1.0f); } #endif diff --git a/src/d/d_msg_object.cpp b/src/d/d_msg_object.cpp index 0157d2521f..6433d54151 100644 --- a/src/d/d_msg_object.cpp +++ b/src/d/d_msg_object.cpp @@ -24,10 +24,9 @@ #include "f_op/f_op_msg_mng.h" #include #include - -#include "JSystem/JKernel/JKRExpHeap.h" #include "m_Do/m_Do_controller_pad.h" #include "m_Do/m_Do_lib.h" +#include "JSystem/JKernel/JKRExpHeap.h" #if TARGET_PC #include "dusk/interp/user_interface.h" @@ -39,9 +38,20 @@ #include "dusk/settings.h" #include "dusk/version.hpp" +#include + #include #include #include + +namespace { +struct MessageAnimation { + int phase = -1; + dusk::vdt::FrameAnimation frame, alpha; +}; + +absl::flat_hash_map sMessageAnimations; +} // namespace #endif static void dMsgObject_addFundRaising(s16 param_0); @@ -321,6 +331,7 @@ dMsgObject_HIO_c::dMsgObject_HIO_c() { int dMsgObject_c::_create(msg_class* param_1) { #if TARGET_PC + sMessageAnimations.erase(this); if (dusk::version::isRegionJpn()) g_MsgObject_HIO_c.mBoxTalkScaleX = 1.1f; else @@ -572,12 +583,33 @@ int dMsgObject_c::_draw() { } #if TARGET_PC +static void startAnimation(dMsgObject_c& message, int phase, f32 from, f32 target) { + auto& animations = sMessageAnimations[&message]; + auto& animation = animations.frame; + if (animations.phase != phase) { + animation.start(from, target); + } else { + animation.approach(from, target); + } + animations.phase = phase; +} + void dMsgObject_c::presentAnims() { - if (mpScrnDraw == NULL || mpOutFont == NULL) { + if (mpScrnDraw == NULL || mpOutFont == NULL || !dusk::game_clock::g_frameTiming.interpolating) { + return; + } + const auto found = sMessageAnimations.find(this); + if (found == sMessageAnimations.end()) { + return; + } + auto& animations = found->second; + if (animations.phase < 0) { return; } - const u16 status = getStatusLocal(); + const u16 status = animations.phase; + auto& animation = animations.frame; + const f32 frame = animation.advance(getStatusLocal() == animations.phase ? field_0x16a : animation.value()); f32 target = 0.0f; if (status == 2) { if (isPlaceMessage() || isStaffMessage()) { @@ -590,33 +622,19 @@ void dMsgObject_c::presentAnims() { } else { target = g_MsgObject_HIO_c.mBoxAppearFrame; } - dusk::vdt::advance_toward_frame(field_0x16a, target, 1.0f); + const f32 ratio = dusk::vdt::clamped_fraction(frame, target); + mpScrnDraw->fukiAlpha(ratio); + mpOutFont->setAlphaRatio(ratio); if (isKanbanMessage() || isPlaceMessage() || isStaffMessage() || isBossMessage()) { - f32 ratio = dusk::vdt::clamped_fraction(field_0x16a, target); mpScrnDraw->fukiScale(ratio); - mpScrnDraw->fukiAlpha(ratio); - mpOutFont->setAlphaRatio(ratio); } else if (isBookMessage()) { - if (field_0x16a <= g_MsgObject_HIO_c.mBoxAppearFrame) { - f32 ratio = dusk::vdt::clamped_fraction(field_0x16a, g_MsgObject_HIO_c.mBoxAppearFrame); - mpScrnDraw->fukiAlpha(ratio); - mpOutFont->setAlphaRatio(ratio); - } else { - mpScrnDraw->fukiAlpha(1.0f); - mpOutFont->setAlphaRatio(1.0f); - } s16 waitEnd = g_MsgObject_HIO_c.mBoxAppearFrame + g_MsgObject_HIO_c.mWaitFrame; - if (field_0x16a >= waitEnd && - field_0x16a <= waitEnd + g_MsgObject_HIO_c.mLightAppearFrame) - { - f32 scale = dusk::vdt::clamped_fraction(field_0x16a - waitEnd, g_MsgObject_HIO_c.mLightAppearFrame); + if (frame >= waitEnd && frame <= waitEnd + g_MsgObject_HIO_c.mLightAppearFrame) { + f32 scale = dusk::vdt::clamped_fraction(frame - waitEnd, g_MsgObject_HIO_c.mLightAppearFrame); mpScrnDraw->fukiScale(scale); } } else { - f32 ratio = dusk::vdt::clamped_fraction(field_0x16a, target); mpScrnDraw->fukiScale(1.0f); - mpScrnDraw->fukiAlpha(ratio); - mpOutFont->setAlphaRatio(ratio); } } else if (status == 17) { if (isKanbanMessage() || isBookMessage()) { @@ -628,33 +646,27 @@ void dMsgObject_c::presentAnims() { } else { target = 5.0f; } - dusk::vdt::advance_toward_frame(field_0x16a, target, 1.0f); - f32 ratio = dusk::vdt::clamped_fraction(field_0x16a, target); + f32 ratio = dusk::vdt::clamped_fraction(frame, target); mpScrnDraw->fukiAlpha(1.0f - ratio); if (isBookMessage()) { mpScrnDraw->fukiScale(1.0f - ratio); } mpOutFont->setAlphaRatio(1.0f - ratio); } else if (status == 6) { - if (isBookMessage() && field_0x16a > 0) { - dusk::vdt::advance_toward_frame(field_0x16a, 0.0f, 1.0f); - f32 alpha = (10 - field_0x16a) / 10.0f; + if (isBookMessage() && frame > 0) { + f32 alpha = (10 - frame) / 10.0f; mpScrnDraw->fontAlpha(alpha); mpOutFont->setAlphaRatio(alpha); } jmessage_tReference* pRef = (jmessage_tReference*)mpRenProc->getReference(); if (pRef->getCharAllAlphaRate() < 1.0f) { - pRef->addCharAllAlphaRate(); - f32 alpha = pRef->getCharAllAlphaRate(); + f32 alpha = animations.alpha.advance(pRef->getCharAllAlphaRate()); mpScrnDraw->setCharAlphaRate(alpha); mpOutFont->setAlphaRatio(alpha); } - } else if (status == 5 && isBookMessage() && - (isSend() || field_0x16a > 0)) - { - dusk::vdt::advance_toward_frame(field_0x16a, 10.0f, 1.0f); - if (field_0x16a <= 10) { - f32 alpha = (10 - field_0x16a) / 10.0f; + } else if (status == 5 && isBookMessage() && (field_0x199 || frame > 0)) { + if (frame <= 10) { + f32 alpha = (10 - frame) / 10.0f; mpScrnDraw->fontAlpha(alpha); mpOutFont->setAlphaRatio(alpha); } @@ -663,6 +675,7 @@ void dMsgObject_c::presentAnims() { #endif int dMsgObject_c::_delete() { + IF_DUSK(sMessageAnimations.erase(this)); mpResCont->destroyResource_all(); if (mpScrnDraw != NULL) { JKR_DELETE(mpScrnDraw); @@ -1052,9 +1065,16 @@ void dMsgObject_c::waitProc() { } void dMsgObject_c::openProc() { +#if TARGET_PC + auto& animations = sMessageAnimations[this]; + if (field_0x16a == 0) { + animations.phase = -1; + animations.alpha = {}; + } +#endif if (isMidonaMessage()) { bool uVar12 = 0; - if (DUSK_IF_ELSE(!mpScrnDraw->isSelectAnimeActive(), field_0x16a == 0)) { + if (field_0x16a == 0) { jmessage_tReference* pRef = (jmessage_tReference*)mpRenProc->getReference(); field_0x1a3 = 0; if (mpRefer->getMsgID() == 0x7fa) { @@ -1078,7 +1098,7 @@ void dMsgObject_c::openProc() { field_0x16a = 9; } if (mpRefer->getMsgID() == 0x7fa) { - mpScrnDraw->selectAnimeMove(2, getSelectCursorPosLocal(), uVar12); + mpScrnDraw->selectAnimeMove(DUSK_IF_ELSE(3, 2), getSelectCursorPosLocal(), uVar12); } else { if (getSelectCursorPosLocal() != 0xff) { mpScrnDraw->selectAnimeMove(2, getSelectCursorPosLocal() + 1, uVar12); @@ -1088,7 +1108,7 @@ void dMsgObject_c::openProc() { } } } - IF_NOT_DUSK(field_0x16a++); + field_0x16a++; s16 sVar7; if (isKanbanMessage()) { sVar7 = g_MsgObject_HIO_c.mBoxAppearFrame; @@ -1134,7 +1154,9 @@ void dMsgObject_c::openProc() { } mpRenProc->setTextInitPos(mpScrnDraw->getTextBoxPosX(), mpScrnDraw->getTextBoxPosY()); mpRenProc->setTextScale(mpScrnDraw->getTextBoxScaleX(), mpScrnDraw->getTextBoxScaleY()); + IF_DUSK(startAnimation(*this, 2, field_0x16a - 1.0f, sVar7)); if (field_0x16a >= sVar7) { + IF_DUSK(animations.frame.finish(field_0x16a)); mpScrnDraw->fukiTrans(0.0f, 0.0f); for (int i = 0; i < 3; i++) { mpRenProc->setSelTextInitPos(i, mpScrnDraw->getSelTextBoxPosX(i), @@ -1154,9 +1176,16 @@ void dMsgObject_c::openProc() { } void dMsgObject_c::outnowProc() { + IF_DUSK(auto& animations = sMessageAnimations[this]); mpRefer->shiftCharCountBuffer(); if (isBookMessage() && field_0x16a DUSK_IF_ELSE(>, !=) 0) { - IF_NOT_DUSK(field_0x16a--); + field_0x16a--; +#if TARGET_PC + startAnimation(*this, 6, field_0x16a + 1.0f, 0.0f); + if (field_0x16a == 0) { + animations.frame.finish(0.0f); + } +#endif f32 alpha = (10 - field_0x16a) / 10.0f; mpScrnDraw->fontAlpha(alpha); mpOutFont->setAlphaRatio(alpha); @@ -1167,12 +1196,24 @@ void dMsgObject_c::outnowProc() { jmessage_tReference* pRef = (jmessage_tReference*)mpRenProc->getReference(); if (pRef->getCharAllAlphaRate() < 1.0f) { + IF_DUSK(const f32 before = pRef->getCharAllAlphaRate()); if (mDoCPd_c::getTrigA(0)) { pRef->setCharAllAlphaRate(1.0f); } else { - IF_NOT_DUSK(pRef->addCharAllAlphaRate()); + pRef->addCharAllAlphaRate(); } f32 alpha = pRef->getCharAllAlphaRate(); +#if TARGET_PC + if (animations.phase != 6) { + animations.frame.finish(field_0x16a); + } + animations.phase = 6; + if (alpha >= 1.0f) { + animations.alpha.finish(alpha); + } else { + animations.alpha.approach(before, 1.0f, alpha - before); + } +#endif mpScrnDraw->setCharAlphaRate(alpha); mpOutFont->setAlphaRatio(alpha); } else if (mpRefer->isLightEnd()) { @@ -1220,17 +1261,20 @@ void dMsgObject_c::outnowProc() { } void dMsgObject_c::outwaitProc() { + IF_DUSK(auto& animations = sMessageAnimations[this]); jmessage_tReference* pRef = (jmessage_tReference*)mpRenProc->getReference(); mpScrnDraw->arwAnimeMove(); if (isBookMessage()) { if (isSend() || field_0x16a DUSK_IF_ELSE(>, !=) 0) { - IF_NOT_DUSK(field_0x16a++); + field_0x16a++; + IF_DUSK(startAnimation(*this, 5, field_0x16a - 1.0f, 10.0f)); if (field_0x16a <= 10) { f32 alpha = (10 - field_0x16a) / 10.0f; mpScrnDraw->fontAlpha(alpha); mpOutFont->setAlphaRatio(alpha); if (field_0x16a >= 10) { + IF_DUSK(animations.frame.finish(10.0f)); field_0x172++; mpRefer->setPageNum(field_0x172); mpCtrl->render_synchronize(); @@ -1323,11 +1367,7 @@ void dMsgObject_c::selectProc() { pRef->setSelectPos(pointerChoice); } #endif - if (mDoCPd_c::getTrigA(0) -#if TARGET_PC - || pointerConfirm -#endif - ) { + if (mDoCPd_c::getTrigA(0) IF_DUSK(|| pointerConfirm)) { if (getSelectCursorPosLocal() != 0xff) { field_0x1a3 = 1; } @@ -1349,15 +1389,13 @@ void dMsgObject_c::selectProc() { } field_0x1a3 = 2; } -#ifndef TARGET_PC - jmessage_tReference* pRef = (jmessage_tReference*)mpRenProc->getReference(); -#endif + IF_NOT_DUSK(jmessage_tReference* pRef = (jmessage_tReference*)mpRenProc->getReference()); if (getStatusLocal() == 8) { if (isMidonaMessage() && field_0x1a3 != 0) { if (field_0x1a3 == 2 && getSelectCancelPos() == 3) { iVar8 = true; } else { - while (!iVar8) { + IF_NOT_DUSK(while (!iVar8)) { if (getSelectCursorPosLocal() != 0xff) { iVar8 = mpScrnDraw->selectAnimeMove(2, getSelectCursorPosLocal() + 1, uVar7); @@ -1381,7 +1419,7 @@ void dMsgObject_c::selectProc() { if (field_0x1a3 == 2 && getSelectCancelPos() == 4) { iVar8 = true; } else { - while (!iVar8) { + IF_NOT_DUSK(while (!iVar8)) { iVar8 = mpScrnDraw->selectAnimeMove(3, getSelectCursorPosLocal(), uVar7); } } @@ -1506,7 +1544,8 @@ void dMsgObject_c::finishProc() { } void dMsgObject_c::endProc() { - IF_NOT_DUSK(field_0x16a++); + field_0x16a++; + IF_DUSK(auto& animations = sMessageAnimations[this]); s16 sVar4 = 5; if (isKanbanMessage() || isBookMessage()) { sVar4 = g_MsgObject_HIO_c.field_0x304; @@ -1515,6 +1554,7 @@ void dMsgObject_c::endProc() { } else if (isBossMessage()) { sVar4 = g_MsgObject_HIO_c.mBossNameFadeOut; } + IF_DUSK(startAnimation(*this, 17, field_0x16a - 1.0f, sVar4)); f32 dVar6 = (f32)field_0x16a / sVar4; mpScrnDraw->fukiAlpha(1.0f - dVar6); if (isBookMessage()) { @@ -1522,6 +1562,7 @@ void dMsgObject_c::endProc() { } mpOutFont->setAlphaRatio(1.0f - dVar6); if (field_0x16a >= sVar4) { + IF_DUSK(animations.frame.finish(field_0x16a)); mpScrnDraw->arwAnimeInit(); mpScrnDraw->dotAnimeInit(); if (mNoDemoFlag && !field_0x4d4) { diff --git a/src/d/d_msg_scrn_3select.cpp b/src/d/d_msg_scrn_3select.cpp index 1545f51584..8840797dff 100644 --- a/src/d/d_msg_scrn_3select.cpp +++ b/src/d/d_msg_scrn_3select.cpp @@ -21,7 +21,24 @@ #include "dusk/menu_pointer.h" #include "dusk/version.hpp" +#include "d/d_msg_scrn_talk.h" + +#include "JSystem/J3DGraphBase/J3DTransform.h" + +#include + namespace { +struct SelectionAnimation { + dusk::vdt::FrameAnimation bck[3], bpk, btk; + f32 paneFrames[3] = {-1.0f, -1.0f, -1.0f}; + f32 scaleFrames[3] = {}; + f32 appliedFrames[3] = {-1.0f, -1.0f, -1.0f}; +}; + +absl::flat_hash_map sSelectionAnimations; + +void selectTransform(dMsgScrn3Select_c& screen, int selection, bool preserveScale, f32 step = 0.0f, f32 target = 0.0f); + bool hit_choice_pane(CPaneMgr* pane, f32 padding) { return pane != NULL && pane->getPanePtr() != NULL && pane->getPanePtr()->isVisible() && dusk::menu_pointer::hit_pane(pane, padding); @@ -29,6 +46,13 @@ bool hit_choice_pane(CPaneMgr* pane, f32 padding) { } // namespace #endif +#define PROC_OPEN2_BCK_START 190.0f +#define PROC_OPEN2_BCK_END 199.0f +#define PROC_CHANGE_BCK_START_A 300.0f +#define PROC_CHANGE_BCK_START_B 400.0f +#define PROC_CHANGE_BCK_END_A 303.0f +#define PROC_CHANGE_BCK_END_B 403.0f + typedef void (dMsgScrn3Select_c::*processFn)(); DUSK_GAME_DATA processFn process[] = { &dMsgScrn3Select_c::open1Proc, &dMsgScrn3Select_c::open2Proc, &dMsgScrn3Select_c::waitProc, @@ -36,6 +60,7 @@ DUSK_GAME_DATA processFn process[] = { }; dMsgScrn3Select_c::dMsgScrn3Select_c() { + IF_DUSK(sSelectionAnimations.erase(this)); static u64 const tag_name[3][12] = { // A 'a_n', 'a_g', 'a_gr', 'a_m', MULTI_CHAR('w_yes_00'), MULTI_CHAR('w_yes_01'), MULTI_CHAR('w_yes_02'), MULTI_CHAR('w_yes_03'), MULTI_CHAR('w_yes_04'), @@ -62,7 +87,7 @@ dMsgScrn3Select_c::dMsgScrn3Select_c() { mpAnmBck = (J2DAnmTransform*)J2DAnmLoaderDataBase::load( JKRGetNameResource("zelda_window_3menu.bck", dComIfGp_getMsgArchive(0))); mBckFrame = 0.0f; - mBckFrame = 190.0f; + mBckFrame = PROC_OPEN2_BCK_START; mOffsetX = 0.0f; mpAnmBpk = (J2DAnmColorKey*)J2DAnmLoaderDataBase::load( @@ -316,12 +341,12 @@ dMsgScrn3Select_c::dMsgScrn3Select_c() { mDPDPoint = 0xFF; field_0x116 = 0xFF; field_0x117 = 0; - IF_DUSK(mPresenting = false); mSelMsgInactiveCol.set(0xFF, 0xFF, 0xFF, 0xFF); } dMsgScrn3Select_c::~dMsgScrn3Select_c() { + IF_DUSK(sSelectionAnimations.erase(this)); JKR_DELETE(mpScreen); mpScreen = NULL; @@ -445,15 +470,47 @@ void dMsgScrn3Select_c::translate(f32 i_x, f32 i_y) { #if TARGET_PC void dMsgScrn3Select_c::presentAnims() { - if (mProcess >= PROC_MAX_e) { + const auto found = sSelectionAnimations.find(this); + if (found == sSelectionAnimations.end()) { return; } - mPresenting = true; - if (mProcess == PROC_OPEN2_e || mProcess == PROC_CHANGE_e || mProcess == PROC_CLOSE_e) { - (this->*process[mProcess])(); + auto& animations = found->second; + if (animations.paneFrames[0] < 0.0f || + (mProcess == PROC_MAX_e && animations.appliedFrames[0] == animations.paneFrames[0])) + { + return; } - dusk::vdt::present_looping(mBpkFrame, mpAnmBpk, 1.0f); - dusk::vdt::present_looping(mBtkFrame, mpAnmBtk, 1.0f); + const u8 updates = field_0x114; + field_0x114 = 0; + for (int i = 0; i < 3; ++i) { + auto* pane = mpSel_c[i]->getPanePtr(); + const f32 frame = animations.bck[i].advance(animations.paneFrames[i]); + if (frame == animations.appliedFrames[i]) { + continue; + } + animations.appliedFrames[i] = frame; + mpAnmBck->setFrame(frame); + pane->setAnimation(mpAnmBck); + pane->animationTransform(); + pane->setAnimation((J2DAnmTransform*)nullptr); + if (pane->field_0x4 != 0xFFFF) { + J3DTransformInfo layout; + if (animations.scaleFrames[i] >= 0.0f) { + mpAnmBck->setFrame(animations.scaleFrames[i]); + mpAnmBck->getTransform(pane->field_0x4, &layout); + pane->scale(layout.mScale.x, layout.mScale.z); + } + if (mProcess == PROC_OPEN2_e && mBckFrame == PROC_OPEN2_BCK_START) { + mpAnmBck->setFrame(PROC_OPEN2_BCK_START); + mpAnmBck->getTransform(pane->field_0x4, &layout); + pane->translate(layout.mTranslate.x, pane->getTranslateY()); + } + } + onAnimeUpdate(i); + } + mpAnmBpk->setFrame(animations.bpk.advance(mBpkFrame)); + mpAnmBtk->setFrame(animations.btk.advance(mBtkFrame)); + mpScreen->animation(); for (int i = 0; i < 3; i++) { @@ -467,7 +524,7 @@ void dMsgScrn3Select_c::presentAnims() { } selectTrans(); - mPresenting = false; + field_0x114 = updates; } #endif @@ -518,6 +575,10 @@ void dMsgScrn3Select_c::draw(f32 i_xPos, f32 i_yPos) { } void dMsgScrn3Select_c::selAnimeInit(u8 i_selNum, u8 i_selNo, u8 param_2, f32 i_width, u8 param_4) { +#if TARGET_PC + auto& animations = sSelectionAnimations[this]; + animations = {}; +#endif mSelNo = i_selNo; field_0x110 = i_selNo; mSelNum = i_selNum; @@ -569,11 +630,13 @@ void dMsgScrn3Select_c::selAnimeInit(u8 i_selNum, u8 i_selNo, u8 param_2, f32 i_ break; } - mBckFrame = 190.0f; + mBckFrame = PROC_OPEN2_BCK_START; + IF_DUSK(mProcess = PROC_OPEN1_e); for (int i = 0; i < 3; i++) { selectAnimeTransform(i); } + IF_DUSK(presentAnims()); selectScale(); selectTrans(); @@ -595,23 +658,46 @@ void dMsgScrn3Select_c::selAnimeInit(u8 i_selNum, u8 i_selNo, u8 param_2, f32 i_ } mProcess = PROC_OPEN1_e; +#if TARGET_PC + animations.bpk.start(mBpkFrame, 0.0f, 1.0f, mpAnmBpk->getFrameMax()); + animations.btk.start(mBtkFrame, 0.0f, 1.0f, mpAnmBtk->getFrameMax()); +#endif } bool dMsgScrn3Select_c::selAnimeMove(u8 i_selNum, u8 param_1, bool param_2) { + IF_DUSK(auto& animations = sSelectionAnimations[this]); field_0x110 = param_1; mSelNum = i_selNum; field_0x114 = 0; field_0x108 = param_2; #if TARGET_PC - pointerMove(); + dMsgObject_c* msg = dMsgObject_getMsgObjectClass(); if (mProcess >= PROC_MAX_e) { return false; } -#endif - + const bool midnaPending = msg != NULL && msg->isMidonaMessage() && msg->field_0x1a3 != 0 && + msg->mpScrnDraw != NULL && + static_cast(msg->mpScrnDraw)->mpSelect_c == this; + if (!midnaPending) { + pointerMove(); + } + const bool finishingMidna = midnaPending && (msg->getStatusLocal() == 8 || msg->getStatusLocal() == 9); + do { + (this->*process[mProcess])(); + mBpkFrame = std::fmod(mBpkFrame + 1.0f, mpAnmBpk->getFrameMax()); + mBtkFrame = std::fmod(mBtkFrame + 1.0f, mpAnmBtk->getFrameMax()); + } while (finishingMidna && mProcess != PROC_SELECT_e && mProcess < PROC_MAX_e); + if (finishingMidna) { + for (int i = 0; i < 3; ++i) { + animations.bck[i].finish(animations.paneFrames[i]); + } + animations.bpk.start(mBpkFrame, 0.0f, 1.0f, mpAnmBpk->getFrameMax()); + animations.btk.start(mBtkFrame, 0.0f, 1.0f, mpAnmBtk->getFrameMax()); + } + presentAnims(); +#else (this->*process[mProcess])(); -#if !TARGET_PC mBpkFrame++; if (mBpkFrame >= mpAnmBpk->getFrameMax()) { mBpkFrame -= mpAnmBpk->getFrameMax(); @@ -708,14 +794,18 @@ bool dMsgScrn3Select_c::selAnimeEnd() { field_0x114 = 0; if (mProcess != PROC_CLOSE_e) { - mBckFrame = 199.0f; + mBckFrame = PROC_OPEN2_BCK_END; for (int i = 0; i < 3; i++) { +#if TARGET_PC + selectTransform(*this, i, true); +#else f32 scale_x = mpSel_c[i]->getPanePtr()->getScaleX(); f32 scale_y = mpSel_c[i]->getPanePtr()->getScaleY(); selectAnimeTransform(i); mpSel_c[i]->getPanePtr()->scale(scale_x, scale_y); +#endif } mProcess = PROC_CLOSE_e; @@ -724,21 +814,22 @@ bool dMsgScrn3Select_c::selAnimeEnd() { (this->*process[mProcess])(); -#if !TARGET_PC mBpkFrame++; if (mBpkFrame >= mpAnmBpk->getFrameMax()) { mBpkFrame -= mpAnmBpk->getFrameMax(); } - mpAnmBpk->setFrame(mBpkFrame); + IF_NOT_DUSK(mpAnmBpk->setFrame(mBpkFrame)); mBtkFrame++; if (mBtkFrame >= mpAnmBtk->getFrameMax()) { mBtkFrame -= mpAnmBtk->getFrameMax(); } +#if TARGET_PC + presentAnims(); +#else mpAnmBtk->setFrame(mBtkFrame); mpScreen->animation(); -#endif for (int i = 0; i < 3; i++) { if (i == mSelNo) { @@ -751,6 +842,7 @@ bool dMsgScrn3Select_c::selAnimeEnd() { } selectTrans(); +#endif return mProcess == PROC_MAX_e ? TRUE : FALSE; } @@ -801,30 +893,28 @@ void dMsgScrn3Select_c::open1Proc() { mBckFrame = sel_anm_frame[mSelNo]; for (int i = 0; i < 3; i++) { +#if TARGET_PC + selectAnimeTransform(i); +#else f32 x = mpSel_c[i]->getPosX(); selectAnimeTransform(i); mpSel_c[i]->move(x, mpSel_c[i]->getPosY()); +#endif } } - mBckFrame = 190.0f; + mBckFrame = PROC_OPEN2_BCK_START; mProcess = PROC_OPEN2_e; } void dMsgScrn3Select_c::open2Proc() { f32 sel_anm_frame[3] = {204.0f, 304.0f, 404.0f}; -#if TARGET_PC - if (mPresenting) { - dusk::vdt::advance_toward_frame(mBckFrame, 199.0f, 1.0f); - } -#else mBckFrame++; -#endif mpSelectCursor->setAlphaRate(1.0f); - if (mBckFrame >= 199.0f) { + if (mBckFrame >= PROC_OPEN2_BCK_END) { if (mSelNo != 0xFF) { mBckFrame = sel_anm_frame[mSelNo]; } @@ -834,11 +924,15 @@ void dMsgScrn3Select_c::open2Proc() { } for (int i = 0; i < 3; i++) { +#if TARGET_PC + selectTransform(*this, i, true, mProcess == PROC_OPEN2_e ? 1.0f : 0.0f, PROC_OPEN2_BCK_END); +#else f32 scale_x = mpSel_c[i]->getPanePtr()->getScaleX(); f32 scale_y = mpSel_c[i]->getPanePtr()->getScaleY(); selectAnimeTransform(i); mpSel_c[i]->getPanePtr()->scale(scale_x, scale_y); +#endif } } @@ -858,56 +952,56 @@ void dMsgScrn3Select_c::selectProc() { switch (mLastSelNo) { case 0: if (mSelNo == 1) { - mBckFrame = 303.0f; + mBckFrame = PROC_CHANGE_BCK_END_A; selectAnimeTransform(1); - mBckFrame = 300.0f; + mBckFrame = PROC_CHANGE_BCK_START_A; selectAnimeTransform(0); } else if (mSelNo == 2) { - mBckFrame = 403.0f; + mBckFrame = PROC_CHANGE_BCK_END_B; selectAnimeTransform(2); - mBckFrame = 400.0f; + mBckFrame = PROC_CHANGE_BCK_START_B; selectAnimeTransform(0); } break; case 1: if (mSelNo == 0) { - mBckFrame = 300.0f; + mBckFrame = PROC_CHANGE_BCK_START_A; selectAnimeTransform(0); - mBckFrame = 303.0f; + mBckFrame = PROC_CHANGE_BCK_END_A; selectAnimeTransform(1); } else if (mSelNo == 2) { - mBckFrame = 403.0f; + mBckFrame = PROC_CHANGE_BCK_END_B; selectAnimeTransform(2); - mBckFrame = 400.0f; + mBckFrame = PROC_CHANGE_BCK_START_B; selectAnimeTransform(1); } break; case 2: if (mSelNo == 1) { - mBckFrame = 400.0f; + mBckFrame = PROC_CHANGE_BCK_START_B; selectAnimeTransform(1); - mBckFrame = 403.0f; + mBckFrame = PROC_CHANGE_BCK_END_B; selectAnimeTransform(2); } else if (mSelNo == 0) { - mBckFrame = 300.0f; + mBckFrame = PROC_CHANGE_BCK_START_A; selectAnimeTransform(0); - mBckFrame = 303.0f; + mBckFrame = PROC_CHANGE_BCK_END_A; selectAnimeTransform(2); } break; case 0xFF: if (mSelNo == 0) { - mBckFrame = 300.0f; + mBckFrame = PROC_CHANGE_BCK_START_A; } else if (mSelNo == 1) { - mBckFrame = 303.0f; + mBckFrame = PROC_CHANGE_BCK_END_A; } else if (mSelNo == 2) { - mBckFrame = 403.0f; + mBckFrame = PROC_CHANGE_BCK_END_B; } for (int i = 0; i < 3; i++) { @@ -928,13 +1022,13 @@ void dMsgScrn3Select_c::selectProc() { void dMsgScrn3Select_c::changeProc() { if (mSelNo == 0xFF) { if (mLastSelNo == 0) { - mBckFrame = 303.0f; + mBckFrame = PROC_CHANGE_BCK_END_A; selectAnimeTransform(0); } else if (mLastSelNo == 1) { - mBckFrame = 300.0f; + mBckFrame = PROC_CHANGE_BCK_START_A; selectAnimeTransform(1); } else if (mLastSelNo == 2) { - mBckFrame = 400.0f; + mBckFrame = PROC_CHANGE_BCK_START_B; selectAnimeTransform(2); } @@ -944,38 +1038,28 @@ void dMsgScrn3Select_c::changeProc() { case 0: if (mSelNo == 1) { if (field_0x108 != 0) { - mBckFrame = 303.0f; + mBckFrame = PROC_CHANGE_BCK_END_A; } else { -#if TARGET_PC - if (mPresenting) { - dusk::vdt::advance_toward_frame(mBckFrame, 303.0f, 1.0f); - } -#else mBckFrame++; -#endif } - selectAnimeTransform(0); + DUSK_IF_ELSE(selectTransform(*this, 0, false, 1.0f, PROC_CHANGE_BCK_END_A), + selectAnimeTransform(0)); - if (mBckFrame >= 303.0f) { + if (mBckFrame >= PROC_CHANGE_BCK_END_A) { mProcess = PROC_SELECT_e; } } else if (mSelNo == 2) { if (field_0x108 != 0) { - mBckFrame = 403.0f; + mBckFrame = PROC_CHANGE_BCK_END_B; } else { -#if TARGET_PC - if (mPresenting) { - dusk::vdt::advance_toward_frame(mBckFrame, 403.0f, 1.0f); - } -#else mBckFrame++; -#endif } - selectAnimeTransform(0); + DUSK_IF_ELSE(selectTransform(*this, 0, false, 1.0f, PROC_CHANGE_BCK_END_B), + selectAnimeTransform(0)); - if (mBckFrame >= 403.0f) { + if (mBckFrame >= PROC_CHANGE_BCK_END_B) { mProcess = PROC_SELECT_e; } } @@ -983,38 +1067,28 @@ void dMsgScrn3Select_c::changeProc() { case 1: if (mSelNo == 0) { if (field_0x108 != 0) { - mBckFrame = 300.0f; + mBckFrame = PROC_CHANGE_BCK_START_A; } else { -#if TARGET_PC - if (mPresenting) { - dusk::vdt::advance_toward_frame(mBckFrame, 300.0f, 1.0f); - } -#else mBckFrame--; -#endif } - selectAnimeTransform(1); + DUSK_IF_ELSE(selectTransform(*this, 1, false, -1.0f, PROC_CHANGE_BCK_START_A), + selectAnimeTransform(1)); - if (mBckFrame <= 300.0f) { + if (mBckFrame <= PROC_CHANGE_BCK_START_A) { mProcess = PROC_SELECT_e; } } else if (mSelNo == 2) { if (field_0x108 != 0) { - mBckFrame = 403.0f; + mBckFrame = PROC_CHANGE_BCK_END_B; } else { -#if TARGET_PC - if (mPresenting) { - dusk::vdt::advance_toward_frame(mBckFrame, 403.0f, 1.0f); - } -#else mBckFrame++; -#endif } - selectAnimeTransform(1); + DUSK_IF_ELSE(selectTransform(*this, 1, false, 1.0f, PROC_CHANGE_BCK_END_B), + selectAnimeTransform(1)); - if (mBckFrame >= 403.0f) { + if (mBckFrame >= PROC_CHANGE_BCK_END_B) { mProcess = PROC_SELECT_e; } } @@ -1022,38 +1096,28 @@ void dMsgScrn3Select_c::changeProc() { case 2: if (mSelNo == 1) { if (field_0x108 != 0) { - mBckFrame = 400.0f; + mBckFrame = PROC_CHANGE_BCK_START_B; } else { -#if TARGET_PC - if (mPresenting) { - dusk::vdt::advance_toward_frame(mBckFrame, 400.0f, 1.0f); - } -#else mBckFrame--; -#endif } - selectAnimeTransform(2); + DUSK_IF_ELSE(selectTransform(*this, 2, false, -1.0f, PROC_CHANGE_BCK_START_B), + selectAnimeTransform(2)); - if (mBckFrame <= 400.0f) { + if (mBckFrame <= PROC_CHANGE_BCK_START_B) { mProcess = PROC_SELECT_e; } } else if (mSelNo == 0) { if (field_0x108 != 0) { - mBckFrame = 300.0f; + mBckFrame = PROC_CHANGE_BCK_START_A; } else { -#if TARGET_PC - if (mPresenting) { - dusk::vdt::advance_toward_frame(mBckFrame, 300.0f, 1.0f); - } -#else mBckFrame--; -#endif } - selectAnimeTransform(2); + DUSK_IF_ELSE(selectTransform(*this, 2, false, -1.0f, PROC_CHANGE_BCK_START_A), + selectAnimeTransform(2)); - if (mBckFrame <= 300.0f) { + if (mBckFrame <= PROC_CHANGE_BCK_START_A) { mProcess = PROC_SELECT_e; } } @@ -1064,23 +1128,21 @@ void dMsgScrn3Select_c::changeProc() { void dMsgScrn3Select_c::closeProc() { mpSelectCursor->setAlphaRate(0.0f); -#if TARGET_PC - if (mPresenting) { - dusk::vdt::advance_toward_frame(mBckFrame, 190.0f, 1.0f); - } -#else mBckFrame--; -#endif for (int i = 0; i < 3; i++) { +#if TARGET_PC + selectTransform(*this, i, true, -1.0f, PROC_OPEN2_BCK_START); +#else f32 scale_x = mpSel_c[i]->getPanePtr()->getScaleX(); f32 scale_y = mpSel_c[i]->getPanePtr()->getScaleY(); selectAnimeTransform(i); mpSel_c[i]->getPanePtr()->scale(scale_x, scale_y); +#endif } - if (mBckFrame <= 190.0f) { + if (mBckFrame <= PROC_OPEN2_BCK_START) { mProcess = PROC_MAX_e; } } @@ -1227,11 +1289,36 @@ void dMsgScrn3Select_c::selectTrans() { } } +#if TARGET_PC +namespace { +void selectTransform(dMsgScrn3Select_c& screen, int selection, bool preserveScale, f32 step, f32 target) { + auto& animations = sSelectionAnimations[&screen]; + animations.appliedFrames[selection] = -1.0f; + auto& visual = animations.bck[selection]; + if (step != 0.0f && screen.mBckFrame != target) { + visual.approach(screen.mBckFrame - step, target); + } else { + visual.finish(screen.mBckFrame); + } + if (!preserveScale) { + animations.scaleFrames[selection] = -1.0f; + } else if (animations.scaleFrames[selection] < 0.0f) { + animations.scaleFrames[selection] = animations.paneFrames[selection]; + } + animations.paneFrames[selection] = screen.mBckFrame; +} +} // namespace +#endif + void dMsgScrn3Select_c::selectAnimeTransform(int i_sel) { +#if TARGET_PC + selectTransform(*this, i_sel, false); +#else mpAnmBck->setFrame(mBckFrame); mpSel_c[i_sel]->getPanePtr()->setAnimation(mpAnmBck); mpSel_c[i_sel]->getPanePtr()->animationTransform(); mpSel_c[i_sel]->getPanePtr()->setAnimation((J2DAnmTransform*)NULL); onAnimeUpdate(i_sel); +#endif } diff --git a/src/d/d_timer.cpp b/src/d/d_timer.cpp index 8b138d507c..7e05830e67 100644 --- a/src/d/d_timer.cpp +++ b/src/d/d_timer.cpp @@ -26,6 +26,21 @@ #if TARGET_PC #include "dusk/interp/user_interface.h" #include "dusk/version.hpp" + +#include + +namespace { +struct TimerAnimation { + dusk::vdt::FrameAnimation close; + bool presenting = false; + bool opening = false; + bool closing = false; + bool slide = false; +}; + +absl::flat_hash_map sTimerAnimations; + +} // namespace #endif static int dTimer_createStart2D(s32 param_0, u16 param_1); @@ -216,7 +231,7 @@ int dTimer_c::_execute() { if (m_timer_mode == 3 || m_timer_mode == 4 || m_timer_mode == 6 || m_timer_mode == 5) { #if TARGET_PC - mp_tm_scrn->mPresentTimerSlide = true; + sTimerAnimations[mp_tm_scrn].slide = true; #else f32 pos_y = mp_tm_scrn->getTimerTransY(); if (pos_y < 145.0f) { @@ -296,6 +311,11 @@ static f32 dummyLiteralOrder2() { int dTimer_c::_draw() { if (dComIfGp_isPauseFlag() || dMsgObject_isTalkNowCheck()) { +#if TARGET_PC + if (m_mode == 8 && dusk::game_clock::is_presentation_frame()) { + mp_tm_scrn->presentAnims(); + } +#endif return 1; } @@ -483,6 +503,7 @@ bool dTimer_c::isStart() { } dDlst_TimerScrnDraw_c::dDlst_TimerScrnDraw_c() { + IF_DUSK(sTimerAnimations.erase(this)); field_0x3e2 = 0; mHIOType = 0; field_0x3D8 = 0; @@ -1121,8 +1142,9 @@ void dDlst_TimerScrnDraw_c::setShowType(u8 i_type) { void dDlst_TimerScrnDraw_c::anime() { #if TARGET_PC - if (!mPresentStep) { - mPresentOpening = true; + auto& animations = sTimerAnimations[this]; + if (!animations.presenting) { + animations.opening = true; return; } const f32 previousFrame = field_0x3D8; @@ -1196,18 +1218,22 @@ void dDlst_TimerScrnDraw_c::anime() { } BOOL dDlst_TimerScrnDraw_c::closeAnime() { -#if TARGET_PC - if (!mPresentStep) { - mPresentClosing = true; - return field_0x3D8 >= 7.0f; - } -#endif BOOL var_r31 = false; - DUSK_IF_ELSE(dusk::vdt::advance_toward_frame(field_0x3D8, 7.0f, 1.0f), field_0x3D8++); +#if TARGET_PC + auto& animations = sTimerAnimations[this]; + if (!animations.presenting) { + animations.closing = true; + field_0x3D8 = std::min(field_0x3D8 + 1.0f, 7.0f); + animations.close.approach(field_0x3D8 - 1.0f, 7.0f); + } + const f32 frame = animations.presenting ? animations.close.advance(field_0x3D8) : field_0x3D8; +#else + field_0x3D8++; +#endif - if (field_0x3D8 <= 7) { - f32 temp_f31 = acc(7, field_0x3D8, 0); - f32 temp_f30 = acc(7, 7 - field_0x3D8, 0); + if (DUSK_IF_ELSE(frame, field_0x3D8) <= 7) { + f32 temp_f31 = acc(7, DUSK_IF_ELSE(frame, field_0x3D8), 0); + f32 temp_f30 = acc(7, 7 - DUSK_IF_ELSE(frame, field_0x3D8), 0); f32 temp_f1 = temp_f31 * -50.0f; if (mpTimeParent != NULL) { @@ -1355,8 +1381,9 @@ s32 dDlst_TimerScrnDraw_c::createStart(u16 i_messageID) { #if TARGET_PC void dDlst_TimerScrnDraw_c::presentAnims() { - mPresentStep = true; - if (mPresentTimerSlide) { + auto& animations = sTimerAnimations[this]; + animations.presenting = true; + if (animations.slide) { dusk::vdt::advance_toward_frame(mTimerTransY, 145.0f, 25.0f); if (field_0x3e2 != 0 && mHIOType == 0) { setTimerPos(g_drawHIO.mMiniGame.mTimerPosX_4x3, @@ -1366,10 +1393,10 @@ void dDlst_TimerScrnDraw_c::presentAnims() { g_drawHIO.mMiniGame.mTimerPosY[mHIOType]); } } - if (mPresentClosing) { + if (animations.closing) { closeAnime(); } else { - if (mPresentOpening) { + if (animations.opening) { anime(); } const bool event = dComIfGp_event_getMode() == 1; @@ -1381,8 +1408,8 @@ void dDlst_TimerScrnDraw_c::presentAnims() { const f32 previous = frame; dusk::vdt::advance_toward_frame(frame, hidden ? 5.0f : 0.0f, 1.0f); if (ready || frame != previous) { - const f32 rate = timer ? acc(5.0f, 5.0f - frame, 0.0f) : - 1.0f - acc(5.0f, frame, 0.0f); + const f32 rate = timer ? acc(5.0f, 5.0f - frame, 0.0f) + : 1.0f - acc(5.0f, frame, 0.0f); pane->setAlphaRate(mParentAlpha * alpha * rate); } pane->alphaAnimeStart(frame); @@ -1395,7 +1422,7 @@ void dDlst_TimerScrnDraw_c::presentAnims() { fade(mpImageParent, hideCounter, field_0x3E0 != 0, g_drawHIO.mMiniGame.mIconAlpha[mHIOType], false); } - mPresentStep = false; + animations.presenting = false; } #endif @@ -1725,7 +1752,9 @@ u8 dTimer_isReadyFlag() { return 0; } -dDlst_TimerScrnDraw_c::~dDlst_TimerScrnDraw_c() {} +dDlst_TimerScrnDraw_c::~dDlst_TimerScrnDraw_c() { + IF_DUSK(sTimerAnimations.erase(this)); +} int dTimer_c::createGetIn(cXyz i_pos) { return mp_tm_scrn->createGetIn(i_pos); diff --git a/src/dusk/game_clock.cpp b/src/dusk/game_clock.cpp index 52472f42a9..cbaa49ec02 100644 --- a/src/dusk/game_clock.cpp +++ b/src/dusk/game_clock.cpp @@ -4,7 +4,6 @@ #include #include -#include namespace dusk::game_clock { @@ -20,13 +19,11 @@ native_clock::time_point s_previousNativeSample{}; game_clock::time_point s_latestGameSample{}; game_clock::time_point s_currentSnapshotTime{}; game_clock::time_point s_pendingSimTime{}; -std::unordered_map s_intervalLastSample; uint64_t s_presentationEpoch = 1; bool s_timingModeInitialized = false; bool s_previousSeparatePresentation = false; bool s_previousInterpolating = false; bool s_previousTimeStopped = false; -float s_presentationDtSeconds = kUiInitialDt; constexpr game_clock::duration kSimPeriodDuration = std::chrono::duration_cast(std::chrono::duration(kSimPeriod)); @@ -39,7 +36,7 @@ float ui_dt() { } const float maximumDt = kUiMaximumDt * aurora::time::scale(); - return std::clamp(s_presentationDtSeconds, 0.0f, maximumDt); + return std::clamp(g_frameTiming.dt, 0.0f, maximumDt); } } // namespace @@ -86,7 +83,6 @@ const FrameTiming& advance() { .dt = std::chrono::duration(gameFrameGap).count(), .presentationEpoch = s_presentationEpoch, }; - s_presentationDtSeconds = out.dt; const float timeScale = aurora::time::scale(); const bool interpolating = @@ -169,21 +165,19 @@ float sample_interpolation_step() { return std::clamp(step, 0.0f, 1.0f); } +double sample_time() { + const auto now = s_simTickActive ? s_pendingSimTime : game_clock::now(); + return std::chrono::duration(now.time_since_epoch()).count(); +} + float original_frames() { return ui_dt() / kSimPeriod; } -float consume_interval(const void* consumer) { - const auto key = reinterpret_cast(consumer); - const auto now = s_simTickActive ? s_pendingSimTime : game_clock::now(); - const float timeScale = aurora::time::scale(); - float dt = kUiInitialDt * timeScale; - if (const auto it = s_intervalLastSample.find(key); it != s_intervalLastSample.end()) { - dt = std::chrono::duration(now - it->second).count(); - const float maximumDt = std::max(kUiMaximumDt * timeScale, kSimPeriod); - dt = std::min(dt, maximumDt); - } - s_intervalLastSample[key] = now; +float consume_interval(double& lastSample) { + const double now = sample_time(); + const float dt = std::max(0.0, now - lastSample); + lastSample = now; return dt; } diff --git a/src/dusk/game_clock.h b/src/dusk/game_clock.h index df04d7d420..5731e8cbd3 100644 --- a/src/dusk/game_clock.h +++ b/src/dusk/game_clock.h @@ -33,7 +33,8 @@ float sample_interpolation_step(); bool is_sim_frame(); bool is_presentation_frame(); -float consume_interval(const void* consumer); +double sample_time(); +float consume_interval(double& lastSample); // Sets the effective simulation rate through the game clock time scale. void set_sim_rate(float hz); diff --git a/src/dusk/interp/user_interface.h b/src/dusk/interp/user_interface.h index 30ddbf2034..a6f34bbe8d 100644 --- a/src/dusk/interp/user_interface.h +++ b/src/dusk/interp/user_interface.h @@ -15,6 +15,55 @@ namespace dusk::vdt { +class FrameAnimation { +public: + void start(f32 from, f32 target, f32 speed = 1.0f, f32 period = 0.0f) { + mFrame = from; + mTarget = target; + mSpeed = speed; + mPeriod = period; + mTime = game_clock::sample_time(); + mEpoch = game_clock::g_frameTiming.presentationEpoch; + } + + void approach(f32 from, f32 target, f32 speed = 1.0f) { + if (mTarget != target || std::abs(mSpeed - speed) > 0.000001f || mPeriod != 0.0f || + mEpoch != game_clock::g_frameTiming.presentationEpoch) + { + start(from, target, speed); + } + } + + void finish(f32 frame) { start(frame, frame, 0.0f); } + + f32 advance(f32 logicalFrame) { + const f32 frames = game_clock::consume_interval(mTime) / game_clock::kSimPeriod; + if (!game_clock::g_frameTiming.interpolating || + mEpoch != game_clock::g_frameTiming.presentationEpoch) + { + mFrame = logicalFrame; + mEpoch = game_clock::g_frameTiming.presentationEpoch; + } else if (mPeriod > 0.0f) { + mFrame = std::fmod(mFrame + mSpeed * frames, mPeriod); + } else { + const f32 step = mSpeed * frames; + mFrame = mFrame < mTarget ? std::min(mFrame + step, mTarget) + : std::max(mFrame - step, mTarget); + } + return mFrame; + } + + f32 value() const { return mFrame; } + +private: + f32 mFrame = 0.0f; + f32 mTarget = 0.0f; + f32 mSpeed = 0.0f; + f32 mPeriod = 0.0f; + double mTime = 0.0; + u64 mEpoch = 0; +}; + inline f32 clamped_fraction(f32 timer, f32 duration) { if (duration <= 0.0f) { return 1.0f; diff --git a/src/dusk/ui/component.cpp b/src/dusk/ui/component.cpp index 7b9a50a766..8eb0ce7e0b 100644 --- a/src/dusk/ui/component.cpp +++ b/src/dusk/ui/component.cpp @@ -16,7 +16,7 @@ void Component::update() { } bool Component::focus() { - if (disabled() || !mRoot->IsVisible(true)) { + if (disabled()) { return false; } // Can we focus self? diff --git a/src/dusk/ui/select_button.cpp b/src/dusk/ui/select_button.cpp index 39769f533f..7f50321da7 100644 --- a/src/dusk/ui/select_button.cpp +++ b/src/dusk/ui/select_button.cpp @@ -80,6 +80,7 @@ void SelectButton::update_props(Props props) { if (!props.icon.empty()) { mIconElem->SetClass(props.icon, true); } + mRoot->SetClass("has-icon", !props.icon.empty()); } set_value_label(props.value); set_modified(props.modified);