Skip to content

Commit

Permalink
Added FLICK_SNAP_MODE and FLICK_SNAP_STRENGTH for snapping to cardina…
Browse files Browse the repository at this point in the history
…l and intercardinal directions with the initial flick
  • Loading branch information
JibbSmart committed Apr 26, 2020
1 parent c39595d commit 445d7ae
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 7 deletions.
7 changes: 4 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ Most recent updates will appear first.
This is a summary of new features and bugfixes. Read the README to learn how to use the features mentioned here.

## 1.5.0
Nicolas added double press bindings, improved chorded mapping behaviour when combined with taps and holds, and refactored a lot of code to prepare for some future changes. Also added support for mouse buttons forward and back, and changed the way logs are displayed. Jibb made it so ring bindings work alongside any stick mode, added more settings to customise flick stick, and added the MOUSE\_RING stick mode for 2D twin-stick aiming.
Nicolas added double press bindings, improved chorded mapping behaviour when combined with taps and holds, and refactored a lot of code to prepare for some future changes. Also added support for mouse buttons forward and back, and changed the way logs are displayed. Jibb added new ways to configue flick stick: customise flick stick's smoothing, disable some of its features, or snap to angles. Also made it so ring bindings work alongside any stick mode and added the MOUSE\_RING stick mode for 2D twin-stick aiming.

### Features
* Added ability to assign Double Press mappings to a button, by entering the button chorded with itself (eg: S,S = SPACE).
* Chords are now active when the controller button is down, instead of waiting for a bounded input to be resolved (such as taps and holds).
* Support mouse buttons 4 and 5 (back and forward).
* LEFT\_RING\_MODE and RIGHT\_RING\_MODE = INNER/OUTER allows setting ring bindings regardless of what LEFT_STICK_MODE and RIGHT_STICK_MODE are.
* Added MOUSE\_RING stick mode to let you use the stick to point the mouse in a direction relative to the centre of the screen.
* Added new stick modes enabling only the flick (FLICK\_ONLY) or rotation (ROTATE\_ONLY) with flick stick.
* MOUSE\_RING stick mode lets you use the stick to point the mouse in a direction relative to the centre of the screen.
* Added FLICK\_SNAP\_MODE and FLICK\_SNAP\_STRENGTH for those who'd prefer flick stick snapped to cardinal (or intercardinal) directions with the initial flick.
* LEFT\_RING\_MODE and RIGHT\_RING\_MODE = INNER/OUTER allows setting ring bindings regardless of what LEFT_STICK_MODE and RIGHT_STICK_MODE are.
* Added the option to override the smoothing threshold for flick stick with ROTATE\_SMOOTH\_OVERRIDE. The smoothing window is still small, but it might soften things for those who found Switch sticks too twitchy.
* Change some behind the scene mapping of commands to windows virtual key codes.

Expand Down
76 changes: 74 additions & 2 deletions JoyShockMapper/src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,8 @@ const char* version = "1.5.0";
#define SCREEN_RESOLUTION_X 81
#define SCREEN_RESOLUTION_Y 82
#define ROTATE_SMOOTH_OVERRIDE 83
#define FLICK_SNAP_MODE 84
#define FLICK_SNAP_STRENGTH 85

#define MAGIC_DST_DELAY 150.0f // in milliseconds
#define MAGIC_TAP_DURATION 40.0f // in milliseconds
Expand All @@ -130,6 +132,7 @@ static_assert(MAGIC_HOLD_TIME < MAGIC_DBL_PRESS_WINDOW, "Hold delay has to be sm

enum class RingMode { outer, inner, invalid };
enum class StickMode { none, aim, flick, flickOnly, rotateOnly, mouseRing, outer, inner, invalid };
enum class FlickSnapMode { none, four, eight, invalid };
enum AxisMode { standard=1, inverted=-1, invalid=0 }; // valid values are true!
enum class TriggerMode { noFull, noSkip, maySkip, mustSkip, maySkipResp, mustSkipResp, invalid };
enum class GyroAxisMask { none = 0, x = 1, y = 2, z = 4, invalid = 8 };
Expand Down Expand Up @@ -534,6 +537,8 @@ float mouse_ring_radius = 128.0f;
int screen_resolution_x = 1920;
int screen_resolution_y = 1080;
float rotate_smooth_override = -1.0f;
float flick_snap_strength = 1.0f;
FlickSnapMode flick_snap_mode = FlickSnapMode::none;
std::unique_ptr<PollingThread> autoLoadThread;
std::unique_ptr<PollingThread> consoleMonitor;
std::unique_ptr<TrayIcon> tray;
Expand Down Expand Up @@ -1396,6 +1401,12 @@ static int keyToMappingIndex(std::string& s) {
if (s.compare("MOUSE_RING_RADIUS") == 0) {
return MOUSE_RING_RADIUS;
}
if (s.compare("FLICK_SNAP_MODE") == 0) {
return FLICK_SNAP_MODE;
}
if (s.compare("FLICK_SNAP_STRENGTH") == 0) {
return FLICK_SNAP_STRENGTH;
}
if (s.compare("STICK_POWER") == 0) {
return STICK_POWER;
}
Expand Down Expand Up @@ -1579,6 +1590,23 @@ static RingMode nameToRingMode(std::string& name, bool print = false) {
return RingMode::invalid;
}

static FlickSnapMode nameToFlickSnapMode(std::string& name, bool print = false) {
if (name.compare("NONE") == 0) {
if (print) printf("None");
return FlickSnapMode::none;
}
if (name.compare("4") == 0) {
if (print) printf("Four");
return FlickSnapMode::four;
}
if (name.compare("8") == 0) {
if (print) printf("Eight");
return FlickSnapMode::eight;
}
if (print) printf("\"%s\" invalid", name.c_str());
return FlickSnapMode::invalid;
}

static AxisMode nameToAxisMode(std::string& name, bool print = false) {
if (name.compare("STANDARD") == 0) {
if (print) printf("Standard");
Expand Down Expand Up @@ -1722,6 +1750,8 @@ static void resetAllMappings() {
screen_resolution_y = 1080;
mouse_ring_radius = 128.0f;
rotate_smooth_override = -1.0f;
flick_snap_strength = 1.0f;
flick_snap_mode = FlickSnapMode::none;
}

void joyShockPollCallback(int jcHandle, JOY_SHOCK_STATE state, JOY_SHOCK_STATE lastState, IMU_STATE imuState, IMU_STATE lastImuState, float deltaTime);
Expand Down Expand Up @@ -1940,7 +1970,7 @@ static void parseCommand(std::string line) {
case STICK_POWER:
try {
stick_power = max(0.0f, std::stof(value));
printf("Stick power set to %s\n", value);
printf("Stick power set to %0.4f\n", stick_power);
}
catch (std::invalid_argument ia) {
printf("Can't convert \"%s\" to a number\n", value);
Expand Down Expand Up @@ -2019,12 +2049,40 @@ static void parseCommand(std::string line) {
temp = -1.0f;
}
rotate_smooth_override = temp;
printf("Rotate smooth override set to %s\n", value);
printf("Rotate smooth override set to %0.4f\n", temp);
}
catch (std::invalid_argument ia) {
printf("Can't convert \"%s\" to a number\n", value);
}
return;
case FLICK_SNAP_STRENGTH:
try {
float temp = std::stof(value);
if (temp < 0.0f) {
temp = 0.0f;
}
if (temp > 1.0f) {
temp = 1.0f;
}
flick_snap_strength = temp;
printf("Flick snap strength set to %0.4f\n", temp);
}
catch (std::invalid_argument ia) {
printf("Can't convert \"%s\" to a number\n", value);
}
return;
case FLICK_SNAP_MODE:
{
FlickSnapMode temp = nameToFlickSnapMode(std::string(value));
if (temp != FlickSnapMode::invalid) {
flick_snap_mode = temp;
printf("Flick snap mode set to %s\n", value);
}
else {
printf("Valid settings for FLICK_SNAP_MODE are NONE, 4, or 8\n");
}
return;
}
case TRIGGER_THRESHOLD:
try {
trigger_threshold = std::stof(value);
Expand Down Expand Up @@ -2650,6 +2708,20 @@ static float handleFlickStick(float calX, float calY, float lastCalX, float last
isFlicking = true;
if (!rotateOnly)
{
if (flick_snap_mode != FlickSnapMode::none) {
// handle snapping
float snapInterval;
if (flick_snap_mode == FlickSnapMode::four) {
snapInterval = PI / 2.0f; // 90 degrees
}
else if (flick_snap_mode == FlickSnapMode::eight) {
snapInterval = PI / 4.0f; // 45 degrees
}
float snappedAngle = round(stickAngle / snapInterval) * snapInterval;
// lerp by snap strength
stickAngle = stickAngle * (1.0f - flick_snap_strength) + snappedAngle * flick_snap_strength;
}

jc->started_flick = std::chrono::steady_clock::now();
jc->delta_flick = stickAngle;
jc->flick_percent_done = 0.0f;
Expand Down
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -364,14 +364,16 @@ When using the ```FLICK``` stick mode, there is less to configure. There are no

Since *flick stick* only turns the camera horizontally, it's generally only practical in combination with gyro aiming that can handle vertical aiming.

*Flick stick* will use the above-mentioned STICK\_DEADZONE\_OUTER to decide if the stick has been pressed far enough for a flick or rotation. *Flick stick* relies on REAL\_WORLD\_CALIBRATION to work correctly ([covered later](#4-real-world-calibration), as it affects all inputs that translate to mouse-movements). This is because JoyShockMapper can only point the camera in a given direction by making the *right* mouse movement, and REAL\_WORLD\_CALIBRATION helps JoyShockMapper calculate what that movement should be. A game that natively implements *flick stick* would have no need for calibration. *Flick stick*'s only setting is:
*Flick stick* will use the above-mentioned STICK\_DEADZONE\_OUTER to decide if the stick has been pressed far enough for a flick or rotation. *Flick stick* relies on REAL\_WORLD\_CALIBRATION to work correctly ([covered later](#4-real-world-calibration), as it affects all inputs that translate to mouse-movements). This is because JoyShockMapper can only point the camera in a given direction by making the *right* mouse movement, and REAL\_WORLD\_CALIBRATION helps JoyShockMapper calculate what that movement should be. A game that natively implements *flick stick* would have no need for calibration. *Flick stick* has a few settings if you really want to mess with it:

* **FLICK\_TIME** (default 0.1 seconds) - When you tilt the stick a direction, how long does it take the camera to complete its turn to face that direction? I find that 0.1 seconds provides a nice, snappy response, while still looking good. Set the value too low and it may look like you're cheating, instantly going from one direction to facing another.
Keep in mind that, once tilted, rotating the stick will rotate the camera instantly. There’s no need to smooth it out\*; the camera just needs to make the same movement the stick is. FLICK\_TIME only affects behaviour when you first tilt the stick.
* **FLICK\_SNAP\_MODE** (default none) - Without practice, sometimes you'll flick to a different angle than you intended. If you want to limit the angles you can flick to, FLICK\_SNAP\_MODE gives you three options. The default, NONE, is no snapping at all. With practice, I expect players will find this most useful, as surprises can come from any angle. But your other options are 4, which snaps the flick to the nearest of directly forward, directly left, directly right, or directly backwards. These are 90° intervals. If you want to be able to snap to 45° intervals, too, you can set FLICK\_SNAP\_MODE to 8.
* **FLICK\_SNAP\_STRENGTH** (default 1.0) - If you have a snap mode other than NONE set, this value gives you the strength of its snapping, ranging from 0 (no snapping) to 1 (full snapping).

**\*Developer note:** The DualShock 4's stick input resolution is low enough that small *flick stick* rotations can be jittery. JoyShockMapper applies some smoothing just to very small changes in the *flick stick* angle, which is very effective at covering this up. Larger movements are not smoothed at all. This is more thoroughly explained for developers to implement in their own games on [GyroWiki](http://gyrowiki.jibbsmart.com). JoyShockMapper automatically calculates different smoothing thresholds for the DualShock 4 and Switch controllers, but you can override the smoothing threshold by setting ROTATE\_SMOOTH\_OVERRIDE any small number, or to 0 to disable smoothing, or to -1 to return to the automatically calculated threshold.

```FLICK_ONLY``` and ```ROTATE_ONLY``` work the same as flick stick with some features blocked out. The former means you'll get the initial flick, but no subsequent rotation when rotating the stick. The latter means you won't get the initial flick, but subsequent rotations will work.
The ```FLICK_ONLY``` and ```ROTATE_ONLY``` stick modes work the same as flick stick with some features blocked out. The former means you'll get the initial flick, but no subsequent rotation when rotating the stick. The latter means you won't get the initial flick, but subsequent rotations will work.

When using the ```MOUSE_RING``` stick mode, tilting the stick will put the mouse cursor in a position offset from the centre of the screen by your stick position. To do this, the application needs to know your screen resolution (SCREEN\_RESOLUTION\_X and SCREEN\_RESOLUTION\_Y) and how far you want the cursor to sit from the centre of the screen (MOUSE\_RING\_RADIUS).

Expand Down

0 comments on commit 445d7ae

Please sign in to comment.