diff --git a/files.cmake b/files.cmake index a65b7572c4..d43e98bf42 100644 --- a/files.cmake +++ b/files.cmake @@ -1334,6 +1334,7 @@ set(DUSK_FILES include/dusk/endian_gx.hpp include/dusk/config.hpp include/dusk/dvd_asset.hpp + include/dusk/scope_guard.hpp src/dusk/dvd_asset.cpp src/d/actor/d_a_alink_dusk.cpp src/dusk/asserts.cpp @@ -1345,6 +1346,7 @@ set(DUSK_FILES src/dusk/file_select.cpp src/dusk/file_select.hpp src/dusk/frame_interpolation.cpp + src/dusk/game_clock.cpp src/dusk/globals.cpp src/dusk/gyro.cpp src/dusk/io.cpp diff --git a/include/d/actor/d_a_alink.h b/include/d/actor/d_a_alink.h index 53228af84b..9980ab776f 100644 --- a/include/d/actor/d_a_alink.h +++ b/include/d/actor/d_a_alink.h @@ -4551,7 +4551,7 @@ public: #if TARGET_PC void handleWolfHowl(); void handleQuickTransform(); - bool checkGyroAimItemContext(); + bool checkGyroAimContext(); #endif }; // Size: 0x385C diff --git a/include/d/d_menu_ring.h b/include/d/d_menu_ring.h index f28c20aac2..39d29a35e8 100644 --- a/include/d/d_menu_ring.h +++ b/include/d/d_menu_ring.h @@ -204,6 +204,9 @@ private: /* 0x6D1 */ u8 field_0x6d1; /* 0x6D2 */ u8 field_0x6d2; /* 0x6D3 */ u8 field_0x6d3; +#if TARGET_PC + f32 mSelectItemSlideElapsed[4]; +#endif }; #endif /* D_MENU_D_MENU_RING_H */ diff --git a/include/dusk/frame_interpolation.h b/include/dusk/frame_interpolation.h index 383d16d288..9594529feb 100644 --- a/include/dusk/frame_interpolation.h +++ b/include/dusk/frame_interpolation.h @@ -19,14 +19,12 @@ void end_record(); void interpolate(float step); float get_interpolation_step(); -void notify_presentation_frame(); void request_presentation_sync(); bool presentation_sync_active(); -void notify_sim_tick_complete(); -uint32_t begin_presentation_ui_pass(); -uint32_t get_presentation_ui_advance_ticks(); -void end_presentation_ui_pass(); +// TODO: These should be phased out as UI is progressively updated to use game_clock +void set_ui_tick_pending(bool value); +bool get_ui_tick_pending(); void open_child(const void* key, int32_t id); void close_child(); diff --git a/include/dusk/game_clock.h b/include/dusk/game_clock.h new file mode 100644 index 0000000000..6e7ea500eb --- /dev/null +++ b/include/dusk/game_clock.h @@ -0,0 +1,32 @@ +#ifndef DUSK_GAME_CLOCK_H +#define DUSK_GAME_CLOCK_H + +#include + +namespace dusk { +namespace game_clock { + +void ensure_initialized(); +void reset_accumulator(); + +constexpr float sim_pace() { return 1.0f / 30.0f; } +constexpr float period_for_original_frames(float frame_count) { return frame_count * sim_pace(); } +constexpr float ui_maximum_dt() { return 0.05f; } +constexpr float ui_initial_dt() { return 1.0f / 60.0f; } + +struct MainLoopPacer { + float presentation_dt_seconds; + bool is_interpolating; + bool do_sim_tick; + float interpolation_step; + float sim_pace; +}; + +MainLoopPacer advance_main_loop(); + +float consume_interval(const void* consumer); + +} // namespace game_clock +} // namespace dusk + +#endif // DUSK_GAME_CLOCK_H diff --git a/include/dusk/gyro.h b/include/dusk/gyro.h index 4ee1c61737..279aeae16e 100644 --- a/include/dusk/gyro.h +++ b/include/dusk/gyro.h @@ -4,7 +4,7 @@ namespace dusk::gyro { void read(float dt); void getAimDeltas(float& out_yaw, float& out_pitch); -bool queryGyroAimItemContext(); +bool queryGyroAimContext(); void rollgoalTick(bool play_active, s16 camera_yaw); void rollgoalTableOffset(s16& out_ax, s16& out_az); diff --git a/include/dusk/scope_guard.hpp b/include/dusk/scope_guard.hpp new file mode 100644 index 0000000000..281d23434d --- /dev/null +++ b/include/dusk/scope_guard.hpp @@ -0,0 +1,20 @@ +#ifndef DUSK_SCOPE_GUARD_HPP +#define DUSK_SCOPE_GUARD_HPP + +#include + +class SimpleScopeGuard { +public: + // Store the function in the constructor + explicit SimpleScopeGuard(const std::function& func) : m_func(func) {} + + // Run the function when the object goes out of scope + ~SimpleScopeGuard() { + if (m_func) m_func(); + } + +private: + std::function m_func; +}; + +#endif //DUSK_SCOPE_GUARD_HPP diff --git a/include/dusk/settings.h b/include/dusk/settings.h index cf0ac0a3ef..a10971b49f 100644 --- a/include/dusk/settings.h +++ b/include/dusk/settings.h @@ -61,6 +61,7 @@ struct UserSettings { ConfigVar noMissClimbing; ConfigVar fastTears; ConfigVar instantSaves; + ConfigVar instantText; ConfigVar sunsSong; // Preferences diff --git a/libs/JSystem/include/JSystem/JFramework/JFWDisplay.h b/libs/JSystem/include/JSystem/JFramework/JFWDisplay.h index fab39a296b..28967cc0a2 100644 --- a/libs/JSystem/include/JSystem/JFramework/JFWDisplay.h +++ b/libs/JSystem/include/JSystem/JFramework/JFWDisplay.h @@ -101,10 +101,6 @@ public: void setDrawDoneMethod(EDrawDone drawDone) { mDrawDoneMethod = drawDone; } void setFader(JUTFader* fader) { mFader = fader; } -#ifdef TARGET_PC - // For frame interpolation - void setFaderSimSteps(u32 steps); -#endif void resetFader() { setFader(NULL); } JUTFader* getFader() const { return mFader; } void setClearColor(JUtility::TColor color) { mClearColor = color; } diff --git a/libs/JSystem/src/JFramework/JFWDisplay.cpp b/libs/JSystem/src/JFramework/JFWDisplay.cpp index 38dbc7eab1..c35b283419 100644 --- a/libs/JSystem/src/JFramework/JFWDisplay.cpp +++ b/libs/JSystem/src/JFramework/JFWDisplay.cpp @@ -205,14 +205,6 @@ void JFWDisplay::preGX() { } } -#ifdef TARGET_PC -static s32 s_faderSimSteps = -1; - -void JFWDisplay::setFaderSimSteps(u32 steps) { - s_faderSimSteps = static_cast(steps); -} -#endif - void JFWDisplay::endGX() { s32 bufferNum = JUTXfb::getManager()->getBufferNum(); u16 width = JUTVideo::getManager()->getFbWidth(); @@ -224,14 +216,7 @@ void JFWDisplay::endGX() { if (mFader != NULL) { ortho.setPort(); #ifdef TARGET_PC - u32 advance_count = 1; - if (dusk::getSettings().game.enableFrameInterpolation && s_faderSimSteps >= 0) { - advance_count = static_cast(s_faderSimSteps); - s_faderSimSteps = -1; - } else { - s_faderSimSteps = -1; - } - for (u32 i = 0; i < advance_count; i++) { + if (dusk::frame_interp::get_ui_tick_pending()) { mFader->advance(); } if (mFader->getStatus() != 1) { diff --git a/libs/JSystem/src/JStudio/JStudio_JStage/object-actor.cpp b/libs/JSystem/src/JStudio/JStudio_JStage/object-actor.cpp index dc0c5c56e8..0ee7d53fb7 100644 --- a/libs/JSystem/src/JStudio/JStudio_JStage/object-actor.cpp +++ b/libs/JSystem/src/JStudio/JStudio_JStage/object-actor.cpp @@ -314,10 +314,17 @@ void JStudio_JStage::TAdaptor_actor::getJSG_SRT_(JStudio::TControl const* pContr } void JStudio_JStage::TAdaptor_actor::TVVOutput_ANIMATION_FRAME_::operator()( - f32 param_1, JStudio::TAdaptor* adaptor) const { + f32 param_1, JStudio::TAdaptor* adaptor) const { +#if TARGET_PC + TAdaptor_actor* actor_adaptor = static_cast(adaptor); + JStage::TActor* actor = actor_adaptor->get_pJSG_(); + // field_0x8 is always hardcoded to either 305 or 309 + u32 idx = (field_0x8 == 305) ? actor_adaptor->field_0x130 : actor_adaptor->field_0x134; +#else JStage::TActor* actor = static_cast(adaptor)->get_pJSG_(); // not sure what this bit is u32 idx = *(u32*)(((uintptr_t)adaptor - 1) + field_0x8); +#endif u8 idx_lowBytes = idx; u8 idx_highBytes = idx >> 8; diff --git a/platforms/android/app/src/main/AndroidManifest.xml b/platforms/android/app/src/main/AndroidManifest.xml index d09b0d2785..e8c2bb29a8 100644 --- a/platforms/android/app/src/main/AndroidManifest.xml +++ b/platforms/android/app/src/main/AndroidManifest.xml @@ -15,7 +15,7 @@ android:allowBackup="true" android:hardwareAccelerated="true" android:appCategory="game" - android:icon="@android:drawable/sym_def_app_icon" + android:icon="@mipmap/icon" android:label="@string/app_name" android:theme="@android:style/Theme.NoTitleBar" android:enableOnBackInvokedCallback="false"> diff --git a/platforms/android/app/src/main/res/mipmap/icon.png b/platforms/android/app/src/main/res/mipmap/icon.png new file mode 100644 index 0000000000..05e11c9b41 Binary files /dev/null and b/platforms/android/app/src/main/res/mipmap/icon.png differ diff --git a/src/d/actor/d_a_alink.cpp b/src/d/actor/d_a_alink.cpp index 6787c53de7..e6943baf96 100644 --- a/src/d/actor/d_a_alink.cpp +++ b/src/d/actor/d_a_alink.cpp @@ -4258,6 +4258,12 @@ int daAlink_c::createHeap() { return 0; } +#if TARGET_PC + // lets try to zero-initialize the arrays instead of having garbage values + std::memset(sp1C, 0, sizeof(J3DTransformInfo) * sp38); + std::memset(sp30, 0, sizeof(Quaternion) * sp38); +#endif + field_0x2060 = JKR_NEW mDoExt_MtxCalcOldFrame(sp1C, sp30); if (field_0x2060 == NULL) { return 0; diff --git a/src/d/actor/d_a_alink_dusk.cpp b/src/d/actor/d_a_alink_dusk.cpp index 9b937d3678..045a9655bd 100644 --- a/src/d/actor/d_a_alink_dusk.cpp +++ b/src/d/actor/d_a_alink_dusk.cpp @@ -143,12 +143,14 @@ void daAlink_c::handleQuickTransform() { procCoMetamorphoseInit(); } -bool daAlink_c::checkGyroAimItemContext() { - if (checkWolf()) { - return false; - } - +bool daAlink_c::checkGyroAimContext() { switch (mProcID) { + case PROC_SUBJECTIVITY: + case PROC_SWIM_SUBJECTIVITY: + case PROC_HORSE_SUBJECTIVITY: + case PROC_CANOE_SUBJECTIVITY: + case PROC_BOARD_SUBJECTIVITY: + case PROC_WOLF_ROPE_SUBJECTIVITY: case PROC_BOW_SUBJECT: case PROC_BOOMERANG_SUBJECT: case PROC_COPY_ROD_SUBJECT: diff --git a/src/d/actor/d_a_alink_link.inc b/src/d/actor/d_a_alink_link.inc index 6de02a7598..b278d866fc 100644 --- a/src/d/actor/d_a_alink_link.inc +++ b/src/d/actor/d_a_alink_link.inc @@ -130,7 +130,7 @@ BOOL daAlink_c::setBodyAngleToCamera() { } #if TARGET_PC - if (dusk::getSettings().game.enableGyroAim && checkGyroAimItemContext()) { + if (dusk::getSettings().game.enableGyroAim && checkGyroAimContext()) { f32 gyro_scale = 1.0f; if (checkWolfEyeUp()) { gyro_scale *= 0.6f; diff --git a/src/d/actor/d_a_midna.cpp b/src/d/actor/d_a_midna.cpp index 01d472e075..6df9d2cc34 100644 --- a/src/d/actor/d_a_midna.cpp +++ b/src/d/actor/d_a_midna.cpp @@ -1054,7 +1054,7 @@ void daMidna_c::setBodyPartMatrix() { } mpModel->calcWeightEnvelopeMtx(); #ifdef TARGET_PC - // Frame interpolation: Record weight envelopes for Midna here, as they are otherwise missed causing distortion + // FRAME INTERP NOTE: Record weight envelopes for Midna here, as they are otherwise missed causing distortion for (u16 i = 0; i < mpModel->getModelData()->getWEvlpMtxNum(); i++) { dusk::frame_interp::record_final_mtx_raw(reinterpret_cast(mpModel->getWeightAnmMtx(i)), mpModel->getWeightAnmMtx(i)); } diff --git a/src/d/actor/d_a_npc_inko.cpp b/src/d/actor/d_a_npc_inko.cpp index 3c277085f6..4d55a31f8e 100644 --- a/src/d/actor/d_a_npc_inko.cpp +++ b/src/d/actor/d_a_npc_inko.cpp @@ -110,6 +110,10 @@ static int daNpc_Inko_Execute(npc_inko_class* i_this) { } f32 var_f31; + + #if AVOID_UB + var_f31 = 0.0f; + #endif if (i_this->field_0x598 == 0) { if (i_this->field_0x59c[1] == 0) { i_this->field_0x59c[1] = 30.0f + cM_rndF(70.0f); diff --git a/src/d/d_menu_fmap2D.cpp b/src/d/d_menu_fmap2D.cpp index eacd2d04a8..3d3b96c57f 100644 --- a/src/d/d_menu_fmap2D.cpp +++ b/src/d/d_menu_fmap2D.cpp @@ -379,16 +379,15 @@ void dMenu_Fmap2DBack_c::draw() { &mArrowPos2DY); #ifdef TARGET_PC - for (u32 i = 0; i < dusk::frame_interp::get_presentation_ui_advance_ticks(); ++i) { + if (dusk::frame_interp::get_ui_tick_pending()) #endif + { field_0x11e0 -= g_fmapHIO.mCursorSpeed; if (field_0x11e0 < 0.0f) { field_0x11e0 += 360.0f; } -#ifdef TARGET_PC } -#endif mpPointParent->getPanePtr()->rotate(mpPointParent->getSizeX() / 2.0f, mpPointParent->getSizeY() / 2.0f, ROTATE_Z, diff --git a/src/d/d_menu_ring.cpp b/src/d/d_menu_ring.cpp index 8e5b3cacd2..529597248a 100644 --- a/src/d/d_menu_ring.cpp +++ b/src/d/d_menu_ring.cpp @@ -29,6 +29,10 @@ #include +#if TARGET_PC +#include "dusk/game_clock.h" +#endif + typedef void (dMenu_Ring_c::*initFunc)(); static initFunc stick_init[] = { /* STATUS_WAIT */ &dMenu_Ring_c::stick_wait_init, @@ -183,6 +187,9 @@ dMenu_Ring_c::dMenu_Ring_c(JKRExpHeap* i_heap, STControl* i_stick, CSTControl* i } for (int i = 0; i < 4; i++) { field_0x674[i] = 0; +#if TARGET_PC + mSelectItemSlideElapsed[i] = 0.0f; +#endif field_0x518[i] = 0.0f; field_0x528[i] = 0.0f; field_0x538[i] = 0.0f; @@ -1022,6 +1029,9 @@ void dMenu_Ring_c::setJumpItem(bool i_useVibrationM) { field_0x6b8[0] != dComIfGs_getMixItemIndex(0)) { field_0x674[0] = 1; +#if TARGET_PC + mSelectItemSlideElapsed[0] = 0.0f; +#endif } } else if (field_0x6b3 == 1) { field_0x538[0] = g_ringHIO.mUnselectItemScale; @@ -1030,6 +1040,9 @@ void dMenu_Ring_c::setJumpItem(bool i_useVibrationM) { field_0x6b8[1] != dComIfGs_getMixItemIndex(1)) { field_0x674[1] = 1; +#if TARGET_PC + mSelectItemSlideElapsed[1] = 0.0f; +#endif } } if (field_0x674[0] == 1) { @@ -1520,7 +1533,15 @@ void dMenu_Ring_c::setSelectItem(int i_idx, u8 i_itemNo) { void dMenu_Ring_c::drawSelectItem() { for (int i = 0; i < 4; i++) { if (field_0x674[i] != 0) { +#if TARGET_PC + mSelectItemSlideElapsed[i] += dusk::game_clock::consume_interval(this); + const f32 u = std::min(mSelectItemSlideElapsed[i] / dusk::game_clock::period_for_original_frames(10.0f), 1.0f); + if (u >= 1.0f) { + setSelectItemForce(i); + } else { +#else if (field_0x674[i] < 10) { +#endif f32 initSizeX = dMeter2Info_getMeterItemPanePtr(i)->getInitSizeX() * 1.7f; f32 initSizeY = dMeter2Info_getMeterItemPanePtr(i)->getInitSizeY() * 1.7f; f32 initScaleX = dMeter2Info_getMeterItemPanePtr(i)->getInitScaleX(); @@ -1528,7 +1549,11 @@ void dMenu_Ring_c::drawSelectItem() { Vec pos = dMeter2Info_getMeterItemPanePtr(i)->getGlobalVtxCenter( dMeter2Info_getMeterItemPanePtr(i)->mPane, true, 0); +#if TARGET_PC + f32 fVar14 = 0.1f + 0.8f * u; +#else f32 fVar14 = field_0x674[i] / 10.0f; +#endif if (field_0x6cd != 0xff) { fVar14 = 1.0f - fVar14; } @@ -1549,9 +1574,11 @@ void dMenu_Ring_c::drawSelectItem() { 0); } } +#if !TARGET_PC field_0x674[i]++; } else { setSelectItemForce(i); +#endif } } } @@ -1562,6 +1589,9 @@ void dMenu_Ring_c::setSelectItemForce(int i_idx) { if (field_0x674[i_idx] != 0) { dComIfGs_setSelectItemIndex(i_idx, field_0x6b4[i_idx]); field_0x674[i_idx] = 0; +#if TARGET_PC + mSelectItemSlideElapsed[i_idx] = 0.0f; +#endif } } else if (field_0x674[i_idx] != 0) { for (int i = 0; i < 2; i++) { @@ -1569,6 +1599,9 @@ void dMenu_Ring_c::setSelectItemForce(int i_idx) { dComIfGs_setSelectItemIndex(i, field_0x6b4[i]); } field_0x674[i_idx] = 0; +#if TARGET_PC + mSelectItemSlideElapsed[i_idx] = 0.0f; +#endif } } diff --git a/src/d/d_meter2_draw.cpp b/src/d/d_meter2_draw.cpp index 417dcad319..8e824f583b 100644 --- a/src/d/d_meter2_draw.cpp +++ b/src/d/d_meter2_draw.cpp @@ -638,12 +638,11 @@ void dMeter2Draw_c::draw() { var_f29 = g_drawHIO.mLightDrop.mDropPikariAnimSpeed_Completed; int temp_r5_2 = g_drawHIO.mLightDrop.mPikariInterval * 15; #ifdef TARGET_PC - // Set even if not advancing + // FRAME INTERP NOTE: Set even if not advancing var_f28 = g_drawHIO.mLightDrop.mPikariScaleComplete; - - const u32 ui_advance_ticks = dusk::frame_interp::get_presentation_ui_advance_ticks(); - for (u32 tick = 0; tick < ui_advance_ticks; ++tick) { + if (dusk::frame_interp::get_ui_tick_pending()) #endif + { if (field_0x756 <= temp_r5_2) { int temp_r4 = (field_0x756 % g_drawHIO.mLightDrop.mPikariInterval); int temp_r3_5 = field_0x756 / g_drawHIO.mLightDrop.mPikariInterval; @@ -669,17 +668,12 @@ void dMeter2Draw_c::draw() { } field_0x756 = -1; -#ifdef TARGET_PC - break; -#endif } else { field_0x756++; } } } -#ifdef TARGET_PC } -#endif for (int i = 0; i < 16; i++) { if (field_0x66c[i] > 0.0f) { @@ -1349,9 +1343,9 @@ void dMeter2Draw_c::drawPikari(f32 i_posX, f32 i_posY, f32* i_framep, f32 i_scal *i_framep = 0.0f; } else { #ifdef TARGET_PC - const u32 ui_advance_ticks = dusk::frame_interp::get_presentation_ui_advance_ticks(); - for (u32 i = 0; i < ui_advance_ticks; ++i) { + if (dusk::frame_interp::get_ui_tick_pending()) #endif + { *i_framep += param_8; if (*i_framep > var_f31) { if (param_9 == 1 || param_9 == 2 || param_9 == 3) { @@ -1366,9 +1360,7 @@ void dMeter2Draw_c::drawPikari(f32 i_posX, f32 i_posY, f32* i_framep, f32 i_scal } else if (*i_framep == 18.0f && param_9 == 2) { mDoAud_seStart(Z2SE_SY_ITEM_COMBINE_ICON, NULL, 0, 0); } -#ifdef TARGET_PC } -#endif playPikariBckAnimation(*i_framep); playPikariBpkAnimation(*i_framep); diff --git a/src/d/d_msg_class.cpp b/src/d/d_msg_class.cpp index debea084dc..37085a6bb9 100644 --- a/src/d/d_msg_class.cpp +++ b/src/d/d_msg_class.cpp @@ -12,6 +12,10 @@ #include "d/d_lib.h" #include "JSystem/JUtility/JUTFont.h" +#if TARGET_PC +#include "dusk/scope_guard.hpp" +#endif + #if REGION_JPN #define CHAR_CODE_MALE_ICON 0x8189 #define CHAR_CODE_FEMALE_ICON 0x818A @@ -1918,6 +1922,11 @@ void jmessage_tSequenceProcessor::do_begin(void const* pEntry, char const* pszTe pReference->resetReference(); field_0xb5 = 0; +#if TARGET_PC + if (dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0)) { + field_0xb2 = 1; + } +#endif } void jmessage_tSequenceProcessor::do_end() { @@ -2154,6 +2163,18 @@ void jmessage_tSequenceProcessor::do_character(int iCharacter) { bool jmessage_tSequenceProcessor::do_tag(u32 i_tag, void const* i_data, u32 i_size) { jmessage_tReference* pReference = (jmessage_tReference*)getReference(); +#if TARGET_PC + // This class runs the lambda function when it goes out of scope. We want to run + // this code after the switch statement and this saves us from having to litter + // the switch statement with IF_DUSK before every return. + auto instantTextRun = SimpleScopeGuard([&]() { + if (dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0)) { + field_0xb2 = 1; + pReference->setSendTimer(0); + } + }); +#endif + switch (i_tag & 0xFF0000) { case MSGTAG_GROUP(1): { cXyz pos = pReference->getActorPos(); diff --git a/src/d/d_msg_object.cpp b/src/d/d_msg_object.cpp index 3676639908..488af685db 100644 --- a/src/d/d_msg_object.cpp +++ b/src/d/d_msg_object.cpp @@ -28,6 +28,10 @@ #include "m_Do/m_Do_lib.h" #include "JSystem/JKernel/JKRExpHeap.h" +#if TARGET_PC +#include "dusk/settings.h" +#endif + static void dMsgObject_addFundRaising(s16 param_0); static void dMsgObject_addTotalPayment(s16 param_0); @@ -1566,7 +1570,8 @@ u8 dMsgObject_c::isSend() { if (pRef->getSendFlag() == 5) { if (getStatusLocal() == 21) { setButtonStatusLocal(); - if (mDoCPd_c::getTrigA(0) != 0 || mDoCPd_c::getTrigB(0) != 0) { + if (IF_DUSK((dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0)) ||) + mDoCPd_c::getTrigA(0) != 0 || mDoCPd_c::getTrigB(0) != 0) { return 2; } return 0; @@ -1585,7 +1590,8 @@ u8 dMsgObject_c::isSend() { } if (pRef->getSendFlag() == 2) { setButtonStatusLocal(); - if (mDoCPd_c::getTrigA(0) != 0 || mDoCPd_c::getTrigB(0) != 0) { + if (IF_DUSK((dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0)) ||) + mDoCPd_c::getTrigA(0) != 0 || mDoCPd_c::getTrigB(0) != 0) { return 2; } } @@ -1598,7 +1604,8 @@ u8 dMsgObject_c::isSend() { return 2; } } else { - if (mDoCPd_c::getTrigA(0) != 0 || mDoCPd_c::getTrigB(0) != 0) { + if (IF_DUSK((dusk::getSettings().game.instantText && mDoCPd_c::getHoldB(0)) ||) + mDoCPd_c::getTrigA(0) != 0 || mDoCPd_c::getTrigB(0) != 0) { return 2; } if (mesgCancelButton) { diff --git a/src/d/d_msg_scrn_light.cpp b/src/d/d_msg_scrn_light.cpp index c34397aad2..19671b5d57 100644 --- a/src/d/d_msg_scrn_light.cpp +++ b/src/d/d_msg_scrn_light.cpp @@ -204,16 +204,14 @@ void dMsgScrnLight_c::draw(f32* i_anmFrame, f32 i_posX, f32 i_posY, f32 i_scaleX if (mPlayAnim) { #ifdef TARGET_PC - const u32 ui_advance_ticks = dusk::frame_interp::get_presentation_ui_advance_ticks(); - for (u32 i = 0; i < ui_advance_ticks; ++i) { + if (dusk::frame_interp::get_ui_tick_pending()) #endif + { *i_anmFrame += 1.0f; if (*i_anmFrame >= mpBck->getFrameMax()) { *i_anmFrame = 0.0f; } -#ifdef TARGET_PC } -#endif mBckFrame = *i_anmFrame; mBpkFrame = *i_anmFrame; @@ -229,17 +227,15 @@ void dMsgScrnLight_c::draw(f32* i_anmFrame, f32 i_posX, f32 i_posY, f32 i_scaleX if (mPlayAnim) { #ifdef TARGET_PC - const u32 ui_advance_ticks = dusk::frame_interp::get_presentation_ui_advance_ticks(); - for (u32 i = 0; i < ui_advance_ticks; ++i) { + if (dusk::frame_interp::get_ui_tick_pending()) #endif + { *i_anmFrame += i_anmRate; if (*i_anmFrame >= mpBck->getFrameMax()) { *i_anmFrame = 0.0f; } -#ifdef TARGET_PC } -#endif mBckFrame = *i_anmFrame; mBpkFrame = *i_anmFrame; diff --git a/src/d/d_select_cursor.cpp b/src/d/d_select_cursor.cpp index 2cdd454737..545b54c9a3 100644 --- a/src/d/d_select_cursor.cpp +++ b/src/d/d_select_cursor.cpp @@ -272,9 +272,9 @@ void dSelect_cursor_c::update() { if (field_0x30) { if (chkPlayAnime(0)) { #ifdef TARGET_PC - const u32 ui_advance_ticks = dusk::frame_interp::get_presentation_ui_advance_ticks(); - for (u32 tick = 0; tick < ui_advance_ticks; ++tick) { + if (dusk::frame_interp::get_ui_tick_pending()) #endif + { if (mNameIdx == 1) { field_0x44 += mpCursorHIO->field_0x8 * fVar1; } else { @@ -284,9 +284,7 @@ void dSelect_cursor_c::update() { if (field_0x44 >= field_0x30->getFrameMax()) { field_0x44 -= field_0x30->getFrameMax(); } -#ifdef TARGET_PC } -#endif field_0x30->setFrame(field_0x44); setBpkAnimation(field_0x30); @@ -303,9 +301,9 @@ void dSelect_cursor_c::update() { if (field_0x34[i]) { if ((i == 0 && chkPlayAnime(2)) || (i == 1 && chkPlayAnime(3))) { #ifdef TARGET_PC - const u32 ui_advance_ticks = dusk::frame_interp::get_presentation_ui_advance_ticks(); - for (u32 tick = 0; tick < ui_advance_ticks; ++tick) { + if (dusk::frame_interp::get_ui_tick_pending()) #endif + { if (mNameIdx == 1) { field_0x48[i] += mpCursorHIO->field_0x8 * fVar1; } else { @@ -314,9 +312,7 @@ void dSelect_cursor_c::update() { if (field_0x48[i] >= field_0x34[i]->getFrameMax()) { field_0x48[i] -= field_0x34[i]->getFrameMax(); } -#ifdef TARGET_PC } -#endif field_0x34[i]->setFrame(field_0x48[i]); } @@ -326,9 +322,9 @@ void dSelect_cursor_c::update() { if (field_0x2C && chkPlayAnime(1)) { #ifdef TARGET_PC - const u32 ui_advance_ticks = dusk::frame_interp::get_presentation_ui_advance_ticks(); - for (u32 tick = 0; tick < ui_advance_ticks; ++tick) { + if (dusk::frame_interp::get_ui_tick_pending()) #endif + { if (mNameIdx == 1) { field_0x40 += mpCursorHIO->field_0x8 * fVar1; } else { @@ -337,9 +333,7 @@ void dSelect_cursor_c::update() { if (field_0x40 >= field_0x2C->getFrameMax()) { field_0x40 -= field_0x2C->getFrameMax(); } -#ifdef TARGET_PC } -#endif field_0x2C->setFrame(field_0x40); setBckAnimation(field_0x2C); @@ -348,13 +342,11 @@ void dSelect_cursor_c::update() { if (chkPlayAnime(1) && mNameIdx == 0) { #ifdef TARGET_PC - const u32 ui_advance_ticks = dusk::frame_interp::get_presentation_ui_advance_ticks(); - for (u32 tick = 0; tick < ui_advance_ticks; ++tick) { + if (dusk::frame_interp::get_ui_tick_pending()) #endif + { setCursorAnimation(); -#ifdef TARGET_PC } -#endif } mpScreen->animation(); diff --git a/src/d/d_select_icon.cpp b/src/d/d_select_icon.cpp index 3a4671fefa..32be9c15e2 100644 --- a/src/d/d_select_icon.cpp +++ b/src/d/d_select_icon.cpp @@ -9,9 +9,9 @@ dSi_HIO_c::dSi_HIO_c() {} void dSelect_icon_c::animation() { if (field_0x10->getAlpha() != 0) { #ifdef TARGET_PC - const u32 ui_advance_ticks = dusk::frame_interp::get_presentation_ui_advance_ticks(); - for (u32 i = 0; i < ui_advance_ticks; ++i) { + if (dusk::frame_interp::get_ui_tick_pending()) #endif + { field_0x20 += field_0x2c; if (field_0x20 >= field_0x1c->getFrameMax()) { field_0x20 = 0.0f; @@ -22,9 +22,9 @@ void dSelect_icon_c::animation() { if (field_0x28 >= field_0x24->getFrameMax()) { field_0x28 = 0.0f; } -#ifdef TARGET_PC } - // Set even if not advancing +#ifdef TARGET_PC + // FRAME INTERP NOTE: Set even if not advancing field_0x1c->setFrame(field_0x20); #endif diff --git a/src/d/d_timer.cpp b/src/d/d_timer.cpp index 552874b552..2e89ccbbef 100644 --- a/src/d/d_timer.cpp +++ b/src/d/d_timer.cpp @@ -1341,75 +1341,70 @@ void dDlst_TimerScrnDraw_c::draw() { ((f32)g_drawHIO.mMiniGame.mGetInTextWaitFrames + 60.0f); #if TARGET_PC - const u32 pending_ui_ticks = dusk::frame_interp::get_presentation_ui_advance_ticks(); -#else - const u32 pending_ui_ticks = 1u; + if (dusk::frame_interp::get_ui_tick_pending()) #endif - - for (int i = 0; i < 51; i++) { - for (u32 tick = 0; tick < pending_ui_ticks; tick++) { - if (!(m_getin_info[i].bck_frame > 0.0f && m_getin_info[i].bck_frame < temp)) { - break; - } - - if (m_getin_info[i].bck_frame < 60.0f) { - m_getin_info[i].bck_frame += g_drawHIO.mMiniGame.mGetInTextAnimSpeed; - if (m_getin_info[i].bck_frame > 60.0f) { - m_getin_info[i].bck_frame = 60.0f; - } - } else if (m_getin_info[i].bck_frame < g_drawHIO.mMiniGame.mGetInTextWaitFrames + 60.0f) { - m_getin_info[i].bck_frame++; - } else if (m_getin_info[i].bck_frame < temp) { - m_getin_info[i].bck_frame++; - } - } - - if (m_getin_info[i].bck_frame > 0.0f && m_getin_info[i].bck_frame < temp) { - f32 var_f29 = 1.0f; - if (m_getin_info[i].bck_frame >= g_drawHIO.mMiniGame.mGetInTextWaitFrames + 60.0f && - m_getin_info[i].bck_frame < temp) { - var_f29 = acc(g_drawHIO.mMiniGame.mGetInTextAlphaFrames, - temp - m_getin_info[i].bck_frame, 0); - } - - if (m_getin_info[i].bck_frame < 60.0f) { - playBckAnimation(m_getin_info[i].bck_frame); - } else { - playBckAnimation(60.0f); - } - - mpGetInParent->setAlphaRate(var_f29); - - if (g_drawHIO.mMiniGame.mGetInTextLocation == 1) { - mpGetInRoot->translate(m_getin_info[i].pos_x + g_drawHIO.mMiniGame.mGetInTextPosX, - m_getin_info[i].pos_y + g_drawHIO.mMiniGame.mGetInTextPosY); - } else { - f32 temp_f2 = m_getin_info[i].bck_frame - 40.0f; - f32 var_f3 = ((temp_f2 * 0.5f) * temp_f2) * 0.15f; - if (i == 0) { - var_f3 = 0.0f; - } - - mpGetInRoot->paneTrans( - m_getin_info[i].pos_x + g_drawHIO.mMiniGame.mGetInTextPosX, - (m_getin_info[i].pos_y + g_drawHIO.mMiniGame.mGetInTextPosY) - var_f3); - } - - mpGetInRoot->scale(g_drawHIO.mMiniGame.mGetInTextSizeX, - g_drawHIO.mMiniGame.mGetInTextSizeY); - mpGetInScreen->draw(0.0f, 0.0f, graf_ctx); - - if (m_getin_info[i].pikari_frame > 0.0f) { - drawPikari(i); - } else if (m_getin_info[i].pikari_frame == -1.0f) { - if (m_getin_info[i].field_0xc == 0) { - if (m_getin_info[i].bck_frame > g_drawHIO.mMiniGame.mGetInPikariAppearFrames) { - m_getin_info[i].pikari_frame = - 18.0f - g_drawHIO.mMiniGame.mGetInPikariAnimSpeed; + { + for (int i = 0; i < 51; i++) { + if (m_getin_info[i].bck_frame > 0.0f && m_getin_info[i].bck_frame < temp) { + if (m_getin_info[i].bck_frame < 60.0f) { + m_getin_info[i].bck_frame += g_drawHIO.mMiniGame.mGetInTextAnimSpeed; + if (m_getin_info[i].bck_frame > 60.0f) { + m_getin_info[i].bck_frame = 60.0f; + } + } else if (m_getin_info[i].bck_frame < g_drawHIO.mMiniGame.mGetInTextWaitFrames + 60.0f) { + m_getin_info[i].bck_frame++; + } else if (m_getin_info[i].bck_frame < temp) { + m_getin_info[i].bck_frame++; + } + } + + if (m_getin_info[i].bck_frame > 0.0f && m_getin_info[i].bck_frame < temp) { + f32 var_f29 = 1.0f; + if (m_getin_info[i].bck_frame >= g_drawHIO.mMiniGame.mGetInTextWaitFrames + 60.0f && + m_getin_info[i].bck_frame < temp) { + var_f29 = acc(g_drawHIO.mMiniGame.mGetInTextAlphaFrames, + temp - m_getin_info[i].bck_frame, 0); + } + + if (m_getin_info[i].bck_frame < 60.0f) { + playBckAnimation(m_getin_info[i].bck_frame); + } else { + playBckAnimation(60.0f); + } + + mpGetInParent->setAlphaRate(var_f29); + + if (g_drawHIO.mMiniGame.mGetInTextLocation == 1) { + mpGetInRoot->translate(m_getin_info[i].pos_x + g_drawHIO.mMiniGame.mGetInTextPosX, + m_getin_info[i].pos_y + g_drawHIO.mMiniGame.mGetInTextPosY); + } else { + f32 temp_f2 = m_getin_info[i].bck_frame - 40.0f; + f32 var_f3 = ((temp_f2 * 0.5f) * temp_f2) * 0.15f; + if (i == 0) { + var_f3 = 0.0f; + } + + mpGetInRoot->paneTrans( + m_getin_info[i].pos_x + g_drawHIO.mMiniGame.mGetInTextPosX, + (m_getin_info[i].pos_y + g_drawHIO.mMiniGame.mGetInTextPosY) - var_f3); + } + + mpGetInRoot->scale(g_drawHIO.mMiniGame.mGetInTextSizeX, + g_drawHIO.mMiniGame.mGetInTextSizeY); + mpGetInScreen->draw(0.0f, 0.0f, graf_ctx); + + if (m_getin_info[i].pikari_frame > 0.0f) { + drawPikari(i); + } else if (m_getin_info[i].pikari_frame == -1.0f) { + if (m_getin_info[i].field_0xc == 0) { + if (m_getin_info[i].bck_frame > g_drawHIO.mMiniGame.mGetInPikariAppearFrames) { + m_getin_info[i].pikari_frame = + 18.0f - g_drawHIO.mMiniGame.mGetInPikariAnimSpeed; + } + } else if (m_getin_info[i].bck_frame > g_drawHIO.mMiniGame.mStartPikariAppearFrames) { + m_getin_info[i].pikari_frame = + 18.0f - g_drawHIO.mMiniGame.mStartPikariAnimSpeed; } - } else if (m_getin_info[i].bck_frame > g_drawHIO.mMiniGame.mStartPikariAppearFrames) { - m_getin_info[i].pikari_frame = - 18.0f - g_drawHIO.mMiniGame.mStartPikariAnimSpeed; } } } diff --git a/src/dusk/frame_interpolation.cpp b/src/dusk/frame_interpolation.cpp index e3de48bcfc..24454de99f 100644 --- a/src/dusk/frame_interpolation.cpp +++ b/src/dusk/frame_interpolation.cpp @@ -2,6 +2,7 @@ #include #include "f_op/f_op_camera_mng.h" +#include "m_Do/m_Do_graphic.h" namespace { enum class Op : uint8_t { @@ -63,11 +64,9 @@ bool g_enabled = false; bool g_recording = false; bool g_interpolating = false; bool g_sync_presentation = false; -uint32_t g_presentation_counter = 0; float g_step = 0.0f; -uint32_t g_pending_presentation_ui_ticks = 0; -uint32_t g_current_presentation_ui_ticks = 0; +bool g_ui_tick_pending = false; Recording g_current_recording; Recording g_previous_recording; @@ -84,6 +83,7 @@ struct CameraSnapshot { f32 aspect{}; f32 near_{}; f32 far_{}; + bool wideZoom{}; bool valid{}; }; @@ -347,6 +347,9 @@ void begin_record() { return; } else { copy_view_to_snap(&s_cam_prev, cam->view); +#if WIDESCREEN_SUPPORT + s_cam_prev.wideZoom = s_cam_curr.valid ? s_cam_curr.wideZoom : false; +#endif } } @@ -366,11 +369,6 @@ void interpolate(float step) { interpolate_branch(old_root, g_current_recording.root, g_step); } -void notify_presentation_frame() { - ensure_initialized(); - ++g_presentation_counter; -} - void request_presentation_sync() { ensure_initialized(); if (!g_enabled) { @@ -391,33 +389,14 @@ float get_interpolation_step() { return presentation_sync_active() ? 1.0f : g_step; } -void notify_sim_tick_complete() { +void set_ui_tick_pending(bool value) { + if (g_ui_tick_pending == value) { return; } + g_ui_tick_pending = value; +} + +bool get_ui_tick_pending() { ensure_initialized(); - g_pending_presentation_ui_ticks++; -} - -uint32_t begin_presentation_ui_pass() { - ensure_initialized(); - g_current_presentation_ui_ticks = g_pending_presentation_ui_ticks; - g_pending_presentation_ui_ticks = 0; - return g_current_presentation_ui_ticks; -} - -uint32_t get_presentation_ui_advance_ticks() { - if (!s_initialized) { - return 0; - } - if (!g_enabled) { - return 1; - } - return g_current_presentation_ui_ticks; -} - -void end_presentation_ui_pass() { - if (!s_initialized) { - return; - } - g_current_presentation_ui_ticks = 0; + return g_enabled ? g_ui_tick_pending : true; } void open_child(const void* key, int32_t id) { @@ -502,6 +481,9 @@ void record_camera(::camera_process_class* cam, int camera_id) { return; } copy_view_to_snap(&s_cam_curr, cam->view); +#if WIDESCREEN_SUPPORT + s_cam_curr.wideZoom = mDoGph_gInf_c::isWideZoom(); +#endif } void begin_presentation_camera() { @@ -545,6 +527,13 @@ void begin_presentation_camera() { view->near_ = s_cam_prev.near_ + (s_cam_curr.near_ - s_cam_prev.near_) * step; view->far_ = s_cam_prev.far_ + (s_cam_curr.far_ - s_cam_prev.far_) * step; + // FRAME INTERP TODO: It might be better if I rewired the game to not clear this flag until the next sim frame, but I don't care enough to right now +#if WIDESCREEN_SUPPORT + if (mDoGph_gInf_c::isWide() && !mDoGph_gInf_c::isWideZoom() && step >= 0.5f ? s_cam_curr.wideZoom : s_cam_prev.wideZoom) { + mDoGph_gInf_c::onWideZoom(); + } +#endif + // FRAME INTERP TODO: Largely copied from d_camera's camera_draw function from this point, got any better ideas? C_MTXPerspective(view->projMtx, view->fovy, view->aspect, view->near_, view->far_); mDoMtx_lookAt(view->viewMtx, &view->lookat.eye, &view->lookat.center, &view->lookat.up, view->bank); @@ -601,6 +590,10 @@ void begin_presentation_camera() { mDoLib_clipper::setup(view->fovy, view->aspect, view->near_, far_); +#if WIDESCREEN_SUPPORT + mDoGph_gInf_c::offWideZoom(); +#endif + s_presentation_depth = 1; } diff --git a/src/dusk/game_clock.cpp b/src/dusk/game_clock.cpp new file mode 100644 index 0000000000..176c43c3b7 --- /dev/null +++ b/src/dusk/game_clock.cpp @@ -0,0 +1,76 @@ +#include "dusk/game_clock.h" + +#include +#include +#include + +namespace dusk { +namespace game_clock { + +using clock = std::chrono::steady_clock; + +bool s_initialized = false; +clock::time_point s_previous_sample{}; +float s_sim_accumulator = 0.0f; + +std::unordered_map s_interval_last_sample; + +void ensure_initialized() { + if (s_initialized) { + return; + } + s_previous_sample = clock::now(); + s_sim_accumulator = sim_pace(); + s_initialized = true; +} + +void reset_accumulator() { + ensure_initialized(); + s_sim_accumulator = 0.0f; +} + +MainLoopPacer advance_main_loop() { + ensure_initialized(); + + const clock::time_point now = clock::now(); + const float presentation_dt = std::chrono::duration(now - s_previous_sample).count(); + s_previous_sample = now; + + s_sim_accumulator += presentation_dt; + + MainLoopPacer out{}; + out.presentation_dt_seconds = presentation_dt; + + const bool should_interpolate = dusk::getSettings().game.enableFrameInterpolation && !dusk::getTransientSettings().skipFrameRateLimit; + out.is_interpolating = should_interpolate; + out.sim_pace = sim_pace(); + + if (!should_interpolate) { + s_sim_accumulator = 0.0f; + out.do_sim_tick = true; + out.interpolation_step = 0.0f; + return out; + } else { + out.do_sim_tick = s_sim_accumulator >= sim_pace(); + out.interpolation_step = out.do_sim_tick ? 0.0f : s_sim_accumulator / sim_pace(); + return out; + } +} + +float consume_interval(const void* consumer) { + ensure_initialized(); + const uintptr_t key = reinterpret_cast(consumer); + const clock::time_point now = clock::now(); + + float dt = ui_initial_dt(); + const auto it = s_interval_last_sample.find(key); + if (it != s_interval_last_sample.end()) { + dt = std::chrono::duration(now - it->second).count(); + dt = std::min(dt, ui_maximum_dt()); + } + s_interval_last_sample[key] = now; + return dt; +} + +} // namespace game_clock +} // namespace dusk diff --git a/src/dusk/gyro.cpp b/src/dusk/gyro.cpp index 0905bd59fd..d390c9c8b4 100644 --- a/src/dusk/gyro.cpp +++ b/src/dusk/gyro.cpp @@ -35,7 +35,7 @@ bool s_sensor_keep_alive = false; bool get_sensor_keep_alive() { return s_sensor_keep_alive; } void set_sensor_keep_alive(bool value) { s_sensor_keep_alive = value; } -bool queryGyroAimItemContext() { +bool queryGyroAimContext() { if (!static_cast(dusk::getSettings().game.enableGyroAim)) { return false; } @@ -45,11 +45,11 @@ bool queryGyroAimItemContext() { return false; } - return link->checkGyroAimItemContext() && dComIfGp_checkCameraAttentionStatus(link->field_0x317c, 0x10); + return link->checkGyroAimContext() && dComIfGp_checkCameraAttentionStatus(link->field_0x317c, 0x10); } void read(float dt) { - if (!s_sensor_keep_alive && !(dusk::getSettings().game.enableGyroAim && queryGyroAimItemContext())) { + if (!s_sensor_keep_alive && !queryGyroAimContext()) { if (s_sensor_enabled) { PADSetSensorEnabled(PAD_CHAN0, PAD_SENSOR_GYRO, FALSE); s_sensor_enabled = false; diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index f6ed8b4c8e..f83b77b274 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -368,10 +368,16 @@ namespace dusk { m_menuTools.ShowStateShare(); DuskDebugPad(); // temporary, remove later - // Only show cursor when menu or any windows are open - if (showMenu || ImGui::GetIO().MetricsRenderWindows > 0) { - ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NoMouseCursorChange; - // Imgui will re-show cursor. + // Hide mouse cursor if the F1 menu is not open and the cursor is idle for 3 seconds. + ImGuiIO& io = ImGui::GetIO(); + if (showMenu) { + mouseHideTimer = 0.0f; + ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NoMouseCursorChange; // Imgui will re-show cursor. + } else if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f) { + mouseHideTimer = 0.0f; + ImGui::GetIO().ConfigFlags &= ~ImGuiConfigFlags_NoMouseCursorChange; // Imgui will re-show cursor. + } else if (mouseHideTimer <= 3.0f) { + mouseHideTimer += ImGui::GetIO().DeltaTime; } else { ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_NoMouseCursorChange; SDL_HideCursor(); diff --git a/src/dusk/imgui/ImGuiConsole.hpp b/src/dusk/imgui/ImGuiConsole.hpp index 0670618cf2..70c5184d0d 100644 --- a/src/dusk/imgui/ImGuiConsole.hpp +++ b/src/dusk/imgui/ImGuiConsole.hpp @@ -38,6 +38,8 @@ private: remain(duration) {} }; + float mouseHideTimer = 0.0f; + bool m_isHidden = true; bool m_isLaunchInitialized = false; bool m_touchTapActive = false; diff --git a/src/dusk/imgui/ImGuiMenuEnhancements.cpp b/src/dusk/imgui/ImGuiMenuEnhancements.cpp index d47b4e8d4b..a494f81771 100644 --- a/src/dusk/imgui/ImGuiMenuEnhancements.cpp +++ b/src/dusk/imgui/ImGuiMenuEnhancements.cpp @@ -65,6 +65,11 @@ namespace dusk { ImGui::SetTooltip("Skip the delay when writing to the Memory Card."); } + config::ImGuiCheckbox("Hold B for Instant Text", getSettings().game.instantText); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Make text scroll immediately by holding B."); + } + config::ImGuiCheckbox("No Climbing Miss Animation", getSettings().game.noMissClimbing); if (ImGui::IsItemHovered()) { ImGui::SetTooltip("Prevents Link from playing a struggle animation\n" @@ -144,8 +149,10 @@ namespace dusk { config::ImGuiCheckbox("Gyro Aim", getSettings().game.enableGyroAim); if (ImGui::IsItemHovered()) { - ImGui::SetTooltip("Enables the gyroscope on supported controllers while aiming the\n" - "Slingshot, Gale Boomerang, Hero's Bow, Clawshot(s), Ball and Chain, and Dominion Rod."); + ImGui::SetTooltip("Enables the gyroscope on supported controllers\n" + "while in look mode (C-Up) and while aiming the\n" + "Slingshot, Gale Boomerang, Hero's Bow, Clawshot(s),\n" + "Ball and Chain, and Dominion Rod."); } config::ImGuiCheckbox("Gyro Rollgoal", getSettings().game.enableGyroRollgoal); diff --git a/src/dusk/settings.cpp b/src/dusk/settings.cpp index 0236f30afa..53704412e6 100644 --- a/src/dusk/settings.cpp +++ b/src/dusk/settings.cpp @@ -35,6 +35,7 @@ UserSettings g_userSettings = { .noMissClimbing {"game.noMissClimbing", false}, .fastTears {"game.fastTears", false}, .instantSaves {"game.instantSaves", false}, + .instantText {"game.instantText", false}, .sunsSong {"game.sunsSong", false}, // Preferences @@ -121,6 +122,7 @@ void registerSettings() { Register(g_userSettings.game.fastClimbing); Register(g_userSettings.game.fastTears); Register(g_userSettings.game.instantSaves); + Register(g_userSettings.game.instantText); Register(g_userSettings.game.sunsSong); Register(g_userSettings.game.enableMirrorMode); Register(g_userSettings.game.invertCameraXAxis); diff --git a/src/m_Do/m_Do_graphic.cpp b/src/m_Do/m_Do_graphic.cpp index a4a31e3228..95c9a76d1b 100644 --- a/src/m_Do/m_Do_graphic.cpp +++ b/src/m_Do/m_Do_graphic.cpp @@ -471,72 +471,32 @@ void darwFilter(GXColor matColor) { GXEnd(); } -#ifdef TARGET_PC -static void mDoGph_AdvanceFadeState() { - if (mDoGph_gInf_c::isFade() != 0) { - f32 fade_rate = mDoGph_gInf_c::getFadeRate() + mDoGph_gInf_c::getFadeSpeed(); - - if (fade_rate < 0.0f) { - fade_rate = 0.0f; - mDoGph_gInf_c::offFade(); - } else if (fade_rate > 1.0f) { - fade_rate = 1.0f; - } - - mDoGph_gInf_c::setFadeRate(fade_rate); - mDoGph_gInf_c::getFadeColor().a = 255.0f * fade_rate; - } else { - GXColor& fade_color = mDoGph_gInf_c::getFadeColor(); - if (dComIfG_getBrightness() != 255) { - fade_color.r = 0; - fade_color.g = 0; - fade_color.b = 0; - fade_color.a = 255 - dComIfG_getBrightness(); - } else { - fade_color.a = 0; - } - } -} - -static void mDoGph_AdvanceFadeState(u32 tick_count) { - for (u32 i = 0; i < tick_count; ++i) { - mDoGph_AdvanceFadeState(); - } -} - -static void mDoGph_DrawStoredFade() { - GXColor& fade_color = mDoGph_gInf_c::getFadeColor(); - if (fade_color.a != 0) { - darwFilter(fade_color); - } -} - void mDoGph_gInf_c::calcFade() { - mDoGph_AdvanceFadeState(); - mDoGph_DrawStoredFade(); -} -#else -void mDoGph_gInf_c::calcFade() { - if (mDoGph_gInf_c::mFade != 0) { - mFadeRate += mFadeSpeed; +#if TARGET_PC + if (dusk::frame_interp::get_ui_tick_pending()) +#endif + { + if (mFade != 0) { + mFadeRate += mFadeSpeed; - if (mFadeRate < 0.0f) { - mFadeRate = 0.0f; - mDoGph_gInf_c::mFade = 0; - } else { - if (mFadeRate > 1.0f) { - mFadeRate = 1.0f; + if (mFadeRate < 0.0f) { + mFadeRate = 0.0f; + mFade = 0; + } else { + if (mFadeRate > 1.0f) { + mFadeRate = 1.0f; + } } - } - mFadeColor.a = 255.0f * mFadeRate; - } else { - if (dComIfG_getBrightness() != 255) { - mFadeColor.r = 0; - mFadeColor.g = 0; - mFadeColor.b = 0; - mFadeColor.a = 255 - dComIfG_getBrightness(); + mFadeColor.a = 255.0f * mFadeRate; } else { - mFadeColor.a = 0; + if (dComIfG_getBrightness() != 255) { + mFadeColor.r = 0; + mFadeColor.g = 0; + mFadeColor.b = 0; + mFadeColor.a = 255 - dComIfG_getBrightness(); + } else { + mFadeColor.a = 0; + } } } @@ -544,7 +504,6 @@ void mDoGph_gInf_c::calcFade() { darwFilter(mFadeColor); } } -#endif #if PLATFORM_WII || PLATFORM_SHIELD u32 mDoGph_gInf_c::csr_c::m_blurID; @@ -944,9 +903,6 @@ int mDoGph_AfterOfDraw() { JUTVideo::getManager()->setRenderMode(mDoMch_render_c::getRenderModeObj()); mDoGph_gInf_c::endFrame(); -#ifdef TARGET_PC - dusk::frame_interp::notify_sim_tick_complete(); -#endif return 1; } @@ -2099,8 +2055,6 @@ int mDoGph_Painter() { #if TARGET_PC dusk::g_imguiConsole.PreDraw(); - - const u32 pending_ui_ticks = dusk::frame_interp::begin_presentation_ui_pass(); #endif #if DEBUG @@ -2108,7 +2062,7 @@ int mDoGph_Painter() { #endif #ifdef TARGET_PC - for (u32 i = 0; i < pending_ui_ticks; ++i) + if (dusk::frame_interp::get_ui_tick_pending()) #endif { dComIfGp_particle_calcMenu(); @@ -2193,7 +2147,7 @@ int mDoGph_Painter() { view_port->height); #ifdef TARGET_PC - // Frame interpolation: Call setViewMtx earlier so that it's interpolated in time for draw_info to use it + // FRAME INTERP NOTE: Call setViewMtx earlier so that it's interpolated in time for draw_info to use it j3dSys.setViewMtx(camera_p->view.viewMtx); JPADrawInfo draw_info(j3dSys.getViewMtx(), camera_p->view.fovy, camera_p->view.aspect); #else @@ -2590,12 +2544,7 @@ int mDoGph_Painter() { if (strcmp(dComIfGp_getStartStageName(), "F_SP127") != 0 && (mDoGph_gInf_c::isFade() & 0x80) == 0) { -#ifdef TARGET_PC - mDoGph_AdvanceFadeState(pending_ui_ticks); - mDoGph_DrawStoredFade(); -#else mDoGph_gInf_c::calcFade(); -#endif } #if DEBUG @@ -2661,7 +2610,7 @@ int mDoGph_Painter() { GXSetClipMode(GX_CLIP_ENABLE); #if TARGET_PC - for (u32 i = 0; i < pending_ui_ticks; ++i) + if (dusk::frame_interp::get_ui_tick_pending()) #endif { dDlst_list_c::calcWipe(); @@ -2715,12 +2664,7 @@ int mDoGph_Painter() { if (strcmp(dComIfGp_getStartStageName(), "F_SP127") == 0 || (mDoGph_gInf_c::isFade() & 0x80) != 0) { -#ifdef TARGET_PC - mDoGph_AdvanceFadeState(pending_ui_ticks); - mDoGph_DrawStoredFade(); -#else mDoGph_gInf_c::calcFade(); -#endif } GX_DEBUG_GROUP(dComIfGp_particle_draw2DmenuFore, &draw_info3); @@ -2753,18 +2697,10 @@ int mDoGph_Painter() { #if TARGET_PC dusk::g_imguiConsole.PostDraw(); - - if (dusk::getSettings().game.enableFrameInterpolation) { - JFWDisplay::getManager()->setFaderSimSteps(pending_ui_ticks); - } #endif mDoGph_gInf_c::endRender(); -#ifdef TARGET_PC - dusk::frame_interp::end_presentation_ui_pass(); -#endif - #if WIDESCREEN_SUPPORT mDoGph_gInf_c::offWideZoom(); #endif diff --git a/src/m_Do/m_Do_main.cpp b/src/m_Do/m_Do_main.cpp index 71981e40f3..fe94891b28 100644 --- a/src/m_Do/m_Do_main.cpp +++ b/src/m_Do/m_Do_main.cpp @@ -42,7 +42,6 @@ #include "SSystem/SComponent/c_counter.h" #include -#include #include #include #include @@ -51,6 +50,7 @@ #include "dusk/crash_reporting.h" #include "dusk/dusk.h" #include "dusk/frame_interpolation.h" +#include "dusk/game_clock.h" #include "dusk/gyro.h" #include "dusk/imgui/ImGuiEngine.hpp" #include "dusk/logging.h" @@ -197,9 +197,7 @@ void main01(void) { OSReport("Entering Main Loop (main01)...\n"); - constexpr float kSimStepSeconds = 1.0 / 30.0; - auto previous_time = std::chrono::steady_clock::now(); - float accumulator = kSimStepSeconds; + dusk::game_clock::ensure_initialized(); do { // 1. Update Window Events @@ -223,10 +221,7 @@ void main01(void) { eventsDone:; - auto current_time = std::chrono::steady_clock::now(); - float frame_seconds = std::chrono::duration(current_time - previous_time).count(); - previous_time = current_time; - accumulator += frame_seconds; + const dusk::game_clock::MainLoopPacer pacing = dusk::game_clock::advance_main_loop(); VIWaitForRetrace(); @@ -238,26 +233,27 @@ void main01(void) { mDoGph_gInf_c::updateRenderSize(); - if (dusk::getSettings().game.enableFrameInterpolation && !dusk::getTransientSettings().skipFrameRateLimit) { - dusk::frame_interp::notify_presentation_frame(); - if (accumulator >= kSimStepSeconds) { + if (pacing.is_interpolating) { + if (pacing.do_sim_tick) { + dusk::frame_interp::set_ui_tick_pending(true); mDoCPd_c::read(); - dusk::gyro::read(kSimStepSeconds); + dusk::gyro::read(pacing.sim_pace); fapGm_Execute(); mDoAud_Execute(); - accumulator = 0.0f; + dusk::game_clock::reset_accumulator(); } - dusk::frame_interp::interpolate(static_cast(accumulator / kSimStepSeconds)); + dusk::frame_interp::interpolate(pacing.interpolation_step); { dusk::frame_interp::PresentationCameraScope presentation_camera; cAPIGph_Painter(); } + dusk::frame_interp::set_ui_tick_pending(false); } else { - accumulator = 0.0f; - + dusk::frame_interp::set_ui_tick_pending(true); + // Game Inputs mDoCPd_c::read(); - dusk::gyro::read(frame_seconds); + dusk::gyro::read(pacing.presentation_dt_seconds); // EXECUTE GAME LOGIC & RENDER // This calls mDoGph_Painter -> JFWDisplay -> GX Functions