diff --git a/assets/config_menu/general.rml b/assets/config_menu/general.rml
index b32f80d..42b7182 100644
--- a/assets/config_menu/general.rml
+++ b/assets/config_menu/general.rml
@@ -154,7 +154,7 @@
data-checked="autosave_mode"
value="On"
id="autosave_enabled"
- style="nav-up: #bg_input_enabled; nav-down: #analog_cam_enabled"
+ style="nav-up: #bg_input_enabled; nav-down: #camera_inversion_none"
/>
@@ -166,11 +166,95 @@
data-checked="autosave_mode"
value="Off"
id="autosave_disabled"
- style="nav-up: #bg_input_disabled; nav-down: #analog_cam_disabled"
+ style="nav-up: #bg_input_disabled; nav-down: #camera_inversion_x"
/>
+
+
+
+
+
+
@@ -209,6 +293,12 @@
If autosaving is disabled, existing autosaves will be deleted when loaded.
+
+ Camera inversion description.
+
+
+ Analog cam description.
+
diff --git a/include/recomp_config.h b/include/recomp_config.h
index e616a80..51e0424 100644
--- a/include/recomp_config.h
+++ b/include/recomp_config.h
@@ -28,13 +28,27 @@ namespace recomp {
OptionCount
};
+ enum class AnalogCamMode {
+ On,
+ Off,
+ OptionCount
+ };
+
NLOHMANN_JSON_SERIALIZE_ENUM(recomp::AutosaveMode, {
{recomp::AutosaveMode::On, "On"},
{recomp::AutosaveMode::Off, "Off"}
});
+ NLOHMANN_JSON_SERIALIZE_ENUM(recomp::AnalogCamMode, {
+ {recomp::AnalogCamMode::On, "On"},
+ {recomp::AnalogCamMode::Off, "Off"}
+ });
+
AutosaveMode get_autosave_mode();
void set_autosave_mode(AutosaveMode mode);
+
+ AnalogCamMode get_analog_cam_mode();
+ void set_analog_cam_mode(AnalogCamMode mode);
};
#endif
diff --git a/include/recomp_input.h b/include/recomp_input.h
index b95d53e..a5a33e3 100644
--- a/include/recomp_input.h
+++ b/include/recomp_input.h
@@ -67,6 +67,7 @@ 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);
+ void get_right_analog(float* x, float* y);
enum class InputDevice {
Controller,
@@ -133,6 +134,7 @@ namespace recomp {
void set_gyro_sensitivity(int strength);
void set_mouse_sensitivity(int strength);
void set_joystick_deadzone(int strength);
+ void apply_joystick_deadzone(float x_in, float y_in, float* x_out, float* y_out);
enum class TargetingMode {
Switch,
@@ -162,6 +164,24 @@ namespace recomp {
BackgroundInputMode get_background_input_mode();
void set_background_input_mode(BackgroundInputMode mode);
+ enum class CameraInvertMode {
+ InvertNone,
+ InvertX,
+ InvertY,
+ InvertBoth,
+ OptionCount
+ };
+
+ NLOHMANN_JSON_SERIALIZE_ENUM(recomp::CameraInvertMode, {
+ {recomp::CameraInvertMode::InvertNone, "InvertNone"},
+ {recomp::CameraInvertMode::InvertX, "InvertX"},
+ {recomp::CameraInvertMode::InvertY, "InvertY"},
+ {recomp::CameraInvertMode::InvertBoth, "InvertBoth"}
+ });
+
+ CameraInvertMode get_camera_invert_mode();
+ void set_camera_invert_mode(CameraInvertMode mode);
+
bool game_input_disabled();
bool all_input_disabled();
diff --git a/patches/camera_patches.c b/patches/camera_patches.c
index 32365a1..d40a20a 100644
--- a/patches/camera_patches.c
+++ b/patches/camera_patches.c
@@ -1,65 +1,44 @@
#include "patches.h"
#include "input.h"
#include "z64quake.h"
-#if 0
-RecompCameraMode recomp_camera_mode = RECOMP_CAMERA_NORMAL;
+#include "play_patches.h"
-VecGeo recomp_camera_pos = { .r = 66.0f, .pitch = 0, .yaw = 0 };
+static bool was_in_analog_cam = false;
+static bool is_in_analog_cam = false;
-float recomp_camera_yaw_vel = 0.0f;
-float recomp_camera_pitch_vel = 0.0f;
+VecGeo analog_camera_pos = { .r = 66.0f, .pitch = 0, .yaw = 0 };
+float analog_camera_yaw_vel = 0.0f;
+float analog_camera_pitch_vel = 0.0f;
-float recomp_deadzone = 0.2f;
-float recomp_camera_x_sensitivity = 1500.0f;
-float recomp_camera_y_sensitivity = 500.0f;
-// float recomp_camera_acceleration = 500.0f;
+float analog_deadzone = 0.2f;
+float analog_camera_x_sensitivity = 1500.0f;
+float analog_camera_y_sensitivity = 500.0f;
+// float analog_camera_acceleration = 500.0f;
-void update_recomp_camera_params(Camera* camera) {
- recomp_camera_pos.yaw = Math_Atan2S(-camera->at.x + camera->eye.x, -camera->at.z + camera->eye.z);
+void update_analog_camera_params(Camera* camera) {
+ analog_camera_pos.yaw = Math_Atan2S(-camera->at.x + camera->eye.x, -camera->at.z + camera->eye.z);
// recomp_printf("Camera at: %.2f %.2f %.2f\n"
// " eye: %.2f %.2f %.2f\n"
// " yaw: %d",
// camera->at.x, camera->at.y, camera->at.z,
// camera->eye.x, camera->eye.y, camera->eye.z,
- // recomp_camera_pos.yaw);
+ // analog_camera_pos.yaw);
- float input_x, input_y;
- recomp_get_camera_inputs(&input_x, &input_y);
+ if (was_in_analog_cam) {
+ float input_x, input_y;
+ recomp_get_camera_inputs(&input_x, &input_y);
- // Math_StepToF(&recomp_camera_yaw_vel, input_x * recomp_camera_x_sensitivity, recomp_camera_acceleration);
- // Math_StepToF(&recomp_camera_pitch_vel, input_y * recomp_camera_y_sensitivity, recomp_camera_acceleration);
- if (fabsf(input_x) > recomp_deadzone) {
- recomp_camera_yaw_vel = input_x * recomp_camera_x_sensitivity;
- }
- else {
- recomp_camera_yaw_vel = 0;
- }
-
- if (fabsf(input_y) > recomp_deadzone) {
- recomp_camera_pitch_vel = input_y * recomp_camera_y_sensitivity;
- }
- else {
- recomp_camera_pitch_vel = 0;
- }
+ analog_camera_yaw_vel = input_x * analog_camera_x_sensitivity;
+ analog_camera_pitch_vel = input_y * analog_camera_y_sensitivity;
- recomp_camera_pos.pitch += recomp_camera_pitch_vel;
- recomp_camera_pos.yaw += recomp_camera_yaw_vel;
+ analog_camera_pos.pitch += analog_camera_pitch_vel;
+ analog_camera_pos.yaw += analog_camera_yaw_vel;
+ }
}
extern s32 sUpdateCameraDirection;
-static s32 sIsFalse = false;
-extern s32 sCameraInitSceneTimer;
-
-extern s16 sCameraNextUID;
extern s32 sCameraInterfaceFlags;
-extern s32 sCameraHudVisibility;
-extern s32 sCameraLetterboxSize;
-extern s32 sCameraNegOne1;
-
-#define CAM_DATA_IS_BG (1 << 12) // if not set, then cam data is for actor cutscenes
-
-typedef s32 (*CameraUpdateFunc)(Camera*);
typedef struct {
/* 0x0 */ s16 val;
@@ -78,20 +57,34 @@ typedef struct {
/* 0x8 */ CameraMode* cameraModes;
} CameraSetting;
-extern CameraUpdateFunc sCameraUpdateHandlers[];
extern CameraSetting sCameraSettings[];
+f32 Camera_GetFocalActorHeight(Camera* camera);
+f32 Camera_Vec3fMagnitude(Vec3f* vec);
+
+#if 0
+static s32 sIsFalse = false;
+extern s32 sCameraInitSceneTimer;
+
+extern s16 sCameraNextUID;
+extern s32 sCameraHudVisibility;
+extern s32 sCameraLetterboxSize;
+extern s32 sCameraNegOne1;
+
+#define CAM_DATA_IS_BG (1 << 12) // if not set, then cam data is for actor cutscenes
+
+typedef s32 (*CameraUpdateFunc)(Camera*);
+
+extern CameraUpdateFunc sCameraUpdateHandlers[];
Vec3f Camera_CalcUpVec(s16 pitch, s16 yaw, s16 roll);
f32 Camera_fabsf(f32 f);
s32 Camera_GetBgCamIndex(Camera* camera, s32* bgId, CollisionPoly* poly);
-f32 Camera_GetFocalActorHeight(Camera* camera);
f32 Camera_GetRunSpeedLimit(Camera* camera);
s32 Camera_IsDekuHovering(Camera* camera);
s32 Camera_IsMountedOnHorse(Camera* camera);
s32 Camera_IsUnderwaterAsZora(Camera* camera);
s32 Camera_IsUsingZoraFins(Camera* camera);
void Camera_UpdateInterface(s32 interfaceFlags);
-f32 Camera_Vec3fMagnitude(Vec3f* vec);
s32 func_800CB7CC(Camera* camera);
s32 func_800CB854(Camera* camera);
@@ -275,7 +268,7 @@ Vec3s Camera_Update(Camera* camera) {
sCameraUpdateHandlers[sCameraSettings[camera->setting].cameraModes[camera->mode].funcId](camera);
// @recomp
- update_recomp_camera_params(camera);
+ update_analog_camera_params(camera);
// Update the interface
if (sCameraInitSceneTimer != 0) {
@@ -357,6 +350,8 @@ Vec3s Camera_Update(Camera* camera) {
return camera->inputDir;
}
+#endif
+
extern SwingAnimation D_801EDC30[4];
s32 Camera_CalcAtDefault(Camera* camera, VecGeo* eyeAtDir, f32 yOffset, s16 calcSlope);
s32 Camera_CalcAtForNormal1(Camera* camera, VecGeo* arg1, f32 yOffset, f32 forwardDist);
@@ -691,10 +686,11 @@ s32 Camera_Normal1(Camera* camera) {
}
// @recomp
- if (recomp_camera_mode == RECOMP_CAMERA_DUALANALOG) {
- spB4.pitch = recomp_camera_pos.pitch;
- // spB4.r = recomp_camera_pos.r;
- spB4.yaw = recomp_camera_pos.yaw;
+ if (recomp_analog_cam_enabled()) {
+ is_in_analog_cam = true;
+ spB4.pitch = analog_camera_pos.pitch;
+ // spB4.r = analog_camera_pos.r;
+ spB4.yaw = analog_camera_pos.yaw;
}
// 76.9 degrees
@@ -708,7 +704,7 @@ s32 Camera_Normal1(Camera* camera) {
}
// @recomp
- recomp_camera_pos.pitch = spB4.pitch;
+ analog_camera_pos.pitch = spB4.pitch;
*eyeNext = OLib_AddVecGeoToVec3f(at, &spB4);
@@ -755,11 +751,10 @@ s32 Camera_Normal1(Camera* camera) {
phi_f2 = (gSaveContext.save.saveInfo.playerData.health <= 0x10) ? 0.8f : 1.0f;
- // @recomp
- // // Don't zoom in on low health when dual analog is used
- // if (recomp_camera_mode == RECOMP_CAMERA_DUALANALOG) {
- // phi_f2;
- // }
+ // @recomp Don't zoom in on low health when dual analog is used
+ if (recomp_analog_cam_enabled()) {
+ phi_f2 = 1.0f;
+ }
camera->fov = Camera_ScaledStepToCeilF(roData->unk_18 * phi_f2, camera->fov, camera->fovUpdateRate, 0.1f);
@@ -783,4 +778,14 @@ s32 Camera_Normal1(Camera* camera) {
return true;
}
-#endif // #if 0
+
+void analog_cam_pre_play_update(PlayState* play) {
+ Camera *active_cam = play->cameraPtrs[play->activeCamId];
+ update_analog_camera_params(active_cam);
+}
+
+void analog_cam_post_play_update(PlayState* play) {
+ // recomp_printf("was_in_analog_cam: %d is_in_analog_cam: %d\n", was_in_analog_cam, is_in_analog_cam);
+ was_in_analog_cam = is_in_analog_cam;
+ is_in_analog_cam = false;
+}
diff --git a/patches/input.c b/patches/input.c
index 5000b46..4e7e1fe 100644
--- a/patches/input.c
+++ b/patches/input.c
@@ -95,18 +95,6 @@ u32 sPlayerItemButtons[] = {
BTN_CRIGHT,
};
-// u32 sPlayerItemButtonsDualAnalog[] = {
-// BTN_B,
-// BTN_DLEFT,
-// BTN_DDOWN,
-// BTN_DRIGHT
-// };
-
-// u32 prev_item_buttons = 0;
-// u32 cur_item_buttons = 0;
-// u32 pressed_item_buttons = 0;
-// u32 released_item_buttons = 0;
-
// D-Pad items
#define EXTRA_ITEM_SLOT_COUNT 4
@@ -129,31 +117,6 @@ typedef enum {
EQUIP_SLOT_EX_DDOWN,
} EquipSlotEx;
-// static inline void dup_to_cup(u16* button) {
-// if (*button & BTN_DUP) {
-// *button |= BTN_CUP;
-// }
-// }
-
-void GameState_GetInput(GameState* gameState) {
- PadMgr_GetInput(gameState->input, true);
-
- // if (recomp_camera_mode == RECOMP_CAMERA_DUALANALOG) {
- // gameState->input[0].cur.button &= ~BTN_CUP;
- // gameState->input[0].press.button &= ~BTN_CUP;
- // gameState->input[0].rel.button &= ~BTN_CUP;
- // dup_to_cup(&gameState->input[0].cur.button);
- // dup_to_cup(&gameState->input[0].press.button);
- // dup_to_cup(&gameState->input[0].rel.button);
- // }
-
- // prev_item_buttons = cur_item_buttons;
- // recomp_get_item_inputs(&cur_item_buttons);
- // u32 button_diff = prev_item_buttons ^ cur_item_buttons;
- // pressed_item_buttons = cur_item_buttons & button_diff;
- // released_item_buttons = prev_item_buttons & button_diff;
-}
-
struct ExButtonMapping {
u32 button;
EquipSlotEx slot;
diff --git a/patches/input.h b/patches/input.h
index d1399b7..39735d2 100644
--- a/patches/input.h
+++ b/patches/input.h
@@ -12,6 +12,9 @@ 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);
+DECLARE_FUNC(s32, recomp_get_targeting_mode);
+DECLARE_FUNC(void, recomp_get_inverted_axes, s32* x, s32* y);
+DECLARE_FUNC(s32, recomp_analog_cam_enabled);
+DECLARE_FUNC(void, recomp_get_camera_inputs, float* x, float* y);
#endif
diff --git a/patches/play_patches.c b/patches/play_patches.c
index 3cc5a83..bff7066 100644
--- a/patches/play_patches.c
+++ b/patches/play_patches.c
@@ -16,6 +16,7 @@ void Play_Main(GameState* thisx) {
// @recomp
debug_play_update(this);
controls_play_update(this);
+ analog_cam_pre_play_update(this);
matrix_play_update(this);
// @recomp avoid unused variable warning
@@ -33,6 +34,7 @@ void Play_Main(GameState* thisx) {
camera_pre_play_update(this);
Play_Update(this);
camera_post_play_update(this);
+ analog_cam_post_play_update(this);
autosave_post_play_update(this);
this->state.gfxCtx = gfxCtx;
}
diff --git a/patches/play_patches.h b/patches/play_patches.h
index 62c39fb..19e7db2 100644
--- a/patches/play_patches.h
+++ b/patches/play_patches.h
@@ -6,6 +6,8 @@
void debug_play_update(PlayState* play);
void camera_pre_play_update(PlayState* play);
void camera_post_play_update(PlayState* play);
+void analog_cam_pre_play_update(PlayState* play);
+void analog_cam_post_play_update(PlayState* play);
void matrix_play_update(PlayState* play);
void autosave_post_play_update(PlayState* play);
diff --git a/patches/syms.ld b/patches/syms.ld
index 81418e8..7993208 100644
--- a/patches/syms.ld
+++ b/patches/syms.ld
@@ -52,5 +52,7 @@ osGetTime_recomp = 0x8F000088;
recomp_autosave_enabled = 0x8F00008C;
recomp_load_overlays = 0x8F000090;
osInvalICache_recomp = 0x8F000094;
+recomp_analog_cam_enabled = 0x8F000098;
+recomp_get_camera_inputs = 0x8F00009C;
recomp_high_precision_fb_enabled = 0x8F0000A8;
recomp_get_resolution_scale = 0x8F0000AC;
diff --git a/src/game/config.cpp b/src/game/config.cpp
index 3216f1f..2ecdb52 100644
--- a/src/game/config.cpp
+++ b/src/game/config.cpp
@@ -165,6 +165,8 @@ void save_general_config(const std::filesystem::path& path) {
config_json["mouse_sensitivity"] = recomp::get_mouse_sensitivity();
config_json["joystick_deadzone"] = recomp::get_joystick_deadzone();
config_json["autosave_mode"] = recomp::get_autosave_mode();
+ config_json["camera_invert_mode"] = recomp::get_camera_invert_mode();
+ config_json["analog_cam_mode"] = recomp::get_analog_cam_mode();
config_json["debug_mode"] = recomp::get_debug_mode_enabled();
config_file << std::setw(4) << config_json;
}
@@ -177,6 +179,8 @@ void set_general_settings_from_json(const nlohmann::json& config_json) {
recomp::set_mouse_sensitivity(from_or_default(config_json, "mouse_sensitivity", is_steam_deck ? 50 : 0));
recomp::set_joystick_deadzone(from_or_default(config_json, "joystick_deadzone", 5));
recomp::set_autosave_mode(from_or_default(config_json, "autosave_mode", recomp::AutosaveMode::On));
+ recomp::set_camera_invert_mode(from_or_default(config_json, "camera_invert_mode", recomp::CameraInvertMode::InvertY));
+ recomp::set_analog_cam_mode(from_or_default(config_json, "analog_cam_mode", recomp::AnalogCamMode::Off));
recomp::set_debug_mode_enabled(from_or_default(config_json, "debug_mode", false));
}
diff --git a/src/game/controls.cpp b/src/game/controls.cpp
index 726e674..35db400 100644
--- a/src/game/controls.cpp
+++ b/src/game/controls.cpp
@@ -95,33 +95,7 @@ void recomp::get_n64_input(uint16_t* buttons_out, float* x_out, float* y_out) {
float joystick_y = recomp::get_input_analog(controller_input_mappings[(size_t)GameInput::Y_AXIS_POS])
- recomp::get_input_analog(controller_input_mappings[(size_t)GameInput::Y_AXIS_NEG]);
- if(fabsf(joystick_x) < joystick_deadzone) {
- joystick_x = 0.0f;
- }
- else {
- if(joystick_x > 0.0f) {
- joystick_x -= joystick_deadzone;
- }
- else {
- joystick_x += joystick_deadzone;
- }
-
- joystick_x /= (1.0f - joystick_deadzone);
- }
-
- if(fabsf(joystick_y) < joystick_deadzone) {
- joystick_y = 0.0f;
- }
- else {
- if(joystick_y > 0.0f) {
- joystick_y -= joystick_deadzone;
- }
- else {
- joystick_y += joystick_deadzone;
- }
-
- joystick_y /= (1.0f - joystick_deadzone);
- }
+ recomp::apply_joystick_deadzone(joystick_x, joystick_y, &joystick_x, &joystick_y);
cur_x = recomp::get_input_analog(keyboard_input_mappings[(size_t)GameInput::X_AXIS_POS])
- recomp::get_input_analog(keyboard_input_mappings[(size_t)GameInput::X_AXIS_NEG]) + joystick_x;
diff --git a/src/game/input.cpp b/src/game/input.cpp
index 786536b..71a4a53 100644
--- a/src/game/input.cpp
+++ b/src/game/input.cpp
@@ -580,6 +580,51 @@ void recomp::get_mouse_deltas(float* x, float* y) {
*y = cur_mouse_delta[1] * sensitivity;
}
+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;
+
+ if(fabsf(x_in) < joystick_deadzone) {
+ x_in = 0.0f;
+ }
+ else {
+ if(x_in > 0.0f) {
+ x_in -= joystick_deadzone;
+ }
+ else {
+ x_in += joystick_deadzone;
+ }
+
+ x_in /= (1.0f - joystick_deadzone);
+ }
+
+ if(fabsf(y_in) < joystick_deadzone) {
+ y_in = 0.0f;
+ }
+ else {
+ if(y_in > 0.0f) {
+ y_in -= joystick_deadzone;
+ }
+ else {
+ y_in += joystick_deadzone;
+ }
+
+ y_in /= (1.0f - joystick_deadzone);
+ }
+
+ *x_out = x_in;
+ *y_out = y_in;
+}
+
+void recomp::get_right_analog(float* x, float* y) {
+ float x_val =
+ controller_axis_state((SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTX + 1)) -
+ controller_axis_state(-(SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTX + 1));
+ float y_val =
+ controller_axis_state((SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTY + 1)) -
+ controller_axis_state(-(SDL_GameControllerAxis::SDL_CONTROLLER_AXIS_RIGHTY + 1));
+ recomp::apply_joystick_deadzone(x_val, y_val, x, y);
+}
+
bool recomp::game_input_disabled() {
// Disable input if any menu is open.
return recomp::get_current_menu() != recomp::Menu::None;
diff --git a/src/game/recomp_api.cpp b/src/game/recomp_api.cpp
index 8bdfae8..d85ecba 100644
--- a/src/game/recomp_api.cpp
+++ b/src/game/recomp_api.cpp
@@ -110,3 +110,23 @@ extern "C" void recomp_high_precision_fb_enabled(uint8_t * rdram, recomp_context
extern "C" void recomp_get_resolution_scale(uint8_t* rdram, recomp_context* ctx) {
_return(ctx, ultramodern::get_resolution_scale());
}
+
+extern "C" void recomp_get_inverted_axes(uint8_t* rdram, recomp_context* ctx) {
+ s32* x_out = _arg<0, s32*>(rdram, ctx);
+ s32* y_out = _arg<1, s32*>(rdram, ctx);
+
+ // TODO implement this
+ *x_out = 0;
+ *y_out = 1;
+}
+
+extern "C" void recomp_analog_cam_enabled(uint8_t* rdram, recomp_context* ctx) {
+ _return(ctx, recomp::get_analog_cam_mode() == recomp::AnalogCamMode::On);
+}
+
+extern "C" void recomp_get_camera_inputs(uint8_t* rdram, recomp_context* ctx) {
+ float* x_out = _arg<0, float*>(rdram, ctx);
+ float* y_out = _arg<1, float*>(rdram, ctx);
+
+ recomp::get_right_analog(x_out, y_out);
+}
diff --git a/src/ui/ui_config.cpp b/src/ui/ui_config.cpp
index d8d20a4..a6bf2f9 100644
--- a/src/ui/ui_config.cpp
+++ b/src/ui/ui_config.cpp
@@ -278,6 +278,8 @@ struct ControlOptionsContext {
recomp::TargetingMode targeting_mode;
recomp::BackgroundInputMode background_input_mode;
recomp::AutosaveMode autosave_mode;
+ recomp::CameraInvertMode camera_invert_mode;
+ recomp::AnalogCamMode analog_cam_mode;
};
ControlOptionsContext control_options_context;
@@ -365,6 +367,28 @@ void recomp::set_autosave_mode(recomp::AutosaveMode mode) {
}
}
+recomp::CameraInvertMode recomp::get_camera_invert_mode() {
+ return control_options_context.camera_invert_mode;
+}
+
+void recomp::set_camera_invert_mode(recomp::CameraInvertMode mode) {
+ control_options_context.camera_invert_mode = mode;
+ if (general_model_handle) {
+ general_model_handle.DirtyVariable("camera_invert_mode");
+ }
+}
+
+recomp::AnalogCamMode recomp::get_analog_cam_mode() {
+ return control_options_context.analog_cam_mode;
+}
+
+void recomp::set_analog_cam_mode(recomp::AnalogCamMode mode) {
+ control_options_context.analog_cam_mode = mode;
+ if (general_model_handle) {
+ general_model_handle.DirtyVariable("analog_cam_mode");
+ }
+}
+
struct SoundOptionsContext {
std::atomic main_volume; // Option to control the volume of all sound
std::atomic bgm_volume;
@@ -864,6 +888,8 @@ public:
bind_option(constructor, "targeting_mode", &control_options_context.targeting_mode);
bind_option(constructor, "background_input_mode", &control_options_context.background_input_mode);
bind_option(constructor, "autosave_mode", &control_options_context.autosave_mode);
+ bind_option(constructor, "camera_invert_mode", &control_options_context.camera_invert_mode);
+ bind_option(constructor, "analog_cam_mode", &control_options_context.analog_cam_mode);
general_model_handle = constructor.GetModelHandle();
}