mirror of
				https://github.com/Zelda64Recomp/Zelda64Recomp.git
				synced 2025-10-30 08:03:03 +00:00 
			
		
		
		
	Implemented mouse aiming
This commit is contained in:
		
							parent
							
								
									539497f84d
								
							
						
					
					
						commit
						b28614b128
					
				
					 9 changed files with 126 additions and 30 deletions
				
			
		|  | @ -74,31 +74,50 @@ | |||
|                         </div> | ||||
|                     </div> | ||||
| 
 | ||||
|                     <!-- mouse sensitivity --> | ||||
|                     <div class="config-option" data-event-mouseover="set_cur_config_index(3)"> | ||||
|                         <label class="config-option__title">Mouse Sensitivity</label> | ||||
|                         <div class="config-option__range-wrapper config-option__list"> | ||||
|                             <label class="config-option__range-label">{{mouse_sensitivity}}%</label> | ||||
|                             <input | ||||
|                                 class="nav-vert" | ||||
|                                 data-event-blur="set_cur_config_index(-1)" | ||||
|                                 data-event-focus="set_cur_config_index(3)" | ||||
|                                 id="mouse_sensitivity_input" | ||||
|                                 type="range" | ||||
|                                 min="0" | ||||
|                                 max="100" | ||||
|                                 style="flex: 1; margin: 0dp;" | ||||
|                                 data-value="mouse_sensitivity" | ||||
|                             /> | ||||
|                         </div> | ||||
|                     </div> | ||||
| 
 | ||||
|                     <!-- targeting mode --> | ||||
|                     <div class="config-option" data-event-mouseover="set_cur_config_index(3)" id="conf-general__Background-Input"> | ||||
|                     <div class="config-option" data-event-mouseover="set_cur_config_index(4)" id="conf-general__Background-Input"> | ||||
|                         <label class="config-option__title">Background Input</label> | ||||
|                         <div class="config-option__list"> | ||||
|                             <input | ||||
|                                 type="radio" | ||||
|                                 data-event-blur="set_cur_config_index(-1)" | ||||
|                                 data-event-focus="set_cur_config_index(3)" | ||||
|                                 data-event-focus="set_cur_config_index(4)" | ||||
|                                 name="background_input_mode" | ||||
|                                 data-checked="background_input_mode" | ||||
|                                 value="On" | ||||
|                                 id="bg_input_enabled" | ||||
|                                 style="nav-up: #gyro_sensitivity_input" | ||||
|                                 style="nav-up: #mouse_sensitivity_input" | ||||
|                             /> | ||||
|                             <label class="config-option__tab-label" for="bg_input_enabled">On</label> | ||||
| 
 | ||||
|                             <input | ||||
|                                 type="radio" | ||||
|                                 data-event-blur="set_cur_config_index(-1)" | ||||
|                                 data-event-focus="set_cur_config_index(3)" | ||||
|                                 data-event-focus="set_cur_config_index(4)" | ||||
|                                 name="background_input_mode" | ||||
|                                 data-checked="background_input_mode" | ||||
|                                 value="Off" | ||||
|                                 id="bg_input_disabled" | ||||
|                                 style="nav-up: #gyro_sensitivity_input" | ||||
|                                 style="nav-up: #mouse_sensitivity_input" | ||||
|                             /> | ||||
|                             <label class="config-option__tab-label" for="bg_input_disabled">Off</label> | ||||
|                         </div> | ||||
|  | @ -119,6 +138,12 @@ | |||
|                         <b>Note: To recalibrate controller gyro, set the controller down on a still, flat surface for 5 seconds.</b> | ||||
|                     </p> | ||||
|                     <p data-if="cur_config_index == 3"> | ||||
|                         Controls the sensitivity of mouse aiming when using items in first person for controllers that support it. <b>Setting this to zero will disable mouse aiming.</b> | ||||
|                         <br /> | ||||
|                         <br /> | ||||
|                         <b>Note: This option does not allow mouse buttons to activate items. Mouse aiming is meant for using inputs that are mapped to mouse movement, such as gyro on Steam Deck.</b> | ||||
|                     </p> | ||||
|                     <p data-if="cur_config_index == 4"> | ||||
|                         Allows the game to read controller input when out of focus. | ||||
|                         <br/> | ||||
|                         <b>This setting does not affect keyboard input.</b> | ||||
|  |  | |||
|  | @ -66,6 +66,7 @@ namespace recomp { | |||
|     bool get_input_digital(const InputField& field); | ||||
|     bool get_input_digital(const std::span<const recomp::InputField> fields); | ||||
|     void get_gyro_deltas(float* x, float* y); | ||||
|     void get_mouse_deltas(float* x, float* y); | ||||
| 
 | ||||
|     enum class InputDevice { | ||||
|         Controller, | ||||
|  | @ -125,9 +126,11 @@ namespace recomp { | |||
|     int get_rumble_strength(); | ||||
|     void set_rumble_strength(int strength); | ||||
|      | ||||
|     // Gyro sensitivity ranges from 0 to 100 (gets doubled).
 | ||||
|     // Gyro and mouse sensitivities range from 0 to 100.
 | ||||
|     int get_gyro_sensitivity(); | ||||
|     int get_mouse_sensitivity(); | ||||
|     void set_gyro_sensitivity(int strength); | ||||
|     void set_mouse_sensitivity(int strength); | ||||
| 
 | ||||
|     enum class TargetingMode { | ||||
|         Switch, | ||||
|  |  | |||
|  | @ -9,7 +9,7 @@ s32 func_80847190(PlayState* play, Player* this, s32 arg2); | |||
| s16 func_80832754(Player* this, s32 arg1); | ||||
| s32 func_8082EF20(Player* this); | ||||
| 
 | ||||
| // Patched to add gyro aiming
 | ||||
| // @recomp Patched to add gyro and mouse aiming.
 | ||||
| s32 func_80847190(PlayState* play, Player* this, s32 arg2) { | ||||
|     s32 pad; | ||||
|     s16 var_s0; | ||||
|  | @ -24,18 +24,19 @@ s32 func_80847190(PlayState* play, Player* this, s32 arg2) { | |||
|     } | ||||
|     else { | ||||
|         static float total_gyro_x, total_gyro_y; | ||||
|         static float total_mouse_x, total_mouse_y; | ||||
|         static float filtered_gyro_x, filtered_gyro_y; | ||||
|         static int applied_gyro_x, applied_gyro_y; | ||||
|         static int applied_aim_x, applied_aim_y; | ||||
| 
 | ||||
|         const float filter_factor = 0.00f; | ||||
|         const float gyro_filter_factor = 0.00f; | ||||
| 
 | ||||
|         // TODO remappable gyro reset button
 | ||||
|         if (play->state.input[0].press.button & BTN_L) { | ||||
|             total_gyro_x = 0; | ||||
|             total_gyro_y = 0; | ||||
|             filtered_gyro_x = 0; | ||||
|             filtered_gyro_y = 0; | ||||
|         } | ||||
|         // // TODO remappable gyro reset button
 | ||||
|         // if (play->state.input[0].press.button & BTN_L) {
 | ||||
|         //     total_gyro_x = 0;
 | ||||
|         //     total_gyro_y = 0;
 | ||||
|         //     filtered_gyro_x = 0;
 | ||||
|         //     filtered_gyro_y = 0;
 | ||||
|         // }
 | ||||
| 
 | ||||
|         float delta_gyro_x, delta_gyro_y; | ||||
|         recomp_get_gyro_deltas(&delta_gyro_x, &delta_gyro_y); | ||||
|  | @ -43,18 +44,28 @@ s32 func_80847190(PlayState* play, Player* this, s32 arg2) { | |||
|         total_gyro_x += delta_gyro_x; | ||||
|         total_gyro_y += delta_gyro_y; | ||||
| 
 | ||||
|         filtered_gyro_x = filtered_gyro_x * filter_factor + total_gyro_x * (1.0f - filter_factor); | ||||
|         filtered_gyro_y = filtered_gyro_y * filter_factor + total_gyro_y * (1.0f - filter_factor); | ||||
|         filtered_gyro_x = filtered_gyro_x * gyro_filter_factor + total_gyro_x * (1.0f - gyro_filter_factor); | ||||
|         filtered_gyro_y = filtered_gyro_y * gyro_filter_factor + total_gyro_y * (1.0f - gyro_filter_factor); | ||||
| 
 | ||||
|         int target_gyro_x = (int)filtered_gyro_x; | ||||
|         int target_gyro_y = (int)filtered_gyro_y; | ||||
|         float delta_mouse_x, delta_mouse_y; | ||||
|         recomp_get_mouse_deltas(&delta_mouse_x, &delta_mouse_y); | ||||
|          | ||||
|         total_mouse_x += delta_mouse_x; | ||||
|         total_mouse_y += delta_mouse_y; | ||||
| 
 | ||||
|         // The gyro X-axis (tilt) corresponds to the camera X-axis (tilt).
 | ||||
|         // The gyro Y-axis (left/right rotation) corresponds to the camera Y-axis (left/right rotation).
 | ||||
|         // The mouse Y-axis (up/down movement) corresponds to the camera X-axis (tilt).
 | ||||
|         // The mouse X-axis (left/right movement) corresponds to the camera Y-axis (left/right rotation).
 | ||||
|         int target_aim_x = (int)(filtered_gyro_x * -3.0f + total_mouse_y * 20.0f); | ||||
|         int target_aim_y = (int)(filtered_gyro_y * 3.0f  + total_mouse_x * -20.0f); | ||||
| 
 | ||||
|         s16 temp3; | ||||
| 
 | ||||
|         temp3 = ((play->state.input[0].rel.stick_y >= 0) ? 1 : -1) * | ||||
|             (s32)((1.0f - Math_CosS(play->state.input[0].rel.stick_y * 0xC8)) * 1500.0f); | ||||
|         this->actor.focus.rot.x += temp3 + (s32)((target_gyro_x - applied_gyro_x) * -1.5f); | ||||
|         applied_gyro_x = target_gyro_x; | ||||
|         this->actor.focus.rot.x += temp3 + (s32)(target_aim_x - applied_aim_x); | ||||
|         applied_aim_x = target_aim_x; | ||||
| 
 | ||||
|         if (this->stateFlags1 & PLAYER_STATE1_800000) { | ||||
|             this->actor.focus.rot.x = CLAMP(this->actor.focus.rot.x, -0x1F40, 0xFA0); | ||||
|  | @ -66,8 +77,8 @@ s32 func_80847190(PlayState* play, Player* this, s32 arg2) { | |||
|         var_s0 = this->actor.focus.rot.y - this->actor.shape.rot.y; | ||||
|         temp3 = ((play->state.input[0].rel.stick_x >= 0) ? 1 : -1) * | ||||
|             (s32)((1.0f - Math_CosS(play->state.input[0].rel.stick_x * 0xC8)) * -1500.0f); | ||||
|         var_s0 += temp3 + (s32)((target_gyro_y - applied_gyro_y) * 1.5f); | ||||
|         applied_gyro_y = target_gyro_y; | ||||
|         var_s0 += temp3 + (s32)(target_aim_y - applied_aim_y); | ||||
|         applied_aim_y = target_aim_y; | ||||
| 
 | ||||
|         this->actor.focus.rot.y = CLAMP(var_s0, -0x4AAA, 0x4AAA) + this->actor.shape.rot.y; | ||||
|     } | ||||
|  |  | |||
|  | @ -11,6 +11,7 @@ typedef enum { | |||
| extern RecompCameraMode recomp_camera_mode; | ||||
| 
 | ||||
| DECLARE_FUNC(void, recomp_get_gyro_deltas, float* x, float* y); | ||||
| DECLARE_FUNC(void, recomp_get_mouse_deltas, float* x, float* y); | ||||
| DECLARE_FUNC(int, recomp_get_targeting_mode); | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -46,3 +46,4 @@ osContStartReadData_recomp = 0x8F000070; | |||
| osContGetReadData_recomp = 0x8F000074; | ||||
| osContStartQuery_recomp = 0x8F000078; | ||||
| osContGetQuery_recomp = 0x8F00007C; | ||||
| recomp_get_mouse_deltas = 0x8F000080;  | ||||
|  |  | |||
|  | @ -130,6 +130,7 @@ void save_general_config(const std::filesystem::path& path) { | |||
|     recomp::to_json(config_json["background_input_mode"], recomp::get_background_input_mode()); | ||||
|     config_json["rumble_strength"] = recomp::get_rumble_strength(); | ||||
|     config_json["gyro_sensitivity"] = recomp::get_gyro_sensitivity(); | ||||
|     config_json["mouse_sensitivity"] = recomp::get_mouse_sensitivity(); | ||||
|     config_json["debug_mode"] = recomp::get_debug_mode_enabled(); | ||||
|     config_file << std::setw(4) << config_json; | ||||
| } | ||||
|  | @ -144,6 +145,7 @@ void load_general_config(const std::filesystem::path& path) { | |||
|     recomp::set_background_input_mode(from_or_default(config_json, "background_input_mode", recomp::BackgroundInputMode::On)); | ||||
|     recomp::set_rumble_strength(from_or_default(config_json, "rumble_strength", 25)); | ||||
|     recomp::set_gyro_sensitivity(from_or_default(config_json, "gyro_sensitivity", 50)); | ||||
|     recomp::set_mouse_sensitivity(from_or_default(config_json, "mouse_sensitivity", 0)); | ||||
|     recomp::set_debug_mode_enabled(from_or_default(config_json, "debug_mode", false)); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -30,9 +30,13 @@ static struct { | |||
|     std::mutex cur_controllers_mutex; | ||||
|     std::vector<SDL_GameController*> cur_controllers{}; | ||||
|     std::unordered_map<SDL_JoystickID, ControllerState> controller_states; | ||||
|      | ||||
|     std::array<float, 2> rotation_delta{}; | ||||
|     std::mutex pending_rotation_mutex; | ||||
|     std::array<float, 2> mouse_delta{}; | ||||
|     std::mutex pending_input_mutex; | ||||
|     std::array<float, 2> pending_rotation_delta{}; | ||||
|     std::array<float, 2> pending_mouse_delta{}; | ||||
| 
 | ||||
|     float cur_rumble; | ||||
|     bool rumble_active; | ||||
| } InputState; | ||||
|  | @ -189,12 +193,19 @@ bool sdl_event_filter(void* userdata, SDL_Event* event) { | |||
|             state.motion.GetPlayerSpaceGyro(rot_x, rot_y); | ||||
| 
 | ||||
|             { | ||||
|                 std::lock_guard lock{ InputState.pending_rotation_mutex }; | ||||
|                 std::lock_guard lock{ InputState.pending_input_mutex }; | ||||
|                 InputState.pending_rotation_delta[0] += rot_x; | ||||
|                 InputState.pending_rotation_delta[1] += rot_y; | ||||
|             } | ||||
|         } | ||||
|         break; | ||||
|     case SDL_EventType::SDL_MOUSEMOTION: | ||||
|         if (!recomp::game_input_disabled()) { | ||||
|             SDL_MouseMotionEvent* motion_event = &event->motion; | ||||
|             std::lock_guard lock{ InputState.pending_input_mutex }; | ||||
|             InputState.pending_mouse_delta[0] += motion_event->xrel; | ||||
|             InputState.pending_mouse_delta[1] += motion_event->yrel; | ||||
|         } | ||||
|     default: | ||||
|         queue_if_enabled(event); | ||||
|         break; | ||||
|  | @ -207,7 +218,18 @@ void recomp::handle_events() { | |||
|     static bool exited = false; | ||||
|     while (SDL_PollEvent(&cur_event) && !exited) { | ||||
|         exited = sdl_event_filter(nullptr, &cur_event); | ||||
|         SDL_ShowCursor(cursor_enabled ? SDL_ENABLE : SDL_DISABLE); | ||||
| 
 | ||||
|         // Lock the cursor if all three conditions are true: mouse aiming is enabled, game input is not disabled, and the game has been started. 
 | ||||
|         bool cursor_locked = (recomp::get_mouse_sensitivity() != 0) && !recomp::game_input_disabled() && ultramodern::is_game_started(); | ||||
| 
 | ||||
|         // Hide the cursor based on its enable state, but override visibility to false if the cursor is locked.
 | ||||
|         bool cursor_visible = cursor_enabled; | ||||
|         if (cursor_locked) { | ||||
|             cursor_visible = false; | ||||
|         } | ||||
| 
 | ||||
|         SDL_ShowCursor(cursor_visible ? SDL_ENABLE : SDL_DISABLE); | ||||
|         SDL_SetRelativeMouseMode(cursor_locked ? SDL_TRUE : SDL_FALSE); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  | @ -352,9 +374,13 @@ void recomp::poll_inputs() { | |||
| 
 | ||||
|     // Read the deltas while resetting them to zero.
 | ||||
|     { | ||||
|         std::lock_guard lock{ InputState.pending_rotation_mutex }; | ||||
|         std::lock_guard lock{ InputState.pending_input_mutex }; | ||||
|          | ||||
|         InputState.rotation_delta = InputState.pending_rotation_delta; | ||||
|         InputState.pending_rotation_delta = { 0.0f, 0.0f }; | ||||
| 
 | ||||
|         InputState.mouse_delta = InputState.pending_mouse_delta; | ||||
|         InputState.pending_mouse_delta = { 0.0f, 0.0f }; | ||||
|     } | ||||
|      | ||||
|     // Quicksaving is disabled for now and will likely have more limited functionality
 | ||||
|  | @ -503,11 +529,18 @@ bool recomp::get_input_digital(const std::span<const recomp::InputField> fields) | |||
| 
 | ||||
| void recomp::get_gyro_deltas(float* x, float* y) { | ||||
|     std::array<float, 2> cur_rotation_delta = InputState.rotation_delta; | ||||
|     float sensitivity = (float)recomp::get_gyro_sensitivity() / 50.0f; | ||||
|     float sensitivity = (float)recomp::get_gyro_sensitivity() / 100.0f; | ||||
|     *x = cur_rotation_delta[0] * sensitivity; | ||||
|     *y = cur_rotation_delta[1] * sensitivity; | ||||
| } | ||||
| 
 | ||||
| void recomp::get_mouse_deltas(float* x, float* y) { | ||||
|     std::array<float, 2> cur_mouse_delta = InputState.mouse_delta; | ||||
|     float sensitivity = (float)recomp::get_mouse_sensitivity() / 100.0f; | ||||
|     *x = cur_mouse_delta[0] * sensitivity; | ||||
|     *y = cur_mouse_delta[1] * sensitivity; | ||||
| } | ||||
| 
 | ||||
| bool recomp::game_input_disabled() { | ||||
|     // Disable input if any menu is open.
 | ||||
|     return recomp::get_current_menu() != recomp::Menu::None; | ||||
|  |  | |||
|  | @ -35,6 +35,13 @@ extern "C" void recomp_get_gyro_deltas(uint8_t* rdram, recomp_context* ctx) { | |||
|     recomp::get_gyro_deltas(x_out, y_out); | ||||
| } | ||||
| 
 | ||||
| extern "C" void recomp_get_mouse_deltas(uint8_t* rdram, recomp_context* ctx) { | ||||
|     float* x_out = _arg<0, float*>(rdram, ctx); | ||||
|     float* y_out = _arg<1, float*>(rdram, ctx); | ||||
| 
 | ||||
|     recomp::get_mouse_deltas(x_out, y_out); | ||||
| } | ||||
| 
 | ||||
| extern "C" void recomp_powf(uint8_t* rdram, recomp_context* ctx) { | ||||
|     float a = _arg<0, float>(rdram, ctx); | ||||
|     float b = ctx->f14.fl; //_arg<1, float>(rdram, ctx);
 | ||||
|  |  | |||
|  | @ -264,7 +264,8 @@ void open_quit_game_prompt() { | |||
| 
 | ||||
| struct ControlOptionsContext { | ||||
| 	int rumble_strength = 50; // 0 to 100
 | ||||
| 	int gyro_sensitivity = 50; // 0 to 200
 | ||||
| 	int gyro_sensitivity = 50; // 0 to 100
 | ||||
| 	int mouse_sensitivity = 50; // 0 to 100
 | ||||
| 	recomp::TargetingMode targeting_mode = recomp::TargetingMode::Switch; | ||||
| 	recomp::BackgroundInputMode background_input_mode = recomp::BackgroundInputMode::On; | ||||
| }; | ||||
|  | @ -286,6 +287,10 @@ int recomp::get_gyro_sensitivity() { | |||
| 	return control_options_context.gyro_sensitivity; | ||||
| } | ||||
| 
 | ||||
| int recomp::get_mouse_sensitivity() { | ||||
| 	return control_options_context.mouse_sensitivity; | ||||
| } | ||||
| 
 | ||||
| void recomp::set_gyro_sensitivity(int sensitivity) { | ||||
| 	control_options_context.gyro_sensitivity = sensitivity; | ||||
| 	if (general_model_handle) { | ||||
|  | @ -293,6 +298,13 @@ void recomp::set_gyro_sensitivity(int sensitivity) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| void recomp::set_mouse_sensitivity(int sensitivity) { | ||||
| 	control_options_context.mouse_sensitivity = sensitivity; | ||||
| 	if (general_model_handle) { | ||||
| 		general_model_handle.DirtyVariable("mouse_sensitivity"); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| recomp::TargetingMode recomp::get_targeting_mode() { | ||||
| 	return control_options_context.targeting_mode; | ||||
| } | ||||
|  | @ -787,6 +799,7 @@ public: | |||
| 		 | ||||
| 		constructor.Bind("rumble_strength", &control_options_context.rumble_strength); | ||||
| 		constructor.Bind("gyro_sensitivity", &control_options_context.gyro_sensitivity); | ||||
| 		constructor.Bind("mouse_sensitivity", &control_options_context.mouse_sensitivity); | ||||
| 		bind_option(constructor, "targeting_mode", &control_options_context.targeting_mode); | ||||
| 		bind_option(constructor, "background_input_mode", &control_options_context.background_input_mode); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 Mr-Wiseguy
						Mr-Wiseguy