From 1c4088e203282738f51f51c816ee544bf9a53067 Mon Sep 17 00:00:00 2001 From: JosJuice Date: Tue, 30 Jun 2020 21:07:25 +0200 Subject: [PATCH] DolphinQt: Allow customizing TAS input turbo interval As a side effect of 9c5c3c0, Dolphin's frame counter was changed to run at 60/50 Hz even if the game is running at a lower framerate such as 30 fps. Since the TAS input turbo button functionality toggled the state of a button every other frame as reported by the frame counter, this change made the turbo button functionality not work with 30/25 fps games. I believe it would be hard to change the frame counter back to how it used to work without undermining the point of 9c5c3c0, and I'm not sure if doing so would be desireable or not anyway, so what I'm doing instead is letting the user determine how long turbo button presses should last. This lets users avoid the 30/25 fps game problem while also granting additional flexibility. Perhaps there is some game where it is useful to mash at a speed which is slower than frame perfect. --- .../Core/DolphinQt/TAS/GCTASInputWindow.cpp | 26 ++++----- Source/Core/DolphinQt/TAS/TASCheckBox.cpp | 13 ++++- Source/Core/DolphinQt/TAS/TASCheckBox.h | 8 ++- Source/Core/DolphinQt/TAS/TASInputWindow.cpp | 33 +++++++++++ Source/Core/DolphinQt/TAS/TASInputWindow.h | 8 +++ .../Core/DolphinQt/TAS/WiiTASInputWindow.cpp | 58 +++++++++---------- 6 files changed, 99 insertions(+), 47 deletions(-) diff --git a/Source/Core/DolphinQt/TAS/GCTASInputWindow.cpp b/Source/Core/DolphinQt/TAS/GCTASInputWindow.cpp index 1cb606b807..1dee285351 100644 --- a/Source/Core/DolphinQt/TAS/GCTASInputWindow.cpp +++ b/Source/Core/DolphinQt/TAS/GCTASInputWindow.cpp @@ -43,18 +43,18 @@ GCTASInputWindow::GCTASInputWindow(QWidget* parent, int num) : TASInputWindow(pa triggers_layout->addLayout(r_trigger_layout); m_triggers_box->setLayout(triggers_layout); - m_a_button = new TASCheckBox(QStringLiteral("&A")); - m_b_button = new TASCheckBox(QStringLiteral("&B")); - m_x_button = new TASCheckBox(QStringLiteral("&X")); - m_y_button = new TASCheckBox(QStringLiteral("&Y")); - m_z_button = new TASCheckBox(QStringLiteral("&Z")); - m_l_button = new TASCheckBox(QStringLiteral("&L")); - m_r_button = new TASCheckBox(QStringLiteral("&R")); - m_start_button = new TASCheckBox(QStringLiteral("&START")); - m_left_button = new TASCheckBox(QStringLiteral("L&eft")); - m_up_button = new TASCheckBox(QStringLiteral("&Up")); - m_down_button = new TASCheckBox(QStringLiteral("&Down")); - m_right_button = new TASCheckBox(QStringLiteral("R&ight")); + m_a_button = CreateButton(QStringLiteral("&A")); + m_b_button = CreateButton(QStringLiteral("&B")); + m_x_button = CreateButton(QStringLiteral("&X")); + m_y_button = CreateButton(QStringLiteral("&Y")); + m_z_button = CreateButton(QStringLiteral("&Z")); + m_l_button = CreateButton(QStringLiteral("&L")); + m_r_button = CreateButton(QStringLiteral("&R")); + m_start_button = CreateButton(QStringLiteral("&START")); + m_left_button = CreateButton(QStringLiteral("L&eft")); + m_up_button = CreateButton(QStringLiteral("&Up")); + m_down_button = CreateButton(QStringLiteral("&Down")); + m_right_button = CreateButton(QStringLiteral("R&ight")); auto* buttons_layout = new QGridLayout; buttons_layout->addWidget(m_a_button, 0, 0); @@ -80,7 +80,7 @@ GCTASInputWindow::GCTASInputWindow(QWidget* parent, int num) : TASInputWindow(pa layout->addLayout(top_layout); layout->addWidget(m_triggers_box); layout->addWidget(m_buttons_box); - layout->addWidget(m_use_controller); + layout->addWidget(m_settings_box); setLayout(layout); } diff --git a/Source/Core/DolphinQt/TAS/TASCheckBox.cpp b/Source/Core/DolphinQt/TAS/TASCheckBox.cpp index 917309b860..8395e7f3f3 100644 --- a/Source/Core/DolphinQt/TAS/TASCheckBox.cpp +++ b/Source/Core/DolphinQt/TAS/TASCheckBox.cpp @@ -7,8 +7,10 @@ #include #include "Core/Movie.h" +#include "DolphinQt/TAS/TASInputWindow.h" -TASCheckBox::TASCheckBox(const QString& text) : QCheckBox(text) +TASCheckBox::TASCheckBox(const QString& text, TASInputWindow* parent) + : QCheckBox(text, parent), m_parent(parent) { setTristate(true); } @@ -16,7 +18,10 @@ TASCheckBox::TASCheckBox(const QString& text) : QCheckBox(text) bool TASCheckBox::GetValue() const { if (checkState() == Qt::PartiallyChecked) - return Movie::GetCurrentFrame() % 2 == static_cast(m_trigger_on_odd); + { + const u64 frames_elapsed = Movie::GetCurrentFrame() - m_frame_turbo_started; + return frames_elapsed % m_turbo_total_frames < m_turbo_press_frames; + } return isChecked(); } @@ -35,6 +40,8 @@ void TASCheckBox::mousePressEvent(QMouseEvent* event) return; } - m_trigger_on_odd = Movie::GetCurrentFrame() % 2 == 0; + m_frame_turbo_started = Movie::GetCurrentFrame(); + m_turbo_press_frames = m_parent->GetTurboPressFrames(); + m_turbo_total_frames = m_turbo_press_frames + m_parent->GetTurboReleaseFrames(); setCheckState(Qt::PartiallyChecked); } diff --git a/Source/Core/DolphinQt/TAS/TASCheckBox.h b/Source/Core/DolphinQt/TAS/TASCheckBox.h index 4e02e45b46..79f3c28eb3 100644 --- a/Source/Core/DolphinQt/TAS/TASCheckBox.h +++ b/Source/Core/DolphinQt/TAS/TASCheckBox.h @@ -7,12 +7,13 @@ #include class QMouseEvent; +class TASInputWindow; class TASCheckBox : public QCheckBox { Q_OBJECT public: - explicit TASCheckBox(const QString& text); + explicit TASCheckBox(const QString& text, TASInputWindow* parent); bool GetValue() const; @@ -20,5 +21,8 @@ protected: void mousePressEvent(QMouseEvent* event) override; private: - bool m_trigger_on_odd; + const TASInputWindow* m_parent; + int m_frame_turbo_started; + int m_turbo_press_frames; + int m_turbo_total_frames; }; diff --git a/Source/Core/DolphinQt/TAS/TASInputWindow.cpp b/Source/Core/DolphinQt/TAS/TASInputWindow.cpp index aee223b3c6..2bb830db7f 100644 --- a/Source/Core/DolphinQt/TAS/TASInputWindow.cpp +++ b/Source/Core/DolphinQt/TAS/TASInputWindow.cpp @@ -29,9 +29,42 @@ TASInputWindow::TASInputWindow(QWidget* parent) : QDialog(parent) setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); setWindowIcon(Resources::GetAppIcon()); + QGridLayout* settings_layout = new QGridLayout; + m_use_controller = new QCheckBox(QStringLiteral("Enable Controller Inpu&t")); m_use_controller->setToolTip(tr("Warning: Analog inputs may reset to controller values at " "random. In some cases this can be fixed by adding a deadzone.")); + settings_layout->addWidget(m_use_controller, 0, 0, 1, 2); + + QLabel* turbo_press_label = new QLabel(tr("Duration of Turbo Button Press (frames):")); + m_turbo_press_frames = new QSpinBox(); + m_turbo_press_frames->setMinimum(1); + settings_layout->addWidget(turbo_press_label, 1, 0); + settings_layout->addWidget(m_turbo_press_frames, 1, 1); + + QLabel* turbo_release_label = new QLabel(tr("Duration of Turbo Button Release (frames):")); + m_turbo_release_frames = new QSpinBox(); + m_turbo_release_frames->setMinimum(1); + settings_layout->addWidget(turbo_release_label, 2, 0); + settings_layout->addWidget(m_turbo_release_frames, 2, 1); + + m_settings_box = new QGroupBox(tr("Settings")); + m_settings_box->setLayout(settings_layout); +} + +int TASInputWindow::GetTurboPressFrames() const +{ + return m_turbo_press_frames->value(); +} + +int TASInputWindow::GetTurboReleaseFrames() const +{ + return m_turbo_release_frames->value(); +} + +TASCheckBox* TASInputWindow::CreateButton(const QString& name) +{ + return new TASCheckBox(name, this); } QGroupBox* TASInputWindow::CreateStickInputs(QString name, QSpinBox*& x_value, QSpinBox*& y_value, diff --git a/Source/Core/DolphinQt/TAS/TASInputWindow.h b/Source/Core/DolphinQt/TAS/TASInputWindow.h index 90e25b03f2..c5eb125776 100644 --- a/Source/Core/DolphinQt/TAS/TASInputWindow.h +++ b/Source/Core/DolphinQt/TAS/TASInputWindow.h @@ -23,7 +23,11 @@ class TASInputWindow : public QDialog public: explicit TASInputWindow(QWidget* parent); + int GetTurboPressFrames() const; + int GetTurboReleaseFrames() const; + protected: + TASCheckBox* CreateButton(const QString& name); QGroupBox* CreateStickInputs(QString name, QSpinBox*& x_value, QSpinBox*& y_value, u16 max_x, u16 max_y, Qt::Key x_shortcut_key, Qt::Key y_shortcut_key); QBoxLayout* CreateSliderValuePairLayout(QString name, QSpinBox*& value, u16 max, @@ -36,7 +40,11 @@ protected: void GetButton(TASCheckBox* button, UX& pad, UX mask); void GetSpinBoxU8(QSpinBox* spin, u8& controller_value); void GetSpinBoxU16(QSpinBox* spin, u16& controller_value); + + QGroupBox* m_settings_box; QCheckBox* m_use_controller; + QSpinBox* m_turbo_press_frames; + QSpinBox* m_turbo_release_frames; private: std::map m_checkbox_set_by_controller; diff --git a/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp b/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp index 39a6db58a1..b8b7495e92 100644 --- a/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp +++ b/Source/Core/DolphinQt/TAS/WiiTASInputWindow.cpp @@ -160,19 +160,19 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow( triggers_layout->addLayout(r_trigger_layout); m_triggers_box->setLayout(triggers_layout); - m_a_button = new TASCheckBox(QStringLiteral("&A")); - m_b_button = new TASCheckBox(QStringLiteral("&B")); - m_1_button = new TASCheckBox(QStringLiteral("&1")); - m_2_button = new TASCheckBox(QStringLiteral("&2")); - m_plus_button = new TASCheckBox(QStringLiteral("&+")); - m_minus_button = new TASCheckBox(QStringLiteral("&-")); - m_home_button = new TASCheckBox(QStringLiteral("&HOME")); - m_left_button = new TASCheckBox(QStringLiteral("&Left")); - m_up_button = new TASCheckBox(QStringLiteral("&Up")); - m_down_button = new TASCheckBox(QStringLiteral("&Down")); - m_right_button = new TASCheckBox(QStringLiteral("&Right")); - m_c_button = new TASCheckBox(QStringLiteral("&C")); - m_z_button = new TASCheckBox(QStringLiteral("&Z")); + m_a_button = CreateButton(QStringLiteral("&A")); + m_b_button = CreateButton(QStringLiteral("&B")); + m_1_button = CreateButton(QStringLiteral("&1")); + m_2_button = CreateButton(QStringLiteral("&2")); + m_plus_button = CreateButton(QStringLiteral("&+")); + m_minus_button = CreateButton(QStringLiteral("&-")); + m_home_button = CreateButton(QStringLiteral("&HOME")); + m_left_button = CreateButton(QStringLiteral("&Left")); + m_up_button = CreateButton(QStringLiteral("&Up")); + m_down_button = CreateButton(QStringLiteral("&Down")); + m_right_button = CreateButton(QStringLiteral("&Right")); + m_c_button = CreateButton(QStringLiteral("&C")); + m_z_button = CreateButton(QStringLiteral("&Z")); auto* buttons_layout = new QGridLayout; buttons_layout->addWidget(m_a_button, 0, 0); @@ -201,21 +201,21 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow( m_nunchuk_buttons_box = new QGroupBox(tr("Nunchuk Buttons")); m_nunchuk_buttons_box->setLayout(nunchuk_buttons_layout); - m_classic_a_button = new TASCheckBox(QStringLiteral("&A")); - m_classic_b_button = new TASCheckBox(QStringLiteral("&B")); - m_classic_x_button = new TASCheckBox(QStringLiteral("&X")); - m_classic_y_button = new TASCheckBox(QStringLiteral("&Y")); - m_classic_l_button = new TASCheckBox(QStringLiteral("&L")); - m_classic_r_button = new TASCheckBox(QStringLiteral("&R")); - m_classic_zl_button = new TASCheckBox(QStringLiteral("&ZL")); - m_classic_zr_button = new TASCheckBox(QStringLiteral("ZR")); - m_classic_plus_button = new TASCheckBox(QStringLiteral("&+")); - m_classic_minus_button = new TASCheckBox(QStringLiteral("&-")); - m_classic_home_button = new TASCheckBox(QStringLiteral("&HOME")); - m_classic_left_button = new TASCheckBox(QStringLiteral("L&eft")); - m_classic_up_button = new TASCheckBox(QStringLiteral("&Up")); - m_classic_down_button = new TASCheckBox(QStringLiteral("&Down")); - m_classic_right_button = new TASCheckBox(QStringLiteral("R&ight")); + m_classic_a_button = CreateButton(QStringLiteral("&A")); + m_classic_b_button = CreateButton(QStringLiteral("&B")); + m_classic_x_button = CreateButton(QStringLiteral("&X")); + m_classic_y_button = CreateButton(QStringLiteral("&Y")); + m_classic_l_button = CreateButton(QStringLiteral("&L")); + m_classic_r_button = CreateButton(QStringLiteral("&R")); + m_classic_zl_button = CreateButton(QStringLiteral("&ZL")); + m_classic_zr_button = CreateButton(QStringLiteral("ZR")); + m_classic_plus_button = CreateButton(QStringLiteral("&+")); + m_classic_minus_button = CreateButton(QStringLiteral("&-")); + m_classic_home_button = CreateButton(QStringLiteral("&HOME")); + m_classic_left_button = CreateButton(QStringLiteral("L&eft")); + m_classic_up_button = CreateButton(QStringLiteral("&Up")); + m_classic_down_button = CreateButton(QStringLiteral("&Down")); + m_classic_right_button = CreateButton(QStringLiteral("R&ight")); auto* classic_buttons_layout = new QGridLayout; classic_buttons_layout->addWidget(m_classic_a_button, 0, 0); @@ -248,7 +248,7 @@ WiiTASInputWindow::WiiTASInputWindow(QWidget* parent, int num) : TASInputWindow( layout->addWidget(m_remote_buttons_box); layout->addWidget(m_nunchuk_buttons_box); layout->addWidget(m_classic_buttons_box); - layout->addWidget(m_use_controller); + layout->addWidget(m_settings_box); setLayout(layout);