mirror of
https://github.com/BanjoRecomp/BanjoRecomp
synced 2026-06-10 04:15:43 -04:00
Initial support for multiple controllers. (#3)
* Support for multiple controllers. * Fix default bindings. * Backwards compatibility with controls from previous version.
This commit is contained in:
@@ -15,9 +15,9 @@ namespace banjo {
|
||||
void save_config();
|
||||
|
||||
void reset_input_bindings();
|
||||
void reset_cont_input_bindings();
|
||||
void reset_kb_input_bindings();
|
||||
void reset_single_input_binding(recomp::InputDevice device, recomp::GameInput input);
|
||||
void reset_cont_input_bindings(int controller_num);
|
||||
void reset_kb_input_bindings(int controller_num);
|
||||
void reset_single_input_binding(int controller_num, recomp::InputDevice device, recomp::GameInput input);
|
||||
|
||||
std::filesystem::path get_app_folder_path();
|
||||
|
||||
|
||||
+30
-7
@@ -70,13 +70,13 @@ namespace recomp {
|
||||
};
|
||||
|
||||
void poll_inputs();
|
||||
float get_input_analog(const InputField& field);
|
||||
float get_input_analog(const std::span<const recomp::InputField> fields);
|
||||
bool get_input_digital(const InputField& field);
|
||||
bool get_input_digital(const std::span<const recomp::InputField> fields);
|
||||
float get_input_analog(int controller_num, const InputField& field);
|
||||
float get_input_analog(int controller_num, const std::span<const recomp::InputField> fields);
|
||||
bool get_input_digital(int controller_num, const InputField& field);
|
||||
bool get_input_digital(int controller_num, const std::span<const recomp::InputField> fields);
|
||||
void get_gyro_deltas(float* x, float* y);
|
||||
void get_mouse_deltas(float* x, float* y);
|
||||
void get_right_analog(float* x, float* y);
|
||||
void get_right_analog(int controller_num, float* x, float* y);
|
||||
|
||||
enum class InputDevice {
|
||||
Controller,
|
||||
@@ -157,8 +157,28 @@ namespace recomp {
|
||||
const std::string& get_input_name(GameInput input);
|
||||
const std::string& get_input_enum_name(GameInput input);
|
||||
GameInput get_input_from_enum_name(const std::string_view name);
|
||||
InputField& get_input_binding(GameInput input, size_t binding_index, InputDevice device);
|
||||
void set_input_binding(GameInput input, size_t binding_index, InputDevice device, InputField value);
|
||||
InputField& get_input_binding(int controller_num, GameInput input, size_t binding_index, InputDevice device);
|
||||
void set_input_binding(int controller_num, GameInput input, size_t binding_index, InputDevice device, InputField value);
|
||||
|
||||
struct ControllerGUID {
|
||||
std::string serial;
|
||||
int vendor{};
|
||||
int product{};
|
||||
int version{};
|
||||
int crc16{};
|
||||
int player_index{};
|
||||
};
|
||||
|
||||
void set_input_controller_guid(int controller_num, const ControllerGUID &guid);
|
||||
ControllerGUID get_input_controller_guid(int controller_num);
|
||||
|
||||
struct ControllerOption {
|
||||
std::string name;
|
||||
ControllerGUID guid;
|
||||
};
|
||||
|
||||
void refresh_controller_options();
|
||||
const std::vector<ControllerOption> &get_controller_options();
|
||||
|
||||
bool get_n64_input(int controller_num, uint16_t* buttons_out, float* x_out, float* y_out);
|
||||
void set_rumble(int controller_num, bool);
|
||||
@@ -195,6 +215,9 @@ namespace recomp {
|
||||
BackgroundInputMode get_background_input_mode();
|
||||
void set_background_input_mode(BackgroundInputMode mode);
|
||||
|
||||
bool get_single_controller_mode();
|
||||
void set_single_controller_mode(bool single_controller);
|
||||
|
||||
bool game_input_disabled();
|
||||
bool all_input_disabled();
|
||||
}
|
||||
|
||||
+106
-50
@@ -129,6 +129,19 @@ namespace recomp {
|
||||
j.at("input_type").get_to(field.input_type);
|
||||
j.at("input_id").get_to(field.input_id);
|
||||
}
|
||||
|
||||
void to_json(json& j, const ControllerGUID& guid) {
|
||||
j = json{ {"serial", guid.serial}, {"vendor", guid.vendor}, {"product", guid.product}, {"version", guid.version}, {"crc16", guid.crc16}, {"player_index", guid.player_index} };
|
||||
}
|
||||
|
||||
void from_json(const json& j, ControllerGUID& guid) {
|
||||
j.at("serial").get_to(guid.serial);
|
||||
j.at("vendor").get_to(guid.vendor);
|
||||
j.at("product").get_to(guid.product);
|
||||
j.at("version").get_to(guid.version);
|
||||
j.at("crc16").get_to(guid.crc16);
|
||||
j.at("player_index").get_to(guid.player_index);
|
||||
}
|
||||
}
|
||||
|
||||
std::filesystem::path banjo::get_app_folder_path() {
|
||||
@@ -266,64 +279,68 @@ bool load_general_config(const std::filesystem::path& path) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void assign_mapping(recomp::InputDevice device, recomp::GameInput input, const std::vector<recomp::InputField>& value) {
|
||||
void assign_mapping(int controller_num, recomp::InputDevice device, recomp::GameInput input, const std::vector<recomp::InputField>& value) {
|
||||
for (size_t binding_index = 0; binding_index < std::min(value.size(), recomp::bindings_per_input); binding_index++) {
|
||||
recomp::set_input_binding(input, binding_index, device, value[binding_index]);
|
||||
recomp::set_input_binding(controller_num, input, binding_index, device, value[binding_index]);
|
||||
}
|
||||
};
|
||||
|
||||
// same as assign_mapping, except will clear unassigned bindings if not in value
|
||||
void assign_mapping_complete(recomp::InputDevice device, recomp::GameInput input, const std::vector<recomp::InputField>& value) {
|
||||
void assign_mapping_complete(int controller_num, recomp::InputDevice device, recomp::GameInput input, const std::vector<recomp::InputField>& value) {
|
||||
for (size_t binding_index = 0; binding_index < recomp::bindings_per_input; binding_index++) {
|
||||
if (binding_index >= value.size()) {
|
||||
recomp::set_input_binding(input, binding_index, device, recomp::InputField{});
|
||||
recomp::set_input_binding(controller_num, input, binding_index, device, recomp::InputField{});
|
||||
} else {
|
||||
recomp::set_input_binding(input, binding_index, device, value[binding_index]);
|
||||
recomp::set_input_binding(controller_num, input, binding_index, device, value[binding_index]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void assign_all_mappings(recomp::InputDevice device, const recomp::DefaultN64Mappings& values) {
|
||||
assign_mapping_complete(device, recomp::GameInput::A, values.a);
|
||||
assign_mapping_complete(device, recomp::GameInput::B, values.b);
|
||||
assign_mapping_complete(device, recomp::GameInput::Z, values.z);
|
||||
assign_mapping_complete(device, recomp::GameInput::START, values.start);
|
||||
assign_mapping_complete(device, recomp::GameInput::DPAD_UP, values.dpad_up);
|
||||
assign_mapping_complete(device, recomp::GameInput::DPAD_DOWN, values.dpad_down);
|
||||
assign_mapping_complete(device, recomp::GameInput::DPAD_LEFT, values.dpad_left);
|
||||
assign_mapping_complete(device, recomp::GameInput::DPAD_RIGHT, values.dpad_right);
|
||||
assign_mapping_complete(device, recomp::GameInput::L, values.l);
|
||||
assign_mapping_complete(device, recomp::GameInput::R, values.r);
|
||||
assign_mapping_complete(device, recomp::GameInput::C_UP, values.c_up);
|
||||
assign_mapping_complete(device, recomp::GameInput::C_DOWN, values.c_down);
|
||||
assign_mapping_complete(device, recomp::GameInput::C_LEFT, values.c_left);
|
||||
assign_mapping_complete(device, recomp::GameInput::C_RIGHT, values.c_right);
|
||||
void assign_all_mappings(int controller_num, recomp::InputDevice device, const recomp::DefaultN64Mappings& values) {
|
||||
assign_mapping_complete(controller_num, device, recomp::GameInput::A, values.a);
|
||||
assign_mapping_complete(controller_num, device, recomp::GameInput::B, values.b);
|
||||
assign_mapping_complete(controller_num, device, recomp::GameInput::Z, values.z);
|
||||
assign_mapping_complete(controller_num, device, recomp::GameInput::START, values.start);
|
||||
assign_mapping_complete(controller_num, device, recomp::GameInput::DPAD_UP, values.dpad_up);
|
||||
assign_mapping_complete(controller_num, device, recomp::GameInput::DPAD_DOWN, values.dpad_down);
|
||||
assign_mapping_complete(controller_num, device, recomp::GameInput::DPAD_LEFT, values.dpad_left);
|
||||
assign_mapping_complete(controller_num, device, recomp::GameInput::DPAD_RIGHT, values.dpad_right);
|
||||
assign_mapping_complete(controller_num, device, recomp::GameInput::L, values.l);
|
||||
assign_mapping_complete(controller_num, device, recomp::GameInput::R, values.r);
|
||||
assign_mapping_complete(controller_num, device, recomp::GameInput::C_UP, values.c_up);
|
||||
assign_mapping_complete(controller_num, device, recomp::GameInput::C_DOWN, values.c_down);
|
||||
assign_mapping_complete(controller_num, device, recomp::GameInput::C_LEFT, values.c_left);
|
||||
assign_mapping_complete(controller_num, device, recomp::GameInput::C_RIGHT, values.c_right);
|
||||
|
||||
assign_mapping_complete(device, recomp::GameInput::X_AXIS_NEG, values.analog_left);
|
||||
assign_mapping_complete(device, recomp::GameInput::X_AXIS_POS, values.analog_right);
|
||||
assign_mapping_complete(device, recomp::GameInput::Y_AXIS_NEG, values.analog_down);
|
||||
assign_mapping_complete(device, recomp::GameInput::Y_AXIS_POS, values.analog_up);
|
||||
assign_mapping_complete(controller_num, device, recomp::GameInput::X_AXIS_NEG, values.analog_left);
|
||||
assign_mapping_complete(controller_num, device, recomp::GameInput::X_AXIS_POS, values.analog_right);
|
||||
assign_mapping_complete(controller_num, device, recomp::GameInput::Y_AXIS_NEG, values.analog_down);
|
||||
assign_mapping_complete(controller_num, device, recomp::GameInput::Y_AXIS_POS, values.analog_up);
|
||||
|
||||
assign_mapping_complete(device, recomp::GameInput::TOGGLE_MENU, values.toggle_menu);
|
||||
assign_mapping_complete(device, recomp::GameInput::ACCEPT_MENU, values.accept_menu);
|
||||
assign_mapping_complete(device, recomp::GameInput::APPLY_MENU, values.apply_menu);
|
||||
assign_mapping_complete(controller_num, device, recomp::GameInput::TOGGLE_MENU, values.toggle_menu);
|
||||
assign_mapping_complete(controller_num, device, recomp::GameInput::ACCEPT_MENU, values.accept_menu);
|
||||
assign_mapping_complete(controller_num, device, recomp::GameInput::APPLY_MENU, values.apply_menu);
|
||||
};
|
||||
|
||||
void banjo::reset_input_bindings() {
|
||||
assign_all_mappings(recomp::InputDevice::Keyboard, recomp::default_n64_keyboard_mappings);
|
||||
assign_all_mappings(recomp::InputDevice::Controller, recomp::default_n64_controller_mappings);
|
||||
assign_all_mappings(0, recomp::InputDevice::Keyboard, recomp::default_n64_keyboard_mappings);
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
assign_all_mappings(i, recomp::InputDevice::Controller, recomp::default_n64_controller_mappings);
|
||||
}
|
||||
}
|
||||
|
||||
void banjo::reset_cont_input_bindings() {
|
||||
assign_all_mappings(recomp::InputDevice::Controller, recomp::default_n64_controller_mappings);
|
||||
void banjo::reset_cont_input_bindings(int controller_num) {
|
||||
assign_all_mappings(controller_num, recomp::InputDevice::Controller, recomp::default_n64_controller_mappings);
|
||||
}
|
||||
|
||||
void banjo::reset_kb_input_bindings() {
|
||||
assign_all_mappings(recomp::InputDevice::Keyboard, recomp::default_n64_keyboard_mappings);
|
||||
void banjo::reset_kb_input_bindings(int controller_num) {
|
||||
assign_all_mappings(controller_num, recomp::InputDevice::Keyboard, recomp::default_n64_keyboard_mappings);
|
||||
}
|
||||
|
||||
void banjo::reset_single_input_binding(recomp::InputDevice device, recomp::GameInput input) {
|
||||
void banjo::reset_single_input_binding(int controller_num, recomp::InputDevice device, recomp::GameInput input) {
|
||||
assign_mapping_complete(
|
||||
controller_num,
|
||||
device,
|
||||
input,
|
||||
recomp::get_default_mapping_for_input(
|
||||
@@ -368,32 +385,39 @@ bool load_graphics_config(const std::filesystem::path& path) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void add_input_bindings(nlohmann::json& out, recomp::GameInput input, recomp::InputDevice device) {
|
||||
void add_input_bindings(nlohmann::json& out, int controller_num, recomp::GameInput input, recomp::InputDevice device) {
|
||||
const std::string& input_name = recomp::get_input_enum_name(input);
|
||||
nlohmann::json& out_array = out[input_name];
|
||||
out_array = nlohmann::json::array();
|
||||
for (size_t binding_index = 0; binding_index < recomp::bindings_per_input; binding_index++) {
|
||||
out_array[binding_index] = recomp::get_input_binding(input, binding_index, device);
|
||||
out_array[binding_index] = recomp::get_input_binding(controller_num, input, binding_index, device);
|
||||
}
|
||||
};
|
||||
|
||||
constexpr int controls_version = 2;
|
||||
|
||||
bool save_controls_config(const std::filesystem::path& path) {
|
||||
nlohmann::json config_json{};
|
||||
config_json["version"] = controls_version;
|
||||
config_json["players"] = std::vector<nlohmann::json>(4);
|
||||
for (size_t i = 0; i < config_json["players"].size(); i++) {
|
||||
nlohmann::json &player = config_json["players"][i];
|
||||
player["keyboard"] = {};
|
||||
player["controller"] = {};
|
||||
|
||||
config_json["keyboard"] = {};
|
||||
config_json["controller"] = {};
|
||||
for (size_t j = 0; j < recomp::get_num_inputs(); j++) {
|
||||
recomp::GameInput cur_input = static_cast<recomp::GameInput>(j);
|
||||
add_input_bindings(player["keyboard"], i, cur_input, recomp::InputDevice::Keyboard);
|
||||
add_input_bindings(player["controller"], i, cur_input, recomp::InputDevice::Controller);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < recomp::get_num_inputs(); i++) {
|
||||
recomp::GameInput cur_input = static_cast<recomp::GameInput>(i);
|
||||
|
||||
add_input_bindings(config_json["keyboard"], cur_input, recomp::InputDevice::Keyboard);
|
||||
add_input_bindings(config_json["controller"], cur_input, recomp::InputDevice::Controller);
|
||||
player["controller"]["guid"] = recomp::get_input_controller_guid(i);
|
||||
}
|
||||
|
||||
return save_json_with_backups(path, config_json);
|
||||
}
|
||||
|
||||
bool load_input_device_from_json(const nlohmann::json& config_json, recomp::InputDevice device, const std::string& key) {
|
||||
bool load_input_device_from_json(const nlohmann::json& config_json, int controller_num, recomp::InputDevice device, const std::string& key) {
|
||||
// Check if the json object for the given key exists.
|
||||
auto find_it = config_json.find(key);
|
||||
if (find_it == config_json.end()) {
|
||||
@@ -410,6 +434,7 @@ bool load_input_device_from_json(const nlohmann::json& config_json, recomp::Inpu
|
||||
auto find_input_it = mappings_json.find(input_name);
|
||||
if (find_input_it == mappings_json.end() || !find_input_it->is_array()) {
|
||||
assign_mapping(
|
||||
controller_num,
|
||||
device,
|
||||
cur_input,
|
||||
recomp::get_default_mapping_for_input(
|
||||
@@ -427,26 +452,57 @@ bool load_input_device_from_json(const nlohmann::json& config_json, recomp::Inpu
|
||||
for (size_t binding_index = 0; binding_index < std::min(recomp::bindings_per_input, input_json.size()); binding_index++) {
|
||||
recomp::InputField cur_field{};
|
||||
recomp::from_json(input_json[binding_index], cur_field);
|
||||
recomp::set_input_binding(cur_input, binding_index, device, cur_field);
|
||||
recomp::set_input_binding(controller_num, cur_input, binding_index, device, cur_field);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void load_bindings_config(const nlohmann::json &config_json, int controller_num) {
|
||||
if (!load_input_device_from_json(config_json, controller_num, recomp::InputDevice::Keyboard, "keyboard")) {
|
||||
assign_all_mappings(controller_num, recomp::InputDevice::Keyboard, recomp::default_n64_keyboard_mappings);
|
||||
}
|
||||
|
||||
if (!load_input_device_from_json(config_json, controller_num, recomp::InputDevice::Controller, "controller")) {
|
||||
assign_all_mappings(controller_num, recomp::InputDevice::Controller, recomp::default_n64_controller_mappings);
|
||||
}
|
||||
}
|
||||
|
||||
bool load_controls_config(const std::filesystem::path& path) {
|
||||
nlohmann::json config_json{};
|
||||
if (!read_json_with_backups(path, config_json)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!load_input_device_from_json(config_json, recomp::InputDevice::Keyboard, "keyboard")) {
|
||||
assign_all_mappings(recomp::InputDevice::Keyboard, recomp::default_n64_keyboard_mappings);
|
||||
auto version_it = config_json.find("version");
|
||||
if (version_it != config_json.end()) {
|
||||
auto players_it = config_json.find("players");
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
const bool player_exists = players_it != config_json.end() && players_it->is_array() && (players_it->size() > i);
|
||||
const nlohmann::json &player = player_exists ? (*players_it)[i] : nlohmann::json();
|
||||
load_bindings_config(player, (int)(i));
|
||||
|
||||
auto controller_it = player.find("controller");
|
||||
if (controller_it != player.end()) {
|
||||
const nlohmann::json &controller = *controller_it;
|
||||
auto guid_it = controller.find("guid");
|
||||
if (guid_it != controller.end()) {
|
||||
recomp::set_input_controller_guid((int)(i), *guid_it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Version 1 of the format only had bindings for Player 1 on the root element.
|
||||
load_bindings_config(config_json, 0);
|
||||
|
||||
for (int i = 1; i < 4; i++) {
|
||||
// Assign defaults for every other controller.
|
||||
assign_all_mappings(i, recomp::InputDevice::Controller, recomp::default_n64_controller_mappings);
|
||||
}
|
||||
}
|
||||
|
||||
if (!load_input_device_from_json(config_json, recomp::InputDevice::Controller, "controller")) {
|
||||
assign_all_mappings(recomp::InputDevice::Controller, recomp::default_n64_controller_mappings);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+25
-21
@@ -7,8 +7,9 @@
|
||||
// Arrays that hold the mappings for every input for keyboard and controller respectively.
|
||||
using input_mapping = std::array<recomp::InputField, recomp::bindings_per_input>;
|
||||
using input_mapping_array = std::array<input_mapping, static_cast<size_t>(recomp::GameInput::COUNT)>;
|
||||
static input_mapping_array keyboard_input_mappings{};
|
||||
static input_mapping_array controller_input_mappings{};
|
||||
static std::array<input_mapping_array, 4> keyboard_input_mappings{};
|
||||
static std::array<input_mapping_array, 4> controller_input_mappings{};
|
||||
static std::array<recomp::ControllerGUID, 4> controller_guids{};
|
||||
|
||||
// Make the button value array, which maps a button index to its bit field.
|
||||
#define DEFINE_INPUT(name, value, readable) uint16_t(value##u),
|
||||
@@ -53,8 +54,8 @@ recomp::GameInput recomp::get_input_from_enum_name(const std::string_view enum_n
|
||||
}
|
||||
|
||||
// Due to an RmlUi limitation this can't be const. Ideally it would return a const reference or even just a straight up copy.
|
||||
recomp::InputField& recomp::get_input_binding(GameInput input, size_t binding_index, recomp::InputDevice device) {
|
||||
input_mapping_array& device_mappings = (device == recomp::InputDevice::Controller) ? controller_input_mappings : keyboard_input_mappings;
|
||||
recomp::InputField& recomp::get_input_binding(int controller_num, GameInput input, size_t binding_index, recomp::InputDevice device) {
|
||||
input_mapping_array& device_mappings = (device == recomp::InputDevice::Controller) ? controller_input_mappings[controller_num] : keyboard_input_mappings[controller_num];
|
||||
input_mapping& cur_input_mapping = device_mappings.at(static_cast<size_t>(input));
|
||||
|
||||
if (binding_index < cur_input_mapping.size()) {
|
||||
@@ -66,8 +67,8 @@ recomp::InputField& recomp::get_input_binding(GameInput input, size_t binding_in
|
||||
}
|
||||
}
|
||||
|
||||
void recomp::set_input_binding(recomp::GameInput input, size_t binding_index, recomp::InputDevice device, recomp::InputField value) {
|
||||
input_mapping_array& device_mappings = (device == recomp::InputDevice::Controller) ? controller_input_mappings : keyboard_input_mappings;
|
||||
void recomp::set_input_binding(int controller_num, recomp::GameInput input, size_t binding_index, recomp::InputDevice device, recomp::InputField value) {
|
||||
input_mapping_array &device_mappings = (device == recomp::InputDevice::Controller) ? controller_input_mappings[controller_num] : keyboard_input_mappings[controller_num];
|
||||
input_mapping& cur_input_mapping = device_mappings.at(static_cast<size_t>(input));
|
||||
|
||||
if (binding_index < cur_input_mapping.size()) {
|
||||
@@ -75,37 +76,40 @@ void recomp::set_input_binding(recomp::GameInput input, size_t binding_index, re
|
||||
}
|
||||
}
|
||||
|
||||
void recomp::set_input_controller_guid(int controller_num, const ControllerGUID &guid) {
|
||||
controller_guids[controller_num] = guid;
|
||||
}
|
||||
|
||||
recomp::ControllerGUID recomp::get_input_controller_guid(int controller_num) {
|
||||
return controller_guids[controller_num];
|
||||
}
|
||||
|
||||
bool recomp::get_n64_input(int controller_num, uint16_t* buttons_out, float* x_out, float* y_out) {
|
||||
uint16_t cur_buttons = 0;
|
||||
float cur_x = 0.0f;
|
||||
float cur_y = 0.0f;
|
||||
|
||||
if (controller_num != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!recomp::game_input_disabled()) {
|
||||
for (size_t i = 0; i < n64_button_values.size(); i++) {
|
||||
size_t input_index = (size_t)GameInput::N64_BUTTON_START + i;
|
||||
cur_buttons |= recomp::get_input_digital(keyboard_input_mappings[input_index]) ? n64_button_values[i] : 0;
|
||||
cur_buttons |= recomp::get_input_digital(controller_input_mappings[input_index]) ? n64_button_values[i] : 0;
|
||||
cur_buttons |= recomp::get_input_digital(controller_num, keyboard_input_mappings[controller_num][input_index]) ? n64_button_values[i] : 0;
|
||||
cur_buttons |= recomp::get_input_digital(controller_num, controller_input_mappings[controller_num][input_index]) ? n64_button_values[i] : 0;
|
||||
}
|
||||
|
||||
float joystick_deadzone = recomp::get_joystick_deadzone() / 100.0f;
|
||||
|
||||
float joystick_x = recomp::get_input_analog(controller_input_mappings[(size_t)GameInput::X_AXIS_POS])
|
||||
- recomp::get_input_analog(controller_input_mappings[(size_t)GameInput::X_AXIS_NEG]);
|
||||
float joystick_x = recomp::get_input_analog(controller_num, controller_input_mappings[controller_num][(size_t)GameInput::X_AXIS_POS])
|
||||
- recomp::get_input_analog(controller_num, controller_input_mappings[controller_num][(size_t)GameInput::X_AXIS_NEG]);
|
||||
|
||||
float joystick_y = recomp::get_input_analog(controller_input_mappings[(size_t)GameInput::Y_AXIS_POS])
|
||||
- recomp::get_input_analog(controller_input_mappings[(size_t)GameInput::Y_AXIS_NEG]);
|
||||
float joystick_y = recomp::get_input_analog(controller_num, controller_input_mappings[controller_num][(size_t)GameInput::Y_AXIS_POS])
|
||||
- recomp::get_input_analog(controller_num, controller_input_mappings[controller_num][(size_t)GameInput::Y_AXIS_NEG]);
|
||||
|
||||
recomp::apply_joystick_deadzone(joystick_x, joystick_y, &joystick_x, &joystick_y);
|
||||
|
||||
cur_x = recomp::get_input_analog(keyboard_input_mappings[(size_t)GameInput::X_AXIS_POS])
|
||||
- recomp::get_input_analog(keyboard_input_mappings[(size_t)GameInput::X_AXIS_NEG]) + joystick_x;
|
||||
cur_x = recomp::get_input_analog(controller_num, keyboard_input_mappings[controller_num][(size_t)GameInput::X_AXIS_POS])
|
||||
- recomp::get_input_analog(controller_num, keyboard_input_mappings[controller_num][(size_t)GameInput::X_AXIS_NEG]) + joystick_x;
|
||||
|
||||
cur_y = recomp::get_input_analog(keyboard_input_mappings[(size_t)GameInput::Y_AXIS_POS])
|
||||
- recomp::get_input_analog(keyboard_input_mappings[(size_t)GameInput::Y_AXIS_NEG]) + joystick_y;
|
||||
cur_y = recomp::get_input_analog(controller_num, keyboard_input_mappings[controller_num][(size_t)GameInput::Y_AXIS_POS])
|
||||
- recomp::get_input_analog(controller_num, keyboard_input_mappings[controller_num][(size_t)GameInput::Y_AXIS_NEG]) + joystick_y;
|
||||
}
|
||||
|
||||
*buttons_out = cur_buttons;
|
||||
|
||||
+175
-52
@@ -28,9 +28,12 @@ static struct {
|
||||
SDL_Keymod keymod = SDL_Keymod::KMOD_NONE;
|
||||
int numkeys = 0;
|
||||
std::atomic_int32_t mouse_wheel_pos = 0;
|
||||
std::mutex cur_controllers_mutex;
|
||||
std::vector<SDL_GameController*> cur_controllers{};
|
||||
std::mutex controllers_mutex;
|
||||
std::vector<SDL_GameController*> detected_controllers{};
|
||||
std::vector<recomp::ControllerOption> detected_controller_options{};
|
||||
std::array<SDL_GameController*, 4> assigned_controllers{}; // Only used when Multiplayer is enabled.
|
||||
std::unordered_map<SDL_JoystickID, ControllerState> controller_states;
|
||||
bool single_controller = true;
|
||||
|
||||
std::array<float, 2> rotation_delta{};
|
||||
std::array<float, 2> mouse_delta{};
|
||||
@@ -38,8 +41,8 @@ static struct {
|
||||
std::array<float, 2> pending_rotation_delta{};
|
||||
std::array<float, 2> pending_mouse_delta{};
|
||||
|
||||
float cur_rumble;
|
||||
bool rumble_active;
|
||||
std::array<float, 4> cur_rumble{};
|
||||
std::array<bool, 4> rumble_active{};
|
||||
} InputState;
|
||||
|
||||
static struct {
|
||||
@@ -175,8 +178,9 @@ bool sdl_event_filter(void* userdata, SDL_Event* event) {
|
||||
break;
|
||||
case SDL_EventType::SDL_CONTROLLERBUTTONDOWN:
|
||||
if (scanning_device != recomp::InputDevice::COUNT) {
|
||||
auto menuToggleBinding0 = recomp::get_input_binding(recomp::GameInput::TOGGLE_MENU, 0, recomp::InputDevice::Controller);
|
||||
auto menuToggleBinding1 = recomp::get_input_binding(recomp::GameInput::TOGGLE_MENU, 1, recomp::InputDevice::Controller);
|
||||
// TODO: Needs the active controller tab index.
|
||||
auto menuToggleBinding0 = recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 0, recomp::InputDevice::Controller);
|
||||
auto menuToggleBinding1 = recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 1, recomp::InputDevice::Controller);
|
||||
// note - magic number: 0 is InputType::None
|
||||
if ((menuToggleBinding0.input_type != 0 && event->cbutton.button == menuToggleBinding0.input_id) ||
|
||||
(menuToggleBinding1.input_type != 0 && event->cbutton.button == menuToggleBinding1.input_id)) {
|
||||
@@ -476,14 +480,71 @@ void recomp::poll_inputs() {
|
||||
InputState.keymod = SDL_GetModState();
|
||||
|
||||
{
|
||||
std::lock_guard lock{ InputState.cur_controllers_mutex };
|
||||
InputState.cur_controllers.clear();
|
||||
std::lock_guard lock{ InputState.controllers_mutex };
|
||||
InputState.detected_controllers.clear();
|
||||
|
||||
static std::vector<size_t> free_controllers;
|
||||
free_controllers.clear();
|
||||
|
||||
for (const auto& [id, state] : InputState.controller_states) {
|
||||
(void)id; // Avoid unused variable warning.
|
||||
SDL_GameController* controller = state.controller;
|
||||
if (controller != nullptr) {
|
||||
InputState.cur_controllers.push_back(controller);
|
||||
free_controllers.emplace_back(InputState.detected_controllers.size());
|
||||
InputState.detected_controllers.push_back(controller);
|
||||
}
|
||||
}
|
||||
|
||||
// Assign controllers based on configuration.
|
||||
InputState.assigned_controllers.fill(nullptr);
|
||||
|
||||
// FIXME: Use active player count instead of iterating on all possible players.
|
||||
Uint16 vendor, product, version, crc16;
|
||||
for (size_t i = 0; i < 4; i++) {
|
||||
recomp::ControllerGUID controller_guid = recomp::get_input_controller_guid(i);
|
||||
int min_index_difference = INT_MAX;
|
||||
size_t j = 0;
|
||||
while (j < free_controllers.size()) {
|
||||
SDL_GameController *controller = InputState.detected_controllers[free_controllers[j]];
|
||||
SDL_Joystick *joystick = SDL_GameControllerGetJoystick(controller);
|
||||
if (joystick != nullptr) {
|
||||
SDL_JoystickGUID joystick_guid = SDL_JoystickGetGUID(joystick);
|
||||
const char *joystick_serial = SDL_JoystickGetSerial(joystick);
|
||||
SDL_GetJoystickGUIDInfo(joystick_guid, &vendor, &product, &version, &crc16);
|
||||
|
||||
bool empty_serials = controller_guid.serial.empty() && joystick_serial == nullptr;
|
||||
bool matching_serials = joystick_serial != nullptr && strcmp(joystick_serial, controller_guid.serial.c_str()) == 0;
|
||||
if (vendor == controller_guid.vendor && product == controller_guid.product && version == controller_guid.version && crc16 == controller_guid.crc16 && (empty_serials || matching_serials)) {
|
||||
// The controller seems to be a match, but we use the controller with the least difference in player index to sort out potential duplicates.
|
||||
int joystick_player_index = SDL_JoystickGetPlayerIndex(joystick);
|
||||
int index_difference = abs(controller_guid.player_index - joystick_player_index);
|
||||
if (min_index_difference > index_difference) {
|
||||
InputState.assigned_controllers[i] = controller;
|
||||
min_index_difference = index_difference;
|
||||
free_controllers.erase(free_controllers.begin() + j);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
j++;
|
||||
}
|
||||
}
|
||||
|
||||
// Do a second pass to assign controllers that are currently unused to the remaining players that failed to be assigned a controller.
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (InputState.assigned_controllers[i] != nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!free_controllers.empty()) {
|
||||
// Assign a free controller only.
|
||||
InputState.assigned_controllers[i] = InputState.detected_controllers[free_controllers.front()];
|
||||
free_controllers.erase(free_controllers.begin());
|
||||
}
|
||||
else {
|
||||
// Prefer not to assign a controller if none of them are free.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -501,9 +562,7 @@ void recomp::poll_inputs() {
|
||||
}
|
||||
|
||||
void recomp::set_rumble(int controller_num, bool on) {
|
||||
if (controller_num == 0) {
|
||||
InputState.rumble_active = on;
|
||||
}
|
||||
InputState.rumble_active[controller_num] = on;
|
||||
}
|
||||
|
||||
ultramodern::input::connected_device_info_t recomp::get_connected_device_info(int controller_num) {
|
||||
@@ -528,36 +587,52 @@ static float smoothstep(float from, float to, float amount) {
|
||||
|
||||
// Update rumble to attempt to mimic the way n64 rumble ramps up and falls off
|
||||
void recomp::update_rumble() {
|
||||
// Note: values are not accurate! just approximations based on feel
|
||||
if (InputState.rumble_active) {
|
||||
InputState.cur_rumble += 0.17f;
|
||||
if (InputState.cur_rumble > 1) InputState.cur_rumble = 1;
|
||||
}
|
||||
else {
|
||||
InputState.cur_rumble *= 0.92f;
|
||||
InputState.cur_rumble -= 0.01f;
|
||||
if (InputState.cur_rumble < 0) InputState.cur_rumble = 0;
|
||||
}
|
||||
float smooth_rumble = smoothstep(0, 1, InputState.cur_rumble);
|
||||
for (size_t i = 0; i < InputState.cur_rumble.size(); i++) {
|
||||
// Note: values are not accurate! just approximations based on feel
|
||||
if (InputState.rumble_active[i]) {
|
||||
InputState.cur_rumble[i] += 0.17f;
|
||||
if (InputState.cur_rumble[i] > 1) InputState.cur_rumble[i] = 1;
|
||||
}
|
||||
else {
|
||||
InputState.cur_rumble[i] *= 0.92f;
|
||||
InputState.cur_rumble[i] -= 0.01f;
|
||||
if (InputState.cur_rumble[i] < 0) InputState.cur_rumble[i] = 0;
|
||||
}
|
||||
float smooth_rumble = smoothstep(0, 1, InputState.cur_rumble[i]);
|
||||
|
||||
uint16_t rumble_strength = smooth_rumble * (recomp::get_rumble_strength() * 0xFFFF / 100);
|
||||
uint32_t duration = 1000000; // Dummy duration value that lasts long enough to matter as the game will reset rumble on its own.
|
||||
{
|
||||
std::lock_guard lock{ InputState.cur_controllers_mutex };
|
||||
for (const auto& controller : InputState.cur_controllers) {
|
||||
SDL_GameControllerRumble(controller, 0, rumble_strength, duration);
|
||||
uint16_t rumble_strength = smooth_rumble * (recomp::get_rumble_strength() * 0xFFFF / 100);
|
||||
uint32_t duration = 1000000; // Dummy duration value that lasts long enough to matter as the game will reset rumble on its own.
|
||||
{
|
||||
std::lock_guard lock{ InputState.controllers_mutex };
|
||||
if (InputState.single_controller) {
|
||||
for (const auto &controller : InputState.detected_controllers) {
|
||||
SDL_GameControllerRumble(controller, 0, rumble_strength, duration);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (InputState.assigned_controllers[i] != nullptr) {
|
||||
SDL_GameControllerRumble(InputState.assigned_controllers[i], 0, rumble_strength, duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool controller_button_state(int32_t input_id) {
|
||||
bool controller_button_state(int controller_num, int32_t input_id) {
|
||||
if (input_id >= 0 && input_id < SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_MAX) {
|
||||
SDL_GameControllerButton button = (SDL_GameControllerButton)input_id;
|
||||
bool ret = false;
|
||||
{
|
||||
std::lock_guard lock{ InputState.cur_controllers_mutex };
|
||||
for (const auto& controller : InputState.cur_controllers) {
|
||||
ret |= SDL_GameControllerGetButton(controller, button);
|
||||
std::lock_guard lock{ InputState.controllers_mutex };
|
||||
if (InputState.single_controller) {
|
||||
for (const auto &controller : InputState.detected_controllers) {
|
||||
ret |= SDL_GameControllerGetButton(controller, button);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (InputState.assigned_controllers[controller_num] != nullptr) {
|
||||
ret |= SDL_GameControllerGetButton(InputState.assigned_controllers[controller_num], button);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -568,16 +643,15 @@ bool controller_button_state(int32_t input_id) {
|
||||
|
||||
static std::atomic_bool right_analog_suppressed = false;
|
||||
|
||||
float controller_axis_state(int32_t input_id, bool allow_suppression) {
|
||||
float controller_axis_state(int controller_num, int32_t input_id, bool allow_suppression) {
|
||||
if (abs(input_id) - 1 < SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_MAX) {
|
||||
SDL_GameControllerAxis axis = (SDL_GameControllerAxis)(abs(input_id) - 1);
|
||||
bool negative_range = input_id < 0;
|
||||
float ret = 0.0f;
|
||||
|
||||
{
|
||||
std::lock_guard lock{ InputState.cur_controllers_mutex };
|
||||
for (const auto& controller : InputState.cur_controllers) {
|
||||
float cur_val = SDL_GameControllerGetAxis(controller, axis) * (1/32768.0f);
|
||||
auto gather_axis_state = [&](SDL_GameController* controller) {
|
||||
float cur_val = SDL_GameControllerGetAxis(controller, axis) * (1 / 32768.0f);
|
||||
if (negative_range) {
|
||||
cur_val = -cur_val;
|
||||
}
|
||||
@@ -588,6 +662,18 @@ float controller_axis_state(int32_t input_id, bool allow_suppression) {
|
||||
cur_val = 0;
|
||||
}
|
||||
ret += std::clamp(cur_val, 0.0f, 1.0f);
|
||||
};
|
||||
|
||||
std::lock_guard lock{ InputState.controllers_mutex };
|
||||
if (InputState.single_controller) {
|
||||
for (SDL_GameController *controller : InputState.detected_controllers) {
|
||||
gather_axis_state(controller);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (InputState.assigned_controllers[controller_num] != nullptr) {
|
||||
gather_axis_state(InputState.assigned_controllers[controller_num]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -596,7 +682,7 @@ float controller_axis_state(int32_t input_id, bool allow_suppression) {
|
||||
return false;
|
||||
}
|
||||
|
||||
float recomp::get_input_analog(const recomp::InputField& field) {
|
||||
float recomp::get_input_analog(int controller_num, const recomp::InputField& field) {
|
||||
switch ((InputType)field.input_type) {
|
||||
case InputType::Keyboard:
|
||||
if (InputState.keys && field.input_id >= 0 && field.input_id < InputState.numkeys) {
|
||||
@@ -607,9 +693,9 @@ float recomp::get_input_analog(const recomp::InputField& field) {
|
||||
}
|
||||
return 0.0f;
|
||||
case InputType::ControllerDigital:
|
||||
return controller_button_state(field.input_id) ? 1.0f : 0.0f;
|
||||
return controller_button_state(controller_num, field.input_id) ? 1.0f : 0.0f;
|
||||
case InputType::ControllerAnalog:
|
||||
return controller_axis_state(field.input_id, true);
|
||||
return controller_axis_state(controller_num, field.input_id, true);
|
||||
case InputType::Mouse:
|
||||
// TODO mouse support
|
||||
return 0.0f;
|
||||
@@ -618,15 +704,15 @@ float recomp::get_input_analog(const recomp::InputField& field) {
|
||||
}
|
||||
}
|
||||
|
||||
float recomp::get_input_analog(const std::span<const recomp::InputField> fields) {
|
||||
float recomp::get_input_analog(int controller_num, const std::span<const recomp::InputField> fields) {
|
||||
float ret = 0.0f;
|
||||
for (const auto& field : fields) {
|
||||
ret += get_input_analog(field);
|
||||
ret += get_input_analog(controller_num, field);
|
||||
}
|
||||
return std::clamp(ret, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
bool recomp::get_input_digital(const recomp::InputField& field) {
|
||||
bool recomp::get_input_digital(int controller_num, const recomp::InputField& field) {
|
||||
switch ((InputType)field.input_type) {
|
||||
case InputType::Keyboard:
|
||||
if (InputState.keys && field.input_id >= 0 && field.input_id < InputState.numkeys) {
|
||||
@@ -637,10 +723,10 @@ bool recomp::get_input_digital(const recomp::InputField& field) {
|
||||
}
|
||||
return false;
|
||||
case InputType::ControllerDigital:
|
||||
return controller_button_state(field.input_id);
|
||||
return controller_button_state(controller_num, field.input_id);
|
||||
case InputType::ControllerAnalog:
|
||||
// TODO adjustable threshold
|
||||
return controller_axis_state(field.input_id, true) >= axis_threshold;
|
||||
return controller_axis_state(controller_num, field.input_id, true) >= axis_threshold;
|
||||
case InputType::Mouse:
|
||||
// TODO mouse support
|
||||
return false;
|
||||
@@ -649,10 +735,10 @@ bool recomp::get_input_digital(const recomp::InputField& field) {
|
||||
}
|
||||
}
|
||||
|
||||
bool recomp::get_input_digital(const std::span<const recomp::InputField> fields) {
|
||||
bool recomp::get_input_digital(int controller_num, const std::span<const recomp::InputField> fields) {
|
||||
bool ret = 0;
|
||||
for (const auto& field : fields) {
|
||||
ret |= get_input_digital(field);
|
||||
ret |= get_input_digital(controller_num, field);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@@ -706,13 +792,13 @@ void recomp::apply_joystick_deadzone(float x_in, float y_in, float* x_out, float
|
||||
*y_out = y_in;
|
||||
}
|
||||
|
||||
void recomp::get_right_analog(float* x, float* y) {
|
||||
void recomp::get_right_analog(int controller_num, float* x, float* y) {
|
||||
float x_val =
|
||||
controller_axis_state((SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTX + 1), false) -
|
||||
controller_axis_state(-(SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTX + 1), false);
|
||||
controller_axis_state(controller_num, (SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTX + 1), false) -
|
||||
controller_axis_state(controller_num, -(SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTX + 1), false);
|
||||
float y_val =
|
||||
controller_axis_state((SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTY + 1), false) -
|
||||
controller_axis_state(-(SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTY + 1), false);
|
||||
controller_axis_state(controller_num, (SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTY + 1), false) -
|
||||
controller_axis_state(controller_num, -(SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTY + 1), false);
|
||||
recomp::apply_joystick_deadzone(x_val, y_val, x, y);
|
||||
}
|
||||
|
||||
@@ -730,6 +816,14 @@ bool recomp::all_input_disabled() {
|
||||
return scanning_device != recomp::InputDevice::COUNT;
|
||||
}
|
||||
|
||||
bool recomp::get_single_controller_mode() {
|
||||
return InputState.single_controller;
|
||||
}
|
||||
|
||||
void recomp::set_single_controller_mode(bool single_controller) {
|
||||
InputState.single_controller = single_controller;
|
||||
}
|
||||
|
||||
std::string controller_button_to_string(SDL_GameControllerButton button) {
|
||||
switch (button) {
|
||||
case SDL_GameControllerButton::SDL_CONTROLLER_BUTTON_A:
|
||||
@@ -895,3 +989,32 @@ std::string recomp::InputField::to_string() const {
|
||||
return std::to_string(input_type) + "," + std::to_string(input_id);
|
||||
}
|
||||
}
|
||||
|
||||
void recomp::refresh_controller_options() {
|
||||
// TODO: Is it okay to use this to make sure the detected controllers are up to date here?
|
||||
recomp::poll_inputs();
|
||||
|
||||
std::lock_guard lock{ InputState.controllers_mutex };
|
||||
InputState.detected_controller_options.clear();
|
||||
for (SDL_GameController* controller : InputState.detected_controllers) {
|
||||
SDL_Joystick *joystick = SDL_GameControllerGetJoystick(controller);
|
||||
if (joystick == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Uint16 vendor, product, version, crc16;
|
||||
const char *joystick_name = SDL_JoystickName(joystick);
|
||||
const char *joystick_serial = SDL_JoystickGetSerial(joystick);
|
||||
std::string joystick_name_string = joystick_name != nullptr ? std::string(joystick_name) : "Unknown controller";
|
||||
SDL_JoystickGUID joystick_guid = SDL_JoystickGetGUID(joystick);
|
||||
int joystick_player_index = SDL_JoystickGetPlayerIndex(joystick);
|
||||
SDL_GetJoystickGUIDInfo(joystick_guid, &vendor, &product, &version, &crc16);
|
||||
|
||||
ControllerGUID guid = { joystick_serial != nullptr ? std::string(joystick_serial) : std::string(), vendor, product, version, crc16, joystick_player_index};
|
||||
InputState.detected_controller_options.emplace_back(ControllerOption{ joystick_name_string, guid });
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<recomp::ControllerOption> &recomp::get_controller_options() {
|
||||
return InputState.detected_controller_options;
|
||||
}
|
||||
@@ -157,7 +157,7 @@ extern "C" void recomp_get_camera_inputs(uint8_t* rdram, recomp_context* ctx) {
|
||||
|
||||
float x, y;
|
||||
|
||||
recomp::get_right_analog(&x, &y);
|
||||
recomp::get_right_analog(0, &x, &y);
|
||||
|
||||
float magnitude = sqrtf(x * x + y * y);
|
||||
|
||||
|
||||
@@ -663,6 +663,9 @@ int main(int argc, char** argv) {
|
||||
// Register the .rtz texture pack file format with the previous content type as its only allowed content type.
|
||||
recomp::mods::register_mod_container_type("rtz", std::vector{ texture_pack_content_type_id }, false);
|
||||
|
||||
// TODO: Where is it best to place this?
|
||||
recomp::set_single_controller_mode(false);
|
||||
|
||||
recomp::start(
|
||||
project_version,
|
||||
{},
|
||||
|
||||
+23
-14
@@ -110,7 +110,8 @@ int recomp::get_scanned_input_index() {
|
||||
}
|
||||
|
||||
void recomp::finish_scanning_input(recomp::InputField scanned_field) {
|
||||
recomp::set_input_binding(static_cast<recomp::GameInput>(scanned_input_index), scanned_binding_index, cur_device, scanned_field);
|
||||
// TODO: Needs the active controller tab.
|
||||
recomp::set_input_binding(0, static_cast<recomp::GameInput>(scanned_input_index), scanned_binding_index, cur_device, scanned_field);
|
||||
scanned_input_index = -1;
|
||||
scanned_binding_index = -1;
|
||||
controls_model_handle.DirtyVariable("inputs");
|
||||
@@ -593,13 +594,14 @@ public:
|
||||
|
||||
constructor.BindFunc("gfx_help__apply", [](Rml::Variant& out) {
|
||||
if (cont_active) {
|
||||
// TODO: Needs the active controller tab.
|
||||
out = \
|
||||
(recomp::get_input_binding(recomp::GameInput::APPLY_MENU, 0, recomp::InputDevice::Controller).to_string() != "" ?
|
||||
" " + recomp::get_input_binding(recomp::GameInput::APPLY_MENU, 0, recomp::InputDevice::Controller).to_string() :
|
||||
(recomp::get_input_binding(0, recomp::GameInput::APPLY_MENU, 0, recomp::InputDevice::Controller).to_string() != "" ?
|
||||
" " + recomp::get_input_binding(0, recomp::GameInput::APPLY_MENU, 0, recomp::InputDevice::Controller).to_string() :
|
||||
""
|
||||
) + \
|
||||
(recomp::get_input_binding(recomp::GameInput::APPLY_MENU, 1, recomp::InputDevice::Controller).to_string() != "" ?
|
||||
" " + recomp::get_input_binding(recomp::GameInput::APPLY_MENU, 1, recomp::InputDevice::Controller).to_string() :
|
||||
(recomp::get_input_binding(0, recomp::GameInput::APPLY_MENU, 1, recomp::InputDevice::Controller).to_string() != "" ?
|
||||
" " + recomp::get_input_binding(0, recomp::GameInput::APPLY_MENU, 1, recomp::InputDevice::Controller).to_string() :
|
||||
""
|
||||
);
|
||||
} else {
|
||||
@@ -644,9 +646,11 @@ public:
|
||||
constructor.BindEventCallback("reset_input_bindings_to_defaults",
|
||||
[](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) {
|
||||
if (cur_device == recomp::InputDevice::Controller) {
|
||||
banjo::reset_cont_input_bindings();
|
||||
// TODO: Needs the active controller tab.
|
||||
banjo::reset_cont_input_bindings(0);
|
||||
} else {
|
||||
banjo::reset_kb_input_bindings();
|
||||
// TODO: Needs the active controller tab.
|
||||
banjo::reset_kb_input_bindings(0);
|
||||
}
|
||||
model_handle.DirtyAllVariables();
|
||||
nav_help_model_handle.DirtyVariable("nav_help__accept");
|
||||
@@ -658,7 +662,8 @@ public:
|
||||
[](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) {
|
||||
recomp::GameInput input = static_cast<recomp::GameInput>(inputs.at(0).Get<size_t>());
|
||||
for (size_t binding_index = 0; binding_index < recomp::bindings_per_input; binding_index++) {
|
||||
recomp::set_input_binding(input, binding_index, cur_device, recomp::InputField{});
|
||||
// TODO: Needs the active controller tab.
|
||||
recomp::set_input_binding(0, input, binding_index, cur_device, recomp::InputField{});
|
||||
}
|
||||
model_handle.DirtyVariable("inputs");
|
||||
graphics_model_handle.DirtyVariable("gfx_help__apply");
|
||||
@@ -666,8 +671,9 @@ public:
|
||||
|
||||
constructor.BindEventCallback("reset_single_input_binding_to_default",
|
||||
[](Rml::DataModelHandle model_handle, Rml::Event& event, const Rml::VariantList& inputs) {
|
||||
// TODO: Needs the active controller tab.
|
||||
recomp::GameInput input = static_cast<recomp::GameInput>(inputs.at(0).Get<size_t>());
|
||||
banjo::reset_single_input_binding(cur_device, input);
|
||||
banjo::reset_single_input_binding(0, cur_device, input);
|
||||
model_handle.DirtyVariable("inputs");
|
||||
nav_help_model_handle.DirtyVariable("nav_help__accept");
|
||||
nav_help_model_handle.DirtyVariable("nav_help__exit");
|
||||
@@ -704,7 +710,8 @@ public:
|
||||
virtual int Size(void* ptr) override { return recomp::bindings_per_input; }
|
||||
virtual Rml::DataVariable Child(void* ptr, const Rml::DataAddressEntry& address) override {
|
||||
recomp::GameInput input = static_cast<recomp::GameInput>((uintptr_t)ptr);
|
||||
return Rml::DataVariable{&input_field_definition_instance, &recomp::get_input_binding(input, address.index, cur_device)};
|
||||
// TODO: Needs the active controller tab.
|
||||
return Rml::DataVariable{&input_field_definition_instance, &recomp::get_input_binding(0, input, address.index, cur_device)};
|
||||
}
|
||||
};
|
||||
// Static instance of the InputField array variable definition to have a fixed pointer to return to RmlUi.
|
||||
@@ -794,20 +801,22 @@ public:
|
||||
});
|
||||
|
||||
constructor.BindFunc("nav_help__accept", [](Rml::Variant& out) {
|
||||
// TODO: Needs the active controller tab.
|
||||
if (cont_active) {
|
||||
out = \
|
||||
recomp::get_input_binding(recomp::GameInput::ACCEPT_MENU, 0, recomp::InputDevice::Controller).to_string() + \
|
||||
recomp::get_input_binding(recomp::GameInput::ACCEPT_MENU, 1, recomp::InputDevice::Controller).to_string();
|
||||
recomp::get_input_binding(0, recomp::GameInput::ACCEPT_MENU, 0, recomp::InputDevice::Controller).to_string() + \
|
||||
recomp::get_input_binding(0, recomp::GameInput::ACCEPT_MENU, 1, recomp::InputDevice::Controller).to_string();
|
||||
} else {
|
||||
out = PF_KEYBOARD_ENTER;
|
||||
}
|
||||
});
|
||||
|
||||
constructor.BindFunc("nav_help__exit", [](Rml::Variant& out) {
|
||||
// TODO: Needs the active controller tab.
|
||||
if (cont_active) {
|
||||
out = \
|
||||
recomp::get_input_binding(recomp::GameInput::TOGGLE_MENU, 0, recomp::InputDevice::Controller).to_string() + \
|
||||
recomp::get_input_binding(recomp::GameInput::TOGGLE_MENU, 1, recomp::InputDevice::Controller).to_string();
|
||||
recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 0, recomp::InputDevice::Controller).to_string() + \
|
||||
recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 1, recomp::InputDevice::Controller).to_string();
|
||||
} else {
|
||||
out = PF_KEYBOARD_ESCAPE;
|
||||
}
|
||||
|
||||
+10
-8
@@ -464,9 +464,10 @@ bool recompui::try_deque_event(SDL_Event& out) {
|
||||
}
|
||||
|
||||
int cont_button_to_key(SDL_ControllerButtonEvent& button) {
|
||||
// TODO: Needs the active controller tab.
|
||||
// Configurable accept button in menu
|
||||
auto menuAcceptBinding0 = recomp::get_input_binding(recomp::GameInput::ACCEPT_MENU, 0, recomp::InputDevice::Controller);
|
||||
auto menuAcceptBinding1 = recomp::get_input_binding(recomp::GameInput::ACCEPT_MENU, 1, recomp::InputDevice::Controller);
|
||||
auto menuAcceptBinding0 = recomp::get_input_binding(0, recomp::GameInput::ACCEPT_MENU, 0, recomp::InputDevice::Controller);
|
||||
auto menuAcceptBinding1 = recomp::get_input_binding(0, recomp::GameInput::ACCEPT_MENU, 1, recomp::InputDevice::Controller);
|
||||
// note - magic number: 0 is InputType::None
|
||||
if ((menuAcceptBinding0.input_type != 0 && button.button == menuAcceptBinding0.input_id) ||
|
||||
(menuAcceptBinding1.input_type != 0 && button.button == menuAcceptBinding1.input_id)) {
|
||||
@@ -474,8 +475,8 @@ int cont_button_to_key(SDL_ControllerButtonEvent& button) {
|
||||
}
|
||||
|
||||
// Configurable apply button in menu
|
||||
auto menuApplyBinding0 = recomp::get_input_binding(recomp::GameInput::APPLY_MENU, 0, recomp::InputDevice::Controller);
|
||||
auto menuApplyBinding1 = recomp::get_input_binding(recomp::GameInput::APPLY_MENU, 1, recomp::InputDevice::Controller);
|
||||
auto menuApplyBinding0 = recomp::get_input_binding(0, recomp::GameInput::APPLY_MENU, 0, recomp::InputDevice::Controller);
|
||||
auto menuApplyBinding1 = recomp::get_input_binding(0, recomp::GameInput::APPLY_MENU, 1, recomp::InputDevice::Controller);
|
||||
// note - magic number: 0 is InputType::None
|
||||
if ((menuApplyBinding0.input_type != 0 && button.button == menuApplyBinding0.input_id) ||
|
||||
(menuApplyBinding1.input_type != 0 && button.button == menuApplyBinding1.input_id)) {
|
||||
@@ -483,8 +484,8 @@ int cont_button_to_key(SDL_ControllerButtonEvent& button) {
|
||||
}
|
||||
|
||||
// Allows closing the menu
|
||||
auto menuToggleBinding0 = recomp::get_input_binding(recomp::GameInput::TOGGLE_MENU, 0, recomp::InputDevice::Controller);
|
||||
auto menuToggleBinding1 = recomp::get_input_binding(recomp::GameInput::TOGGLE_MENU, 1, recomp::InputDevice::Controller);
|
||||
auto menuToggleBinding0 = recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 0, recomp::InputDevice::Controller);
|
||||
auto menuToggleBinding1 = recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 1, recomp::InputDevice::Controller);
|
||||
// note - magic number: 0 is InputType::None
|
||||
if ((menuToggleBinding0.input_type != 0 && button.button == menuToggleBinding0.input_id) ||
|
||||
(menuToggleBinding1.input_type != 0 && button.button == menuToggleBinding1.input_id)) {
|
||||
@@ -713,8 +714,9 @@ void draw_hook(RT64::RenderCommandList* command_list, RT64::RenderFramebuffer* s
|
||||
}
|
||||
break;
|
||||
case SDL_EventType::SDL_CONTROLLERBUTTONDOWN:
|
||||
auto menuToggleBinding0 = recomp::get_input_binding(recomp::GameInput::TOGGLE_MENU, 0, recomp::InputDevice::Controller);
|
||||
auto menuToggleBinding1 = recomp::get_input_binding(recomp::GameInput::TOGGLE_MENU, 1, recomp::InputDevice::Controller);
|
||||
// TODO: Needs the active controller tab.
|
||||
auto menuToggleBinding0 = recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 0, recomp::InputDevice::Controller);
|
||||
auto menuToggleBinding1 = recomp::get_input_binding(0, recomp::GameInput::TOGGLE_MENU, 1, recomp::InputDevice::Controller);
|
||||
// note - magic number: 0 is InputType::None
|
||||
if ((menuToggleBinding0.input_type != 0 && cur_event.cbutton.button == menuToggleBinding0.input_id) ||
|
||||
(menuToggleBinding1.input_type != 0 && cur_event.cbutton.button == menuToggleBinding1.input_id)) {
|
||||
|
||||
Reference in New Issue
Block a user