Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion HAL/avr/avr_nousb/include/core/KeyboardMode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

class KeyboardMode : public InputMode {
public:
KeyboardMode(socd::SocdType socd_type);
KeyboardMode();
~KeyboardMode();
void SendReport(InputState &inputs);

Expand Down
2 changes: 1 addition & 1 deletion HAL/avr/avr_nousb/src/core/KeyboardMode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

#include "core/InputMode.hpp"

KeyboardMode::KeyboardMode(socd::SocdType socd_type) : InputMode(socd_type) {}
KeyboardMode::KeyboardMode() {}

KeyboardMode::~KeyboardMode() {}

Expand Down
2 changes: 1 addition & 1 deletion HAL/avr/avr_usb/include/core/KeyboardMode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

class KeyboardMode : public InputMode {
public:
KeyboardMode(socd::SocdType socd_type);
KeyboardMode();
~KeyboardMode();
void SendReport(InputState &inputs);

Expand Down
2 changes: 1 addition & 1 deletion HAL/avr/avr_usb/src/core/KeyboardMode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

#include <ArduinoKeyboard.hpp>

KeyboardMode::KeyboardMode(socd::SocdType socd_type) : InputMode(socd_type) {}
KeyboardMode::KeyboardMode() {}

KeyboardMode::~KeyboardMode() {
_keyboard.releaseAll();
Expand Down
2 changes: 1 addition & 1 deletion HAL/pico/include/core/KeyboardMode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

class KeyboardMode : public InputMode {
public:
KeyboardMode(socd::SocdType socd_type);
KeyboardMode();
~KeyboardMode();
void SendReport(InputState &inputs);

Expand Down
2 changes: 1 addition & 1 deletion HAL/pico/src/core/KeyboardMode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

#include <TUKeyboard.hpp>

KeyboardMode::KeyboardMode(socd::SocdType socd_type) : InputMode(socd_type) {
KeyboardMode::KeyboardMode() {
_keyboard = new TUKeyboard();
_keyboard->begin();
}
Expand Down
35 changes: 24 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Features include:
- Existing modes for popular games (e.g. Melee, Project M, Ultimate, Rivals of Aether, traditional fighting games)
- Easy to create new controller modes (or keyboard modes) for different games
- USB keyboard game modes for games that lack gamepad support
- Fully customisable SOCD cleaning, allowing you to configure SOCD button pairs (e.g. left/right, up/down) for each controller/keyboard mode, and also easily change the SOCD resolution method
- Fully customisable SOCD cleaning, allowing you to configure SOCD button pairs (e.g. left/right, up/down) for each controller/keyboard mode, and also easily change the SOCD resolution method for each SOCD pair
- Switch modes on the fly without unplugging your controller
- Automatically detects whether plugged into console or USB
- Game modes and communication backends are independent entities, meaning you can use any game mode with any supported console without extra work
Expand Down Expand Up @@ -210,9 +210,8 @@ To configure the button holds for input modes (controller/keyboard modes), edit
the `select_mode()` function in `config/mode_selection.hpp`. Each `if`
statement is a button combination to select an input mode.

All input modes support passing in a SOCD cleaning mode, e.g.
`socd::2IP_NO_REAC`. You can see the other available modes in
`src/include/socd.hpp`.
Most input modes support passing in an SOCD cleaning mode, e.g.
`socd::2IP_NO_REAC`. See [here](#socd) for the other available modes.

### Creating custom input modes

Expand Down Expand Up @@ -281,7 +280,7 @@ along with the other analog outputs.
Also note: You don't need to worry about resetting the output state or clearing
anything from it. This is done automatically at the start of each iteration.

#### SOCD
### SOCD

In the constructor of each mode (for controller modes *and* keyboard modes), you
can configure pairs of opposing direction inputs to apply SOCD cleaning to.
Expand All @@ -290,10 +289,10 @@ For example, in `src/modes/Melee20Button.cpp`:
```
_socd_pair_count = 4;
_socd_pairs = new socd::SocdPair[_socd_pair_count]{
socd::SocdPair{&InputState::left, &InputState::right },
socd::SocdPair{ &InputState::down, &InputState::up },
socd::SocdPair{ &InputState::c_left, &InputState::c_right},
socd::SocdPair{ &InputState::c_down, &InputState::c_up },
socd::SocdPair{&InputState::left, &InputState::right, socd_type},
socd::SocdPair{ &InputState::down, &InputState::up, socd_type},
socd::SocdPair{ &InputState::c_left, &InputState::c_right, socd_type},
socd::SocdPair{ &InputState::c_down, &InputState::c_up, socd_type},
};
```

Expand All @@ -303,8 +302,22 @@ cleaning is automatically done before `UpdateDigitalOutputs()` and
`UpdateAnalogOutputs()`, and you do not need to worry about it any further than
that.

Note that you do not have to write a `HandleSocd()` function like in the
Melee20Button and Melee18Button modes. It is only overridden in these two modes
For each `SocdPair` you can pass in an `SocdType` of your choosing. By default
for most modes this is passed in as a single constructor parameter, but it is
possible to pass in multiple parameters, or simply use a hardcoded value. Both
of these approaches are exemplified in `src/modes/FgcMode.cpp`.

| `SocdType` | Description |
| ---------- | ----------- |
| `SOCD_NEUTRAL` | Left + right = neutral - the default if no `SocdType` specified in the `SocdPair` |
| `SOCD_2IP` | Second input priority - left -> left + right = right, and vice versa. Releasing the second direction gives the original direction |
| `SOCD_2IP_NO_REAC` | Second input priority without reactivation - same as above, except releasing the second direction results in neutral. The original direction must be physically reactuated. |
| `SOCD_DIR1_PRIORITY` | The first button in the `SocdPair` always takes priority over the second |
| `SOCD_DIR2_PRIORITY` | The second button in the `SocdPair` always takes priority over the first |
| `SOCD_NONE` | No SOCD resolution - the game decides |

Note that you do not have to implement a `HandleSocd()` function like in the
Melee20Button and Melee18Button modes. It is only overridden in these modes
so that we can check if left and right are both held *before* SOCD cleaning,
because when they are both held (without a vertical direction being held) we
need to override all modifiers.
Expand Down
2 changes: 1 addition & 1 deletion config/mode_selection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ void select_mode(CommunicationBackend *backend) {
} else if (inputs.down) {
set_mode(backend, new Ultimate(socd::SOCD_2IP));
} else if (inputs.right) {
set_mode(backend, new FgcMode(socd::SOCD_NEUTRAL));
set_mode(backend, new FgcMode(socd::SOCD_NEUTRAL, socd::SOCD_NEUTRAL));
} else if (inputs.b) {
set_mode(backend, new RivalsOfAether(socd::SOCD_2IP));
}
Expand Down
2 changes: 1 addition & 1 deletion include/core/ControllerMode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

class ControllerMode : public InputMode {
public:
ControllerMode(socd::SocdType socd_type);
ControllerMode();
void UpdateOutputs(InputState &inputs, OutputState &outputs);
void ResetDirections();
virtual void UpdateDirections(
Expand Down
5 changes: 1 addition & 4 deletions include/core/InputMode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,12 @@

class InputMode {
public:
InputMode(socd::SocdType socd_type);
InputMode();
virtual ~InputMode();

protected:
socd::SocdPair *_socd_pairs = nullptr;
size_t _socd_pair_count = 0;
/* Exposed to child classes so that game modes are able to have different behaviour depending on
* SOCD cleaning mode. */
socd::SocdType _socd_type;

virtual void HandleSocd(InputState &inputs);

Expand Down
15 changes: 11 additions & 4 deletions include/core/socd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,19 @@
#include "stdlib.hpp"

namespace socd {

typedef enum {
SOCD_NEUTRAL,
SOCD_2IP,
SOCD_2IP_NO_REAC,
SOCD_KEYBOARD,
SOCD_DIR1_PRIORITY,
SOCD_DIR2_PRIORITY,
SOCD_NONE,
} SocdType;

typedef struct {
bool InputState::*input_dir1;
bool InputState::*input_dir2;
SocdType socd_type = SOCD_NEUTRAL;
} SocdPair;

typedef struct {
Expand All @@ -25,12 +27,17 @@ namespace socd {
bool lock_dir2 = false;
} SocdState;

void twoIPNoReactivate(bool &input_dir1, bool &input_dir2, SocdState &socd_state);
void second_input_priority_no_reactivation(
bool &input_dir1,
bool &input_dir2,
SocdState &socd_state
);

void twoIP(bool &input_dir1, bool &input_dir2, SocdState &socd_state);
void second_input_priority(bool &input_dir1, bool &input_dir2, SocdState &socd_state);

void neutral(bool &input_dir1, bool &input_dir2);

void dir1_priority(bool &input_dir1, bool &input_dir2);
}

#endif
3 changes: 1 addition & 2 deletions include/modes/FgcMode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@

class FgcMode : public ControllerMode {
public:
FgcMode(socd::SocdType socd_type);
FgcMode(socd::SocdType horizontal_socd, socd::SocdType vertical_socd);

private:
void HandleSocd(InputState &inputs);
void UpdateDigitalOutputs(InputState &inputs, OutputState &outputs);
void UpdateAnalogOutputs(InputState &inputs, OutputState &outputs);
};
Expand Down
2 changes: 1 addition & 1 deletion src/core/ControllerMode.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include "core/ControllerMode.hpp"

ControllerMode::ControllerMode(socd::SocdType socd_type) : InputMode(socd_type) {
ControllerMode::ControllerMode() {
// Set up initial state.
ResetDirections();
}
Expand Down
22 changes: 15 additions & 7 deletions src/core/InputMode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
#include "core/socd.hpp"
#include "core/state.hpp"

InputMode::InputMode(socd::SocdType socd_type) {
_socd_type = socd_type;
}
InputMode::InputMode() {}

InputMode::~InputMode() {
delete[] _socd_pairs;
Expand All @@ -25,21 +23,31 @@ void InputMode::HandleSocd(InputState &inputs) {
// Handle SOCD resolution for each SOCD button pair.
for (size_t i = 0; i < _socd_pair_count; i++) {
socd::SocdPair pair = _socd_pairs[i];
switch (_socd_type) {
switch (pair.socd_type) {
case socd::SOCD_NEUTRAL:
socd::neutral(inputs.*(pair.input_dir1), inputs.*(pair.input_dir2));
break;
case socd::SOCD_2IP:
socd::twoIP(inputs.*(pair.input_dir1), inputs.*(pair.input_dir2), _socd_states[i]);
socd::second_input_priority(
inputs.*(pair.input_dir1),
inputs.*(pair.input_dir2),
_socd_states[i]
);
break;
case socd::SOCD_2IP_NO_REAC:
socd::twoIPNoReactivate(
socd::second_input_priority_no_reactivation(
inputs.*(pair.input_dir1),
inputs.*(pair.input_dir2),
_socd_states[i]
);
break;
case socd::SOCD_KEYBOARD:
case socd::SOCD_DIR1_PRIORITY:
socd::dir1_priority(inputs.*(pair.input_dir1), inputs.*(pair.input_dir2));
break;
case socd::SOCD_DIR2_PRIORITY:
socd::dir1_priority(inputs.*(pair.input_dir2), inputs.*(pair.input_dir1));
break;
case socd::SOCD_NONE:
break;
}
}
Expand Down
26 changes: 14 additions & 12 deletions src/core/socd.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
#include "core/socd.hpp"

void socd::twoIPNoReactivate(bool &input_dir1, bool &input_dir2, SocdState &socd_state) {
void socd::second_input_priority_no_reactivation(
bool &input_dir1,
bool &input_dir2,
SocdState &socd_state
) {
bool is_dir1 = false;
bool is_dir2 = false;
if (input_dir1 && input_dir2) {
Expand Down Expand Up @@ -39,7 +43,7 @@ void socd::twoIPNoReactivate(bool &input_dir1, bool &input_dir2, SocdState &socd
input_dir2 = is_dir2;
}

void socd::twoIP(bool &input_dir1, bool &input_dir2, SocdState &socd_state) {
void socd::second_input_priority(bool &input_dir1, bool &input_dir2, SocdState &socd_state) {
bool is_dir1 = false;
bool is_dir2 = false;
if (input_dir1 && socd_state.was_dir2) {
Expand Down Expand Up @@ -67,16 +71,14 @@ void socd::twoIP(bool &input_dir1, bool &input_dir2, SocdState &socd_state) {
}

void socd::neutral(bool &input_dir1, bool &input_dir2) {
bool is_dir1 = false;
bool is_dir2 = false;
if (!input_dir1 && input_dir2) {
is_dir1 = false;
is_dir2 = true;
if (input_dir1 && input_dir2) {
input_dir1 = false;
input_dir2 = false;
}
if (input_dir1 && !input_dir2) {
is_dir1 = true;
is_dir2 = false;
}

void socd::dir1_priority(bool &input_dir1, bool &input_dir2) {
if (input_dir1 && input_dir2) {
input_dir2 = false;
}
input_dir1 = is_dir1;
input_dir2 = is_dir2;
}
2 changes: 1 addition & 1 deletion src/modes/DefaultKeyboardMode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#include "core/socd.hpp"
#include "core/state.hpp"

DefaultKeyboardMode::DefaultKeyboardMode(socd::SocdType socd_type) : KeyboardMode(socd_type) {}
DefaultKeyboardMode::DefaultKeyboardMode(socd::SocdType socd_type) {}

void DefaultKeyboardMode::UpdateKeys(InputState &inputs) {
Press(HID_KEY_A, inputs.l);
Expand Down
20 changes: 10 additions & 10 deletions src/modes/FgcMode.cpp
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
#include "modes/FgcMode.hpp"

FgcMode::FgcMode(socd::SocdType socd_type) : ControllerMode(socd_type) {
_socd_pair_count = 1;
FgcMode::FgcMode(socd::SocdType horizontal_socd, socd::SocdType vertical_socd) {
_socd_pair_count = 4;
_socd_pairs = new socd::SocdPair[_socd_pair_count]{
socd::SocdPair{&InputState::left, &InputState::right},
socd::SocdPair{&InputState::left, &InputState::right, horizontal_socd },
/* Mod X override C-Up input if both are pressed. Without this, neutral SOCD doesn't work
properly if Down and both Up buttons are pressed, because it first resolves Down + Mod X
to set both as unpressed, and then it sees C-Up as pressed but not Down, so you get an up
input instead of neutral. */
socd::SocdPair{ &InputState::mod_x, &InputState::c_up, socd::SOCD_DIR1_PRIORITY},
socd::SocdPair{ &InputState::down, &InputState::mod_x, vertical_socd },
socd::SocdPair{ &InputState::down, &InputState::c_up, vertical_socd },
};
}

void FgcMode::HandleSocd(InputState &inputs) {
if (inputs.down && (inputs.mod_x || inputs.c_up)) {
inputs.down = false;
}
InputMode::HandleSocd(inputs);
}

void FgcMode::UpdateDigitalOutputs(InputState &inputs, OutputState &outputs) {
// Directions
outputs.dpadLeft = inputs.left;
Expand Down
11 changes: 5 additions & 6 deletions src/modes/Melee18Button.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@
#define ANALOG_STICK_NEUTRAL 128
#define ANALOG_STICK_MAX 208

Melee18Button::Melee18Button(socd::SocdType socd_type, Melee18ButtonOptions options)
: ControllerMode(socd_type) {
Melee18Button::Melee18Button(socd::SocdType socd_type, Melee18ButtonOptions options) {
_socd_pair_count = 4;
_socd_pairs = new socd::SocdPair[_socd_pair_count]{
socd::SocdPair{&InputState::left, &InputState::right },
socd::SocdPair{ &InputState::down, &InputState::up },
socd::SocdPair{ &InputState::c_left, &InputState::c_right},
socd::SocdPair{ &InputState::c_down, &InputState::c_up },
socd::SocdPair{&InputState::left, &InputState::right, socd_type},
socd::SocdPair{ &InputState::down, &InputState::up, socd_type},
socd::SocdPair{ &InputState::c_left, &InputState::c_right, socd_type},
socd::SocdPair{ &InputState::c_down, &InputState::c_up, socd_type},
};

_options = options;
Expand Down
11 changes: 5 additions & 6 deletions src/modes/Melee20Button.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,13 @@
#define ANALOG_STICK_NEUTRAL 128
#define ANALOG_STICK_MAX 208

Melee20Button::Melee20Button(socd::SocdType socd_type, Melee20ButtonOptions options)
: ControllerMode(socd_type) {
Melee20Button::Melee20Button(socd::SocdType socd_type, Melee20ButtonOptions options) {
_socd_pair_count = 4;
_socd_pairs = new socd::SocdPair[_socd_pair_count]{
socd::SocdPair{&InputState::left, &InputState::right },
socd::SocdPair{ &InputState::down, &InputState::up },
socd::SocdPair{ &InputState::c_left, &InputState::c_right},
socd::SocdPair{ &InputState::c_down, &InputState::c_up },
socd::SocdPair{&InputState::left, &InputState::right, socd_type},
socd::SocdPair{ &InputState::down, &InputState::up, socd_type},
socd::SocdPair{ &InputState::c_left, &InputState::c_right, socd_type},
socd::SocdPair{ &InputState::c_down, &InputState::c_up, socd_type},
};

_options = options;
Expand Down
Loading