diff --git a/port/linux/include/halo_vr.h b/port/linux/include/halo_vr.h index 0b20e58a..ae8f9123 100644 --- a/port/linux/include/halo_vr.h +++ b/port/linux/include/halo_vr.h @@ -252,6 +252,39 @@ int halo_vr_controller_layout(void); /* the hand that holds the weapon (HALO_XR_HAND_*: 1 the right, 0 the left, vr.handedness) */ int halo_vr_weapon_hand(void); +/* The game's actions on the controllers' buttons (vr.button_*; the VR +options' BUTTONS page): each action on one of seven buttons, named as +right-handed (A and B on the right controller, X and Y, the Quest's, on +the left; the move stick's click, the turning stick's, the weapon hand's +grip); left-handed with vr.mirror_controls, their mirror images. The +actions are numbered by the button each has by default. The triggers, the +free hand's grip, the menu button and the Steam Frame's bumpers, D-pad and +View button keep theirs; the menus keep the buttons as they are. */ +enum +{ + HALO_VR_INPUT_A = 0, + HALO_VR_INPUT_B, + HALO_VR_INPUT_X, + HALO_VR_INPUT_Y, + HALO_VR_INPUT_MOVE_STICK, + HALO_VR_INPUT_TURN_STICK, + HALO_VR_INPUT_GRIP, + HALO_VR_INPUT_COUNT, + + HALO_VR_ACTION_JUMP = HALO_VR_INPUT_A, + HALO_VR_ACTION_LIGHT = HALO_VR_INPUT_B, /* a tap the flashlight; held, switch grenades */ + HALO_VR_ACTION_ACTION = HALO_VR_INPUT_X, /* action, reload */ + HALO_VR_ACTION_WEAPON = HALO_VR_INPUT_Y, /* switch weapon */ + HALO_VR_ACTION_CROUCH = HALO_VR_INPUT_MOVE_STICK, + HALO_VR_ACTION_ZOOM = HALO_VR_INPUT_TURN_STICK, + HALO_VR_ACTION_MELEE = HALO_VR_INPUT_GRIP, + HALO_VR_ACTION_COUNT = HALO_VR_INPUT_COUNT, +}; +/* an action's setting ("vr.button_jump"), a button's value in it ("a"), +and the button an action is on, as set */ +const char *halo_vr_action_setting(int action); +const char *halo_vr_input_value(int input); +int halo_vr_action_input(int action); /* the graphics preset (vr.graphics; HALO_VR_GRAPHICS_HIGH outside VR): which effects are drawn (port/linux/game/vr_graphics.c), and with "max" the models lit for each pixel and larger shadows (d3d8_gl.c) */ diff --git a/port/linux/src/port_config.c b/port/linux/src/port_config.c index c9295dfc..1d3a679f 100644 --- a/port/linux/src/port_config.c +++ b/port/linux/src/port_config.c @@ -201,6 +201,28 @@ static const struct config_setting config_settings[] = "and the left one turns, X jumps and A reloads (on the Steam Frame the\n" "D-pad takes A, B, X and Y as their mirror image); false keeps them as\n" "right-handed. The menus keep their buttons either way." }, + { "vr.button_jump", _config_string, "\"a\"", "HALO_VR_BUTTON_JUMP", _environment_value, _platform_android, + "The button each of the game's actions is on (the VR options' BUTTONS\n" + "page swaps them): \"a\", \"b\", \"x\", \"y\" (A and B on the right\n" + "controller, X and Y the Quest's on the left; the Steam Frame has all\n" + "four on the right), \"move_stick\" and \"turn_stick\" (their clicks),\n" + "\"grip\" (the weapon hand's), named as right-handed: left-handed with\n" + "mirror_controls, their mirror images. Jump," }, + { "vr.button_light", _config_string, "\"b\"", "HALO_VR_BUTTON_LIGHT", _environment_value, _platform_android, + "the flashlight (held: switch grenades)," }, + { "vr.button_action", _config_string, "\"x\"", "HALO_VR_BUTTON_ACTION", _environment_value, _platform_android, + "action and reload," }, + { "vr.button_weapon", _config_string, "\"y\"", "HALO_VR_BUTTON_WEAPON", _environment_value, _platform_android, + "switch weapon," }, + { "vr.button_crouch", _config_string, "\"move_stick\"", "HALO_VR_BUTTON_CROUCH", _environment_value, + _platform_android, + "crouch," }, + { "vr.button_zoom", _config_string, "\"turn_stick\"", "HALO_VR_BUTTON_ZOOM", _environment_value, + _platform_android, + "zoom" }, + { "vr.button_melee", _config_string, "\"grip\"", "HALO_VR_BUTTON_MELEE", _environment_value, _platform_android, + "and melee. The triggers, the free hand's grip and the menu button keep\n" + "theirs, as do the menus." }, { "vr.laser", _config_boolean, "false", "HALO_VR_LASER", _environment_value, _platform_android, "A laser from the weapon to what it aims at (with aim_with = \"controller\")." }, { "vr.crosshair", _config_boolean, "true", "HALO_VR_CROSSHAIR", _environment_value, _platform_android, @@ -573,6 +595,9 @@ static const struct config_setting config_settings[] = "The headset's layer copies: 1 deferred (no waits for the GL thread or the\n" "GPU in the middle of the frame), 0 each one restored and released at once,\n" "-1 as the runtime needs (deferred on SteamVR). Read when the app starts." }, + { "debug.vr_options", _config_boolean, "false", "HALO_DEBUG_VR_OPTIONS", _environment_value, _platform_android, + "Off the headset (the emulator), show the VR options panel on the main\n" + "and pause menus all the same, for checking its pages." }, { "debug.mirror_first_person", _config_boolean, "false", "HALO_MIRROR_FIRST_PERSON", _environment_value, _platform_android, "Off the headset (flat), the first-person weapon drawn mirrored, as the\n" diff --git a/port/linux/src/vr.c b/port/linux/src/vr.c index b24769cc..7eadca34 100644 --- a/port/linux/src/vr.c +++ b/port/linux/src/vr.c @@ -17,6 +17,28 @@ yaw plus the head's. #include "halo_vr.h" +/* the actions' settings and the buttons' values in them (halo_vr.h's +HALO_VR_ACTION_* and HALO_VR_INPUT_*), by number */ +static const char *const vr_action_settings[HALO_VR_ACTION_COUNT] = +{ + "vr.button_jump", "vr.button_light", "vr.button_action", "vr.button_weapon", "vr.button_crouch", + "vr.button_zoom", "vr.button_melee", +}; +static const char *const vr_input_values[HALO_VR_INPUT_COUNT] = +{ + "a", "b", "x", "y", "move_stick", "turn_stick", "grip", +}; + +const char *halo_vr_action_setting(int action) +{ + return action >= 0 && action < HALO_VR_ACTION_COUNT ? vr_action_settings[action] : ""; +} + +const char *halo_vr_input_value(int input) +{ + return input >= 0 && input < HALO_VR_INPUT_COUNT ? vr_input_values[input] : ""; +} + #ifdef HALO_ANDROID #include "platform.h" @@ -220,9 +242,9 @@ static struct /* until when a melee attack aims where the head looks: its damage is dealt some ticks into the attack, along the unit's aim */ unsigned long long melee_aim_until_us; - int melee_grip_down; + int melee_button_down; float melee_strength; /* 0..1, the last attack's swing */ - unsigned long long melee_jab_start_us; /* the weapon hand's grip's attack: the weapon jabs */ + unsigned long long melee_jab_start_us; /* the melee button's attack: the weapon jabs */ /* the laser along the barrel, for the frame it was set in */ unsigned long frame_number; unsigned long laser_frame; @@ -250,6 +272,9 @@ static struct vr.mirror_controls the sticks and face buttons mirrored too */ int left_handed; int mirror_controls; + /* the button each action is on (vr.button_*: HALO_VR_INPUT_* by + HALO_VR_ACTION_*) */ + int action_inputs[HALO_VR_ACTION_COUNT]; /* vr.refresh_rate as last asked for (the VR options change it), and vr.resolution_scale as the app started with it */ float refresh_rate_wanted; @@ -307,6 +332,7 @@ static int name_pose(const float point[3], struct halo_xr_pose *pose, float *wid static int weapon_hand_tracked(short local_player_index); static void controller_axes(float forward[3], float up[3], float right[3]); static void hand_offset(float out[3]); +static void controls_in_play(struct halo_xr_hand hands[HALO_XR_HAND_COUNT]); /* the hand that holds the weapon, the free one (the grenade, the foregrip), and the one whose stick turns the body and steers */ @@ -517,6 +543,12 @@ static void vr_read_live_settings(void) vr.left_handed = left_handed; vr.mirror_controls = config_boolean("vr.mirror_controls"); } + { + int action; + + for (action = 0; action < HALO_VR_ACTION_COUNT; action++) + vr.action_inputs[action] = halo_vr_action_input(action); + } { /* (the refresh rate changes at once where the runtime lets it) */ float refresh_rate = (float)config_real("vr.refresh_rate"); @@ -619,6 +651,22 @@ int halo_vr_weapon_hand(void) return weapon_hand(); } +int halo_vr_action_input(int action) +{ + const char *value; + int input; + + if (action < 0 || action >= HALO_VR_ACTION_COUNT) + return HALO_VR_INPUT_A; + value = config_string(vr_action_settings[action]); + for (input = 0; input < HALO_VR_INPUT_COUNT; input++) + { + if (!strcmp(value, vr_input_values[input])) + return input; + } + return action; +} + int halo_vr_refresh_rates(float *rates, int maximum, float *current) { *current = 0.0f; @@ -980,18 +1028,21 @@ static void melee_update(void) vr.hand_last_us = now; { - /* the weapon hand's grip's melee attack (xinput_sdl.c) aims the - same way */ - int grip_down = vr.frame.hands[weapon_hand()].active && - (vr.frame.hands[weapon_hand()].buttons & HALO_XR_BUTTON_SQUEEZE); + /* the melee button's attack (xinput_sdl.c; the weapon hand's grip + unless vr.button_melee moves it) aims the same way */ + struct halo_xr_hand hands[HALO_XR_HAND_COUNT]; + int button_down; - if (grip_down && !vr.melee_grip_down) + memcpy(hands, vr.frame.hands, sizeof(hands)); + controls_in_play(hands); + button_down = (hands[HALO_XR_HAND_RIGHT].buttons & HALO_XR_BUTTON_SQUEEZE) != 0; + if (button_down && !vr.melee_button_down) { vr.melee_aim_until_us = now + 700000; vr.melee_strength = 0.75f; vr.melee_jab_start_us = now; } - vr.melee_grip_down = grip_down; + vr.melee_button_down = button_down; } quaternion_rotate(vr.frame.head.orientation, look, facing); facing[1] = 0.0f; @@ -3046,6 +3097,51 @@ static void controllers_mirrored(struct halo_xr_hand hands[HALO_XR_HAND_COUNT]) } } +/* The buttons the actions are on (vr.button_*), as the gamepad's sides +have them once mirrored: A and B the right hand's, X and Y the left's +A and B, the move stick's click the left's, the turning stick's the +right's, the grip the right's (the weapon hand's). Each action's button +is read and handed on as the button the action has by default, which +xinput_sdl.c turns into it; with the defaults nothing changes. The grips +holding a Warthog's wheel press nothing. */ +static const int input_hands[HALO_VR_INPUT_COUNT] = +{ + HALO_XR_HAND_RIGHT, HALO_XR_HAND_RIGHT, HALO_XR_HAND_LEFT, HALO_XR_HAND_LEFT, HALO_XR_HAND_LEFT, + HALO_XR_HAND_RIGHT, HALO_XR_HAND_RIGHT, +}; +static const int input_buttons[HALO_VR_INPUT_COUNT] = +{ + HALO_XR_BUTTON_A, HALO_XR_BUTTON_B, HALO_XR_BUTTON_A, HALO_XR_BUTTON_B, HALO_XR_BUTTON_THUMBSTICK, + HALO_XR_BUTTON_THUMBSTICK, HALO_XR_BUTTON_SQUEEZE, +}; + +static void controls_remapped(struct halo_xr_hand hands[HALO_XR_HAND_COUNT]) +{ + int down[HALO_VR_INPUT_COUNT]; + int input, action; + + for (input = 0; input < HALO_VR_INPUT_COUNT; input++) + down[input] = (hands[input_hands[input]].buttons & input_buttons[input]) != 0; + if (vr.wheel_hands) + down[HALO_VR_INPUT_GRIP] = 0; + for (input = 0; input < HALO_VR_INPUT_COUNT; input++) + hands[input_hands[input]].buttons &= ~input_buttons[input]; + for (action = 0; action < HALO_VR_ACTION_COUNT; action++) + { + if (down[vr.action_inputs[action]]) + hands[input_hands[action]].buttons |= input_buttons[action]; + } +} + +/* the controllers as the game is played with them: mirrored (left-handed) +and the actions on their buttons */ +static void controls_in_play(struct halo_xr_hand hands[HALO_XR_HAND_COUNT]) +{ + if (vr.left_handed) + controllers_mirrored(hands); + controls_remapped(hands); +} + /* the controllers, for the gamepad the game reads (xinput_sdl.c) */ int halo_vr_controllers(struct halo_xr_hand hands[HALO_XR_HAND_COUNT], int *menus) { @@ -3053,8 +3149,8 @@ int halo_vr_controllers(struct halo_xr_hand hands[HALO_XR_HAND_COUNT], int *menu return 0; memcpy(hands, vr.frame.hands, sizeof(vr.frame.hands)); *menus = vr.last_overlay_mode == HALO_XR_OVERLAY_SCREEN; - if (vr.left_handed && !*menus) - controllers_mirrored(hands); + if (!*menus) + controls_in_play(hands); return 1; } @@ -3062,6 +3158,7 @@ int halo_vr_controllers(struct halo_xr_hand hands[HALO_XR_HAND_COUNT], int *menu int halo_vr_controller_layout(void) { return 0; } int halo_vr_weapon_hand(void) { return 1; } +int halo_vr_action_input(int action) { return action; } int halo_vr_graphics(void) { return HALO_VR_GRAPHICS_HIGH; } int halo_vr_refresh_rates(float *rates, int maximum, float *current) { diff --git a/port/linux/src/xinput_sdl.c b/port/linux/src/xinput_sdl.c index 758cb8de..e3c308ec 100644 --- a/port/linux/src/xinput_sdl.c +++ b/port/linux/src/xinput_sdl.c @@ -859,6 +859,11 @@ face buttons too (the right stick moves, X jumps, A reloads; on the Steam Frame the left D-pad takes A, B, X and Y). Here "right" and "left" are the sides so handed over. +The VR options' BUTTONS page (vr.button_*) moves jump, the flashlight, +action, switch weapon, crouch, zoom and melee between A, B, X, Y, the +stick clicks and the weapon hand's grip: vr.c hands each action's button +over as the one above, so "A" here is the jump wherever it is. + In the menus the buttons are the Xbox's by position: A accepts, B goes back. */ #include "guest_host.h" @@ -937,9 +942,8 @@ static void vr_gamepad_state(XINPUT_GAMEPAD *pad) } merge_button(pad, XINPUT_GAMEPAD_A, (right->buttons & HALO_XR_BUTTON_A) != 0); merge_button(pad, XINPUT_GAMEPAD_X, (left->buttons & HALO_XR_BUTTON_A) != 0); - /* (the grips holding a Warthog's wheel are not a blow) */ - merge_button(pad, XINPUT_GAMEPAD_B, - ((right->buttons & HALO_XR_BUTTON_SQUEEZE) != 0 && !halo_vr_wheel_held()) || halo_vr_melee()); + /* (the grips holding a Warthog's wheel are not a blow: vr.c) */ + merge_button(pad, XINPUT_GAMEPAD_B, (right->buttons & HALO_XR_BUTTON_SQUEEZE) != 0 || halo_vr_melee()); merge_button(pad, XINPUT_GAMEPAD_Y, (left->buttons & HALO_XR_BUTTON_B) != 0); /* (the Steam Frame's bumpers: the left switches grenades, the right is the flashlight) */ diff --git a/source/interface/ui_widget.c b/source/interface/ui_widget.c index 72e81b11..fb3ea8be 100644 --- a/source/interface/ui_widget.c +++ b/source/interface/ui_widget.c @@ -6648,6 +6648,7 @@ double config_real(char const *name); char const *config_string(char const *name); int config_write(char const *name, char const *value); int config_write_boolean(char const *name, int value); +int config_default(char const *name, char *text, size_t size); enum { @@ -6663,6 +6664,10 @@ enum _vr_option_motion_tracker, _vr_option_body, _vr_option_fingers, + /* (the game's actions on the buttons, HALO_VR_ACTION_*'s order) */ + _vr_option_button_first, + _vr_option_button_last = _vr_option_button_first + HALO_VR_ACTION_COUNT - 1, + _vr_option_buttons_reset, _vr_option_graphics, _vr_option_refresh_rate, _vr_option_resolution, @@ -6675,7 +6680,16 @@ enum VR_OPTIONS_ROW_HEIGHT = 22, VR_OPTIONS_FOOTER = 362, VR_OPTIONS_PER_PAGE = 12, - NUMBER_OF_VR_OPTION_PAGES = 2, + + _vr_options_page_play = 0, + _vr_options_page_buttons, + _vr_options_page_more, + NUMBER_OF_VR_OPTION_PAGES, + + /* (the pages' tabs, beside the title, as wide as there is room) */ + VR_OPTIONS_TABS_LEFT = 250, + VR_OPTIONS_TABS_RIGHT = 522, + VR_OPTIONS_TAB_WIDTH = (VR_OPTIONS_TABS_RIGHT - VR_OPTIONS_TABS_LEFT) / NUMBER_OF_VR_OPTION_PAGES, _vr_controls_on_foot = 0, _vr_controls_vehicles, @@ -6694,14 +6708,19 @@ static struct } ui_vr_options_globals; /* the panel's pages (its tabs), their rows in order (NONE past the last) */ -static char const *const vr_option_page_names[NUMBER_OF_VR_OPTION_PAGES] = { "PLAY", "MORE" }; +static char const *const vr_option_page_names[NUMBER_OF_VR_OPTION_PAGES] = { "PLAY", "BUTTONS", "MORE" }; static short const vr_option_pages[NUMBER_OF_VR_OPTION_PAGES][VR_OPTIONS_PER_PAGE] = { { _vr_option_turning, _vr_option_snap_angle, _vr_option_smooth_speed, _vr_option_vignette, _vr_option_wheel, _vr_option_handedness, _vr_option_mirror_controls, _vr_option_laser, _vr_option_crosshair, _vr_option_motion_tracker, _vr_option_body, _vr_option_fingers }, - { _vr_option_graphics, _vr_option_refresh_rate, _vr_option_resolution, _vr_option_controls, _vr_option_quit, - NONE, NONE, NONE, NONE, NONE, NONE, NONE }, + { _vr_option_button_first + HALO_VR_ACTION_JUMP, _vr_option_button_first + HALO_VR_ACTION_LIGHT, + _vr_option_button_first + HALO_VR_ACTION_ACTION, _vr_option_button_first + HALO_VR_ACTION_WEAPON, + _vr_option_button_first + HALO_VR_ACTION_CROUCH, _vr_option_button_first + HALO_VR_ACTION_ZOOM, + _vr_option_button_first + HALO_VR_ACTION_MELEE, _vr_option_buttons_reset, _vr_option_controls, + NONE, NONE, NONE }, + { _vr_option_graphics, _vr_option_refresh_rate, _vr_option_resolution, _vr_option_quit, + NONE, NONE, NONE, NONE, NONE, NONE, NONE, NONE }, }; /* the page a row is on, and its place there */ @@ -6754,6 +6773,113 @@ static boolean ui_vr_controls_mirrored( return ui_vr_left_handed() && config_boolean("vr.mirror_controls"); } +/* Where a button the game's actions go on (HALO_VR_INPUT_*) is, as the +hands are set: its controller (0 the left, 1 the right) and its name, +short under its controller on the controls page, or full in the +options' rows. On the Quest A and B are on the right, X and Y on the +left, and mirrored the other way round; on the Steam Frame all four are +on the right, and mirrored the left D-pad takes them (down, left, right, +up). The sticks' clicks change sides with them; the grip is the weapon +hand's. */ +static char const *ui_vr_input_name( + short input, + boolean full, + short *column) +{ + static char const *const buttons[] = { "A", "B", "X", "Y" }; + static char const *const quest_mirrored[] = { "X", "Y", "A", "B" }; + static char const *const frame_mirrored[] = { "D-pad down", "D-pad left", "D-pad right", "D-pad up" }; + boolean frame = halo_vr_controller_layout() == 1; + boolean mirrored = ui_vr_controls_mirrored(); + short side; + char const *name; + + switch (input) + { + case HALO_VR_INPUT_MOVE_STICK: + case HALO_VR_INPUT_TURN_STICK: + side = (short)((input == HALO_VR_INPUT_TURN_STICK) != (mirrored != 0)); + name = full ? (side ? "Right stick click" : "Left stick click") : "Stick click"; + break; + case HALO_VR_INPUT_GRIP: + side = (short)(ui_vr_left_handed() ? 0 : 1); + name = full ? (side ? "Right grip" : "Left grip") : "Grip"; + break; + default: + input = (short)PIN(input, HALO_VR_INPUT_A, HALO_VR_INPUT_Y); + if (frame) + { + side = (short)(mirrored ? 0 : 1); + name = mirrored ? frame_mirrored[input] : buttons[input]; + } + else + { + side = (short)((input <= HALO_VR_INPUT_B) != (mirrored != 0)); + name = mirrored ? quest_mirrored[input] : buttons[input]; + } + break; + } + if (column) + *column = side; + return name; +} + +/* the game's actions on the buttons (HALO_VR_ACTION_*'s order): as the +options' rows name them, and in short (the Steam Frame's stick lines) */ +static char const *const vr_action_names[HALO_VR_ACTION_COUNT] = +{ + "Jump", "Light; held: grenades", "Action, reload", "Switch weapon", "Crouch", "Zoom", "Melee", +}; +static char const *const vr_action_short_names[HALO_VR_ACTION_COUNT] = +{ + "jump", "light", "action", "weapon", "crouch", "zoom", "melee", +}; + +/* an action moved to the next button or the one before: the action that +was there takes its old one (so that every action keeps a button) */ +static void ui_vr_options_button_change( + short action, + short change) +{ + short input = (short)halo_vr_action_input(action); + short wanted = (short)((input + change + HALO_VR_INPUT_COUNT) % HALO_VR_INPUT_COUNT); + short other; + + for (other = 0; other < HALO_VR_ACTION_COUNT; other++) + { + if (other != action && halo_vr_action_input(other) == wanted) + config_write(halo_vr_action_setting(other), halo_vr_input_value(input)); + } + config_write(halo_vr_action_setting(action), halo_vr_input_value(wanted)); +} + +static boolean ui_vr_options_buttons_default( + void) +{ + short action; + + for (action = 0; action < HALO_VR_ACTION_COUNT; action++) + { + if (halo_vr_action_input(action) != action) + return FALSE; + } + return TRUE; +} + +static void ui_vr_options_buttons_reset( + void) +{ + short action; + + for (action = 0; action < HALO_VR_ACTION_COUNT; action++) + { + char text[32]; + + if (config_default(halo_vr_action_setting(action), text, sizeof(text))) + config_write(halo_vr_action_setting(action), text); + } +} + void platform_request_quit(void); static real const vr_snap_angles[] = { 30.0f, 45.0f, 60.0f, 90.0f }; @@ -6838,8 +6964,9 @@ static boolean ui_vr_options_screen( { struct widget_instance *screen = widget_globals.active_widgets[0]; - /* (the Xbox's main menu, or the PC version's: vr.menus) */ - return halo_vr_active() && screen && screen->name && + /* (the Xbox's main menu, or the PC version's: vr.menus; off the + headset with debug.vr_options, to check the pages) */ + return (halo_vr_active() || config_boolean("debug.vr_options")) && screen && screen->name && (main_menu_screen_is_active() || strstr(screen->name, "pause_game") != NULL || strcmp(screen->name, "main_menu") == 0); } @@ -6928,6 +7055,9 @@ static void ui_vr_options_change( case _vr_option_resolution: ui_vr_options_step("vr.resolution_scale", vr_resolution_scales, NUMBEROF(vr_resolution_scales), change); break; + case _vr_option_buttons_reset: + ui_vr_options_buttons_reset(); + break; case _vr_option_controls: ui_vr_options_globals.controls = TRUE; ui_vr_options_globals.page = _vr_controls_on_foot; @@ -6940,6 +7070,11 @@ static void ui_vr_options_change( ui_vr_options_globals.quit_armed = TRUE; break; } + if (ui_vr_options_globals.selection >= _vr_option_button_first && + ui_vr_options_globals.selection <= _vr_option_button_last) + { + ui_vr_options_button_change((short)(ui_vr_options_globals.selection - _vr_option_button_first), change); + } ui_play_audio_feedback_sound(_ui_audio_feedback_forward); } @@ -7056,7 +7191,8 @@ static boolean ui_vr_options_take_event( { ui_vr_options_change(1); } - else if (change && ui_vr_options_globals.selection != _vr_option_controls) + else if (change && ui_vr_options_globals.selection != _vr_option_controls && + ui_vr_options_globals.selection != _vr_option_buttons_reset) { ui_vr_options_change(change); } @@ -7113,10 +7249,11 @@ static boolean ui_vr_options_take_pointer( { short page = ui_vr_options_page_of(ui_vr_options_globals.selection, NULL); - if (pointer->left_clicks && y >= 68 && y < 88 && x >= 330 && x < 514) + if (pointer->left_clicks && y >= 68 && y < 88 && x >= VR_OPTIONS_TABS_LEFT && x < VR_OPTIONS_TABS_RIGHT) { /* (a tab: its page) */ - short tab = (short)PIN((x - 330) / 92, 0, NUMBER_OF_VR_OPTION_PAGES - 1); + short tab = (short)PIN((x - VR_OPTIONS_TABS_LEFT) / VR_OPTIONS_TAB_WIDTH, 0, + NUMBER_OF_VR_OPTION_PAGES - 1); if (tab != page) { @@ -7196,6 +7333,77 @@ static void ui_vr_controls_add( } } +/* the action on a button (vr.button_*; the swaps keep one on each) */ +static short ui_vr_input_action( + short input) +{ + short action; + + for (action = 0; action < HALO_VR_ACTION_COUNT; action++) + { + if (halo_vr_action_input(action) == input) + return action; + } + return input; +} + +/* in a vehicle: the action button gets in and out, the light's is the +flashlight */ +static void ui_vr_controls_add_vehicle_actions( + struct ui_vr_controls_column columns[2]) +{ + short column; + char const *name = ui_vr_input_name((short)halo_vr_action_input(HALO_VR_ACTION_ACTION), FALSE, &column); + + ui_vr_controls_add(&columns[column], name, "Get in, get out"); + name = ui_vr_input_name((short)halo_vr_action_input(HALO_VR_ACTION_LIGHT), FALSE, &column); + ui_vr_controls_add(&columns[column], name, "Flashlight"); +} + +/* an action's line under the controller its button is on (vr.button_*), as +the Quest's page words it or, shorter, the Steam Frame's (whose page has +more lines) */ +static void ui_vr_controls_add_action( + struct ui_vr_controls_column columns[2], + short action, + boolean frame) +{ + /* (one action is held: its name lives on with the column) */ + static char held[24]; + short column; + char const *name = ui_vr_input_name((short)halo_vr_action_input(action), FALSE, &column); + + switch (action) + { + case HALO_VR_ACTION_LIGHT: + if (frame) + { + ui_vr_controls_add(&columns[column], name, "Light; held: grenades"); + } + else + { + snprintf(held, sizeof(held), "%s held", strcmp(name, "Stick click") == 0 ? "Click" : name); + ui_vr_controls_add(&columns[column], name, "Flashlight"); + ui_vr_controls_add(&columns[column], held, "Switch grenades"); + } + break; + case HALO_VR_ACTION_MELEE: + if (frame) + { + ui_vr_controls_add(&columns[column], name, "Melee (or punch)"); + } + else + { + ui_vr_controls_add(&columns[column], name, "Melee (or punch"); + ui_vr_controls_add(&columns[column], "", "forward)"); + } + break; + default: + ui_vr_controls_add(&columns[column], name, vr_action_names[action]); + break; + } +} + static void ui_vr_controls_column_render( short x, short y, @@ -7264,26 +7472,27 @@ static void ui_vr_controls_render( { /* the Steam Frame's controllers: A, B, X, Y and a menu button on the right one, a D-pad and a View button on the left one; mirrored, the - D-pad does what A, B, X and Y do (down, left, right, up) */ - char const *jump = mirrored ? "D-pad down" : "A"; - char const *light = mirrored ? "D-pad left" : "B"; - char const *act = mirrored ? "D-pad right" : "X"; - char const *weapon = mirrored ? "D-pad up" : "Y"; - short buttons = mirrored ? 0 : 1; + D-pad does what A, B, X and Y do (down, left, right, up). The sticks' + clicks on the sticks' lines, to keep to the page's lines */ + static char move_line[32], turn_line[32]; switch (ui_vr_options_globals.page) { case _vr_controls_on_foot: - ui_vr_controls_add(&columns[move], "Stick", "Move; click: crouch"); - ui_vr_controls_add(&columns[turn], "Stick", "Turn; click: zoom"); + snprintf(move_line, sizeof(move_line), "Move; click: %s", + vr_action_short_names[ui_vr_input_action(HALO_VR_INPUT_MOVE_STICK)]); + snprintf(turn_line, sizeof(turn_line), "Turn; click: %s", + vr_action_short_names[ui_vr_input_action(HALO_VR_INPUT_TURN_STICK)]); + ui_vr_controls_add(&columns[move], "Stick", move_line); + ui_vr_controls_add(&columns[turn], "Stick", turn_line); ui_vr_controls_add(&columns[gun], "Trigger", "Fire"); ui_vr_controls_add(&columns[free_column], "Trigger", "Grenade: hold, throw"); - ui_vr_controls_add(&columns[gun], "Grip", "Melee (or punch)"); + ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_GRIP), TRUE); ui_vr_controls_add(&columns[free_column], "Grip", "Weapon in both hands"); - ui_vr_controls_add(&columns[buttons], jump, "Jump"); - ui_vr_controls_add(&columns[buttons], light, "Light; held: grenades"); - ui_vr_controls_add(&columns[buttons], act, "Action, reload"); - ui_vr_controls_add(&columns[buttons], weapon, "Switch weapon"); + ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_A), TRUE); + ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_B), TRUE); + ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_X), TRUE); + ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_Y), TRUE); ui_vr_controls_add(&columns[mirrored ? 0 : 1], "Bumper", "Flashlight"); ui_vr_controls_add(&columns[mirrored ? 1 : 0], "Bumper", "Switch grenades"); ui_vr_controls_add(&columns[0], "View", "Scores"); @@ -7302,8 +7511,7 @@ static void ui_vr_controls_render( ui_vr_controls_add(&columns[gun], "Trigger", "Fire"); ui_vr_controls_add(&columns[free_column], "Trigger", "Second weapon"); ui_vr_controls_add(&columns[free_column], "", "(Banshee, tank)"); - ui_vr_controls_add(&columns[buttons], act, "Get in, get out"); - ui_vr_controls_add(&columns[buttons], light, "Flashlight"); + ui_vr_controls_add_vehicle_actions(columns); break; default: ui_vr_controls_add(&columns[0], "Stick", "Choose"); @@ -7322,27 +7530,22 @@ static void ui_vr_controls_render( /* the Quest's: A and B on the right controller, X, Y and the menu button on the left; mirrored, X and Y do what A and B do, and the other way round */ - short jumps = mirrored ? 0 : 1; - short acts = (short)(1 - jumps); - switch (ui_vr_options_globals.page) { case _vr_controls_on_foot: ui_vr_controls_add(&columns[move], "Stick", "Move"); - ui_vr_controls_add(&columns[move], "Stick click", "Crouch"); + ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_MOVE_STICK), FALSE); ui_vr_controls_add(&columns[turn], "Stick", "Turn"); - ui_vr_controls_add(&columns[turn], "Stick click", "Zoom"); + ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_TURN_STICK), FALSE); ui_vr_controls_add(&columns[gun], "Trigger", "Fire"); - ui_vr_controls_add(&columns[gun], "Grip", "Melee (or punch"); - ui_vr_controls_add(&columns[gun], "", "forward)"); + ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_GRIP), FALSE); ui_vr_controls_add(&columns[free_column], "Trigger", "Grenade: hold it,"); ui_vr_controls_add(&columns[free_column], "", "let go as you throw"); ui_vr_controls_add(&columns[free_column], "Grip", "Weapon in both hands"); - ui_vr_controls_add(&columns[jumps], jumps ? "A" : "X", "Jump"); - ui_vr_controls_add(&columns[jumps], jumps ? "B" : "Y", "Flashlight"); - ui_vr_controls_add(&columns[jumps], jumps ? "B held" : "Y held", "Switch grenades"); - ui_vr_controls_add(&columns[acts], acts ? "A" : "X", "Action, reload"); - ui_vr_controls_add(&columns[acts], acts ? "B" : "Y", "Switch weapon"); + ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_A), FALSE); + ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_B), FALSE); + ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_X), FALSE); + ui_vr_controls_add_action(columns, ui_vr_input_action(HALO_VR_INPUT_Y), FALSE); ui_vr_controls_add(&columns[0], "Menu", "Pause"); ui_vr_controls_note(320, wrist_note); ui_vr_controls_note(340, "Scope: zoom, and look through the disc above the weapon."); @@ -7355,8 +7558,7 @@ static void ui_vr_controls_render( ui_vr_controls_add(&columns[gun], "Trigger", "Fire"); ui_vr_controls_add(&columns[free_column], "Trigger", "Second weapon"); ui_vr_controls_add(&columns[free_column], "", "(Banshee, tank)"); - ui_vr_controls_add(&columns[acts], acts ? "A" : "X", "Get in, get out"); - ui_vr_controls_add(&columns[jumps], jumps ? "B" : "Y", "Flashlight"); + ui_vr_controls_add_vehicle_actions(columns); break; default: ui_vr_controls_add(&columns[0], "Stick", "Choose"); @@ -7373,6 +7575,9 @@ static void ui_vr_controls_render( ui_vr_controls_column_render(right, y, &columns[1]); switch (ui_vr_options_globals.page) { + case _vr_controls_on_foot: + /* (its notes are with its lines, above) */ + break; case _vr_controls_vehicles: ui_vr_controls_note(300, "Warthog: grip the wheel with one hand or both and turn it."); ui_vr_controls_note(320, left_handed ? "Holding it, the left trigger accelerates, the right one brakes" : @@ -7429,15 +7634,15 @@ static void ui_vr_options_render( ui_join_code_draw_quad(110, 62, 530, 396, 0xFF050A18); ui_join_code_draw_quad(110, 62, 530, 64, 0xFF3A6EA5); ui_join_code_draw_quad(110, 394, 530, 396, 0xFF3A6EA5); - ui_join_code_draw_text(126, 330, 72, "VR OPTIONS", FALSE, 0, &title_color); + ui_join_code_draw_text(126, VR_OPTIONS_TABS_LEFT, 72, "VR OPTIONS", FALSE, 0, &title_color); /* (the pages' tabs, beside the title) */ for (tab = 0; tab < NUMBER_OF_VR_OPTION_PAGES; tab++) { - short x0 = (short)(330 + tab * 92); + short x0 = (short)(VR_OPTIONS_TABS_LEFT + tab * VR_OPTIONS_TAB_WIDTH); if (tab == page) - ui_join_code_draw_quad(x0, 68, (short)(x0 + 88), 88, 0x803A6EA5); - ui_join_code_draw_text(x0, (short)(x0 + 88), 72, vr_option_page_names[tab], FALSE, 2, + ui_join_code_draw_quad(x0, 68, (short)(x0 + VR_OPTIONS_TAB_WIDTH - 4), 88, 0x803A6EA5); + ui_join_code_draw_text(x0, (short)(x0 + VR_OPTIONS_TAB_WIDTH - 4), 72, vr_option_page_names[tab], FALSE, 2, tab == page ? &text_color : &dim_color); } for (row = 0; row < ui_vr_options_page_rows(page); row++) @@ -7537,9 +7742,23 @@ static void ui_vr_options_render( snprintf(value, sizeof(value), "%.0f%%", scale * 100.0f); break; } + case _vr_option_buttons_reset: + label = "Reset buttons to defaults"; + unused = ui_vr_options_buttons_default(); + break; case _vr_option_controls: label = "Controller buttons..."; break; + default: + if (option >= _vr_option_button_first && option <= _vr_option_button_last) + { + short action = (short)(option - _vr_option_button_first); + + label = vr_action_names[action]; + snprintf(value, sizeof(value), "%s", ui_vr_input_name((short)halo_vr_action_input(action), TRUE, + NULL)); + } + break; case _vr_option_quit: label = ui_vr_options_globals.quit_armed && ui_vr_options_globals.selection == _vr_option_quit ? "Quit Halo? Once more to quit" : "Quit Halo"; @@ -7556,7 +7775,16 @@ static void ui_vr_options_render( ui_join_code_draw_text(330, 512, y, shown, FALSE, 1, unused ? &dim_color : &text_color); } } - if (page == 1) + if (page == _vr_options_page_buttons) + { + /* (what the buttons' rows do) */ + ui_join_code_draw_text(128, 512, 302, "An action moved to a button swaps with the one there;", FALSE, 0, + &dim_color); + ui_join_code_draw_text(128, 512, 320, "left-handed and mirrored, the buttons are mirrored too.", FALSE, 0, + &dim_color); + ui_join_code_draw_text(128, 512, 338, "The menus keep their buttons.", FALSE, 0, &dim_color); + } + if (page == _vr_options_page_more) { /* (what the graphics rows do) */ ui_join_code_draw_text(128, 512, 230, "Graphics: Medium and Low leave effects out (shadows,", FALSE, 0,