瀏覽代碼

UI: Relocate tas menu and add brief description

german77 5 年之前
父節點
當前提交
75d8ec1e9f

+ 1 - 1
src/input_common/main.cpp

@@ -117,7 +117,7 @@ struct InputSubsystem::Impl {
             Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "keyboard"}},
         };
         if (Settings::values.tas_enable) {
-            devices.push_back(
+            devices.emplace_back(
                 Common::ParamPackage{{"display", "TAS Controller"}, {"class", "tas"}});
         }
 #ifdef HAVE_SDL2

+ 56 - 32
src/input_common/tas/tas_input.cpp

@@ -40,12 +40,15 @@ constexpr std::array<std::pair<std::string_view, TasButton>, 20> text_to_tas_but
 
 Tas::Tas() {
     if (!Settings::values.tas_enable) {
+        needs_reset = true;
         return;
     }
     LoadTasFiles();
 }
 
-Tas::~Tas() = default;
+Tas::~Tas() {
+    Stop();
+};
 
 void Tas::LoadTasFiles() {
     script_length = 0;
@@ -184,6 +187,9 @@ std::string Tas::ButtonsToString(u32 button) const {
 
 void Tas::UpdateThread() {
     if (!Settings::values.tas_enable) {
+        if (is_running) {
+            Stop();
+        }
         return;
     }
 
@@ -196,34 +202,35 @@ void Tas::UpdateThread() {
         LoadTasFiles();
         LOG_DEBUG(Input, "tas_reset done");
     }
-    if (is_running) {
-        if (current_command < script_length) {
-            LOG_DEBUG(Input, "Playing TAS {}/{}", current_command, script_length);
-            size_t frame = current_command++;
-            for (size_t i = 0; i < commands.size(); i++) {
-                if (frame < commands[i].size()) {
-                    TASCommand command = commands[i][frame];
-                    tas_data[i].buttons = command.buttons;
-                    auto [l_axis_x, l_axis_y] = command.l_axis;
-                    tas_data[i].axis[0] = l_axis_x;
-                    tas_data[i].axis[1] = l_axis_y;
-                    auto [r_axis_x, r_axis_y] = command.r_axis;
-                    tas_data[i].axis[2] = r_axis_x;
-                    tas_data[i].axis[3] = r_axis_y;
-                } else {
-                    tas_data[i] = {};
-                }
-            }
-        } else {
-            is_running = Settings::values.tas_loop.GetValue();
-            current_command = 0;
-            tas_data.fill({});
-            if (!is_running) {
-                SwapToStoredController();
+
+    if (!is_running) {
+        tas_data.fill({});
+        return;
+    }
+    if (current_command < script_length) {
+        LOG_DEBUG(Input, "Playing TAS {}/{}", current_command, script_length);
+        size_t frame = current_command++;
+        for (size_t i = 0; i < commands.size(); i++) {
+            if (frame < commands[i].size()) {
+                TASCommand command = commands[i][frame];
+                tas_data[i].buttons = command.buttons;
+                auto [l_axis_x, l_axis_y] = command.l_axis;
+                tas_data[i].axis[0] = l_axis_x;
+                tas_data[i].axis[1] = l_axis_y;
+                auto [r_axis_x, r_axis_y] = command.r_axis;
+                tas_data[i].axis[2] = r_axis_x;
+                tas_data[i].axis[3] = r_axis_y;
+            } else {
+                tas_data[i] = {};
             }
         }
     } else {
+        is_running = Settings::values.tas_loop.GetValue();
+        current_command = 0;
         tas_data.fill({});
+        if (!is_running) {
+            SwapToStoredController();
+        }
     }
     LOG_DEBUG(Input, "TAS inputs: {}", DebugInputs(tas_data));
 }
@@ -237,8 +244,8 @@ TasAnalog Tas::ReadCommandAxis(const std::string& line) const {
         seglist.push_back(segment);
     }
 
-    const float x = std::stof(seglist.at(0)) / 32767.f;
-    const float y = std::stof(seglist.at(1)) / 32767.f;
+    const float x = std::stof(seglist.at(0)) / 32767.0f;
+    const float y = std::stof(seglist.at(1)) / 32767.0f;
 
     return {x, y};
 }
@@ -293,14 +300,22 @@ std::string Tas::WriteCommandButtons(u32 data) const {
 }
 
 void Tas::StartStop() {
-    is_running = !is_running;
+    if (!Settings::values.tas_enable) {
+        return;
+    }
     if (is_running) {
-        SwapToTasController();
+        Stop();
     } else {
-        SwapToStoredController();
+        is_running = true;
+        SwapToTasController();
     }
 }
 
+void Tas::Stop() {
+    is_running = false;
+    SwapToStoredController();
+}
+
 void Tas::SwapToTasController() {
     if (!Settings::values.tas_swap_controllers) {
         return;
@@ -315,7 +330,8 @@ void Tas::SwapToTasController() {
             continue;
         }
 
-        auto tas_param = Common::ParamPackage{{"pad", static_cast<u8>(index)}};
+        Common::ParamPackage tas_param;
+        tas_param.Set("pad", static_cast<u8>(index));
         auto button_mapping = GetButtonMappingForDevice(tas_param);
         auto analog_mapping = GetAnalogMappingForDevice(tas_param);
         auto& buttons = player.buttons;
@@ -328,25 +344,33 @@ void Tas::SwapToTasController() {
             analogs[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)].Serialize();
         }
     }
+    is_old_input_saved = true;
     Settings::values.is_device_reload_pending.store(true);
 }
 
 void Tas::SwapToStoredController() {
-    if (!Settings::values.tas_swap_controllers) {
+    if (!is_old_input_saved) {
         return;
     }
     auto& players = Settings::values.players.GetValue();
     for (std::size_t index = 0; index < players.size(); index++) {
         players[index] = player_mappings[index];
     }
+    is_old_input_saved = false;
     Settings::values.is_device_reload_pending.store(true);
 }
 
 void Tas::Reset() {
+    if (!Settings::values.tas_enable) {
+        return;
+    }
     needs_reset = true;
 }
 
 bool Tas::Record() {
+    if (!Settings::values.tas_enable) {
+        return true;
+    }
     is_recording = !is_recording;
     return is_recording;
 }

+ 26 - 22
src/input_common/tas/tas_input.h

@@ -13,8 +13,8 @@
 
 /*
 To play back TAS scripts on Yuzu, select the folder with scripts in the configuration menu below
-Emulation -> Configure TAS. The file itself has normal text format and has to be called
-script0-1.txt for controller 1, script0-2.txt for controller 2 and so forth (with max. 8 players).
+Tools -> Configure TAS. The file itself has normal text format and has to be called script0-1.txt
+for controller 1, script0-2.txt for controller 2 and so forth (with max. 8 players).
 
 A script file has the same format as TAS-nx uses, so final files will look like this:
 
@@ -56,26 +56,26 @@ enum class TasState {
 };
 
 enum class TasButton : u32 {
-    BUTTON_A = 0x000001,
-    BUTTON_B = 0x000002,
-    BUTTON_X = 0x000004,
-    BUTTON_Y = 0x000008,
-    STICK_L = 0x000010,
-    STICK_R = 0x000020,
-    TRIGGER_L = 0x000040,
-    TRIGGER_R = 0x000080,
-    TRIGGER_ZL = 0x000100,
-    TRIGGER_ZR = 0x000200,
-    BUTTON_PLUS = 0x000400,
-    BUTTON_MINUS = 0x000800,
-    BUTTON_LEFT = 0x001000,
-    BUTTON_UP = 0x002000,
-    BUTTON_RIGHT = 0x004000,
-    BUTTON_DOWN = 0x008000,
-    BUTTON_SL = 0x010000,
-    BUTTON_SR = 0x020000,
-    BUTTON_HOME = 0x040000,
-    BUTTON_CAPTURE = 0x080000,
+    BUTTON_A = 1U << 0,
+    BUTTON_B = 1U << 1,
+    BUTTON_X = 1U << 2,
+    BUTTON_Y = 1U << 3,
+    STICK_L = 1U << 4,
+    STICK_R = 1U << 5,
+    TRIGGER_L = 1U << 6,
+    TRIGGER_R = 1U << 7,
+    TRIGGER_ZL = 1U << 8,
+    TRIGGER_ZR = 1U << 9,
+    BUTTON_PLUS = 1U << 10,
+    BUTTON_MINUS = 1U << 11,
+    BUTTON_LEFT = 1U << 12,
+    BUTTON_UP = 1U << 13,
+    BUTTON_RIGHT = 1U << 14,
+    BUTTON_DOWN = 1U << 15,
+    BUTTON_SL = 1U << 16,
+    BUTTON_SR = 1U << 17,
+    BUTTON_HOME = 1U << 18,
+    BUTTON_CAPTURE = 1U << 19,
 };
 
 enum class TasAxes : u8 {
@@ -105,6 +105,9 @@ public:
     //  Sets the flag to start or stop the TAS command excecution and swaps controllers profiles
     void StartStop();
 
+    //  Stop the TAS and reverts any controller profile
+    void Stop();
+
     // Sets the flag to reload the file and start from the begining in the next update
     void Reset();
 
@@ -219,6 +222,7 @@ private:
 
     size_t script_length{0};
     std::array<TasData, PLAYER_NUMBER> tas_data;
+    bool is_old_input_saved{false};
     bool is_recording{false};
     bool is_running{false};
     bool needs_reset{false};

+ 1 - 1
src/yuzu/configuration/configure_tas.cpp

@@ -55,7 +55,7 @@ void ConfigureTasDialog::SetDirectory(DirectoryTarget target, QLineEdit* edit) {
 
     QString str = QFileDialog::getExistingDirectory(this, caption, edit->text());
 
-    if (str.isNull() || str.isEmpty()) {
+    if (str.isEmpty()) {
         return;
     }
 

+ 45 - 2
src/yuzu/configuration/configure_tas.ui

@@ -19,7 +19,50 @@
           <item>
             <widget class="QGroupBox" name="groupBox">
               <property name="title">
-                <string>TAS Settings</string>
+                <string>TAS</string>
+              </property>
+              <layout class="QGridLayout" name="gridLayout">
+                <item row="0" column="0" colspan="1">
+                  <widget class="QLabel" name="label_1">
+                    <property name="text">
+                      <string>Reads controller input from scripts in the same format as TAS-nx scripts. For a more detailed explanation please consult the FAQ on the yuzu website.</string>
+                    </property>
+                    <property name="wordWrap">
+                      <bool>true</bool>
+                    </property>
+                  </widget>
+                </item>
+                <item row="1" column="0" colspan="1">
+                  <widget class="QLabel" name="label_2">
+                    <property name="text">
+                      <string>To check which hotkeys control the playback/recording, please refer to the Hotkey settings (General -> Hotkeys).</string>
+                    </property>
+                    <property name="wordWrap">
+                      <bool>true</bool>
+                    </property>
+                  </widget>
+                </item>
+                <item row="2" column="0" colspan="1">
+                  <widget class="QLabel" name="label_2">
+                    <property name="text">
+                      <string>WARNING: This is an experimental feature. It will not play back scripts frame perfectly with the current, imperfect syncing method.</string>
+                    </property>
+                    <property name="wordWrap">
+                      <bool>true</bool>
+                    </property>
+                  </widget>
+                </item>
+              </layout>
+            </widget>
+          </item>
+        </layout>
+      </item>
+      <item>
+        <layout class="QHBoxLayout" name="horizontalLayout">
+          <item>
+            <widget class="QGroupBox" name="groupBox">
+              <property name="title">
+                <string>Settings</string>
               </property>
               <layout class="QGridLayout" name="gridLayout">
                 <item row="0" column="0" colspan="4">
@@ -63,7 +106,7 @@
           <item>
             <widget class="QGroupBox" name="groupBox">
               <property name="title">
-                <string>TAS Directories</string>
+                <string>Script Directory</string>
               </property>
               <layout class="QGridLayout" name="gridLayout">
                 <item row="0" column="0">

+ 1 - 1
src/yuzu/configuration/configure_vibration.cpp

@@ -99,7 +99,7 @@ void ConfigureVibration::SetVibrationDevices(std::size_t player_index) {
         const auto guid = param.Get("guid", "");
         const auto port = param.Get("port", "");
 
-        if (engine.empty() || engine == "keyboard" || engine == "mouse") {
+        if (engine.empty() || engine == "keyboard" || engine == "mouse" || engine == "tas") {
             continue;
         }
 

+ 1 - 1
src/yuzu/debugger/controller.cpp

@@ -78,7 +78,7 @@ void ControllerDialog::InputController(ControllerInput input) {
     u32 buttons = 0;
     int index = 0;
     for (bool btn : input.button_values) {
-        buttons += (btn ? 1 : 0) << index;
+        buttons |= (btn ? 1U : 0U) << index;
         index++;
     }
     input_subsystem->GetTas()->RecordInput(buttons, input.axis_values);

+ 15 - 6
src/yuzu/main.cpp

@@ -1022,18 +1022,25 @@ void GMainWindow::InitializeHotkeys() {
                 }
             });
     connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("TAS Start/Stop"), this),
-            &QShortcut::activated, this, [&] { input_subsystem->GetTas()->StartStop(); });
+            &QShortcut::activated, this, [&] {
+                if (!emulation_running) {
+                    return;
+                }
+                input_subsystem->GetTas()->StartStop();
+            });
     connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("TAS Reset"), this),
             &QShortcut::activated, this, [&] { input_subsystem->GetTas()->Reset(); });
     connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("TAS Record"), this),
             &QShortcut::activated, this, [&] {
+                if (!emulation_running) {
+                    return;
+                }
                 bool is_recording = input_subsystem->GetTas()->Record();
                 if (!is_recording) {
-                    QMessageBox::StandardButton reply;
-                    reply = QMessageBox::question(this, tr("TAS Recording"),
-                                                  tr("Overwrite file of player 1?"),
-                                                  QMessageBox::Yes | QMessageBox::No);
-                    input_subsystem->GetTas()->SaveRecording(reply == QMessageBox::Yes);
+                    const auto res = QMessageBox::question(this, tr("TAS Recording"),
+                                                           tr("Overwrite file of player 1?"),
+                                                           QMessageBox::Yes | QMessageBox::No);
+                    input_subsystem->GetTas()->SaveRecording(res == QMessageBox::Yes);
                 }
             });
 }
@@ -1487,6 +1494,8 @@ void GMainWindow::ShutdownGame() {
         game_list->show();
     }
     game_list->SetFilterFocus();
+    tas_label->clear();
+    input_subsystem->GetTas()->Stop();
 
     render_window->removeEventFilter(render_window);
     render_window->setAttribute(Qt::WA_Hover, false);

+ 1 - 1
src/yuzu/main.h

@@ -320,7 +320,7 @@ private:
     QLabel* emu_speed_label = nullptr;
     QLabel* game_fps_label = nullptr;
     QLabel* emu_frametime_label = nullptr;
-    QLabel* TASlabel;
+    QLabel* tas_label = nullptr;
     QPushButton* gpu_accuracy_button = nullptr;
     QPushButton* renderer_status_button = nullptr;
     QPushButton* dock_status_button = nullptr;

+ 1 - 1
src/yuzu/main.ui

@@ -72,7 +72,6 @@
     <addaction name="action_Restart"/>
     <addaction name="separator"/>
     <addaction name="action_Configure"/>
-    <addaction name="action_Configure_Tas"/>
     <addaction name="action_Configure_Current_Game"/>
    </widget>
    <widget class="QMenu" name="menu_View">
@@ -101,6 +100,7 @@
     <addaction name="action_Rederive"/>
     <addaction name="separator"/>
     <addaction name="action_Capture_Screenshot"/>
+    <addaction name="action_Configure_Tas"/>
    </widget>
    <widget class="QMenu" name="menu_Help">
     <property name="title">