gamepad: direction snapping; more flexible configuration options
- The free/restricted axis distinction is gone; the joystick always operates in "free" mode. - Added direction snapping functionality to help aid exact movement in cardinal and/or diagonal directions. The snapping angle can be adjusted from 0% (disabled) to 100% (similar to the old "restricted" mode). The snapping angle can also be biased towards cardinals or diagonals. - When the maximum zone is less than or equals dead zone, moving the character will always move at maximum speed (as in the old "restricted" mode). - Most of these settings are now visualized in the options menu and can be tested there.
This commit is contained in:
parent
d288f2bd27
commit
7548e4892d
8 changed files with 317 additions and 72 deletions
90
resources/00-taisei.pkgdir/shader/gamepad_circle.frag.glsl
Normal file
90
resources/00-taisei.pkgdir/shader/gamepad_circle.frag.glsl
Normal file
|
@ -0,0 +1,90 @@
|
|||
#version 330 core
|
||||
|
||||
#include "lib/render_context.glslh"
|
||||
#include "lib/util.glslh"
|
||||
#include "interface/standard.glslh"
|
||||
|
||||
UNIFORM(1) vec4 snap_angles_sincos;
|
||||
UNIFORM(2) vec4 joy_pointers;
|
||||
UNIFORM(3) vec2 deadzones;
|
||||
|
||||
float sdCircle(vec2 p, float r) {
|
||||
return length(p) - r;
|
||||
}
|
||||
|
||||
float sdRoundedX(vec2 p, float w, float r) {
|
||||
p = abs(p);
|
||||
return length(p - min(p.x + p.y, w) * 0.5) - r;
|
||||
}
|
||||
|
||||
float sdPieDual(vec2 p, vec2 c, float r) {
|
||||
p = abs(p);
|
||||
float l = length(p) - r;
|
||||
float m = length(p - c * clamp(dot(p, c), 0.0, r));
|
||||
return max(l, m * sign(c.y * p.x - c.x * p.y));
|
||||
}
|
||||
|
||||
vec2 sincos(float a) {
|
||||
return vec2(sin(a), cos(a));
|
||||
}
|
||||
|
||||
float zones(vec2 uv, vec2 sc) {
|
||||
return 0.5 - min(
|
||||
sdPieDual(uv, sc, 1.0),
|
||||
sdPieDual(uv.yx, sc, 1.0));
|
||||
}
|
||||
|
||||
vec4 joystickPointer(vec2 p, float d, vec4 color) {
|
||||
float base = 1.0 - sdCircle(p, -0.4);
|
||||
|
||||
float outer = smoothstep(0.48, 0.52, base) - smoothstep(0.513-d, 0.513+d, base);
|
||||
vec4 c = mix(vec4(color.a), color, outer) * outer;
|
||||
|
||||
float inner = smoothstep(0.51, 0.53, base);
|
||||
float innerGrad = smoothstep(0.52, 0.55, base);
|
||||
c = alphaCompose(c, vec4(color.rgb * mix(1.0, 0.75, innerGrad), color.a) * inner);
|
||||
|
||||
return c * c.a;
|
||||
}
|
||||
|
||||
void main(void) {
|
||||
vec2 uv = 2.22 * (flip_native_to_topleft(texCoordRaw) - 0.5);
|
||||
vec2 snapCardinalsSinCos = snap_angles_sincos.xy;
|
||||
vec2 snapDiagonalsSinCos = snap_angles_sincos.zw;
|
||||
|
||||
vec4 c = vec4(0);
|
||||
|
||||
float d = fwidth(uv.x);
|
||||
float circleBase = 1.0 - sdCircle(uv, 0.5);
|
||||
float circle = smoothstep(0.5 - d, 0.5 + d, circleBase);
|
||||
c = alphaCompose(c, vec4(0.25 * circle));
|
||||
|
||||
float deadzone = smoothstep(0.5 - 0.01, 0.5 + 0.01, circleBase - (1 - deadzones.x));
|
||||
float snapMask = circle * (1 - deadzone);
|
||||
deadzone *= 1.0 - smoothstep(0.25, 0.7, circleBase - (1 - deadzones.x + 0.2));
|
||||
|
||||
float maxzone_val = 1 - max(deadzones.x, deadzones.y);
|
||||
float maxzone = smoothstep(0.5 - maxzone_val, 0.5 + d, circleBase - maxzone_val);
|
||||
maxzone -= smoothstep(0.52 - d, 0.52 + d, circleBase - maxzone_val);
|
||||
|
||||
float cross1 = sdRoundedX(uv * rot(pi/4), 2.0, -0.49);
|
||||
float cross2 = sdRoundedX(uv, 2.0, -0.49);
|
||||
float delimiters = smoothstep(0.48, 0.6, circle * (1.0 - min(cross1, cross2)));
|
||||
|
||||
float cardinals = zones(uv, snapCardinalsSinCos);
|
||||
float diagonals = zones(uv * rot(pi/4), snapDiagonalsSinCos);
|
||||
|
||||
c = alphaCompose(c, vec4(0.5 * maxzone));
|
||||
c = alphaCompose(c, vec4(1, 0.3, 0.3, 1) * 0.5 * snapMask * smoothstep(0.5-d, 0.5+d, cardinals));
|
||||
c = alphaCompose(c, vec4(0.3, 0.3, 1, 1) * 0.5 * snapMask * smoothstep(0.5-d, 0.5+d, diagonals));
|
||||
c = alphaCompose(c, vec4(vec3(0), 0.7 * deadzone));
|
||||
c = alphaCompose(c, joystickPointer(uv - joy_pointers.xy, d, vec4(1, 0.9, 0.2, 1)));
|
||||
c = alphaCompose(c, joystickPointer(uv - joy_pointers.zw, d, vec4(0.6)));
|
||||
c = alphaCompose(c, vec4(0, 0, 0, delimiters));
|
||||
|
||||
if(all(lessThan(c, vec4(1.0 / 255.0)))) {
|
||||
discard;
|
||||
}
|
||||
|
||||
fragColor = c * r_color;
|
||||
}
|
2
resources/00-taisei.pkgdir/shader/gamepad_circle.prog
Normal file
2
resources/00-taisei.pkgdir/shader/gamepad_circle.prog
Normal file
|
@ -0,0 +1,2 @@
|
|||
|
||||
objects = standard.vert gamepad_circle.frag
|
|
@ -31,6 +31,7 @@ glsl_files = [
|
|||
'fireparticles.vert.glsl',
|
||||
'fxaa.frag.glsl',
|
||||
'fxaa.vert.glsl',
|
||||
'gamepad_circle.frag.glsl',
|
||||
'glitch.frag.glsl',
|
||||
'graph.frag.glsl',
|
||||
'healthbar.vert.glsl',
|
||||
|
|
|
@ -123,13 +123,14 @@
|
|||
CONFIGDEF_STRING (GAMEPAD_DEVICE, "gamepad_device", "any") \
|
||||
CONFIGDEF_INT (GAMEPAD_AXIS_UD, "gamepad_axis_ud", 1) \
|
||||
CONFIGDEF_INT (GAMEPAD_AXIS_LR, "gamepad_axis_lr", 0) \
|
||||
CONFIGDEF_INT (GAMEPAD_AXIS_FREE, "gamepad_axis_free", 1) \
|
||||
CONFIGDEF_INT (GAMEPAD_AXIS_SQUARECIRCLE, "gamepad_axis_square_to_circle", 0) \
|
||||
CONFIGDEF_FLOAT (GAMEPAD_AXIS_SENS, "gamepad_axis_sensitivity", 0.0) \
|
||||
CONFIGDEF_FLOAT (GAMEPAD_AXIS_UD_SENS, "gamepad_axis_ud_sensitivity", 0.0) \
|
||||
CONFIGDEF_FLOAT (GAMEPAD_AXIS_LR_SENS, "gamepad_axis_lr_sensitivity", 0.0) \
|
||||
CONFIGDEF_FLOAT (GAMEPAD_AXIS_DEADZONE, "gamepad_axis_deadzone", 0.1) \
|
||||
CONFIGDEF_FLOAT (GAMEPAD_AXIS_MAXZONE, "gamepad_axis_maxzone", 0.9) \
|
||||
CONFIGDEF_FLOAT (GAMEPAD_AXIS_SNAP, "gamepad_axis_snap", 0.5) \
|
||||
CONFIGDEF_FLOAT (GAMEPAD_AXIS_SNAP_DIAG_BIAS, "gamepad_axis_snap_diagonal_bias", 0.0) \
|
||||
CONFIGDEF_FLOAT (GAMEPAD_BTNREPEAT_DELAY, "gamepad_button_repeat_delay", CONFIG_GAMEPAD_BTNREPEAT_DELAY_DEFAULT) \
|
||||
CONFIGDEF_FLOAT (GAMEPAD_BTNREPEAT_INTERVAL,"gamepad_button_repeat_interval", CONFIG_GAMEPAD_BTNREPEAT_INTERVAL_DEFAULT) \
|
||||
GPKEYDEFS \
|
||||
|
|
107
src/gamepad.c
107
src/gamepad.c
|
@ -14,6 +14,7 @@
|
|||
#include "hirestime.h"
|
||||
#include "log.h"
|
||||
#include "transition.h"
|
||||
#include "util.h"
|
||||
#include "util/miscmath.h"
|
||||
#include "util/stringops.h"
|
||||
#include "vfs/public.h"
|
||||
|
@ -357,10 +358,14 @@ static int gamepad_axis2gameevt(GamepadAxis id) {
|
|||
return -1;
|
||||
}
|
||||
|
||||
static inline double gamepad_get_deadzone(void) {
|
||||
double gamepad_get_normalized_deadzone(void) {
|
||||
return clamp(config_get_float(CONFIG_GAMEPAD_AXIS_DEADZONE), MIN_DEADZONE, MAX_DEADZONE);
|
||||
}
|
||||
|
||||
double gamepad_get_normalized_maxzone(void) {
|
||||
return clamp(config_get_float(CONFIG_GAMEPAD_AXIS_MAXZONE), 0, 1);
|
||||
}
|
||||
|
||||
int gamepad_axis_value(GamepadAxis id) {
|
||||
assert(id > GAMEPAD_AXIS_INVALID);
|
||||
assert(id < GAMEPAD_AXIS_MAX);
|
||||
|
@ -419,40 +424,6 @@ static void gamepad_update_game_axis(GamepadAxis axis, int oldval) {
|
|||
}
|
||||
}
|
||||
|
||||
static cmplx gamepad_restrict_analog_input(cmplx z) {
|
||||
typedef enum {
|
||||
UP = (1 << 0),
|
||||
DOWN = (1 << 1),
|
||||
RIGHT = (1 << 3),
|
||||
LEFT = (1 << 2),
|
||||
} MoveDir;
|
||||
|
||||
double x = re(z);
|
||||
double y = im(z);
|
||||
|
||||
MoveDir move = 0;
|
||||
|
||||
if(x || y) {
|
||||
int d = (int)rint(atan2(-y, x) / (M_PI/4));
|
||||
|
||||
switch(d) {
|
||||
case 0: move = 0 | RIGHT; break;
|
||||
case -1: move = UP | RIGHT; break;
|
||||
case -2: move = UP | 0; break;
|
||||
case -3: move = UP | LEFT; break;
|
||||
case -4: case 4: move = 0 | LEFT; break;
|
||||
case 3: move = DOWN | LEFT; break;
|
||||
case 2: move = DOWN | 0; break;
|
||||
case 1: move = DOWN | RIGHT; break;
|
||||
}
|
||||
}
|
||||
|
||||
x = (bool)(move & RIGHT) - (bool)(move & LEFT);
|
||||
y = (bool)(move & UP) - (bool)(move & DOWN);
|
||||
|
||||
return CMPLX(x, y);
|
||||
}
|
||||
|
||||
static double gamepad_apply_sensitivity(double p, double sens) {
|
||||
if(sens == 0) {
|
||||
return p;
|
||||
|
@ -475,6 +446,47 @@ static cmplx square_to_circle(cmplx z) {
|
|||
return CMPLX(u, v);
|
||||
}
|
||||
|
||||
static cmplx gamepad_snap_analog_direction(cmplx z) {
|
||||
double snap = clamp(config_get_float(CONFIG_GAMEPAD_AXIS_SNAP), 0, 1);
|
||||
|
||||
if(snap == 0) {
|
||||
return z;
|
||||
}
|
||||
|
||||
double diagonal_bias = clamp(config_get_float(CONFIG_GAMEPAD_AXIS_SNAP_DIAG_BIAS), -1, 1);
|
||||
double w_cardinal = 1 - diagonal_bias;
|
||||
double w_diagonal = 1 + diagonal_bias;
|
||||
|
||||
#define D 0.7071067811865475
|
||||
struct { cmplx v; double weight; } directions[] = {
|
||||
{ CMPLX( 1, 0), w_cardinal },
|
||||
{ CMPLX( 0, 1), w_cardinal },
|
||||
{ CMPLX(-1, 0), w_cardinal },
|
||||
{ CMPLX( 0, -1), w_cardinal },
|
||||
{ CMPLX( D, D), w_diagonal },
|
||||
{ CMPLX( D, -D), w_diagonal },
|
||||
{ CMPLX(-D, -D), w_diagonal },
|
||||
{ CMPLX(-D, D), w_diagonal },
|
||||
};
|
||||
#undef D
|
||||
|
||||
double m = cabs(z);
|
||||
double thres = snap * M_PI/ARRAY_SIZE(directions);
|
||||
|
||||
for(int i = 0; i < ARRAY_SIZE(directions); ++i) {
|
||||
cmplx q = directions[i].v;
|
||||
double delta_angle = acos(cdot(q, z) / m);
|
||||
|
||||
if(delta_angle < thres * directions[i].weight) {
|
||||
z = q * m;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return z;
|
||||
}
|
||||
|
||||
void gamepad_get_player_analog_input(int *xaxis, int *yaxis) {
|
||||
int xraw = gamepad_player_axis_value(PLRAXIS_LR);
|
||||
int yraw = gamepad_player_axis_value(PLRAXIS_UD);
|
||||
|
@ -482,8 +494,8 @@ void gamepad_get_player_analog_input(int *xaxis, int *yaxis) {
|
|||
*xaxis = 0;
|
||||
*yaxis = 0;
|
||||
|
||||
double deadzone = gamepad_get_deadzone();
|
||||
double maxzone = config_get_float(CONFIG_GAMEPAD_AXIS_MAXZONE);
|
||||
double deadzone = gamepad_get_normalized_deadzone();
|
||||
double maxzone = gamepad_get_normalized_maxzone();
|
||||
|
||||
cmplx z = CMPLX(
|
||||
gamepad_normalize_axis_value(xraw),
|
||||
|
@ -503,25 +515,32 @@ void gamepad_get_player_analog_input(int *xaxis, int *yaxis) {
|
|||
double raw_abs = sqrt(raw_abs2);
|
||||
assert(raw_abs > 0);
|
||||
|
||||
double new_abs = (raw_abs - deadzone) / (maxzone - deadzone);
|
||||
new_abs = gamepad_apply_sensitivity(new_abs, config_get_float(CONFIG_GAMEPAD_AXIS_SENS));
|
||||
z *= new_abs / raw_abs;
|
||||
if(deadzone < maxzone) {
|
||||
double new_abs;
|
||||
if(raw_abs >= maxzone) {
|
||||
new_abs = max(raw_abs, 1);
|
||||
} else {
|
||||
new_abs = (min(raw_abs, maxzone) - deadzone) / (maxzone - deadzone);
|
||||
new_abs = gamepad_apply_sensitivity(new_abs, config_get_float(CONFIG_GAMEPAD_AXIS_SENS));
|
||||
}
|
||||
z *= new_abs / raw_abs;
|
||||
} else {
|
||||
z /= raw_abs;
|
||||
}
|
||||
|
||||
z = CMPLX(
|
||||
gamepad_apply_sensitivity(re(z), config_get_float(CONFIG_GAMEPAD_AXIS_LR_SENS)),
|
||||
gamepad_apply_sensitivity(im(z), config_get_float(CONFIG_GAMEPAD_AXIS_UD_SENS))
|
||||
);
|
||||
|
||||
if(!config_get_int(CONFIG_GAMEPAD_AXIS_FREE)) {
|
||||
z = gamepad_restrict_analog_input(z);
|
||||
}
|
||||
z = gamepad_snap_analog_direction(z);
|
||||
|
||||
*xaxis = gamepad_denormalize_axis_value(re(z));
|
||||
*yaxis = gamepad_denormalize_axis_value(im(z));
|
||||
}
|
||||
|
||||
static int gamepad_axis_get_digital_value(int raw) {
|
||||
double deadzone = gamepad_get_deadzone();
|
||||
double deadzone = gamepad_get_normalized_deadzone();
|
||||
deadzone = max(deadzone, 0.5);
|
||||
int threshold = GAMEPAD_AXIS_MAX_VALUE * deadzone;
|
||||
|
||||
|
|
|
@ -136,5 +136,8 @@ void gamepad_get_player_analog_input(int *xaxis, int *yaxis);
|
|||
double gamepad_normalize_axis_value(int val);
|
||||
int gamepad_denormalize_axis_value(double val);
|
||||
|
||||
double gamepad_get_normalized_deadzone(void);
|
||||
double gamepad_get_normalized_maxzone(void);
|
||||
|
||||
#define GAMEPAD_AXIS_MAX_VALUE 32767
|
||||
#define GAMEPAD_AXIS_MIN_VALUE -32768
|
||||
|
|
|
@ -288,6 +288,7 @@ void menu_preload(ResourceGroup *rg) {
|
|||
NULL);
|
||||
|
||||
res_group_preload(rg, RES_SHADER_PROGRAM, RESF_DEFAULT,
|
||||
"gamepad_circle",
|
||||
"mainmenubg",
|
||||
"sprite_circleclipped_indicator",
|
||||
NULL);
|
||||
|
|
|
@ -9,15 +9,22 @@
|
|||
#include "options.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "config.h"
|
||||
#include "events.h"
|
||||
#include "mainmenu.h"
|
||||
#include "menu.h"
|
||||
|
||||
#include "audio/audio.h"
|
||||
#include "gamepad.h"
|
||||
#include "renderer/api.h"
|
||||
#include "resource/font.h"
|
||||
#include "util/graphics.h"
|
||||
#include "video.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#define OPTIONS_ACTIVE_X_OFFSET 20 /* FIXME hardcoded in draw_menu_list */
|
||||
#define OPTIONS_ITEM_HEIGHT 20 /* FIXME hardcoded in draw_menu_list */
|
||||
#define OPTIONS_X_MARGIN 100
|
||||
#define OPTIONS_Y_MARGIN 100
|
||||
|
||||
typedef struct OptionBinding OptionBinding;
|
||||
|
||||
|
@ -500,6 +507,11 @@ typedef struct OptionsMenuContext {
|
|||
MenuData *submenu_fading;
|
||||
ConfirmDialog *confirm_dialog;
|
||||
float submenu_alpha;
|
||||
void (*draw_overlay)(MenuData *m, struct OptionsMenuContext *ctx);
|
||||
struct {
|
||||
bool allowed;
|
||||
bool active;
|
||||
} gamepad_testmode;
|
||||
} OptionsMenuContext;
|
||||
|
||||
static void destroy_options_menu(MenuData *m) {
|
||||
|
@ -879,12 +891,83 @@ static void destroy_options_menu_gamepad(MenuData *m) {
|
|||
destroy_options_menu(m);
|
||||
}
|
||||
|
||||
static inline GamepadButton options_gamepad_testing_button(void) {
|
||||
return config_get_int(CONFIG_GAMEPAD_KEY_FOCUS);
|
||||
}
|
||||
|
||||
static void draw_gamepad_options_overlay(MenuData *m, OptionsMenuContext *ctx) {
|
||||
float csize = 86;
|
||||
|
||||
int x, y;
|
||||
gamepad_get_player_analog_input(&x, &y);
|
||||
|
||||
cmplx in_processed = CMPLX(
|
||||
gamepad_normalize_axis_value(x),
|
||||
gamepad_normalize_axis_value(y));
|
||||
|
||||
cmplx in_raw = CMPLX(
|
||||
gamepad_normalize_axis_value(gamepad_player_axis_value(PLRAXIS_LR)),
|
||||
gamepad_normalize_axis_value(gamepad_player_axis_value(PLRAXIS_UD)));
|
||||
|
||||
double deadzone = gamepad_get_normalized_deadzone();
|
||||
double maxzone = gamepad_get_normalized_maxzone();
|
||||
|
||||
r_mat_mv_push();
|
||||
r_mat_mv_translate(
|
||||
SCREEN_W - OPTIONS_X_MARGIN,
|
||||
OPTIONS_Y_MARGIN + 6.5f*OPTIONS_ITEM_HEIGHT,
|
||||
0);
|
||||
r_mat_mv_scale(csize, csize, 0);
|
||||
r_mat_mv_translate(0.5f, 0.5f, 0);
|
||||
float snap = clamp(config_get_float(CONFIG_GAMEPAD_AXIS_SNAP), 0, 1);
|
||||
float diagonal_bias = clamp(config_get_float(CONFIG_GAMEPAD_AXIS_SNAP_DIAG_BIAS), -1, 1);
|
||||
float w_cardinal = 1.0f - diagonal_bias;
|
||||
float w_diagonal = 1.0f + diagonal_bias;
|
||||
float thres_card = (snap * (float)M_PI/8.0f) * w_cardinal;
|
||||
float thres_diag = (snap * (float)M_PI/8.0f) * w_diagonal;
|
||||
r_shader("gamepad_circle");
|
||||
r_uniform_vec4("snap_angles_sincos", sinf(thres_card), cosf(thres_card), sinf(thres_diag), cosf(thres_diag));
|
||||
r_uniform_vec2("deadzones", deadzone, maxzone);
|
||||
|
||||
if(ctx->gamepad_testmode.active) {
|
||||
r_uniform_vec4("joy_pointers", re(in_processed), im(in_processed), re(in_raw), im(in_raw));
|
||||
r_color4(1, 1, 1, 1);
|
||||
} else {
|
||||
r_uniform_vec4("joy_pointers", 69, 69, 69, 69);
|
||||
r_color4(0.7, 0.7, 0.7, 0.7);
|
||||
}
|
||||
|
||||
r_draw_quad();
|
||||
r_mat_mv_pop();
|
||||
|
||||
char buf[128];
|
||||
GamepadButton test_btn = options_gamepad_testing_button();
|
||||
|
||||
if(ctx->gamepad_testmode.active) {
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Press any button on your gamepad to exit joystick testing mode");
|
||||
} else {
|
||||
snprintf(buf, sizeof(buf),
|
||||
"Press %s on your gamepad to enter joystick testing mode",
|
||||
gamepad_button_name(test_btn));
|
||||
}
|
||||
|
||||
text_draw(buf, &(TextParams) {
|
||||
.pos = { SCREEN_W/2.0f, SCREEN_H - OPTIONS_Y_MARGIN },
|
||||
.align = ALIGN_CENTER,
|
||||
.shader = "text_default",
|
||||
.color = RGBA(0.7, 0.7, 0.7, 0.7),
|
||||
});
|
||||
}
|
||||
|
||||
static MenuData* create_options_menu_gamepad(MenuData *parent) {
|
||||
MenuData *m = create_options_menu_base("Gamepad Options");
|
||||
m->end = destroy_options_menu_gamepad;
|
||||
|
||||
OptionsMenuContext *ctx = m->context;
|
||||
ctx->data = strdup(config_get_str(CONFIG_GAMEPAD_DEVICE));
|
||||
ctx->draw_overlay = draw_gamepad_options_overlay;
|
||||
ctx->gamepad_testmode.allowed = true;
|
||||
|
||||
OptionBinding *b;
|
||||
|
||||
|
@ -903,15 +986,21 @@ static MenuData* create_options_menu_gamepad(MenuData *parent) {
|
|||
|
||||
add_menu_separator(m);
|
||||
|
||||
add_menu_entry(m, "Axes mode", do_nothing,
|
||||
b = bind_option(CONFIG_GAMEPAD_AXIS_FREE, bind_common_onoff_get, bind_common_onoff_set)
|
||||
); bind_addvalue(b, "free");
|
||||
bind_addvalue(b, "restricted");
|
||||
|
||||
add_menu_entry(m, "Remap square input into circular", do_nothing,
|
||||
b = bind_option(CONFIG_GAMEPAD_AXIS_SQUARECIRCLE, bind_common_onoff_get, bind_gamepad_set)
|
||||
); bind_onoff(b);
|
||||
|
||||
add_menu_separator(m);
|
||||
|
||||
add_menu_entry(m, "Direction snap factor", do_nothing,
|
||||
b = bind_scale(CONFIG_GAMEPAD_AXIS_SNAP, 0, 1, 0.05)
|
||||
);
|
||||
|
||||
add_menu_entry(m, "Diagonal bias", do_nothing,
|
||||
b = bind_scale(CONFIG_GAMEPAD_AXIS_SNAP_DIAG_BIAS, -1, 1, 0.05)
|
||||
); b->pad++;
|
||||
|
||||
|
||||
add_menu_separator(m);
|
||||
|
||||
add_menu_entry(m, "Dead zone", do_nothing,
|
||||
|
@ -1140,7 +1229,7 @@ void draw_options_menu_bg(MenuData* menu) {
|
|||
static void update_options_menu(MenuData *menu) {
|
||||
menu->drawdata[0] += ((SCREEN_W/2 - 100) - menu->drawdata[0])/10.0;
|
||||
menu->drawdata[1] += ((SCREEN_W - 200) * 1.75 - menu->drawdata[1])/10.0;
|
||||
menu->drawdata[2] += (20*menu->cursor - menu->drawdata[2])/10.0;
|
||||
menu->drawdata[2] += (OPTIONS_ITEM_HEIGHT*menu->cursor - menu->drawdata[2])/10.0;
|
||||
|
||||
animate_menu_list_entries(menu);
|
||||
|
||||
|
@ -1185,12 +1274,13 @@ static void options_draw_item(MenuEntry *e, int i, int cnt, void *ctx) {
|
|||
}
|
||||
|
||||
text_draw(e->name, &(TextParams) {
|
||||
.pos = { (1 + (bind ? bind->pad : 0)) * 20 - e->drawdata, 20*i },
|
||||
.pos = { (1 + (bind ? bind->pad : 0)) * OPTIONS_ACTIVE_X_OFFSET - e->drawdata, OPTIONS_ITEM_HEIGHT*i },
|
||||
.color = &clr,
|
||||
});
|
||||
|
||||
if(bind) {
|
||||
int j, origin = SCREEN_W - 220;
|
||||
float margin = OPTIONS_X_MARGIN * 2 + OPTIONS_ACTIVE_X_OFFSET;
|
||||
float origin = SCREEN_W - margin;
|
||||
|
||||
switch(bind->type) {
|
||||
case BT_IntValue: {
|
||||
|
@ -1204,7 +1294,7 @@ static void options_draw_item(MenuEntry *e, int i, int cnt, void *ctx) {
|
|||
val,
|
||||
ALIGN_RIGHT,
|
||||
origin,
|
||||
20*i,
|
||||
OPTIONS_ITEM_HEIGHT*i,
|
||||
fnt_int,
|
||||
fnt_fract,
|
||||
&clr,
|
||||
|
@ -1212,7 +1302,7 @@ static void options_draw_item(MenuEntry *e, int i, int cnt, void *ctx) {
|
|||
false
|
||||
);
|
||||
} else if(bind->values) {
|
||||
for(j = bind->displaysingle? val : bind->valrange_max; (j+1) && (!bind->displaysingle || j == val); --j) {
|
||||
for(int j = bind->displaysingle? val : bind->valrange_max; (j+1) && (!bind->displaysingle || j == val); --j) {
|
||||
if(j != bind->valrange_max && !bind->displaysingle) {
|
||||
origin -= text_width(res_font("standard"), bind->values[j+1], 0) + 5;
|
||||
}
|
||||
|
@ -1224,7 +1314,7 @@ static void options_draw_item(MenuEntry *e, int i, int cnt, void *ctx) {
|
|||
}
|
||||
|
||||
text_draw(bind->values[j], &(TextParams) {
|
||||
.pos = { origin, 20*i },
|
||||
.pos = { origin, OPTIONS_ITEM_HEIGHT*i },
|
||||
.align = ALIGN_RIGHT,
|
||||
.color = &clr,
|
||||
});
|
||||
|
@ -1245,7 +1335,7 @@ static void options_draw_item(MenuEntry *e, int i, int cnt, void *ctx) {
|
|||
case BT_KeyBinding: {
|
||||
if(bind->blockinput) {
|
||||
text_draw("Press a key to assign, ESC to cancel", &(TextParams) {
|
||||
.pos = { origin, 20*i },
|
||||
.pos = { origin, OPTIONS_ITEM_HEIGHT*i },
|
||||
.align = ALIGN_RIGHT,
|
||||
.color = RGBA(0.5, 1, 0.5, 1),
|
||||
});
|
||||
|
@ -1257,7 +1347,7 @@ static void options_draw_item(MenuEntry *e, int i, int cnt, void *ctx) {
|
|||
}
|
||||
|
||||
text_draw(txt, &(TextParams) {
|
||||
.pos = { origin, 20*i },
|
||||
.pos = { origin, OPTIONS_ITEM_HEIGHT*i },
|
||||
.align = ALIGN_RIGHT,
|
||||
.color = &clr,
|
||||
});
|
||||
|
@ -1294,10 +1384,10 @@ static void options_draw_item(MenuEntry *e, int i, int cnt, void *ctx) {
|
|||
}
|
||||
|
||||
text_draw(txt, &(TextParams) {
|
||||
.pos = { origin, 20*i },
|
||||
.pos = { origin, OPTIONS_ITEM_HEIGHT*i },
|
||||
.align = ALIGN_RIGHT,
|
||||
.color = &clr,
|
||||
.max_width = (SCREEN_W - 220) / 2,
|
||||
.max_width = (SCREEN_W - margin) / 2,
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1319,10 +1409,10 @@ static void options_draw_item(MenuEntry *e, int i, int cnt, void *ctx) {
|
|||
char buf[64];
|
||||
snprintf(buf, sizeof(buf), "#%i: %s", bind->selected + 1, video_display_name(bind->selected));
|
||||
text_draw(buf, &(TextParams) {
|
||||
.pos = { origin, 20*i },
|
||||
.pos = { origin, OPTIONS_ITEM_HEIGHT*i },
|
||||
.align = ALIGN_RIGHT,
|
||||
.color = &clr,
|
||||
.max_width = (SCREEN_W - 220) / 2,
|
||||
.max_width = (SCREEN_W - margin) / 2,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
@ -1336,7 +1426,7 @@ static void options_draw_item(MenuEntry *e, int i, int cnt, void *ctx) {
|
|||
: "Press a button to assign, Back to cancel";
|
||||
|
||||
text_draw(text, &(TextParams) {
|
||||
.pos = { origin, 20*i },
|
||||
.pos = { origin, OPTIONS_ITEM_HEIGHT*i },
|
||||
.align = ALIGN_RIGHT,
|
||||
.color = RGBA(0.5, 1, 0.5, 1),
|
||||
});
|
||||
|
@ -1344,13 +1434,13 @@ static void options_draw_item(MenuEntry *e, int i, int cnt, void *ctx) {
|
|||
int id = config_get_int(bind->configentry);
|
||||
const char *name = (is_axis ? gamepad_axis_name(id) : gamepad_button_name(id));
|
||||
text_draw(name, &(TextParams) {
|
||||
.pos = { origin, 20*i },
|
||||
.pos = { origin, OPTIONS_ITEM_HEIGHT*i },
|
||||
.align = ALIGN_RIGHT,
|
||||
.color = &clr,
|
||||
});
|
||||
} else {
|
||||
text_draw("Unbound", &(TextParams) {
|
||||
.pos = { origin, 20*i },
|
||||
.pos = { origin, OPTIONS_ITEM_HEIGHT*i },
|
||||
.align = ALIGN_RIGHT,
|
||||
.color = &clr,
|
||||
});
|
||||
|
@ -1362,14 +1452,14 @@ static void options_draw_item(MenuEntry *e, int i, int cnt, void *ctx) {
|
|||
if(bind->blockinput) {
|
||||
if(*bind->strvalue) {
|
||||
text_draw(bind->strvalue, &(TextParams) {
|
||||
.pos = { origin, 20*i },
|
||||
.pos = { origin, OPTIONS_ITEM_HEIGHT*i },
|
||||
.align = ALIGN_RIGHT,
|
||||
.color = RGBA(0.5, 1, 0.5, 1.0),
|
||||
});
|
||||
}
|
||||
} else {
|
||||
text_draw(config_get_str(bind->configentry), &(TextParams) {
|
||||
.pos = { origin, 20*i },
|
||||
.pos = { origin, OPTIONS_ITEM_HEIGHT*i },
|
||||
.align = ALIGN_RIGHT,
|
||||
.color = &clr,
|
||||
});
|
||||
|
@ -1391,7 +1481,7 @@ static void options_draw_item(MenuEntry *e, int i, int cnt, void *ctx) {
|
|||
|
||||
snprintf(tmp, sizeof(tmp), "%dx%d", w, h);
|
||||
text_draw(tmp, &(TextParams) {
|
||||
.pos = { origin, 20*i },
|
||||
.pos = { origin, OPTIONS_ITEM_HEIGHT*i },
|
||||
.align = ALIGN_RIGHT,
|
||||
.color = &clr,
|
||||
});
|
||||
|
@ -1403,7 +1493,7 @@ static void options_draw_item(MenuEntry *e, int i, int cnt, void *ctx) {
|
|||
char tmp[32];
|
||||
snprintf(tmp, sizeof(tmp), "%gx %dx%d", video_get_scaling_factor(), fbsize.w, fbsize.h);
|
||||
text_draw(tmp, &(TextParams) {
|
||||
.pos = { origin, 20*i },
|
||||
.pos = { origin, OPTIONS_ITEM_HEIGHT*i },
|
||||
.align = ALIGN_RIGHT,
|
||||
.color = &clr,
|
||||
});
|
||||
|
@ -1427,7 +1517,7 @@ static void options_draw_item(MenuEntry *e, int i, int cnt, void *ctx) {
|
|||
strcpy(tmp, "0%");
|
||||
|
||||
r_mat_mv_push();
|
||||
r_mat_mv_translate(origin - (w+cw) * 0.5, 20 * i, 0);
|
||||
r_mat_mv_translate(origin - (w+cw) * 0.5, OPTIONS_ITEM_HEIGHT * i, 0);
|
||||
text_draw(tmp, &(TextParams) {
|
||||
.pos = { -((w+cw) * 0.5 + 10), 0 },
|
||||
.align = ALIGN_RIGHT,
|
||||
|
@ -1457,9 +1547,15 @@ static void draw_options_menu(MenuData *menu) {
|
|||
r_state_push();
|
||||
draw_options_menu_bg(menu);
|
||||
draw_menu_title(menu, ctx->title);
|
||||
draw_menu_list(menu, 100, 100, options_draw_item, SCREEN_H * 1.1, menu);
|
||||
draw_menu_list(menu, OPTIONS_X_MARGIN, OPTIONS_Y_MARGIN, options_draw_item, SCREEN_H * 1.1, menu);
|
||||
r_state_pop();
|
||||
|
||||
if(ctx->draw_overlay) {
|
||||
r_state_push();
|
||||
ctx->draw_overlay(menu, ctx);
|
||||
r_state_pop();
|
||||
}
|
||||
|
||||
if(ctx->submenu_fading) {
|
||||
r_state_push();
|
||||
ctx->submenu_fading->draw(ctx->submenu_fading);
|
||||
|
@ -1707,9 +1803,21 @@ static bool options_text_input_handler(SDL_Event *event, void *arg) {
|
|||
|
||||
static bool options_input_handler(SDL_Event *event, void *arg) {
|
||||
MenuData *menu = arg;
|
||||
OptionsMenuContext *ctx = menu->context;
|
||||
OptionBinding *bind = bind_get(menu, menu->cursor);
|
||||
TaiseiEvent type = TAISEI_EVENT(event->type);
|
||||
|
||||
switch(type) {
|
||||
case TE_MENU_CURSOR_UP:
|
||||
case TE_MENU_CURSOR_DOWN:
|
||||
case TE_MENU_CURSOR_LEFT:
|
||||
case TE_MENU_CURSOR_RIGHT:
|
||||
if((intptr_t)event->user.data1 == INDEV_GAMEPAD && ctx->gamepad_testmode.active) {
|
||||
return false;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
|
||||
switch(type) {
|
||||
case TE_MENU_CURSOR_UP:
|
||||
case TE_MENU_CURSOR_DOWN:
|
||||
|
@ -1784,6 +1892,26 @@ static bool options_input_handler(SDL_Event *event, void *arg) {
|
|||
close_menu(menu);
|
||||
break;
|
||||
|
||||
case TE_GAMEPAD_BUTTON_DOWN:
|
||||
if(ctx->gamepad_testmode.allowed) {
|
||||
if(ctx->gamepad_testmode.active) {
|
||||
switch(event->user.code) {
|
||||
case GAMEPAD_BUTTON_ANALOG_STICK_DOWN:
|
||||
case GAMEPAD_BUTTON_ANALOG_STICK_LEFT:
|
||||
case GAMEPAD_BUTTON_ANALOG_STICK_RIGHT:
|
||||
case GAMEPAD_BUTTON_ANALOG_STICK_UP:
|
||||
break;
|
||||
default:
|
||||
ctx->gamepad_testmode.active = false;
|
||||
}
|
||||
} else {
|
||||
if(event->user.code == options_gamepad_testing_button()) {
|
||||
ctx->gamepad_testmode.active = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue