diff --git a/CMakeSettings.json b/CMakeSettings.json index 2235809..58173e9 100644 --- a/CMakeSettings.json +++ b/CMakeSettings.json @@ -31,8 +31,7 @@ "cmakeCommandArgs": "", "buildCommandArgs": "", "ctestCommandArgs": "", - "inheritEnvironments": [ "clang_cl_x64" ], - "variables": [] + "inheritEnvironments": [ "clang_cl_x64" ] } ] } \ No newline at end of file diff --git a/CONTRIB-Zelda64Recomp.zip b/CONTRIB-Zelda64Recomp.zip new file mode 100644 index 0000000..21cdc38 Binary files /dev/null and b/CONTRIB-Zelda64Recomp.zip differ diff --git a/generate_recomp_functions.ps1 b/generate_recomp_functions.ps1 new file mode 100644 index 0000000..6e5d348 --- /dev/null +++ b/generate_recomp_functions.ps1 @@ -0,0 +1,3 @@ +./N64Recomp us.rev1.toml +./RSPRecomp aspMain.us.rev1.toml +./RSPRecomp njpgdspMain.us.rev1.toml \ No newline at end of file diff --git a/include/recomp_input.h b/include/recomp_input.h index 8599628..064a6b0 100644 --- a/include/recomp_input.h +++ b/include/recomp_input.h @@ -76,6 +76,10 @@ namespace recomp { bool get_input_digital(const std::span fields); void get_gyro_deltas(float* x, float* y); void get_mouse_deltas(float* x, float* y); + int32_t get_mouse_wheel_pos(); + uint32_t get_mouse_buttons(); + uint32_t get_mouse_button_mask(); + void set_mouse_button_mask(uint32_t mask); void get_right_analog(float* x, float* y); enum class InputDevice { diff --git a/patches/camera_patches.c b/patches/camera_patches.c index 938b2b8..092a2e9 100644 --- a/patches/camera_patches.c +++ b/patches/camera_patches.c @@ -28,6 +28,8 @@ float analog_camera_y_sensitivity = 500.0f; static const float analog_cam_threshold = 0.1f; +float mouse_camera_sensitivity = 0.1f; + RECOMP_EXPORT void recomp_set_camera_fixes(bool new_val) { camera_fixes = new_val; } @@ -66,8 +68,12 @@ void update_analog_cam(Camera* c) { } // Enable analog cam if the right stick is held. - float input_x, input_y; - recomp_get_camera_inputs(&input_x, &input_y); + float analog_x, analog_y; + + recomp_get_camera_inputs(&analog_x, &analog_y); + + float input_x = analog_x + (mouse_input_handler.crouch_shielding ? 0.0f : mouse_input_handler.delta_x * MOUSE_CAMERA_SCALE_X); + float input_y = analog_y + (mouse_input_handler.crouch_shielding ? 0.0f : mouse_input_handler.delta_y * MOUSE_CAMERA_SCALE_Y); if (fabsf(input_x) >= analog_cam_threshold || fabsf(input_y) >= analog_cam_threshold) { analog_cam_active = true; diff --git a/patches/input.c b/patches/input.c index ce6eccd..2f53cac 100644 --- a/patches/input.c +++ b/patches/input.c @@ -1,4 +1,5 @@ #include "patches.h" +#include "play_patches.h" #include "input.h" #include "z64snap.h" // Decomp rename, TODO update decomp and remove this @@ -6,6 +7,8 @@ #include "z64voice.h" #include "audiothread_cmd.h" +MouseInputHandler mouse_input_handler; + RECOMP_DECLARE_EVENT(recomp_before_first_person_aiming_update_event(PlayState* play, Player* this, bool in_free_look, RecompAimingOverideMode* recomp_aiming_override_mode)); RECOMP_DECLARE_EVENT(recomp_after_first_person_aiming_update_event(PlayState* play, Player* this, bool in_free_look)); RECOMP_DECLARE_EVENT(recomp_set_extra_item_slot_statuses(PlayState* play, s32 enabled)); @@ -102,12 +105,9 @@ RECOMP_PATCH s32 func_80847190(PlayState* play, Player* this, s32 arg2) { 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); - - 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; + total_mouse_x += mouse_input_handler.delta_x; + total_mouse_y += mouse_input_handler.delta_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). @@ -1473,10 +1473,13 @@ RECOMP_PATCH void Player_Action_18(Player* this, PlayState* play) { func_8082F164(this, BTN_R | BTN_B); } } - if (this->av2.actionVar2 != 0) { - f32 yStick = sPlayerControlInput->rel.stick_y * 180; - f32 xStick = sPlayerControlInput->rel.stick_x * -120; + f32 yStick = sPlayerControlInput->rel.stick_y * 180 + mouse_input_handler.shield_pos_y; + f32 xStick = sPlayerControlInput->rel.stick_x * -120 + mouse_input_handler.shield_pos_x; + // Needed so analog input and mouse input don't double up. + xStick = CLAMP(xStick, -MOUSE_SHIELD_CLAMP_X, MOUSE_SHIELD_CLAMP_X); + yStick = CLAMP(yStick, -MOUSE_SHIELD_CLAMP_Y, MOUSE_SHIELD_CLAMP_Y); + s16 temp_a0 = this->actor.shape.rot.y - Camera_GetInputDirYaw(GET_ACTIVE_CAM(play)); s16 var_a1; s16 temp_ft5; @@ -1493,7 +1496,6 @@ RECOMP_PATCH void Player_Action_18(Player* this, PlayState* play) { if (inverted_x) { xStick = -xStick; } - var_a1 = (yStick * Math_CosS(temp_a0)) + (Math_SinS(temp_a0) * xStick); temp_ft5 = (xStick * Math_CosS(temp_a0)) - (Math_SinS(temp_a0) * yStick); diff --git a/patches/play_patches.c b/patches/play_patches.c index 1b6307a..013b355 100644 --- a/patches/play_patches.c +++ b/patches/play_patches.c @@ -34,7 +34,53 @@ RECOMP_DECLARE_EVENT(recomp_after_play_update(PlayState* play)); void controls_play_update(PlayState* play) { gSaveContext.options.zTargetSetting = recomp_get_targeting_mode(); + + Player* player = GET_PLAYER(play); + Camera* camera = GET_ACTIVE_CAM(play); + + // Looks like this function doesn't like to be called more than once per frame. + // We'll cache the results here for other stuff to use. + recomp_get_mouse_deltas(&mouse_input_handler.delta_x, &mouse_input_handler.delta_y); + + // Best way I could come up with to reallow mouse movement when lockon shielding. + mouse_input_handler.crouch_shielding = ((player->stateFlags1 == PLAYER_STATE1_400000) && !(camera->mode == CAM_MODE_TARGET || camera->mode == CAM_MODE_FOLLOWTARGET)); + + if (mouse_input_handler.crouch_shielding) { + mouse_input_handler.shield_pos_x -= mouse_input_handler.delta_x * 10.0f; + mouse_input_handler.shield_pos_y -= mouse_input_handler.delta_y * 10.0f; + mouse_input_handler.shield_pos_x = CLAMP(mouse_input_handler.shield_pos_x, -MOUSE_SHIELD_CLAMP_X, MOUSE_SHIELD_CLAMP_X); + mouse_input_handler.shield_pos_y = CLAMP(mouse_input_handler.shield_pos_y, -MOUSE_SHIELD_CLAMP_Y, MOUSE_SHIELD_CLAMP_Y); + } + else { + mouse_input_handler.shield_pos_x = 0.0f; + mouse_input_handler.shield_pos_y = 0.0f; + } } + // @recomp mouse deltas export +RECOMP_EXPORT void zelda64_get_mouse_deltas(float* x, float* y) { + *x = mouse_input_handler.delta_x; + *y = mouse_input_handler.delta_y; +} + +RECOMP_EXPORT unsigned int zelda64_get_mouse_wheel_pos() { + return recomp_get_mouse_buttons(); +} + +// @recomp mouse deltas export +RECOMP_EXPORT unsigned int zelda64_get_mouse_buttons() { + return recomp_get_mouse_buttons(); +} + +RECOMP_EXPORT unsigned int zelda64_get_mouse_button_mask() { + return recomp_get_mouse_button_mask(); +} + +RECOMP_EXPORT void zelda64_set_mouse_button_mask(unsigned int mask) { + recomp_set_mouse_button_mask(mask); +} +// The zelda64 prefix is temporary. I can't have the names conflict with the recomp API, +// but those are the most fitting. A better solution is desired. + // @recomp Patched to add hooks for various added functionality. RECOMP_PATCH void Play_Main(GameState* thisx) { @@ -174,6 +220,11 @@ RECOMP_PATCH void Play_Init(GameState* thisx) { // @recomp_event recomp_on_play_init(PlayState* this): A new PlayState is being initialized. recomp_on_play_init(this); + mouse_input_handler.crouch_shielding = false; + mouse_input_handler.delta_x = 0.0f; + mouse_input_handler.delta_y = 0.0f; + mouse_input_handler.shield_pos_x = 0.0f; + mouse_input_handler.shield_pos_y = 0.0f; if ((gSaveContext.respawnFlag == -4) || (gSaveContext.respawnFlag == -0x63)) { if (CHECK_EVENTINF(EVENTINF_TRIGGER_DAYTELOP)) { diff --git a/patches/play_patches.h b/patches/play_patches.h index 19e7db2..a144bcb 100644 --- a/patches/play_patches.h +++ b/patches/play_patches.h @@ -2,6 +2,23 @@ #define __PLAY_PATCHES_H__ #include "patches.h" +#include "patch_helpers.h" + +#define MOUSE_SHIELD_CLAMP_X 7200.0f +#define MOUSE_SHIELD_CLAMP_Y 10800.0f +#define MOUSE_CAMERA_SCALE_X 0.04f +#define MOUSE_CAMERA_SCALE_Y 0.08f // For some reason, the vertical sensitivity seemed less than the horizontal. This compensates. + +typedef struct { + float delta_x; + float delta_y; + + bool crouch_shielding; + float shield_pos_x; + float shield_pos_y; +} MouseInputHandler; + +extern MouseInputHandler mouse_input_handler; void debug_play_update(PlayState* play); void camera_pre_play_update(PlayState* play); @@ -11,4 +28,8 @@ void analog_cam_post_play_update(PlayState* play); void matrix_play_update(PlayState* play); void autosave_post_play_update(PlayState* play); +DECLARE_FUNC(unsigned int, recomp_get_mouse_buttons, ); +DECLARE_FUNC(unsigned int, recomp_get_mouse_button_mask, ); +DECLARE_FUNC(void, recomp_set_mouse_button_mask, unsigned int); +DECLARE_FUNC(unsigned int, recomp_get_mouse_wheel_pos, ); #endif diff --git a/patches/syms.ld b/patches/syms.ld index 2efa2c9..d070411 100644 --- a/patches/syms.ld +++ b/patches/syms.ld @@ -53,3 +53,7 @@ recomp_create_actor_data = 0x8F0000C8; recomp_destroy_actor_data = 0x8F0000CC; recomp_get_actor_data = 0x8F0000D0; recomp_get_actor_spawn_index = 0x8F0000D4; +recomp_get_mouse_buttons = 0x8F0000D8; +recomp_get_mouse_button_mask = 0x8F0000DC; +recomp_set_mouse_button_mask = 0x8F0000E0; +recomp_get_mouse_wheel_pos = 0x8F0000E4; \ No newline at end of file diff --git a/src/game/config.cpp b/src/game/config.cpp index 17ff9e4..6d355a8 100644 --- a/src/game/config.cpp +++ b/src/game/config.cpp @@ -14,7 +14,7 @@ #elif defined(__linux__) #include #include -#elif defined(__APPLE__) +#elif defined(__APPLE__) #include "apple/rt64_apple.h" #endif diff --git a/src/game/input.cpp b/src/game/input.cpp index f06a5b1..a02e689 100644 --- a/src/game/input.cpp +++ b/src/game/input.cpp @@ -1,5 +1,6 @@ #include #include +#include #include "ultramodern/ultramodern.hpp" #include "recomp.h" @@ -27,6 +28,8 @@ static struct { const Uint8* keys = nullptr; SDL_Keymod keymod = SDL_Keymod::KMOD_NONE; int numkeys = 0; + std::atomic mouse_button_state; + std::atomic mouse_button_mask = ~0; std::atomic_int32_t mouse_wheel_pos = 0; std::mutex cur_controllers_mutex; std::vector cur_controllers{}; @@ -130,6 +133,25 @@ bool sdl_event_filter(void* userdata, SDL_Event* event) { } } break; + case SDL_EventType::SDL_MOUSEBUTTONDOWN: + { + SDL_MouseButtonEvent* mouseevent = &event->button; + + // Skip repeated events when not in the menu + if (!recompui::is_context_capturing_input()) { + break; + } + + if (scanning_device != recomp::InputDevice::COUNT) { + if (scanning_device == recomp::InputDevice::Keyboard) { + set_scanned_input({ (uint32_t)InputType::Mouse, mouseevent->button - 1}); // subtract 1 because of the bit-shifting used to process SDL_GetMouseState + } + } + else { + queue_if_enabled(event); + } + } + break; case SDL_EventType::SDL_CONTROLLERDEVICEADDED: { SDL_ControllerDeviceEvent* controller_event = &event->cdevice; @@ -334,7 +356,7 @@ const recomp::DefaultN64Mappings recomp::default_n64_keyboard_mappings = { {.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_SPACE} }, .b = { - {.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_LSHIFT} + {.input_type = (uint32_t)InputType::Mouse, .input_id = 0} }, .l = { {.input_type = (uint32_t)InputType::Keyboard, .input_id = SDL_SCANCODE_E} @@ -467,6 +489,8 @@ const recomp::DefaultN64Mappings recomp::default_n64_controller_mappings = { }; void recomp::poll_inputs() { + InputState.mouse_button_state.store(SDL_GetMouseState(NULL, NULL)); + InputState.keys = SDL_GetKeyboardState(&InputState.numkeys); InputState.keymod = SDL_GetModState(); @@ -654,8 +678,11 @@ bool recomp::get_input_digital(const recomp::InputField& field) { // TODO adjustable threshold return controller_axis_state(field.input_id, true) >= axis_threshold; case InputType::Mouse: - // TODO mouse support - return false; + if (recomp::game_input_disabled()) { + return false; + } + return (InputState.mouse_button_state.load() & InputState.mouse_button_mask.load()) & (1 << field.input_id); + case InputType::None: return false; } @@ -683,6 +710,28 @@ void recomp::get_mouse_deltas(float* x, float* y) { *y = cur_mouse_delta[1] * sensitivity; } +int32_t recomp::get_mouse_wheel_pos() { + if (recomp::game_input_disabled()) { + return 0; + } + return InputState.mouse_wheel_pos.load(); +} + +uint32_t recomp::get_mouse_buttons() { + if (recomp::game_input_disabled()) { + return 0; + } + return InputState.mouse_button_state.load(); +} + +uint32_t recomp::get_mouse_button_mask() { + return InputState.mouse_button_mask.load(); +} + +void recomp::set_mouse_button_mask(unsigned int mask) { + return InputState.mouse_button_mask.store(mask); +} + void recomp::apply_joystick_deadzone(float x_in, float y_in, float* x_out, float* y_out) { float joystick_deadzone = (float)recomp::get_joystick_deadzone() / 100.0f; diff --git a/src/game/recomp_api.cpp b/src/game/recomp_api.cpp index 9ad08be..5d9170e 100644 --- a/src/game/recomp_api.cpp +++ b/src/game/recomp_api.cpp @@ -45,6 +45,24 @@ extern "C" void recomp_get_mouse_deltas(uint8_t* rdram, recomp_context* ctx) { recomp::get_mouse_deltas(x_out, y_out); } +extern "C" void recomp_get_mouse_wheel_pos(uint8_t* rdram, recomp_context* ctx) { + _return(ctx, recomp::get_mouse_wheel_pos()); +} + +extern "C" void recomp_get_mouse_buttons(uint8_t* rdram, recomp_context* ctx) { + _return(ctx, recomp::get_mouse_buttons()); +} + +extern "C" void recomp_get_mouse_button_mask(uint8_t* rdram, recomp_context* ctx) { + _return(ctx, recomp::get_mouse_button_mask()); +} + +extern "C" void recomp_set_mouse_button_mask(uint8_t* rdram, recomp_context* ctx) { + unsigned int out = _arg<0, unsigned int>(rdram, ctx); + recomp::set_mouse_button_mask(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);