diff --git a/CHANGES.md b/CHANGES.md index 419c18d..ddfd95c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,10 @@ +## Beta 1 +* Bumped module and MCM version number to 9 +* Promoted to beta. +* Added shoulder swapping via hotkey. +* Added separable option for swapping the distance clamping X axis when swapping shoulders. +* Fixed issue with `preproc_constexpr_struct.lua` incorrectly assuming all `a.b` syntax was a struct macro to be replaced. + ## Alpha 1.7 * Bumped module and MCM version number to 8 * Added a "group edit" tab in the MCM, allowing users to edit all offset groups at once. diff --git a/MCM/SmoothCamMCM.pex b/MCM/SmoothCamMCM.pex index 904b311..a3543d3 100644 Binary files a/MCM/SmoothCamMCM.pex and b/MCM/SmoothCamMCM.pex differ diff --git a/MCM/mcm.psc b/MCM/mcm.psc index d9cab7f..506e228 100644 --- a/MCM/mcm.psc +++ b/MCM/mcm.psc @@ -6,10 +6,12 @@ Import SKSE string[] interpMethods string[] presets +Function SmoothCam_SetIntConfig(string member, int value) global native Function SmoothCam_SetStringConfig(string member, string value) global native Function SmoothCam_SetBoolConfig(string member, bool value) global native Function SmoothCam_SetFloatConfig(string member, float value) global native +int Function SmoothCam_GetIntConfig(string member) global native string Function SmoothCam_GetStringConfig(string member) global native bool Function SmoothCam_GetBoolConfig(string member) global native float Function SmoothCam_GetFloatConfig(string member) global native @@ -171,8 +173,35 @@ endFunction } } +#constexpr_struct KeyBindSetting { + real_int ref = 0 + string settingName = "" + string displayName = "" + string desc = "" + + MACRO implControl = { + this->ref = AddKeyMapOption(this->displayName, SmoothCam_GetIntConfig(this->settingName)) + } + + MACRO implSelectHandler = { + if (StringUtil.GetLength(a_conflictControl) == 0) + SmoothCam_SetIntConfig(this->settingName, a_keyCode) + SetKeyMapOptionValue(this->ref, a_keyCode) + else + if (ShowMessage(a_conflictControl + " conflicts with another control assigned to " + a_conflictName + ".\nAre you sure you want to assign this control?")) + SmoothCam_SetIntConfig(this->settingName, a_keyCode) + SetKeyMapOptionValue(this->ref, a_keyCode) + endIf + endIf + } + + MACRO implDesc = { + SetInfoText(this->desc) + } +} + ScriptMeta scriptMetaInfo -> { - version: 8 + version: 9 } ; Presets @@ -294,6 +323,11 @@ ToggleSetting cameraDistanceClampZEnable -> { displayName: "Enable Z Distance Clamp" desc: "Clamp the maximum distance the camera may move away from the target position along the Z (up) axis." } +ToggleSetting swapDistanceClampXAxis -> { + settingName: "ShoulderSwapXClamping" + displayName: "Also Swap X Axis Clamping" + desc: "When shoulder swapping, will also swap the distance clamping X axis range." +} ListSetting interpMethod -> { settingName: "InterpolationMethod" @@ -490,6 +524,12 @@ SliderSetting sepLocalSpaceInterpRate -> { displayFormat: "{2}" } +KeyBindSetting shoulderSwapKey -> { + settingName: "ShoulderSwapKeyCode" + displayName: "Shoulder Swap Key" + desc: "Inverts the current X offset of the camera" +} + ; Crosshair ToggleSetting crosshair3DBowEnabled -> { settingName: "Enable3DBowCrosshair" @@ -1366,7 +1406,7 @@ event OnPageReset(string a_page) AddHeaderOption("Misc") IMPL_STRUCT_MACRO_INVOKE_GROUP(implControl, { - zoomMul, disableDeltaTime + shoulderSwapKey, swapDistanceClampXAxis, zoomMul, disableDeltaTime }) elseIf (a_page == " Crosshair") AddHeaderOption("3D Crosshair Settings") @@ -1645,12 +1685,17 @@ event OnOptionInputAccept(int a_option, string a_input) IMPL_IFCHAIN_MACRO_INVOKE(a_option, ref, implAcceptHandler, {IMPL_ALL_IMPLS_OF_STRUCT(SavePresetSetting)}) endEvent +event OnOptionKeyMapChange(int a_option, int a_keyCode, string a_conflictControl, string a_conflictName) + IMPL_IFCHAIN_MACRO_INVOKE(a_option, ref, implSelectHandler, {IMPL_ALL_IMPLS_OF_STRUCT(KeyBindSetting)}) +endEvent + event OnOptionHighlight(int a_option) IMPL_IFCHAIN_MACRO_INVOKE(a_option, ref, implDesc, { IMPL_ALL_IMPLS_OF_STRUCT(SliderSetting), IMPL_ALL_IMPLS_OF_STRUCT(ToggleSetting), IMPL_ALL_IMPLS_OF_STRUCT(ListSetting), IMPL_ALL_IMPLS_OF_STRUCT(SavePresetSetting), - IMPL_ALL_IMPLS_OF_STRUCT(LoadPresetSetting) + IMPL_ALL_IMPLS_OF_STRUCT(LoadPresetSetting), + IMPL_ALL_IMPLS_OF_STRUCT(KeyBindSetting) }) endEvent \ No newline at end of file diff --git a/MCM/preproc/preproc_constexpr_struct.lua b/MCM/preproc/preproc_constexpr_struct.lua index 6125d56..8ba183c 100644 --- a/MCM/preproc/preproc_constexpr_struct.lua +++ b/MCM/preproc/preproc_constexpr_struct.lua @@ -168,25 +168,27 @@ do class "ConstexprStructToVars" : namespace "papyrus.preproc" { gen_member = gen_member:trim() if not generated[member.."."..gen_decl.."."..gen_member] then - local typeInfo = self.m_tblStructTypes[self.m_tblStructImpls[gen_decl].type] - - local usage = self.m_tblStructMemberUsage[gen_decl.."."..gen_member] - if usage then - code = code:replace( gen_decl.."."..gen_member, usage.name ) - else - if typeInfo.members[gen_member].isreal then - code = code:replace( gen_decl.."."..gen_member, gen_decl.. "_".. gen_member ) + if self.m_tblStructImpls[gen_decl] and self.m_tblStructTypes[self.m_tblStructImpls[gen_decl].type] then + local typeInfo = self.m_tblStructTypes[self.m_tblStructImpls[gen_decl].type] + + local usage = self.m_tblStructMemberUsage[gen_decl.."."..gen_member] + if usage then + code = code:replace( gen_decl.."."..gen_member, usage.name ) else - -- Value literal - local value = self.m_tblStructImpls[gen_decl].members[gen_member] - if not value then - value = typeInfo.members[gen_member].value + if typeInfo.members[gen_member].isreal then + code = code:replace( gen_decl.."."..gen_member, gen_decl.. "_".. gen_member ) + else + -- Value literal + local value = self.m_tblStructImpls[gen_decl].members[gen_member] + if not value then + value = typeInfo.members[gen_member].value + end + code = code:replace( gen_decl.."."..gen_member, value ) end - code = code:replace( gen_decl.."."..gen_member, value ) end - end - generated[member.."."..gen_decl.."."..gen_member] = true + generated[member.."."..gen_decl.."."..gen_member] = true + end end end diff --git a/README.md b/README.md index 606bb9c..c3c0224 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Depending on the age of your processor (~2011 and earlier), you might need to us ## Installing If using one of the pre-compiled releases, just copy the `Data` folder in the archive into your skyrim folder or install via a mod manager. -If installing after a build, copy `SmoothCam.dll` to `Data/SKSE/Plugins`, `hudmenu.swf` to `Data/interface`, `SmoothCam.esp` to `Data/` and `SmoothCamMCM.pex` to `Data/Scripts`. Enable the esp file if you wish to use the MCM, otherwise the module will generate a json file in the plugins folder you can edit to manually configure the camera. +If installing after a build, copy `SmoothCam.dll` to `Data/SKSE/Plugins` (along with the address library database file), `SmoothCam.esp` to `Data/` and `SmoothCamMCM.pex` to `Data/Scripts`. Enable the esp file if you wish to use the MCM, otherwise the module will generate a json file in the plugins folder you can edit to manually configure the camera. ## Building To build the project, clone the repo (remember to include `--recurse-submodules`), then run `make_vs2019.bat` or `make_vs2017.bat`. @@ -26,7 +26,7 @@ The build scripts require `premake5` and `7-Zip` to be installed and available o Once premake has finished, open the generated Visual Studio solution, select `Debug` or `Release` and select `Build Solution` from the build dropdown. -Built files are placed in `SmoothCam/bin//SmoothCam`. Be sure to also copy `hudmenu.swf` from the UI folder into `Data/interface` for the 3D crosshair to work. Copy the esp to `Data/` and the pex file to `Data/Scripts`. +Built files are placed in `SmoothCam/bin//SmoothCam`. To build the papyrus script, you'll need `lua` on the system path. To run the code generation just run `MCM/run_preprocess.bat` which will generate `SmoothCamMCM.psc`. From there just compile the generated code like any normal papyrus script. @@ -46,6 +46,7 @@ use3DBowAimCrosshair | Enables the raycasted crosshair while aiming with a bow use3DMagicCrosshair | Enables the raycasted crosshair when magic is equiped hideNonCombatCrosshair | Hides the crosshair when no weapon is drawn hideCrosshairMeleeCombat | Hides the crosshair when melee weapons are drawn +enableCrosshairSizeManip | Enable size manipulation of the crosshair crosshairNPCHitGrowSize | When the 3D crosshair is over an NPC, grow the size of the crosshair by this amount crosshairMinDistSize | Sets the size of the 3D crosshair when the player's aim ray is at the maximum distance crosshairMaxDistSize | Sets the size of the 3D crosshair when the player's aim ray is at the minimum distance @@ -54,6 +55,8 @@ disableDuringDialog | Disables SmoothCam during character-NPC conversations comaptIC_FirstPersonHorse | Enables compat code for dealing with issue when running the Improved Camera mod comaptIC_FirstPersonDragon | Enables compat code for dealing with issue when running the Improved Camera mod compatIC_FirstPersonSitting | Enables compat code for dealing with issue when running the Improved Camera mod +shoulderSwapKey | The key code used for swapping the X axis when pressed +swapXClamping | When swapping the shoulder/X axis, also flips the distance clamping X values currentScalar | A value from 0 - 21: The scalar function to use for interpolation minCameraFollowDistance | The distance the camera follows the player from when at the lowest zoom level minCameraFollowRate | The amount of camera latency when the camera is close to the player (lower = more latency) @@ -100,14 +103,19 @@ OffsetSetting | Description --- | --- sideOffset | The amount to move the camera to the right upOffset | The amount to move the camera up +zoomOffset | The amount to zoom the camera by combatRangedSideOffset | The amount to move the camera to the right during ranged combat combatRangedUpOffset | The amount to move the camera up during ranged combat +combatRangedZoomOffset | The amount to zoom the camera by combatMagicSideOffset | The amount to move the camera to the right during magic combat combatMagicUpOffset | The amount to move the camera up during magic combat +combatMagicZoomOffset | The amount to zoom the camera by combatMeleeSideOffset | The amount to move the camera to the right during melee combat combatMeleeUpOffset | The amount to move the camera up during melee combat +combatMeleeZoomOffset | The amount to zoom the camera by horseSideOffset | The amount to move the camera to the right when on horseback horseUpOffset | The amount to move the camera up when on horseback +horseZoomOffset | The amount to zoom the camera by interp | Enable smoothing during this state interpRangedCombat | Enable smoothing during this state when in ranged combat interpMagicCombat | Enable smoothing during this state when in magic combat diff --git a/SmoothCam/include/camera.h b/SmoothCam/include/camera.h index 93acf86..198ef8a 100644 --- a/SmoothCam/include/camera.h +++ b/SmoothCam/include/camera.h @@ -54,6 +54,8 @@ namespace Camera { void UpdateCamera(PlayerCharacter* player, CorrectedPlayerCamera* camera); // Called when the player toggles the POV void OnTogglePOV(const ButtonEvent* ev) noexcept; + // Called when any other key is pressed + void OnKeyPress(const ButtonEvent* ev) noexcept; // Called when the dialog menu is shown or hidden void OnDialogMenuChanged(const MenuOpenCloseEvent* const ev) noexcept; // Returns our most recent camera position @@ -108,6 +110,8 @@ namespace Camera { glm::vec3 GetCurrentCameraOffset(PlayerCharacter* player, const CorrectedPlayerCamera* camera) const noexcept; // Returns the current smoothing scalar to use for the given distance to the player float GetCurrentSmoothingScalar(const float distance, ScalarSelector method = ScalarSelector::Normal) const; + // Returns the user defined distance clamping vector pair + std::tuple GetDistanceClamping() const noexcept; // Returns true if interpolation is allowed in the current state bool IsInterpAllowed(PlayerCharacter* player) const noexcept; // Constructs the view matrix for the camera @@ -230,6 +234,7 @@ namespace Camera { bool povIsThird = false; bool povWasPressed = false; bool dialogMenuOpen = false; + int shoulderSwap = 1; friend class State::BaseCameraState; }; diff --git a/SmoothCam/include/config.h b/SmoothCam/include/config.h index 5c69b0c..92d4eff 100644 --- a/SmoothCam/include/config.h +++ b/SmoothCam/include/config.h @@ -122,6 +122,8 @@ namespace Config { // Misc bool disableDeltaTime = false; + int shoulderSwapKey = -1; + bool swapXClamping = true; // Comapt bool disableDuringDialog = false; @@ -133,21 +135,21 @@ namespace Config { bool enableInterp = true; ScalarMethods currentScalar = ScalarMethods::SINE_IN; float minCameraFollowDistance = 64.0f; - float minCameraFollowRate = 0.2f; - float maxCameraFollowRate = 0.5f; + float minCameraFollowRate = 0.15f; + float maxCameraFollowRate = 0.4f; float zoomMul = 500.0f; float zoomMaxSmoothingDistance = 650.0f; // Separate local space interpolation - bool separateLocalInterp = false; + bool separateLocalInterp = true; ScalarMethods separateLocalScalar = ScalarMethods::CIRC_IN; - float localScalarRate = 1.0f; + float localScalarRate = 0.75f; // Separate Z - bool separateZInterp = false; + bool separateZInterp = true; ScalarMethods separateZScalar = ScalarMethods::SINE_IN; float separateZMaxSmoothingDistance = 60.0f; - float separateZMinFollowRate = 0.25f; + float separateZMinFollowRate = 0.15f; float separateZMaxFollowRate = 0.4f; // Offset interpolation diff --git a/SmoothCam/include/pch.h b/SmoothCam/include/pch.h index 39b9dee..26c180a 100644 --- a/SmoothCam/include/pch.h +++ b/SmoothCam/include/pch.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #pragma warning( push ) diff --git a/SmoothCam/source/camera.cpp b/SmoothCam/source/camera.cpp index 45e59e2..9e42678 100644 --- a/SmoothCam/source/camera.cpp +++ b/SmoothCam/source/camera.cpp @@ -22,6 +22,12 @@ void Camera::SmoothCamera::OnTogglePOV(const ButtonEvent* ev) noexcept { povWasPressed = true; } +void Camera::SmoothCamera::OnKeyPress(const ButtonEvent* ev) noexcept { + if (config->shoulderSwapKey >= 0 && config->shoulderSwapKey == ev->keyMask && ev->timer <= 0.000001f) { + shoulderSwap = shoulderSwap == 1 ? -1 : 1; + } +} + void Camera::SmoothCamera::OnDialogMenuChanged(const MenuOpenCloseEvent* const ev) noexcept { dialogMenuOpen = ev->opening; } @@ -397,9 +403,9 @@ float Camera::SmoothCamera::GetCurrentCameraSideOffset(PlayerCharacter* player, switch (currentState) { case GameState::CameraState::Horseback: { if (GameState::IsBowDrawn(player)) { - return config->bowAim.horseSideOffset; + return config->bowAim.horseSideOffset * shoulderSwap; } else { - return GetActiveWeaponStateSideOffset(player, &config->horseback); + return GetActiveWeaponStateSideOffset(player, &config->horseback) * shoulderSwap; } } default: @@ -412,14 +418,14 @@ float Camera::SmoothCamera::GetCurrentCameraSideOffset(PlayerCharacter* player, case CameraActionState::Sitting: case CameraActionState::Aiming: case CameraActionState::Swimming: { - return offsetState.currentGroup->sideOffset; + return offsetState.currentGroup->sideOffset * shoulderSwap; } case CameraActionState::Sneaking: case CameraActionState::Sprinting: case CameraActionState::Walking: case CameraActionState::Running: case CameraActionState::Standing: { - return GetActiveWeaponStateSideOffset(player, offsetState.currentGroup); + return GetActiveWeaponStateSideOffset(player, offsetState.currentGroup) * shoulderSwap; } default: { break; @@ -557,6 +563,23 @@ float Camera::SmoothCamera::GetCurrentSmoothingScalar(const float distance, Scal return mmath::RunScalarFunction(scalarMethod, interpValue); } +// Returns the user defined distance clamping vector pair +std::tuple Camera::SmoothCamera::GetDistanceClamping() const noexcept { + float minsX = config->cameraDistanceClampXMin; + float maxsX = config->cameraDistanceClampXMax; + + if (config->swapXClamping && shoulderSwap < 1) { + std::swap(minsX, maxsX); + maxsX *= -1.0f; + minsX *= -1.0f; + } + + return std::tie( + glm::vec3{ minsX, config->cameraDistanceClampYMin, config->cameraDistanceClampZMin }, + glm::vec3{ maxsX, config->cameraDistanceClampYMax, config->cameraDistanceClampZMax } + ); +} + // Returns true if interpolation is allowed in the current state bool Camera::SmoothCamera::IsInterpAllowed(PlayerCharacter* player) const noexcept { auto ofs = offsetState.currentGroup; diff --git a/SmoothCam/source/camera_state.cpp b/SmoothCam/source/camera_state.cpp index f4f1f62..2f695fc 100644 --- a/SmoothCam/source/camera_state.cpp +++ b/SmoothCam/source/camera_state.cpp @@ -57,13 +57,15 @@ glm::vec3 Camera::State::BaseCameraState::ComputeOffsetClamping(PlayerCharacter* forward, right, up, coef ); + auto [mins, maxs] = camera->GetDistanceClamping(); + // Now we can do whatever we want to the interp vector in axis aligned space if (camera->config->cameraDistanceClampXEnable) - coef.x = glm::clamp(coef.x, camera->config->cameraDistanceClampXMin, camera->config->cameraDistanceClampXMax); + coef.x = glm::clamp(coef.x, mins.x, maxs.x); if (camera->config->cameraDistanceClampYEnable) - coef.y = glm::clamp(coef.y, camera->config->cameraDistanceClampYMin, camera->config->cameraDistanceClampYMax); + coef.y = glm::clamp(coef.y, mins.y, maxs.y); if (camera->config->cameraDistanceClampZEnable) - coef.z = glm::clamp(coef.z, camera->config->cameraDistanceClampZMin, camera->config->cameraDistanceClampZMax); + coef.z = glm::clamp(coef.z, mins.z, maxs.z); // Now recompose with the new coefficients and add back the world position return (forward * coef.x) + (right * coef.y) + (up * coef.z) + expectedPosition; @@ -80,13 +82,15 @@ glm::vec3 Camera::State::BaseCameraState::ComputeOffsetClamping(PlayerCharacter* forward, right, up, coef ); + auto [mins, maxs] = camera->GetDistanceClamping(); + // Now we can do whatever we want to the interp vector in axis aligned space if (camera->config->cameraDistanceClampXEnable) - coef.x = glm::clamp(coef.x, camera->config->cameraDistanceClampXMin, camera->config->cameraDistanceClampXMax); + coef.x = glm::clamp(coef.x, mins.x, maxs.x); if (camera->config->cameraDistanceClampYEnable) - coef.y = glm::clamp(coef.y, camera->config->cameraDistanceClampYMin, camera->config->cameraDistanceClampYMax); + coef.y = glm::clamp(coef.y, mins.y, maxs.y); if (camera->config->cameraDistanceClampZEnable) - coef.z = glm::clamp(coef.z, camera->config->cameraDistanceClampZMin, camera->config->cameraDistanceClampZMax); + coef.z = glm::clamp(coef.z, mins.z, maxs.z); // Now recompose with the new coefficients and add back the world position return (forward * coef.x) + (right * coef.y) + (up * coef.z) + cameraWorldTarget/* + expectedPosition*/; diff --git a/SmoothCam/source/config.cpp b/SmoothCam/source/config.cpp index 47eb52f..5952320 100644 --- a/SmoothCam/source/config.cpp +++ b/SmoothCam/source/config.cpp @@ -68,6 +68,8 @@ void Config::to_json(json& j, const UserConfig& obj) { CREATE_JSON_VALUE(obj, crosshairMinDistSize), CREATE_JSON_VALUE(obj, crosshairMaxDistSize), CREATE_JSON_VALUE(obj, disableDeltaTime), + CREATE_JSON_VALUE(obj, shoulderSwapKey), + CREATE_JSON_VALUE(obj, swapXClamping), CREATE_JSON_VALUE(obj, disableDuringDialog), CREATE_JSON_VALUE(obj, currentScalar), CREATE_JSON_VALUE(obj, comaptIC_FirstPersonHorse), @@ -125,6 +127,8 @@ void Config::from_json(const json& j, UserConfig& obj) { VALUE_FROM_JSON(obj, crosshairMinDistSize) VALUE_FROM_JSON(obj, crosshairMaxDistSize) VALUE_FROM_JSON(obj, disableDeltaTime) + VALUE_FROM_JSON(obj, shoulderSwapKey) + VALUE_FROM_JSON(obj, swapXClamping) VALUE_FROM_JSON(obj, disableDuringDialog) VALUE_FROM_JSON(obj, currentScalar) VALUE_FROM_JSON(obj, comaptIC_FirstPersonHorse) diff --git a/SmoothCam/source/detours.cpp b/SmoothCam/source/detours.cpp index 9af7f7f..f51d604 100644 --- a/SmoothCam/source/detours.cpp +++ b/SmoothCam/source/detours.cpp @@ -77,6 +77,11 @@ uintptr_t __fastcall mOnInput(PlayerInputHandler* pThis, InputEvent* input) { if (!g_theCamera.expired() && (lockedPtr = g_theCamera.lock(), lockedPtr != nullptr)) { lockedPtr->OnTogglePOV(ev); } + } else { + std::shared_ptr lockedPtr; + if (!g_theCamera.expired() && (lockedPtr = g_theCamera.lock(), lockedPtr != nullptr)) { + lockedPtr->OnKeyPress(ev); + } } break; diff --git a/SmoothCam/source/main.cpp b/SmoothCam/source/main.cpp index 16ad7e8..1c4069b 100644 --- a/SmoothCam/source/main.cpp +++ b/SmoothCam/source/main.cpp @@ -57,7 +57,7 @@ extern "C" { info->infoVersion = PluginInfo::kInfoVersion; info->name = "SmoothCam"; - info->version = 8; + info->version = 9; g_pluginHandle = skse->GetPluginHandle(); diff --git a/SmoothCam/source/papyrus.cpp b/SmoothCam/source/papyrus.cpp index 8b9f30c..9818ce2 100644 --- a/SmoothCam/source/papyrus.cpp +++ b/SmoothCam/source/papyrus.cpp @@ -76,6 +76,7 @@ const std::unordered_map> boolGetter IMPL_GETTER("CameraDistanceClampXEnable", cameraDistanceClampXEnable) IMPL_GETTER("CameraDistanceClampYEnable", cameraDistanceClampYEnable) IMPL_GETTER("CameraDistanceClampZEnable", cameraDistanceClampZEnable) + IMPL_GETTER("ShoulderSwapXClamping", swapXClamping) IMPL_GETTER("InterpStanding", standing.interp) IMPL_GETTER("InterpStandingRangedCombat", standing.interpRangedCombat) @@ -230,6 +231,10 @@ const std::unordered_map> floatGett IMPL_GETTER("Dragon:UpOffset", dragon.upOffset) }; +const std::unordered_map> intGetters = { + IMPL_GETTER("ShoulderSwapKeyCode", shoulderSwapKey) +}; + const std::unordered_map> stringSetters = { IMPL_SCALAR_METHOD_SETTER("InterpolationMethod", currentScalar) IMPL_SCALAR_METHOD_SETTER("SeparateZInterpMethod", separateZScalar) @@ -258,6 +263,7 @@ const std::unordered_map> boolSetter IMPL_SETTER("CameraDistanceClampXEnable", cameraDistanceClampXEnable, bool) IMPL_SETTER("CameraDistanceClampYEnable", cameraDistanceClampYEnable, bool) IMPL_SETTER("CameraDistanceClampZEnable", cameraDistanceClampZEnable, bool) + IMPL_SETTER("ShoulderSwapXClamping", swapXClamping, bool) IMPL_SETTER("InterpStanding", standing.interp, bool) IMPL_SETTER("InterpStandingRangedCombat", standing.interpRangedCombat, bool) @@ -426,6 +432,10 @@ const std::unordered_map> floatSett IMPL_GROUP_SETTER("Group:Melee:ZoomOffset", combatMeleeZoomOffset, float) }; +const std::unordered_map> intSetters = { + IMPL_SETTER("ShoulderSwapKeyCode", shoulderSwapKey, int) +}; + void PapyrusBindings::Bind(VMClassRegistry* registry) { registry->RegisterFunction( new NativeFunction2( @@ -466,6 +476,19 @@ void PapyrusBindings::Bind(VMClassRegistry* registry) { ) ); + registry->RegisterFunction( + new NativeFunction2( + "SmoothCam_SetIntConfig", + ScriptClassName, + [](StaticFunctionTag* thisInput, BSFixedString var, SInt32 value) { + const auto it = intSetters.find(var.c_str()); + if (it != intSetters.end()) + it->second(static_cast(value)); + }, + registry + ) + ); + registry->RegisterFunction( new NativeFunction1( "SmoothCam_GetStringConfig", @@ -511,6 +534,21 @@ void PapyrusBindings::Bind(VMClassRegistry* registry) { ) ); + registry->RegisterFunction( + new NativeFunction1( + "SmoothCam_GetIntConfig", + ScriptClassName, + [](StaticFunctionTag* thisInput, BSFixedString var) { + const auto it = intGetters.find(var.c_str()); + if (it != intGetters.end()) + return static_cast(it->second()); + else + return static_cast(-1); + }, + registry + ) + ); + registry->RegisterFunction( new NativeFunction2( "SmoothCam_SaveAsPreset",