Added analog cam and camera inversion options to menu, initial implementation of analog cam

This commit is contained in:
Mr-Wiseguy 2024-05-25 03:40:52 -04:00
parent a4f61016bb
commit cda95efc7d
14 changed files with 294 additions and 124 deletions

View file

@ -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"
/>
<label class="config-option__tab-label" for="autosave_enabled">On</label>
@ -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"
/>
<label class="config-option__tab-label" for="autosave_disabled">Off</label>
</div>
</div>
<!-- camera inversion -->
<div class="config-option" data-event-mouseover="set_cur_config_index(7)">
<label class="config-option__title">Inverted Camera Control</label>
<div class="config-option__list">
<input
type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(7)"
name="camera_invert_mode"
data-checked="camera_invert_mode"
value="InvertNone"
id="camera_inversion_none"
style="nav-up: #autosave_enabled; nav-down: #analog_cam_enabled"
/>
<label class="config-option__tab-label" for="camera_inversion_none">None</label>
<input
type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(7)"
name="camera_invert_mode"
data-checked="camera_invert_mode"
value="InvertX"
id="camera_inversion_x"
style="nav-up: #autosave_disabled; nav-down: #analog_cam_disabled"
/>
<label class="config-option__tab-label" for="camera_inversion_x">Invert X</label>
<input
type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(7)"
name="camera_invert_mode"
data-checked="camera_invert_mode"
value="InvertY"
id="camera_inversion_y"
style="nav-up: #autosave_disabled; nav-down: #analog_cam_disabled"
/>
<label class="config-option__tab-label" for="camera_inversion_y">Invert Y</label>
<input
type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(7)"
name="camera_invert_mode"
data-checked="camera_invert_mode"
value="InvertBoth"
id="camera_inversion_both"
style="nav-up: #autosave_disabled; nav-down: #analog_cam_disabled"
/>
<label class="config-option__tab-label" for="camera_inversion_both">Invert Both</label>
</div>
</div>
<!-- analog camera -->
<div class="config-option" data-event-mouseover="set_cur_config_index(8)">
<label class="config-option__title">Analog Camera</label>
<div class="config-option__list">
<input
type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(8)"
name="analog_cam_mode"
data-checked="analog_cam_mode"
value="On"
id="analog_cam_enabled"
style="nav-up: #camera_inversion_none"
/>
<label class="config-option__tab-label" for="analog_cam_enabled">On</label>
<input
type="radio"
data-event-blur="set_cur_config_index(-1)"
data-event-focus="set_cur_config_index(8)"
name="analog_cam_mode"
data-checked="analog_cam_mode"
value="Off"
id="analog_cam_disabled"
style="nav-up: #camera_inversion_x"
/>
<label class="config-option__tab-label" for="analog_cam_disabled">Off</label>
</div>
</div>
</div>
<!-- Descriptions -->
<div class="config__wrapper">
@ -209,6 +293,12 @@
<br/>
<b>If autosaving is disabled, existing autosaves will be deleted when loaded.</b>
</p>
<p data-if="cur_config_index == 7">
Camera inversion description.
</p>
<p data-if="cur_config_index == 8">
Analog cam description.
</p>
</div>
</div>
</form>

View file

@ -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

View file

@ -67,6 +67,7 @@ namespace recomp {
bool get_input_digital(const std::span<const recomp::InputField> 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();

View file

@ -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;
}

View file

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

View file

@ -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

View file

@ -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;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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<s32>(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);
}

View file

@ -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<int> main_volume; // Option to control the volume of all sound
std::atomic<int> 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();
}