mirror of
https://github.com/spice2x/spice2x.github.io.git
synced 2026-08-01 22:30:42 -07:00
ddr: add support for analog axis-only pads and handle left+right / up+down input (#659)
## Link to GitHub Issue or related Pull Request, if one exists n/a ## Description of change Some crappy DDR soft mats (either USB-based or console pads with crappy adapters that lack a dedicated "DDR pad mode") use X and Y analog axis for arrows, which means they cannot cleanly report Up+Down or Left+Right input. According to some ancient Stepmania code, a lot of these report a middle value (value between center and right, for example, for left+right). Borrow this hack from Stepmania and implement it in spice as analog input for DDR. Also, for rawinput/xinput automatic bind scenario, update the logic so that face buttons are always detected before analog axis / dpads for consistency. This would help with binding any DDR pads that simultaneously input face buttons / dpad / analog axis, so that we can prefer face buttons when detected. ## Testing I don't have any soft mats that have this, but it was tested with an xbox controller.
This commit is contained in:
@@ -291,4 +291,50 @@ namespace games::ddr {
|
||||
// dispose device hook
|
||||
devicehook_dispose();
|
||||
}
|
||||
|
||||
static void get_analog_xy_axis(Analog &analog, bool &less, bool &more) {
|
||||
if (!analog.isSet()) {
|
||||
return;
|
||||
}
|
||||
const auto value = GameAPI::Analogs::getState(RI_MGR, analog);
|
||||
|
||||
// stepmania linux source:
|
||||
// https://github.com/stepmania/stepmania/blob/d55acb1ba26f1c5b5e3048d6d6c0bd116625216f/src/arch/InputHandler/InputHandler_Linux_Event.cpp#L410
|
||||
|
||||
// for example, value cap with range [0, 255]:
|
||||
// 127 = 0.498, neutral
|
||||
// 128 = 0.502, both
|
||||
// apparently some adapters report values like above; this obviously makes a lot of
|
||||
// assumptions about bit width of the HID value cap and how the adapter reports
|
||||
// neutral/both values, but this is good enough for parity with stepmania and its many forks
|
||||
if (0.5001f < value && value < 0.75f) {
|
||||
less |= true;
|
||||
more |= true;
|
||||
|
||||
} else if (value <= 0.25f) {
|
||||
less |= true;
|
||||
} else if (value >= 0.75f) {
|
||||
more |= true;
|
||||
}
|
||||
}
|
||||
|
||||
void get_analog_x_axis(int player, bool &left, bool &right) {
|
||||
if (player != 1 && player != 2) {
|
||||
log_fatal("ddr", "invalid player number: {}, expected 1 or 2", player);
|
||||
return;
|
||||
}
|
||||
|
||||
auto &analog = get_analogs().at(player == 1 ? Analogs::P1_LEFT_RIGHT : Analogs::P2_LEFT_RIGHT);
|
||||
get_analog_xy_axis(analog, left, right);
|
||||
}
|
||||
|
||||
void get_analog_y_axis(int player, bool &up, bool &down) {
|
||||
if (player != 1 && player != 2) {
|
||||
log_fatal("ddr", "invalid player number: {}, expected 1 or 2", player);
|
||||
return;
|
||||
}
|
||||
|
||||
auto &analog = get_analogs().at(player == 1 ? Analogs::P1_UP_DOWN : Analogs::P2_UP_DOWN);
|
||||
get_analog_xy_axis(analog, up, down);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,4 +23,7 @@ namespace games::ddr {
|
||||
private:
|
||||
void register_codecs();
|
||||
};
|
||||
|
||||
void get_analog_x_axis(int player, bool &left, bool &right);
|
||||
void get_analog_y_axis(int player, bool &up, bool &down);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,49 @@ std::vector<Button> &games::ddr::get_buttons() {
|
||||
return analogs;
|
||||
}
|
||||
|
||||
|
||||
std::string games::ddr::get_buttons_help() {
|
||||
// keep to max 100 characters wide
|
||||
return
|
||||
"For DDR pad arrows, double check that simultaneous Left+Right and Up+Down can be detected.\n"
|
||||
"You should boot the game, enter test menu, and use Foot Panel Check.\n\n"
|
||||
"When mapping arrows, try the following in order:\n\n"
|
||||
" 1. If your controller uses face buttons (A/B/X/Y), bind them here.\n"
|
||||
" 2. If your controller supports XInput, use that.\n"
|
||||
" 3. If your controller uses analog axis for arrows, try Analogs tab.\n"
|
||||
" 4. Otherwise, you will need to use remapping software or get a better adapter."
|
||||
;
|
||||
}
|
||||
|
||||
std::vector<Analog> &games::ddr::get_analogs() {
|
||||
static std::vector<Analog> analogs;
|
||||
|
||||
if (analogs.empty()) {
|
||||
analogs = GameAPI::Analogs::getAnalogs("Dance Dance Revolution");
|
||||
|
||||
GameAPI::Analogs::sortAnalogs(
|
||||
&analogs,
|
||||
"P1 Left-Right (Axis Fix)",
|
||||
"P1 Up-Down (Axis Fix)",
|
||||
"P2 Left-Right (Axis Fix)",
|
||||
"P2 Up-Down (Axis Fix)"
|
||||
);
|
||||
}
|
||||
return analogs;
|
||||
}
|
||||
|
||||
std::string games::ddr::get_analogs_help() {
|
||||
// keep to max 100 characters wide
|
||||
return
|
||||
"Only use this if your DDR pad outputs analog axis for arrows.\n\n"
|
||||
"If the pad uses face buttons (A/B/X/Y), use Buttons tab instead.\n\n"
|
||||
"Spice will treat values <=25% as Left, >=75% as Right, ~=50% as neutral,\n"
|
||||
"and value between 50% and 75% as both arrows.\n\n"
|
||||
"This is the classic Stepmania \"Axis Fix\" which may or may not work with\n"
|
||||
"your dance pad or your adapter."
|
||||
;
|
||||
}
|
||||
|
||||
std::vector<Light> &games::ddr::get_lights() {
|
||||
static std::vector<Light> lights;
|
||||
|
||||
|
||||
@@ -33,6 +33,16 @@ namespace games::ddr {
|
||||
};
|
||||
}
|
||||
|
||||
// all analogs in correct order
|
||||
namespace Analogs {
|
||||
enum {
|
||||
P1_LEFT_RIGHT,
|
||||
P1_UP_DOWN,
|
||||
P2_LEFT_RIGHT,
|
||||
P2_UP_DOWN
|
||||
};
|
||||
}
|
||||
|
||||
// all lights in correct order
|
||||
namespace Lights {
|
||||
enum {
|
||||
@@ -177,5 +187,8 @@ namespace games::ddr {
|
||||
|
||||
// getters
|
||||
std::vector<Button> &get_buttons();
|
||||
std::string get_buttons_help();
|
||||
std::string get_analogs_help();
|
||||
std::vector<Analog> &get_analogs();
|
||||
std::vector<Light> &get_lights();
|
||||
}
|
||||
|
||||
@@ -498,32 +498,80 @@ int games::ddr::DDRP3IOHandle::device_io(
|
||||
|
||||
// shift table
|
||||
static size_t shift_table[] = {
|
||||
30, 28, 29, 8, 9, 10, 11, 12, 24, 25, 14, 15, 16, 17, 18, 19, 20, 26, 27, 22, 23
|
||||
30, 28, 29, // service, test, coin
|
||||
8, // p1 start
|
||||
9, 10, 11, 12, // p1 panel
|
||||
24, 25, 14, 15, // p1 menu
|
||||
16, // p2 start
|
||||
17, 18, 19, 20, // p2 panel
|
||||
26, 27, 22, 23 // p2 menu
|
||||
};
|
||||
static size_t button_table[] = {
|
||||
Buttons::SERVICE,
|
||||
Buttons::TEST,
|
||||
Buttons::COIN_MECH,
|
||||
|
||||
Buttons::P1_START,
|
||||
|
||||
Buttons::P1_PANEL_UP,
|
||||
Buttons::P1_PANEL_DOWN,
|
||||
Buttons::P1_PANEL_LEFT,
|
||||
Buttons::P1_PANEL_RIGHT,
|
||||
|
||||
Buttons::P1_MENU_UP,
|
||||
Buttons::P1_MENU_DOWN,
|
||||
Buttons::P1_MENU_LEFT,
|
||||
Buttons::P1_MENU_RIGHT,
|
||||
|
||||
Buttons::P2_START,
|
||||
|
||||
Buttons::P2_PANEL_UP,
|
||||
Buttons::P2_PANEL_DOWN,
|
||||
Buttons::P2_PANEL_LEFT,
|
||||
Buttons::P2_PANEL_RIGHT,
|
||||
|
||||
Buttons::P2_MENU_UP,
|
||||
Buttons::P2_MENU_DOWN,
|
||||
Buttons::P2_MENU_LEFT,
|
||||
Buttons::P2_MENU_RIGHT,
|
||||
};
|
||||
|
||||
// get analogs
|
||||
struct {
|
||||
bool up;
|
||||
bool down;
|
||||
bool left;
|
||||
bool right;
|
||||
} analog_values[2] = {};
|
||||
games::ddr::get_analog_x_axis(1, analog_values[0].left, analog_values[0].right);
|
||||
games::ddr::get_analog_y_axis(1, analog_values[0].up, analog_values[0].down);
|
||||
games::ddr::get_analog_x_axis(2, analog_values[1].left, analog_values[1].right);
|
||||
games::ddr::get_analog_y_axis(2, analog_values[1].up, analog_values[1].down);
|
||||
if (analog_values[0].up) {
|
||||
controls |= 1 << 9;
|
||||
}
|
||||
if (analog_values[0].down) {
|
||||
controls |= 1 << 10;
|
||||
}
|
||||
if (analog_values[0].left) {
|
||||
controls |= 1 << 11;
|
||||
}
|
||||
if (analog_values[0].right) {
|
||||
controls |= 1 << 12;
|
||||
}
|
||||
if (analog_values[1].up) {
|
||||
controls |= 1 << 17;
|
||||
}
|
||||
if (analog_values[1].down) {
|
||||
controls |= 1 << 18;
|
||||
}
|
||||
if (analog_values[1].left) {
|
||||
controls |= 1 << 19;
|
||||
}
|
||||
if (analog_values[1].right) {
|
||||
controls |= 1 << 20;
|
||||
}
|
||||
|
||||
// update states
|
||||
auto &buttons = get_buttons();
|
||||
size_t count = 0;
|
||||
|
||||
@@ -92,6 +92,9 @@ namespace games {
|
||||
games.push_back(ddr);
|
||||
buttons.insert({ ddr, ddr::get_buttons() });
|
||||
lights.insert({ ddr, ddr::get_lights() });
|
||||
buttons_help.insert({ ddr, ddr::get_buttons_help() });
|
||||
analogs_help.insert({ ddr, ddr::get_analogs_help() });
|
||||
analogs.insert({ ddr, ddr::get_analogs() });
|
||||
file_hints[ddr].push_back({"ddr.dll"});
|
||||
file_hints[ddr].push_back({"mdxja_945.dll"});
|
||||
file_hints[ddr].push_back({"arkmdxp3.dll"});
|
||||
|
||||
Reference in New Issue
Block a user