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);