summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/input_common/main.cpp2
-rw-r--r--src/input_common/tas/tas_input.cpp88
-rw-r--r--src/input_common/tas/tas_input.h48
-rw-r--r--src/yuzu/configuration/configure_tas.cpp2
-rw-r--r--src/yuzu/configuration/configure_tas.ui47
-rw-r--r--src/yuzu/configuration/configure_vibration.cpp2
-rw-r--r--src/yuzu/debugger/controller.cpp2
-rw-r--r--src/yuzu/main.cpp21
-rw-r--r--src/yuzu/main.h2
-rw-r--r--src/yuzu/main.ui2
10 files changed, 148 insertions, 68 deletions
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index 3b9906b53..18d7d8817 100644
--- a/src/input_common/main.cpp
+++ b/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
diff --git a/src/input_common/tas/tas_input.cpp b/src/input_common/tas/tas_input.cpp
index 877d35088..1598092b6 100644
--- a/src/input_common/tas/tas_input.cpp
+++ b/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;
}
diff --git a/src/input_common/tas/tas_input.h b/src/input_common/tas/tas_input.h
index 52d000db4..3e2db8f00 100644
--- a/src/input_common/tas/tas_input.h
+++ b/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};
diff --git a/src/yuzu/configuration/configure_tas.cpp b/src/yuzu/configuration/configure_tas.cpp
index 00d6c1ba5..b666b175a 100644
--- a/src/yuzu/configuration/configure_tas.cpp
+++ b/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;
}
diff --git a/src/yuzu/configuration/configure_tas.ui b/src/yuzu/configuration/configure_tas.ui
index 445904d8f..8a3ecb834 100644
--- a/src/yuzu/configuration/configure_tas.ui
+++ b/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">
diff --git a/src/yuzu/configuration/configure_vibration.cpp b/src/yuzu/configuration/configure_vibration.cpp
index 9d92c4949..46a0f3025 100644
--- a/src/yuzu/configuration/configure_vibration.cpp
+++ b/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;
}
diff --git a/src/yuzu/debugger/controller.cpp b/src/yuzu/debugger/controller.cpp
index 296000ed5..5a844409b 100644
--- a/src/yuzu/debugger/controller.cpp
+++ b/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);
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index ea77caad5..3c2824362 100644
--- a/src/yuzu/main.cpp
+++ b/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);
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 610b59ee5..36eed6103 100644
--- a/src/yuzu/main.h
+++ b/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;
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 31c1a20f3..653c010d8 100644
--- a/src/yuzu/main.ui
+++ b/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">