//#define ADDR_ASSERTS using ImGuiNET; using System.Numerics; using SharpPluginLoader.Core; using SharpPluginLoader.Core.Entities; using SharpPluginLoader.Core.IO; using SharpPluginLoader.Core.View; using SharpPluginLoader.Core.Memory; using SharpPluginLoader.Core.Actions; using SharpPluginLoader.Core.Components; using SharpPluginLoader.Core.Resources.Animation; using SharpPluginLoader.Core.Configuration; #if ADDR_ASSERTS using System.Diagnostics; #endif // @TODO: // FEATURE: Figure out how to disable gravity. namespace MHWNewCamera { public unsafe class Plugin : IPlugin { public string Name => "MHW New Camera"; public string Author => "Akon City Software"; private const float DEFAULT_FOV = 60.0f; private bool keyboardEnabled; private Config.Binds keyboardBinds; private float cameraSpeed; private float cameraKeySpeed; private float cameraSpeedModifier; private float cameraSensitivity; private int stickDeadzone; private float cameraPitchLimit; private float cameraFov = DEFAULT_FOV; private float cameraForward = 0.0f; private float cameraLeft = 0.0f; private float cameraXOffset = 0.0f; private float cameraYOffset = 0.0f; private float cameraZOffset = 0.0f; private float cameraPitch = 0.0f; private float cameraYaw = 0.0f; private float cameraRoll = 0.0f; private bool freeCamera = false; private bool toggleFreeCamera = false; private bool unlockMovementHeld = false; private bool unlockMovementToggled = false; private bool offsetPerspective = false; private bool toggleOffsetPerspective = false; private Camera? targetCamera = null; private Player? lastPlayer = null; private float previousFov = -1.0f; private bool decreasingFov = false; private bool changeAfterOpenMap = true; private bool changeAfterGetUp = true; private bool resetFovInView = false; private delegate void CalculateCameraDelegate(nint unknownPtr); private Hook? calculateCameraHook; private delegate void CalculateViewDelegate(nint unknownPtr); private Hook? calculateViewHook; private delegate float CalculateMovementDelegate(int stickValue); private Hook? calculateMovementHook; private bool disableCollision = false; private bool disableExtraCollision = false; private Patch disableXCollision; private Patch disableYCollision; private Patch disableSecondaryYCollision; private Patch disableZCollision; private Patch disableExtraYCollision; private string typedPresetName = ""; private static readonly MtObject sMhSteamController = SingletonManager.GetSingleton("sMhSteamController")!; public void OnLoad() { Config config = ConfigManager.GetConfig(this); keyboardEnabled = config.KeyboardEnabled; keyboardBinds = config.KeyboardBinds; Config.Settings settings = config.CameraSettings; cameraSpeed = settings.CameraSpeed; cameraKeySpeed = settings.CameraKeySpeed; cameraSpeedModifier = settings.CameraSpeedModifier; cameraSensitivity = settings.CameraSensitivity; stickDeadzone = settings.StickDeadzone; cameraPitchLimit = settings.CameraPitchLimit; toggleOffsetPerspective = config.PerspectiveCameraEnabled; if (config.Selected != "") { Config.Preset preset = config.Presets[config.Selected]; cameraFov = preset.FOV; cameraForward = preset.Forward; cameraLeft = preset.Left; cameraYOffset = preset.Up; cameraRoll = preset.Roll; } ConfigManager.SaveConfig(this); // https://github.com/Andoryuuta/MHW-ClassPropDump //calculateCameraHook = Hook.Create(0x142290620, CalculateCameraHook); //calculateViewHook2 = Hook.Create(0x14228fb80, CalculateViewHook2); // Asserts based on version 15.23.00. // Hook where we can adjust the camera position. nint addr = PatternScanner.FindFirst(Pattern.FromString("48 8B C4 55 41 57 48 81 EC D8 00 00 00 44 0F 29 40 B8 45 33 FF ?? ?? ?? ?? ?? ?? ?? ?? ?? 48 8B E9")); #if ADDR_ASSERTS Trace.Assert(addr == 0x141fa5380); #endif calculateCameraHook = Hook.Create(addr, CalculateCameraHook); // Hook where we can directly modify the view matrix. addr = PatternScanner.FindFirst(Pattern.FromString("48 89 5C 24 08 48 89 6C 24 10 48 89 74 24 18 48 89 7C 24 20 41 54 41 56 41 57 48 81 EC 90 00 00 00 ?? ?? ?? ?? ?? ?? ?? 48 8B F9")); #if ADDR_ASSERTS Trace.Assert(addr == 0x14228eb60); #endif calculateViewHook = Hook.Create(addr, CalculateViewHook); // Hook where we can adjust the analog stick value the game uses. addr = PatternScanner.FindFirst(Pattern.FromString("66 0F 6E C1 0F 5B C0 85 C9 78 11")); #if ADDR_ASSERTS Trace.Assert(addr == 0x142107cb0); #endif calculateMovementHook = Hook.Create(addr, CalculateMovementHook); unchecked { // Noop collision checks. addr = PatternScanner.FindFirst(Pattern.FromString("F3 0F 11 06 F3 0F 10 48 04 F3 0F 58 4E 04 F3 0F 11 4E 04 F3 0F 10 40 08 F3 0F 58 46 08 F3 0F 11 46 08 44 8B AF E0 0B 00 00")); #if ADDR_ASSERTS Trace.Assert(addr == 0x141c001b5); #endif disableXCollision = new Patch(addr, [0x90, 0x90, 0x90, 0x90]); #if ADDR_ASSERTS Trace.Assert(addr + 0xe == 0x141c001c3); #endif disableYCollision = new Patch(addr + 0xe, [0x90, 0x90, 0x90, 0x90, 0x90]); #if ADDR_ASSERTS Trace.Assert(addr + 0x1d == 0x141c001d2); #endif disableZCollision = new Patch(addr + 0x1d, [0x90, 0x90, 0x90, 0x90, 0x90]); addr = PatternScanner.FindFirst(Pattern.FromString("F3 0F 11 46 04 F3 0F 10 46 08 F3 0F 5C C2 F3 0F 11 46 08 ?? ?? F3 0F")); #if ADDR_ASSERTS Trace.Assert(addr == 0x141c000d2); #endif disableSecondaryYCollision = new Patch(addr, [0x90, 0x90, 0x90, 0x90, 0x90]); addr = PatternScanner.FindFirst(Pattern.FromString("F3 0F 11 46 04 F3 0F 10 58 08 F3 0F 58 5E 08 F3 0F 11 5E 08 44 8B 87 D0 0B 00 00 41 F6 C0 03")); #if ADDR_ASSERTS Trace.Assert(addr == 0x141bfff90); #endif disableExtraYCollision = new Patch(addr, [0x90, 0x90, 0x90, 0x90, 0x90]); } } // These "assumeIn" functions are really scuffed and almost certainly could have // more proper checks, I just haven't gotten around to it. // Quest board menu. private static bool assumeInQuestBoard(float fov) { return fov == 20.000038f || fov == 20.017189f || fov == 20.00004f; } // Camera on rails animation, like when seeing a seasonal gathering hub for the first time. private bool assumeInAnimation(Viewport vp, Camera camera) { return vp.Camera != null && vp.Camera.Instance != camera.Instance && vp.Camera.FarClip == 4000000 && vp.Camera.Fix; } private bool assumeInOpenMapAnimation(float fov) { // Assume the camera's FOV could end up at 50.942066 during normal // gameplay. Try to mitigate that by tracking the triggers for opening // the map (press and quickly release select, press A at departure location). if (fov == 50.942066f) { if (!changeAfterOpenMap) return true; if (Input.IsReleased(Button.Share) || Input.IsPressed(Button.Cross) || Input.IsReleased(Key.M)) { changeAfterOpenMap = false; return true; } } if (!changeAfterOpenMap) changeAfterOpenMap = true; return false; } private bool assumeInCanteen(float fov, float previousFov) { // Same idea as assumeInOpenMapAnimation(). if (fov == 50.0f) { if (!changeAfterGetUp) return true; // FOV = 50.0 is too common, assume the FOV will always be greater than 51.0 // before sitting at the canteen. That's also assuming if FOV is 50.0 otherwise, // that previousFov will be something closer to 50.0 because it was lowering last frame. if (previousFov >= 51.0f) { if (Input.IsPressed(Button.Cross) || (Input.IsDown(Button.Triangle) || Input.IsReleased(Button.Triangle))) { changeAfterGetUp = false; return true; } } } if (!changeAfterGetUp) changeAfterGetUp = true; return false; } private void setPerspective(Camera camera, Vector3 pos, Quaternion forward) { Quaternion left = forward * Quaternion.CreateFromRotationMatrix(Matrix4x4.CreateRotationY(Single.DegreesToRadians(180.0f))); pos.X += forward.X * cameraForward; pos.Y += forward.Y * cameraForward; pos.Z += forward.Z * cameraForward; pos.X -= left.X * cameraLeft; pos.Z -= left.Z * cameraLeft; pos.X += cameraXOffset; pos.Y += cameraYOffset; pos.Z += cameraZOffset; camera.Position = pos; camera.FieldOfView = cameraFov; } private void adjustForCameraRoll(Viewport vp, Camera camera) { camera.AspectRatio = vp.Region.Height / (float)vp.Region.Width; // Increase FOV for culling then set it back in CalculateViewHook. // * 2.0f breaks at higher FOV's and if the FOV goes over 180.0f I think it wraps // around causing more culling. camera.FieldOfView *= 1.5f; if (camera.FieldOfView >= 180.0f) { camera.FieldOfView = 179.9f; } resetFovInView = true; } private static Quaternion getForward(Vector3 pos, Vector3 target) { return new Quaternion(target.X - pos.X, target.Y - pos.Y, target.Z - pos.Z, 0.0f); } private void CalculateCameraHook(nint unknownPtr) { calculateCameraHook!.Original(unknownPtr); Viewport vp = CameraSystem.MainViewport; Camera? camera = vp.Camera; if (offsetPerspective && !freeCamera) { camera = targetCamera; if (camera == null) return; decreasingFov = previousFov >= 0.0f && previousFov > camera.FieldOfView; bool applyPerspective = camera.Fix && camera.Move; applyPerspective &= !assumeInQuestBoard(camera.FieldOfView); applyPerspective &= !(assumeInAnimation(vp, camera) && decreasingFov); applyPerspective &= !assumeInOpenMapAnimation(camera.FieldOfView); applyPerspective &= !assumeInCanteen(camera.FieldOfView, previousFov); previousFov = camera.FieldOfView; if (applyPerspective) { Quaternion forward = Quaternion.Normalize(getForward(camera.Position, camera.Target)); setPerspective(camera, camera.Position, forward); if (cameraRoll != 0.0f) adjustForCameraRoll(vp, camera); } } else if (camera != null) { // Track this just for the debug display. previousFov = camera.FieldOfView; } if (camera != null && toggleFreeCamera && !freeCamera) { freeCamera = true; camera.Move = false; // Camera position could have changed. Quaternion forward = Quaternion.Normalize(getForward(camera.Position, camera.Target)); cameraPitch = Single.RadiansToDegrees((float)Math.Asin(forward.Y)); cameraYaw = Single.RadiansToDegrees((float)Math.Atan2(forward.Z, forward.X)); } } private void CalculateViewHook(nint unknownPtr) { Viewport vp = CameraSystem.MainViewport; bool updateView = freeCamera || offsetPerspective; if (resetFovInView) { if (updateView) { Camera? camera; if ((camera = (freeCamera ? vp.Camera : targetCamera)) != null) { camera.FieldOfView = cameraFov; } } resetFovInView = false; } calculateViewHook!.Original(unknownPtr); if (updateView) { vp.ViewMatrix *= Matrix4x4.CreateRotationZ(Single.DegreesToRadians(cameraRoll)); } } private float CalculateMovementHook(int stickValue) { if (freeCamera && !(unlockMovementHeld || unlockMovementToggled)) { stickValue = 0; } return calculateMovementHook!.Original(stickValue); } public void OnImGuiRender() { Config config = ConfigManager.GetConfig(this); float width = ImGui.GetWindowWidth(); ImGui.Checkbox("Enable Free Camera", ref toggleFreeCamera); if (toggleFreeCamera) { ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); } if (ImGui.Checkbox("Enable Perspective Camera", ref toggleOffsetPerspective)) { // Take the opposite because it's yet to be toggled. config.PerspectiveCameraEnabled = !offsetPerspective; ConfigManager.SaveConfig(this); } if (toggleFreeCamera) { ImGui.PopStyleVar(); } ImGui.PushID("Perspective"); ImGui.DragFloat("Field of View", ref cameraFov, 0.4f, 1.0f, 180.0f); ImGui.DragFloat("Forward", ref cameraForward, 0.4f); ImGui.DragFloat("Left", ref cameraLeft, 0.4f); ImGui.DragFloat("Up", ref cameraYOffset, 0.4f); if (ImGui.Button("Default")) { cameraFov = DEFAULT_FOV; cameraForward = 0.0f; cameraLeft = 0.0f; cameraXOffset = 0.0f; cameraYOffset = 0.0f; cameraZOffset = 0.0f; config.Selected = ""; ConfigManager.SaveConfig(this); } ImGui.SameLine(); if (ImGui.Button("Save")) { string name = typedPresetName; if (name != "") { Config.Preset preset = new Config.Preset(); preset.FOV = cameraFov; preset.Forward = cameraForward; preset.Left = cameraLeft; preset.Up = cameraYOffset; preset.Roll = cameraRoll; if (config.Presets.ContainsKey(name)) { config.Presets[name] = preset; } else { config.Presets.Add(name, preset); } config.Selected = name; ConfigManager.SaveConfig(this); } } ImGui.SameLine(); if (ImGui.Button("Delete")) { if (config.Selected != "") { config.Presets.Remove(config.Selected); config.Selected = ""; ConfigManager.SaveConfig(this); } } ImGui.SameLine(); ImGui.PushItemWidth(width * 0.2f); ImGui.InputText("##Preset Name", ref typedPresetName, 99); ImGui.SameLine(); if (ImGui.BeginCombo("##Preset", config.Selected)) { Dictionary.KeyCollection presetKeys = config.Presets.Keys; for (int i = 0; i < presetKeys.Count; i++) { string presetKey = presetKeys.ElementAt(i); bool isSelected = presetKey == config.Selected; if (ImGui.Selectable(presetKey, isSelected)) { config.Selected = presetKey; ConfigManager.SaveConfig(this); Config.Preset preset = config.Presets[config.Selected]; cameraFov = preset.FOV; cameraForward = preset.Forward; cameraLeft = preset.Left; cameraYOffset = preset.Up; cameraRoll = preset.Roll; } if (isSelected) ImGui.SetItemDefaultFocus(); } ImGui.EndCombo(); } ImGui.PopItemWidth(); ImGui.PopID(); ImGui.PushID("Settings"); ImGui.DragFloat("Camera Speed", ref cameraSpeed, 0.01f, 0.0f); if (ImGui.BeginItemTooltip()) { ImGui.Text("Movement speed in freecam."); ImGui.EndTooltip(); } ImGui.DragFloat("Button Speed", ref cameraKeySpeed, 0.01f, 0.0f); if (ImGui.BeginItemTooltip()) { ImGui.Text("Rate of camera adjustment buttons/keybinds."); ImGui.EndTooltip(); } ImGui.DragFloat("Speed Modifier", ref cameraSpeedModifier, 0.01f, 0.0f); if (ImGui.BeginItemTooltip()) { ImGui.Text("Value to multiply speed by when L2 is held."); ImGui.EndTooltip(); } ImGui.DragFloat("Camera Sensitivity", ref cameraSensitivity, 0.0025f, 0.0f); if (ImGui.BeginItemTooltip()) { ImGui.Text("Camera look sensitivity in freecam."); ImGui.EndTooltip(); } ImGui.InputInt("Stick Deadzone", ref stickDeadzone, 10); if (ImGui.Button("Default")) { cameraSpeed = Config.Settings.DEFAULT_SPEED; cameraKeySpeed = Config.Settings.DEFAULT_KEY_SPEED; cameraSpeedModifier = Config.Settings.DEFAULT_SPEED_MODIFIER; cameraSensitivity = Config.Settings.DEFAULT_SENSITIVITY; stickDeadzone = Config.Settings.DEFAULT_DEADZONE; cameraPitchLimit = Config.Settings.DEFAULT_PITCH_LIMIT; } ImGui.SameLine(); if (ImGui.Button("Reset")) { Config.Settings settings = config.CameraSettings; cameraSpeed = settings.CameraSpeed; cameraKeySpeed = settings.CameraKeySpeed; cameraSpeedModifier = settings.CameraSpeedModifier; cameraSensitivity = settings.CameraSensitivity; stickDeadzone = settings.StickDeadzone; cameraPitchLimit = settings.CameraPitchLimit; } ImGui.SameLine(); if (ImGui.Button("Save")) { Config.Settings settings = config.CameraSettings; settings.CameraSpeed = cameraSpeed; settings.CameraKeySpeed = cameraKeySpeed; settings.CameraSpeedModifier = cameraSpeedModifier; settings.CameraSensitivity = cameraSensitivity; settings.StickDeadzone = stickDeadzone; settings.CameraPitchLimit = cameraPitchLimit; config.CameraSettings = settings; ConfigManager.SaveConfig(this); } ImGui.PopID(); ImGui.PushID("Debug"); if (ImGui.CollapsingHeader("DEBUG")) { ImGui.PushID("Camera"); ImGui.Text("Camera/Viewport:"); Viewport vp; for (int i = 0; i < 8; i++) { if (ImGui.CollapsingHeader($"Camera #{i}")) { ImGui.PushID($"Camera{i}"); vp = CameraSystem.GetViewport(i); ImGui.Text($"Pointer: {vp.Instance:x}"); ImGui.Text($"Visible: {vp.Visible}"); ImGui.Text($"Region: {vp.Region.X} {vp.Region.Y} {vp.Region.Width} {vp.Region.Height}"); if (vp.Camera != null) { ImGui.InputFloat("X Offset", ref cameraXOffset); ImGui.InputFloat("Y Offset", ref cameraYOffset); ImGui.InputFloat("Z Offset", ref cameraZOffset); var camera = vp.Camera; ImGui.Text($"Camera Pointer: {camera.Instance:x}"); ImGui.DragFloat("FOV", ref camera.FieldOfView, 0.45f); ImGui.Text($"Internal FOV: {previousFov}"); if (ImGui.BeginItemTooltip()) { ImGui.Text("What the FOV would be if not set by us."); ImGui.EndTooltip(); } ImGui.InputFloat("Aspect Ratio", ref camera.AspectRatio); ImGui.InputFloat("Far Clip", ref camera.FarClip); ImGui.InputFloat("Near Clip", ref camera.NearClip); ImGui.DragFloat3("Position", ref camera.Position, 0.45f); ImGui.DragFloat3("Target", ref camera.Target, 0.45f); ImGui.DragFloat3("Up", ref camera.Up, 0.45f); ImGui.Text("Offset:"); float offsetForward = 0.0f; if (ImGui.DragFloat("Forward", ref offsetForward, 0.45f)) { Quaternion forward = getForward(camera.Position, camera.Target); forward = Quaternion.Normalize(forward); camera.Position.X += forward.X * offsetForward; camera.Position.Y += forward.Y * offsetForward; camera.Position.Z += forward.Z * offsetForward; } float offsetLeft = 0.0f; if (ImGui.DragFloat("Left", ref offsetLeft, 0.45f)) { Quaternion forward = getForward(camera.Position, camera.Target); forward = Quaternion.Normalize(forward); Quaternion left = forward * Quaternion.CreateFromRotationMatrix(Matrix4x4.CreateRotationY(Single.DegreesToRadians(180.0f))); camera.Position.X += left.X * offsetLeft; camera.Position.Z += left.Z * offsetLeft; } ImGui.Text("Rotation:"); float pitch = cameraPitch; if (ImGui.InputFloat("Pitch", ref pitch, 0.25f)) { cameraPitch = pitch; } float yaw = cameraYaw; if (ImGui.InputFloat("Yaw", ref yaw, 0.25f)) { cameraYaw = yaw; } float roll = cameraRoll; if (ImGui.DragFloat("Roll", ref roll, 0.25f)) { cameraRoll = roll; } float pitchLimit = cameraPitchLimit; if (ImGui.InputFloat("Pitch Limit", ref pitchLimit, 0.0f, 0.0f, null, ImGuiInputTextFlags.EnterReturnsTrue)) { cameraPitchLimit = pitchLimit; } bool move = camera.Move; if (ImGui.Checkbox("Move", ref move)) { camera.Move = move; } ImGui.SameLine(); bool fix = camera.Fix; if (ImGui.Checkbox("Fix", ref fix)) { camera.Fix = fix; } } ImGui.PopID(); } } ImGui.PopID(); ImGui.PushID("Player"); Player? player = Player.MainPlayer; if (player != null) { ImGui.Separator(); ImGui.Text("Player:"); ImGui.Text($"Pointer: {player.Instance:x}"); ImGui.DragFloat3("Position", ref player.Position, 0.5f); if (ImGui.Checkbox("Disable Collision", ref disableCollision)) { if (disableCollision) { disableXCollision.Enable(); disableYCollision.Enable(); disableSecondaryYCollision.Enable(); disableZCollision.Enable(); } else { disableXCollision.Disable(); disableYCollision.Disable(); disableSecondaryYCollision.Disable(); disableZCollision.Disable(); } } if (ImGui.Checkbox("Disable Extra Y Collision", ref disableExtraCollision)) { if (disableExtraCollision) { disableExtraYCollision.Enable(); } else { disableExtraYCollision.Disable(); } } if (ImGui.BeginItemTooltip()) { ImGui.Text("This will make your legs goofy, but it's needed to get on top of some things."); ImGui.EndTooltip(); } Vector4 rotation; rotation.X = player.Rotation.X; rotation.Y = player.Rotation.Y; rotation.Z = player.Rotation.Z; rotation.W = player.Rotation.W; if (ImGui.SliderFloat4("Rotation", ref rotation, -1.0f, 1.0f)) { player.Rotation.X = rotation.X; player.Rotation.Y = rotation.Y; player.Rotation.Z = rotation.Z; player.Rotation.W = rotation.W; } if (ImGui.Button("Reset")) { player.Rotation.X = 0.0f; player.Rotation.Z = 0.0f; } Vector3 forward; forward.X = player.Forward.X; forward.Y = player.Forward.Y; forward.Z = player.Forward.Z; ImGui.DragFloat3("Forward", ref forward, 0.0f); ActionInfo currentActionInfo = player.ActionController.CurrentAction; SharpPluginLoader.Core.Actions.Action? currentAction = null; if (currentActionInfo.ActionSet >= 0 && currentActionInfo.ActionSet <= 3) { ActionList actionList = player.ActionController.GetActionList(currentActionInfo.ActionSet); if (currentActionInfo.ActionId >= 0 && currentActionInfo.ActionId < actionList.Count) { currentAction = actionList[currentActionInfo.ActionId]; } } AnimationLayerComponent? animationLayer = player.AnimationLayer; AnimationId currentAnimation = player.CurrentAnimation; ImGui.Text("Action/Animation:"); ImGui.Text($" Current: {currentAction}, {currentAnimation}"); if (animationLayer != null) { ImGui.Text($" Speed: {animationLayer.Speed:0.000}"); ImGui.Text($" Frame: {animationLayer.CurrentFrame:0.000}/{animationLayer.MaxFrame:0.000}"); } if (currentAction != null) { ImGui.Text($" Active Time: {currentAction.ActiveTime}"); // An experiment to test potential animation error could be try to keep deltasec as consistent as possible. ImGui.Text($" Delta Sec: {currentAction.DeltaSec}"); } /* // https://github.com/HunterPie/HunterPie/blob/fa73f81ed0cdc921a6cf63f96c0fcff3688d88c2/HunterPie.Integrations/Datasources/MonsterHunterWorld/Entity/Game/MHWGame.cs#L74 bool playerInMenu = false; nint offset = MemoryUtil.Read(0x1451c4640); if (offset != 0) { offset += 0x13FD0; offset = MemoryUtil.Read(offset); if (offset != 0) { offset += 0xB734; playerInMenu = MemoryUtil.Read(offset) == 1; } } ImGui.Text($"In Menu: {playerInMenu}"); */ ImGui.Text($"Move: {player.Move}, Fix: {player.Fix}"); } ImGui.PopID(); ImGui.Separator(); ImGui.PushID("Pad"); ImGui.Text("Pad:"); ImGui.Text($"Pointer: {sMhSteamController.Instance:x}"); int PadRx = MemoryUtil.Read(sMhSteamController.Instance + 0x1b0); int PadRy = MemoryUtil.Read(sMhSteamController.Instance + 0x1b4); int PadLx = MemoryUtil.Read(sMhSteamController.Instance + 0x1b8); int PadLy = MemoryUtil.Read(sMhSteamController.Instance + 0x1bc); byte PadRz = MemoryUtil.Read(sMhSteamController.Instance + 0x1c0); byte PadLz = MemoryUtil.Read(sMhSteamController.Instance + 0x1c1); ImGui.Text("Left Stick:"); ImGui.Text($" X: {PadLx}"); ImGui.Text($" Y: {PadLy}"); ImGui.Text($" L2: {PadLz}"); ImGui.Text("Right Stick:"); ImGui.Text($" X: {PadRx}"); ImGui.Text($" Y: {PadRy}"); ImGui.Text($" R2: {PadRz}"); ImGui.PopID(); } ImGui.PopID(); } public void OnUpdate(float deltaTime) { float adjustedKeySpeed = cameraKeySpeed * deltaTime * 1000.0f; float adjustedSpeed = cameraSpeed * deltaTime * 1000.0f; float adjustedSensitivity = cameraSensitivity * deltaTime * 1000.0f; if (keyboardEnabled) { if (Input.IsPressed(keyboardBinds.ToggleFreeCamera)) { toggleFreeCamera = !toggleFreeCamera; } if (Input.IsPressed(keyboardBinds.TogglePerspectiveCamera)) { toggleOffsetPerspective = !toggleOffsetPerspective; } /* if (Input.IsDown(keyboardBinds.IncreaseSpeed)) { cameraSpeed += 0.5f * deltaTime; } if (Input.IsDown(keyboardBinds.DecreaseSpeed)) { cameraSpeed -= 0.5f * deltaTime; if (cameraSpeed < 0.1f) cameraSpeed = 0.1f; } if (Input.IsDown(keyboardBinds.IncreaseFOV)) { cameraFov += adjustedKeySpeed / 2.0f; } if (Input.IsDown(keyboardBinds.DecreaseFOV)) { cameraFov -= adjustedKeySpeed / 2.0f; } if (Input.IsDown(keyboardBinds.Forward)) { cameraForward += adjustedKeySpeed; } if (Input.IsDown(keyboardBinds.Backward)) { cameraForward -= adjustedKeySpeed; } if (Input.IsDown(keyboardBinds.Left)) { cameraLeft += adjustedKeySpeed; } if (Input.IsDown(keyboardBinds.Right)) { cameraLeft -= adjustedKeySpeed; } if (Input.IsDown(keyboardBinds.Up)) { cameraYOffset += adjustedKeySpeed; } if (Input.IsDown(keyboardBinds.Down)) { cameraYOffset -= adjustedKeySpeed; } */ } Viewport vp = CameraSystem.MainViewport; Camera? camera = vp.Camera; if (camera == null) return; Player? player = Player.MainPlayer; if (player != null) { if (offsetPerspective && player != lastPlayer) { // Update target camera on player change. targetCamera = camera; previousFov = -1.0f; } lastPlayer = player; if (toggleOffsetPerspective && !offsetPerspective) { offsetPerspective = true; targetCamera = camera; previousFov = -1.0f; } else if (!toggleOffsetPerspective && offsetPerspective) { offsetPerspective = false; targetCamera = null; } } if (Input.IsDown(Button.L2) && Input.IsPressed(Button.L1)) { toggleFreeCamera = !toggleFreeCamera; } if (!toggleFreeCamera && freeCamera) { freeCamera = false; camera.Move = true; } if (freeCamera) { if (player != null) { if (!unlockMovementHeld && Input.IsPressed(Button.L1)) { unlockMovementToggled = !unlockMovementToggled; } if (!unlockMovementToggled) { if (Input.IsPressed(Button.R2)) { unlockMovementHeld = true; } if (Input.IsReleased(Button.R2)) { unlockMovementHeld = false; } } } if (!(unlockMovementHeld || unlockMovementToggled)) { int PadLx = MemoryUtil.Read(sMhSteamController.Instance + 0x1b8); if (Math.Abs(PadLx) < stickDeadzone) PadLx = 0; int PadLy = MemoryUtil.Read(sMhSteamController.Instance + 0x1bc); if (Math.Abs(PadLy) < stickDeadzone) PadLy = 0; float Lx = PadLx / (Int16.MaxValue / adjustedSpeed); float Ly = PadLy / (Int16.MaxValue / adjustedSpeed); Quaternion forward = getForward(camera.Position, camera.Target); if (Input.IsDown(Button.L2)) { if (Input.IsDown(Button.Down)) { camera.Position.Y -= adjustedKeySpeed * cameraSpeedModifier; } if (Input.IsDown(Button.Up)) { camera.Position.Y += adjustedKeySpeed * cameraSpeedModifier; } Lx *= cameraSpeedModifier; Ly *= cameraSpeedModifier; forward.Y = 0.0f; } forward = Quaternion.Normalize(forward); Quaternion left = forward * Quaternion.CreateFromRotationMatrix(Matrix4x4.CreateRotationY(Single.DegreesToRadians(180.0f))); camera.Position.X += left.X * Lx; camera.Position.Z += left.Z * Lx; camera.Position.X += forward.X * Ly; camera.Position.Y += forward.Y * Ly; camera.Position.Z += forward.Z * Ly; } int PadRx = MemoryUtil.Read(sMhSteamController.Instance + 0x1b0); if (Math.Abs(PadRx) < stickDeadzone) PadRx = 0; int PadRy = MemoryUtil.Read(sMhSteamController.Instance + 0x1b4); if (Math.Abs(PadRy) < stickDeadzone) PadRy = 0; float Rx = PadRx / (Int16.MaxValue / adjustedSensitivity); float Ry = PadRy / (Int16.MaxValue / adjustedSensitivity); cameraYaw += Rx; cameraPitch = Math.Clamp(cameraPitch + Ry, -cameraPitchLimit, cameraPitchLimit); camera.Target.X = camera.Position.X + ((float)Math.Cos(Single.DegreesToRadians(cameraPitch)) * (float)Math.Cos(Single.DegreesToRadians(cameraYaw))); camera.Target.Y = camera.Position.Y + (float)Math.Sin(Single.DegreesToRadians(cameraPitch)); camera.Target.Z = camera.Position.Z + ((float)Math.Cos(Single.DegreesToRadians(cameraPitch)) * (float)Math.Sin(Single.DegreesToRadians(cameraYaw))); camera.FieldOfView = cameraFov; if (cameraRoll != 0.0f) adjustForCameraRoll(vp, camera); } } } }