From c5ecd25a2097cdc8dcf54b16c4e02b222d9aec92 Mon Sep 17 00:00:00 2001 From: Reonu Date: Sun, 4 Jan 2026 23:07:29 +0000 Subject: [PATCH] Add and implement all remaining axis inversion settings (#41) * Add and implement all remaining axis inversion settings * Improve third person inversion description, move enum * Fix X axis inversion with analog camera * formatting * Remove boot map patch + fix newlines * invert vanilla camera properly * Fix comment * add more comments * fix demos and add wrapper functions * rename analog camera mode to third person camera mode * Fix first person Y inversion for demos (oops) * Change phrasing * reorder options --- include/banjo_config.h | 10 +- ....c => camera_and_axis_inversion_patches.c} | 223 +++++++++++++++++- patches/input.h | 2 + patches/syms.ld | 2 + src/game/config.cpp | 73 +++--- src/game/recomp_api.cpp | 22 +- 6 files changed, 298 insertions(+), 34 deletions(-) rename patches/{analog_camera_patches.c => camera_and_axis_inversion_patches.c} (73%) diff --git a/include/banjo_config.h b/include/banjo_config.h index cc20b04..8ff60e1 100644 --- a/include/banjo_config.h +++ b/include/banjo_config.h @@ -16,7 +16,9 @@ namespace banjo { inline const std::string note_saving_mode = "note_saving_mode"; inline const std::string camera_invert_mode = "camera_invert_mode"; inline const std::string analog_cam_mode = "analog_cam_mode"; - inline const std::string analog_camera_invert_mode = "analog_camera_invert_mode"; + inline const std::string third_person_camera_invert_mode = "third_person_camera_invert_mode"; + inline const std::string flying_and_swimming_invert_mode = "flying_and_swimming_invert_mode"; + inline const std::string first_person_invert_mode = "first_person_invert_mode"; } namespace sound { @@ -36,7 +38,11 @@ namespace banjo { CameraInvertMode get_camera_invert_mode(); - CameraInvertMode get_analog_camera_invert_mode(); + CameraInvertMode get_third_person_camera_mode(); + + CameraInvertMode get_flying_and_swimming_invert_mode(); + + CameraInvertMode get_first_person_invert_mode(); enum class AnalogCamMode { On, diff --git a/patches/analog_camera_patches.c b/patches/camera_and_axis_inversion_patches.c similarity index 73% rename from patches/analog_camera_patches.c rename to patches/camera_and_axis_inversion_patches.c index 7590c3a..9f0bf4c 100644 --- a/patches/analog_camera_patches.c +++ b/patches/camera_and_axis_inversion_patches.c @@ -130,7 +130,7 @@ void recomp_analog_camera_get(f32 *x, f32 *y) { s32 inverted_x, inverted_y; recomp_get_camera_inputs(&input_x, &input_y); recomp_get_analog_inverted_axes(&inverted_x, &inverted_y); - *x = input_x * (inverted_x ? -1.0f : 1.0f); + *x = input_x * (inverted_x ? 1.0f : -1.0f); *y = input_y * (inverted_y ? -1.0f : 1.0f); } @@ -146,6 +146,69 @@ f32 recomp_analog_camera_get_y() { return y; } +s32 recomp_get_third_person_inverted_x() { + s32 inverted_x, inverted_y; + if (recomp_in_demo_playback_game_mode()) { + inverted_x = TRUE; + } else { + recomp_get_analog_inverted_axes(&inverted_x, &inverted_y); + } + + return inverted_x; +} + +s32 recomp_get_third_person_inverted_y() { + s32 inverted_x, inverted_y; + if (recomp_in_demo_playback_game_mode()) { + inverted_y = FALSE; + } else { + recomp_get_analog_inverted_axes(&inverted_x, &inverted_y); + } + + return inverted_y; +} + +s32 recomp_get_flying_and_swimming_inverted_x() { + s32 inverted_x, inverted_y; + if (recomp_in_demo_playback_game_mode()) { + inverted_x = FALSE; + } else { + recomp_get_flying_and_swimming_inverted_axes(&inverted_x, &inverted_y); + } + return inverted_x; +} + +s32 recomp_get_flying_and_swimming_inverted_y() { + s32 inverted_x, inverted_y; + if (recomp_in_demo_playback_game_mode()) { + inverted_y = TRUE; + } else { + recomp_get_flying_and_swimming_inverted_axes(&inverted_x, &inverted_y); + } + return inverted_y; +} + +s32 recomp_get_first_person_inverted_x() { + s32 inverted_x, inverted_y; + if (recomp_in_demo_playback_game_mode()) { + inverted_x = TRUE; + } else { + recomp_get_first_person_inverted_axes(&inverted_x, &inverted_y); + } + return inverted_x; +} + +s32 recomp_get_first_person_inverted_y() { + s32 inverted_x, inverted_y; + if (recomp_in_demo_playback_game_mode()) { + inverted_y = FALSE; + } else { + recomp_get_first_person_inverted_axes(&inverted_x, &inverted_y); + } + return inverted_y; +} + + // @recomp Check whether the analog camera stick is currently held. bool recomp_analog_camera_held() { if (recomp_analog_camera_enabled() && recomp_analog_camera_allowed()) { @@ -175,20 +238,30 @@ void recomp_analog_camera_update() { } // @recomp Updates the current yaw based on the analog camera's horizontal movement. +// Also allows axis inversion for vanilla (non-analog) camera. RECOMP_PATCH int func_8029105C(s32 arg0) { if (func_80298850()) return FALSE; + // @recomp: Allow camera axis inversion on the vanilla camera + float axisInversionModifier = -1; + bool inverted_x = recomp_get_third_person_inverted_x(); + if (inverted_x) { + axisInversionModifier = 1; + } + // @recomp Update the analog camera input. recomp_analog_camera_update(); - if (bainput_should_rotate_camera_left() && ncDynamicCamA_func_802C1DB0(-45.0f)) { + // @recomp Account for axis inversion + if (bainput_should_rotate_camera_left() && ncDynamicCamA_func_802C1DB0(-45.0f * axisInversionModifier)) { func_80291488(arg0); func_8029103C(); return TRUE; } - if (bainput_should_rotate_camera_right() && ncDynamicCamA_func_802C1DB0(45.0f)) { + // @recomp Account for axis inversion + if (bainput_should_rotate_camera_right() && ncDynamicCamA_func_802C1DB0(45.0f * axisInversionModifier)) { func_80291488(arg0); func_8029103C(); return TRUE; @@ -561,3 +634,147 @@ RECOMP_PATCH void pfsManager_readData() { if (!pfsManagerContStatus.errno) osContGetReadData(pfsManagerContPadData); } + +extern f32 bastick_getX(void); +extern f32 bastick_getY(void); + +// @recomp Patched to allow configuring Y axis inversion when flying +RECOMP_PATCH void func_802A3648(void){ + // @recomp Get the axis inversion setting + s32 inverted_y = recomp_get_flying_and_swimming_inverted_y(); + + // @recomp Apply axis inversion setting + f32 tmp_f0 = inverted_y ? bastick_getY() : -bastick_getY(); + + if(tmp_f0 < 0.0f) + pitch_setIdeal(ml_map_f(tmp_f0, -1.0f, 0.0f, 300.0f, 360.0f)); + else + pitch_setIdeal(ml_map_f(tmp_f0, 0.0f, 1.0f, 0.0f, 80.0f)); +} + +// @recomp Patched to allow configuring X axis inversion when flying +RECOMP_PATCH void func_802A354C(void){ + f32 yaw_range; + f32 roll_range; + f32 sp2C; + + // @recomp Get the axis inversion setting + s32 inverted_x = recomp_get_flying_and_swimming_inverted_x(); + + // @recomp Apply axis inversion setting + sp2C = inverted_x ? -bastick_getX() : bastick_getX(); + if(bakey_held(BUTTON_R)){ + yaw_setVelocityBounded(500.0f, 30.0f); + yaw_range = 6.0f; + roll_range = 85.0f; + } + else{ + yaw_setVelocityBounded(500.0f, 2.0f); + yaw_range = 3.0f; + roll_range = 75.0f; + } + roll_setIdeal(ml_map_f(sp2C, -1.0f, 1.0f, -roll_range, roll_range)); + yaw_setIdeal(mlNormalizeAngle(yaw_getIdeal() + ml_map_f(sp2C, -1.0f, 1.0f, yaw_range, -yaw_range))); +} + +// @recomp Patched to allow configuring Y axis inversion when swimming +f32 func_802A716C(); +RECOMP_PATCH void func_802A7304() { + f32 temp_f0; + + // @recomp Get the axis inversion setting + s32 inverted_y = recomp_get_flying_and_swimming_inverted_y(); + + pitch_setAngVel(ml_interpolate_f(func_802A716C(), 70.0f, 30.0f), 0.9f); + + // @recomp Apply axis inversion setting + temp_f0 = inverted_y ? bastick_getY() : -bastick_getY(); + if (temp_f0 < 0.0f) { + pitch_setIdeal(ml_map_f(temp_f0, -1.0f, 0.0f, 275.0f, 360.0f)); + return; + } + pitch_setIdeal(ml_map_f(temp_f0, 0.0f, 1.0f, 0.0f, 85.0f)); +} + +// @recomp Patched to allow configuring X axis inversion when swimming +RECOMP_PATCH void func_802A71D8(void) { + f32 yaw_range; + f32 sp38; + f32 roll_range; + f32 sp30; + + // @recomp Get the axis inversion setting + s32 inverted_x = recomp_get_flying_and_swimming_inverted_x(); + + // @recomp Apply axis inversion setting + sp30 = inverted_x ? -bastick_getX() : bastick_getX(); + sp38 = func_802A716C(); + if (bakey_held(BUTTON_R)) { + roll_range = 45.0f; + yaw_range = 4.3f; + yaw_setVelocityBounded(250.0f, 20.0f); + } else { + roll_range = 35.0f; + yaw_range = ml_interpolate_f(sp38, 3.1f, 2.4f); + yaw_setVelocityBounded(90.0f, ml_interpolate_f(sp38, 3.8f, 2.2f)); + } + roll_setIdeal(ml_map_f(sp30, -1.0f, 1.0f, -roll_range, roll_range)); + yaw_setIdeal(mlNormalizeAngle(yaw_getIdeal() + ml_map_f(sp30, -1.0f, 1.0f, yaw_range, -yaw_range))); +} + +extern s32 ncFirstPersonCamera_getState(void); +extern void ncFirstPersonCamera_getZoomedInRotation(f32 *); +extern void ncFirstPersonCamera_setZoomedOutRotation(f32 src[3]); +extern bool func_8028B254(s32 arg0); +extern void __bsDroneLook_getEyePos(f32 arg0[3]); +extern enum bs_e func_8029BDBC(void); +extern void ncFirstPersonCamera_setZoomedOutPosition(f32 src[3]); + +// @recomp: Patched to allow inverting the first person camera +RECOMP_PATCH void bsDroneLook_update(void) { + s32 next_state; + f32 eye_rotation[3]; + f32 eye_position[3]; + f32 dt; + s32 exit_first_person; + + // @recomp: Get the axis inversion setting + s32 inverted_x = recomp_get_first_person_inverted_x(); + s32 inverted_y = recomp_get_first_person_inverted_y(); + float x = inverted_x ? -bastick_getX() : bastick_getX(); + float y = inverted_y ? bastick_getY() : -bastick_getY(); + + next_state = 0; + dt = time_getDelta(); + if (ncFirstPersonCamera_getState() == FIRSTPERSON_STATE_2_IDLE) { + //camera is in "idle" state + ncFirstPersonCamera_getZoomedInRotation(eye_rotation); + // @recomp: Apply the axis inversion setting + eye_rotation[0] -= y * 90.0f * dt; + eye_rotation[1] -= x * 90.0f * dt; + eye_rotation[2] = 0.0f; + eye_rotation[0] = (eye_rotation[0] > 180.0f) ? ml_max_f(305.0f, eye_rotation[0]) : ml_min_f(70.0f, eye_rotation[0]); + ncFirstPersonCamera_setZoomedOutRotation(eye_rotation); + yaw_setIdeal(eye_rotation[1] + 180.0f); + + exit_first_person = FALSE; + // 1st person cancelled via input + if (bakey_pressed(BUTTON_B) || bakey_pressed(BUTTON_A) || bakey_pressed(BUTTON_C_UP)) { + exit_first_person = TRUE; + } + // 1st person cancelled via entering water + if (player_inWater()) { + if (player_getTransformation() == TRANSFORM_1_BANJO && player_getWaterState() == BSWATERGROUP_0_NONE) { + exit_first_person += TRUE; + } + } else if (func_8028B254(25) == 0) { + exit_first_person += TRUE; + } + if (exit_first_person) { + next_state = func_8029BDBC(); + } + } + __bsDroneLook_getEyePos(eye_position); + ncFirstPersonCamera_setZoomedOutPosition(eye_position); + bs_setState(next_state); +} diff --git a/patches/input.h b/patches/input.h index d4dd09f..c073e91 100644 --- a/patches/input.h +++ b/patches/input.h @@ -22,6 +22,8 @@ DECLARE_FUNC(void, recomp_get_mouse_deltas, float* x, float* y); DECLARE_FUNC(void, recomp_get_inverted_axes, s32* x, s32* y); DECLARE_FUNC(s32, recomp_get_analog_cam_enabled); DECLARE_FUNC(void, recomp_get_analog_inverted_axes, s32* x, s32* y); +DECLARE_FUNC(void, recomp_get_flying_and_swimming_inverted_axes, s32* x, s32* y); +DECLARE_FUNC(void, recomp_get_first_person_inverted_axes, s32* x, s32* y); DECLARE_FUNC(void, recomp_get_camera_inputs, float* x, float* y); DECLARE_FUNC(void, recomp_set_right_analog_suppressed, s32 suppressed); diff --git a/patches/syms.ld b/patches/syms.ld index e05d34e..2568464 100644 --- a/patches/syms.ld +++ b/patches/syms.ld @@ -48,3 +48,5 @@ osContGetReadData_recomp = 0x8F0000AC; bcopy_recomp = 0x8F0000B0; recomp_get_note_saving_enabled = 0x8F0000B4; recomp_get_cutscene_aspect_ratio = 0x8F0000B8; +recomp_get_flying_and_swimming_inverted_axes = 0x8F0000BC; +recomp_get_first_person_inverted_axes = 0x8F0000C0; diff --git a/src/game/config.cpp b/src/game/config.cpp index 098cdd8..8cb0a3f 100644 --- a/src/game/config.cpp +++ b/src/game/config.cpp @@ -36,22 +36,6 @@ static void add_general_options(recomp::config::Config &config) { note_saving_mode_options, banjo::NoteSavingMode::On ); - - static EnumOptionVector camera_invert_mode_options = { - {banjo::CameraInvertMode::InvertNone, "InvertNone", "None"}, - {banjo::CameraInvertMode::InvertX, "InvertX", "Invert X"}, - {banjo::CameraInvertMode::InvertY, "InvertY", "Invert Y"}, - {banjo::CameraInvertMode::InvertBoth, "InvertBoth", "Invert Both"} - }; - config.add_enum_option( - banjo::configkeys::general::camera_invert_mode, - "Aiming Camera Inverting", - // TODO: Update for banjo - "Inverts the camera controls. Invert Y is the default and matches the original game.", - camera_invert_mode_options, - banjo::CameraInvertMode::InvertY - ); - static EnumOptionVector analog_cam_mode_options = { {banjo::AnalogCamMode::Off, "Off", "Off"}, {banjo::AnalogCamMode::On, "On", "On"}, @@ -59,23 +43,48 @@ static void add_general_options(recomp::config::Config &config) { config.add_enum_option( banjo::configkeys::general::analog_cam_mode, "Analog Camera", - // TODO: Update for banjo "Enables the analog camera.", analog_cam_mode_options, banjo::AnalogCamMode::Off ); + static EnumOptionVector camera_invert_mode_options = { + {banjo::CameraInvertMode::InvertNone, "InvertNone", "None"}, + {banjo::CameraInvertMode::InvertX, "InvertX", "Invert X"}, + {banjo::CameraInvertMode::InvertY, "InvertY", "Invert Y"}, + {banjo::CameraInvertMode::InvertBoth, "InvertBoth", "Invert Both"} + }; config.add_enum_option( - banjo::configkeys::general::analog_camera_invert_mode, - "Analog Camera Inverting", - // TODO: Update for banjo - "Inverts the camera controls for the analog camera if it's enabled. None is the default.", + banjo::configkeys::general::third_person_camera_invert_mode, + "Invert Camera", + "Inverts the camera controls for the third person camera if it's enabled. Invert X is the default and matches the original game.

If analog camera is off, only the Invert X setting will take effect.", camera_invert_mode_options, - banjo::CameraInvertMode::InvertNone + banjo::CameraInvertMode::InvertX ); - config.add_option_disable_dependency( - banjo::configkeys::general::analog_camera_invert_mode, - banjo::configkeys::general::analog_cam_mode, - banjo::AnalogCamMode::Off + static EnumOptionVector first_person_invert_mode_options = { + {banjo::CameraInvertMode::InvertNone, "InvertNone", "None"}, + {banjo::CameraInvertMode::InvertX, "InvertX", "Invert X"}, + {banjo::CameraInvertMode::InvertY, "InvertY", "Invert Y"}, + {banjo::CameraInvertMode::InvertBoth, "InvertBoth", "Invert Both"} + }; + config.add_enum_option( + banjo::configkeys::general::first_person_invert_mode, + "Invert First Person View", + "Inverts the camera controls in first person view. Invert Y is the default and matches the original game.", + first_person_invert_mode_options, + banjo::CameraInvertMode::InvertY + ); + static EnumOptionVector flying_and_swimming_invert_options = { + {banjo::CameraInvertMode::InvertNone, "InvertNone", "None"}, + {banjo::CameraInvertMode::InvertX, "InvertX", "Invert X"}, + {banjo::CameraInvertMode::InvertY, "InvertY", "Invert Y"}, + {banjo::CameraInvertMode::InvertBoth, "InvertBoth", "Invert Both"} + }; + config.add_enum_option( + banjo::configkeys::general::flying_and_swimming_invert_mode, + "Invert Flying & Swimming", + "Inverts the controls for swimming and flying. Invert Y is the default and matches the original game.", + flying_and_swimming_invert_options, + banjo::CameraInvertMode::InvertY ); } @@ -92,8 +101,16 @@ banjo::CameraInvertMode banjo::get_camera_invert_mode() { return get_general_config_enum_value(banjo::configkeys::general::camera_invert_mode); } -banjo::CameraInvertMode banjo::get_analog_camera_invert_mode() { - return get_general_config_enum_value(banjo::configkeys::general::analog_camera_invert_mode); +banjo::CameraInvertMode banjo::get_third_person_camera_mode() { + return get_general_config_enum_value(banjo::configkeys::general::third_person_camera_invert_mode); +} + +banjo::CameraInvertMode banjo::get_flying_and_swimming_invert_mode() { + return get_general_config_enum_value(banjo::configkeys::general::flying_and_swimming_invert_mode); +} + +banjo::CameraInvertMode banjo::get_first_person_invert_mode() { + return get_general_config_enum_value(banjo::configkeys::general::first_person_invert_mode); } banjo::AnalogCamMode banjo::get_analog_cam_mode() { diff --git a/src/game/recomp_api.cpp b/src/game/recomp_api.cpp index 7e1bf43..34a01b4 100644 --- a/src/game/recomp_api.cpp +++ b/src/game/recomp_api.cpp @@ -146,7 +146,27 @@ extern "C" void recomp_get_analog_inverted_axes(uint8_t* rdram, recomp_context* s32* x_out = _arg<0, s32*>(rdram, ctx); s32* y_out = _arg<1, s32*>(rdram, ctx); - banjo::CameraInvertMode mode = banjo::get_analog_camera_invert_mode(); + banjo::CameraInvertMode mode = banjo::get_third_person_camera_mode(); + + *x_out = (mode == banjo::CameraInvertMode::InvertX || mode == banjo::CameraInvertMode::InvertBoth); + *y_out = (mode == banjo::CameraInvertMode::InvertY || mode == banjo::CameraInvertMode::InvertBoth); +} + +extern "C" void recomp_get_flying_and_swimming_inverted_axes(uint8_t* rdram, recomp_context* ctx) { + s32* x_out = _arg<0, s32*>(rdram, ctx); + s32* y_out = _arg<1, s32*>(rdram, ctx); + + banjo::CameraInvertMode mode = banjo::get_flying_and_swimming_invert_mode(); + + *x_out = (mode == banjo::CameraInvertMode::InvertX || mode == banjo::CameraInvertMode::InvertBoth); + *y_out = (mode == banjo::CameraInvertMode::InvertY || mode == banjo::CameraInvertMode::InvertBoth); +} + +extern "C" void recomp_get_first_person_inverted_axes(uint8_t* rdram, recomp_context* ctx) { + s32* x_out = _arg<0, s32*>(rdram, ctx); + s32* y_out = _arg<1, s32*>(rdram, ctx); + + banjo::CameraInvertMode mode = banjo::get_first_person_invert_mode(); *x_out = (mode == banjo::CameraInvertMode::InvertX || mode == banjo::CameraInvertMode::InvertBoth); *y_out = (mode == banjo::CameraInvertMode::InvertY || mode == banjo::CameraInvertMode::InvertBoth);