From cda95efc7dee5cdce837e57142e8e38c3de6e02c Mon Sep 17 00:00:00 2001 From: Mr-Wiseguy Date: Sat, 25 May 2024 03:40:52 -0400 Subject: [PATCH] Added analog cam and camera inversion options to menu, initial implementation of analog cam --- assets/config_menu/general.rml | 94 +++++++++++++++++++++++++- include/recomp_config.h | 14 ++++ include/recomp_input.h | 20 ++++++ patches/camera_patches.c | 119 +++++++++++++++++---------------- patches/input.c | 37 ---------- patches/input.h | 5 +- patches/play_patches.c | 2 + patches/play_patches.h | 2 + patches/syms.ld | 2 + src/game/config.cpp | 4 ++ src/game/controls.cpp | 28 +------- src/game/input.cpp | 45 +++++++++++++ src/game/recomp_api.cpp | 20 ++++++ src/ui/ui_config.cpp | 26 +++++++ 14 files changed, 294 insertions(+), 124 deletions(-) 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(); }