UI: Relocate tas menu and add brief description

This commit is contained in:
german77 2021-07-25 20:52:19 -05:00 committed by MonsterDruide1
parent 5401cf6eb5
commit 75d8ec1e9f
10 changed files with 150 additions and 70 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -19,7 +19,50 @@
<item> <item>
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox">
<property name="title"> <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> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" colspan="4"> <item row="0" column="0" colspan="4">
@ -63,7 +106,7 @@
<item> <item>
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox">
<property name="title"> <property name="title">
<string>TAS Directories</string> <string>Script Directory</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QGridLayout" name="gridLayout">
<item row="0" column="0"> <item row="0" column="0">

View File

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

View File

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

View File

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

View File

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

View File

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