From 353023e77af45f1f1a719d76b6a03f6343d99737 Mon Sep 17 00:00:00 2001 From: Andrew Opalach Date: Tue, 7 Oct 2025 22:52:31 -0400 Subject: Big update, lots of fixes Signed-off-by: Andrew Opalach --- Config.cs | 69 +++- Plugin.cs | 1267 +++++++++++++++++++++++++++++++++++++------------------------ 2 files changed, 831 insertions(+), 505 deletions(-) diff --git a/Config.cs b/Config.cs index 59eb76a..f40a76c 100755 --- a/Config.cs +++ b/Config.cs @@ -5,21 +5,14 @@ namespace MHWNewCamera { internal class Config : IConfig { - public struct Binds - { - public Binds() - { - } - } - public struct Settings { public const float DEFAULT_SPEED = 3.00f; public const float DEFAULT_ZOOM_SPEED = 1.0f; public const float DEFAULT_SPEED_MODIFIER = 0.25f; public const float DEFAULT_SENSITIVITY = 1.95f; + public const double DEFAULT_PITCH_LIMIT = -1.0; public const int DEFAULT_DEADZONE = 4750; - public const double DEFAULT_PITCH_LIMIT = 88.25; public Settings() { @@ -27,33 +20,85 @@ namespace MHWNewCamera CameraZoomSpeed = DEFAULT_ZOOM_SPEED; CameraSpeedModifier = DEFAULT_SPEED_MODIFIER; CameraSensitivity = DEFAULT_SENSITIVITY; - StickDeadzone = DEFAULT_DEADZONE; CameraPitchLimit = DEFAULT_PITCH_LIMIT; + StickDeadzone = DEFAULT_DEADZONE; } public float CameraSpeed { get; set; } public float CameraZoomSpeed { get; set; } public float CameraSpeedModifier { get; set; } public float CameraSensitivity { get; set; } - public int StickDeadzone { get; set; } public double CameraPitchLimit { get; set; } + public int StickDeadzone { get; set; } } public struct Preset { public double FOV { get; set; } public float Forward { get; set; } - public float Left { get; set; } + public float Right { get; set; } public float Up { get; set; } public double Roll { get; set; } } + public struct SavedPosition + { + public float PosX { get; set; } + public float PosY { get; set; } + public float PosZ { get; set; } + public float TargetX { get; set; } + public float TargetY { get; set; } + public float TargetZ { get; set; } + public double Roll { get; set; } + } + public String Name => "MHWNewCamera"; - public String Version => "1.1"; + public String Version => "1.2"; + + private static Dictionary buttonMap = new Dictionary + { + { "r1", Button.R1 }, { "rb", Button.R1 }, + { "r2", Button.R2 }, { "rt", Button.R2 }, + { "r3", Button.R3 }, { "rstick", Button.R3 }, + { "l1", Button.L1 }, { "lb", Button.L1 }, + { "l2", Button.L2 }, { "lt", Button.L2 }, + { "l3", Button.L3 }, { "lstick", Button.L3 }, + { "a", Button.Cross }, { "cross", Button.Cross }, + { "b", Button.Circle }, { "circle", Button.Circle }, + { "x", Button.Square }, { "square", Button.Square }, + { "y", Button.Triangle }, { "triangle", Button.Triangle }, + { "start", Button.Options }, { "options", Button.Options }, + { "select", Button.Share }, { "share", Button.Share }, + { "up", Button.Up }, + { "down", Button.Down }, + { "left", Button.Left }, + { "right", Button.Right } + }; + + public static Button[]? ParseCombo(String comboString) + { + string[] buttonStrings = comboString.Split(","); + if (buttonStrings.Length == 2) + { + string b1 = buttonStrings[0].ToLower(); + string b2 = buttonStrings[1].ToLower(); + if (buttonMap.ContainsKey(b1) && buttonMap.ContainsKey(b2)) + { + return [ buttonMap[b1], buttonMap[b2] ]; + } + } + return null; + } + + public bool DisableMod { get; set; } = false; + + public bool EnableCombo { get; set; } = true; + public String FreeCameraCombo { get; set; } = "RStick,LT"; public Settings CameraSettings { get; set; } = new Settings(); public bool PerspectiveCameraEnabled { get; set; } = false; public Dictionary Presets { get; set; } = new Dictionary(); public String Selected { get; set; } = ""; + public Dictionary Positions { get; set; } = new Dictionary(); } } diff --git a/Plugin.cs b/Plugin.cs index 2432cd9..5c8cc10 100755 --- a/Plugin.cs +++ b/Plugin.cs @@ -1,4 +1,4 @@ -#define ADDR_ASSERTS +//#define ADDR_ASSERTS using ImGuiNET; using System.Numerics; @@ -15,24 +15,48 @@ using System.Diagnostics; #endif // @TODO: -// - UI button to disable fragile workarounds. -// - Manully set freecam viewport index. -// - Save direct camera offsets as preset. +// - Trigger blue camera on minimap. // - Ability to disable more inputs in freecam. -/* -PadDown = 0x198, PadOld = 0x19C, PadTrg = 0x1A0, PadRel = 0x1A4, PadChg = 0x1A8 -uint PadDown = MemoryUtil.Read(sMhSteamController.Instance + 0x198); -MemoryUtil.WriteBytes(sMhSteamController.Instance + 0x198, BitConverter.GetBytes(PadRel)); -*/ -// Known Issues: -// - Leaving dialoge when vp.Camera.Up is slightly off will not apply camera offsets when it should. -// - Test Case: Go up to the handler with the camera left of the player. Talk to her and click Post a New Quest. -// Then after the open book animation, leave the dialoge and the camera should jump. -// - Getting up from cart doesn't apply offsets when it should. -// - Greatly increasing FOV to mitigate culling when camera is rolled causes issues. -// - HUD overlays misplaced. -// - Other various hard to explain graphical errors. -// - Offsetting the camera too much can easily break culling if the camera is behind the ground/wall/ceiling. +// - Update default binds. +// - Bind to change preset. +// - Bind to reverse freecam input (left AND right stick no effect on camera). +// - Display equiped armor in the debug UI. +// - Manully set freecam viewport index. +// - Override in-game viewmode. +// - +right and +forward to move character. +// - Keyframes? +// - Special case freecam for cutscenes and canteen animations. + +// Test cases: +// Offset Perspective: +// - For all cases roll should an extra consideration. +// Constant FOV: +// - In quest book/board GUI (uses player camera). +// previousCameraAnimState = 5 (Don't apply offset): +// - Zoom into quest board or The Handler's book. +// - Open map animation. +// - Entering/Leaving tent. +// previousCameraAnimState = 4 (We choose to apply offset): +// - Quest depart/return. +// - Traveling on the lift in Astera. +// previousCameraAnimState = 2 (Apply offset): +// - Point camera towards quest board. +// Player camera is the target of a transition: +// - Leaving dialoge with NPC (The Handler, The Smithy, pub lasses, etc). +// - Getting up from hot spring. +// - Getting up from canteen. +// - Getting up from cart. +// - Seasonal gathering hub cutscene. +// Directly moves player camera: +// - Look at monster. +// - Scoutflies point in a direction. +// - Mount monster. +// Free Camera: +// - Toggle rapidly and there should be no visual jump. +// - Story cutscenes. +// - Canteen cutscene/animation. +// - Inside tent. +// - Equipment select inside tent. namespace MHWNewCamera { @@ -43,53 +67,69 @@ namespace MHWNewCamera private const double DEFAULT_FOV = 60.0; - private bool modDisabled = false; - private bool modToggleDisabled = false; + private bool disableMod = false; private float cameraSpeed; private float cameraZoomSpeed; private float cameraSpeedModifier; private float cameraSensitivity; - private int stickDeadzone; private double cameraPitchLimit; + private int stickDeadzone; + + private bool enableCombo = false; + private Button[]? freeCameraCombo = null; private double cameraFov = DEFAULT_FOV; private float cameraForward = 0.0f; - private float cameraLeft = 0.0f; + private float cameraRight = 0.0f; private float cameraXOffset = 0.0f; private float cameraYOffset = 0.0f; private float cameraZOffset = 0.0f; - private double cameraPitch = 0.0; private double cameraYaw = 0.0; + private double cameraPitch = 0.0; private double cameraRoll = 0.0; - - private double? preZoomFov = null; - private double? preZoomRoll = null; + private int cameraWrapState = 0; private bool freeCamera = false; - private bool toggleFreeCamera = false; + private bool enableFreeCamera = false; + private Camera? vCamera = null; + private bool cameraSetFromAlt = false; + private Vector3 cameraPosition; + private Vector3 cameraTarget; + private double? preDetachFov = null; + private double? preDetachRoll = null; private bool unlockMovementHeld = false; private bool unlockMovementToggled = false; private bool offsetPerspective = false; - private bool toggleOffsetPerspective = false; - private Camera? targetPlayerCamera = null; + private bool enableOffsetPerspective = false; + private bool applyPerspective = false; private Player? lastPlayer = null; + private Camera? pCamera = null; private float previousFov = -1.0f; private int previousCameraAnimState = 0; - private bool changeAfterMapOpened = true; - private bool changeAfterGetUp = true; - private bool changeAfterInsideTent = true; - private float? resetFovInView = null; + private bool ignoreAnimState = false; + + private bool debugButton = false; + private float plusRight = 0.0f; + private float plusForward = 0.0f; + + private delegate void SetCameraDelegate(nint unknownPtr); + private Hook? setCameraHook; + + private delegate void SetCameraTentDelegate(nint unknownPtr); + private Hook? setCameraTentHook; private delegate void CalculateCameraDelegate(nint unknownPtr); private Hook? calculateCameraHook; - private delegate void CalculateViewDelegate(nint unknownPtr); - private Hook? calculateViewHook; + private delegate void CameraUpDelegate(nint unknownPtr); + private Hook? cameraUpHook; + + private delegate float CheckMovementDelegate(int stickValue); + private Hook? checkMovementHook; - private delegate float CalculateMovementDelegate(int stickValue); - private Hook? calculateMovementHook; + private Patch disableCameraAnimation; private bool disableCollision = false; private bool disableExtraCollision = false; @@ -102,13 +142,6 @@ namespace MHWNewCamera private Patch disableExtraYCollision; private Patch disableGravityYUpdate; private Patch disableGravityXZUpdate; - /* - private Patch disableRotationGravity; - private Patch disableRotationGravity2; - private Patch disableRotationGravity3; - private Patch disableRotationGravity4; - private Patch disableRotationGravity5; - */ private Patch disableGravityEval; private void disableCollisionEnable() @@ -150,52 +183,52 @@ namespace MHWNewCamera private void disableExtraGravityEnable() { disableGravityEval.Enable(); - /* - disableRotationGravity.Enable(); - disableRotationGravity2.Enable(); - disableRotationGravity3.Enable(); - disableRotationGravity4.Enable(); - disableRotationGravity5.Enable(); - disableGravityXZUpdate.Enable(); - */ } private void disableExtraGravityDisable() { disableGravityEval.Disable(); - /* - disableRotationGravity.Disable(); - disableRotationGravity2.Disable(); - disableRotationGravity3.Disable(); - disableRotationGravity4.Disable(); - disableRotationGravity5.Disable(); - disableGravityXZUpdate.Disable(); - */ } - private string typedPresetName = ""; - private static readonly MtObject sMhSteamController = SingletonManager.GetSingleton("sMhSteamController")!; + private nint controllerAddr() => sMhSteamController.Instance; + + private static readonly MtObject sOtomo = SingletonManager.GetSingleton("sOtomo")!; + + // Gui strings. + private string typedCombo = ""; + private string typedPresetName = ""; + private string typedPositionName = ""; + private string selectedPositionName = ""; public void OnLoad() { Config config = ConfigManager.GetConfig(this); + disableMod = config.DisableMod; + enableCombo = config.EnableCombo; + freeCameraCombo = Config.ParseCombo(config.FreeCameraCombo); + typedCombo = config.FreeCameraCombo.Replace(",", "+"); + Config.Settings settings = config.CameraSettings; cameraSpeed = settings.CameraSpeed; cameraZoomSpeed = settings.CameraZoomSpeed; cameraSpeedModifier = settings.CameraSpeedModifier; cameraSensitivity = settings.CameraSensitivity; - stickDeadzone = settings.StickDeadzone; cameraPitchLimit = settings.CameraPitchLimit; + if (cameraPitchLimit != -1.0f) + { + cameraPitchLimit = Math.Clamp(cameraPitchLimit, 0.0f, 89.95f); + } + stickDeadzone = settings.StickDeadzone; - toggleOffsetPerspective = config.PerspectiveCameraEnabled; + enableOffsetPerspective = config.PerspectiveCameraEnabled; if (config.Selected != "") { Config.Preset preset = config.Presets[config.Selected]; cameraFov = preset.FOV; cameraForward = preset.Forward; - cameraLeft = preset.Left; + cameraRight = preset.Right; cameraYOffset = preset.Up; cameraRoll = preset.Roll; } @@ -203,30 +236,55 @@ namespace MHWNewCamera ConfigManager.SaveConfig(this); // Asserts based on version 15.23.00. + // @TODO: Handle addr not found. + + // A place to check state before CalculateCameraHook. + nint addr = PatternScanner.FindFirst(Pattern.FromString("48 8B C4 48 89 58 08 48 89 70 10 48 89 78 18 4C 89 60 20 55 41 56 41 57 48 8D A8 28 FE FF FF 48 81 EC C0 02 00 00 0F 29 70 D8 48 8B F1")); +#if ADDR_ASSERTS + Trace.Assert(addr == 0x141fa16d0); // nint +#endif + setCameraHook = Hook.Create(addr, SetCameraHook); + + // Very special case for inside tent. + addr = PatternScanner.FindFirst(Pattern.FromString("48 89 5C 24 10 48 89 6C 24 18 48 89 7C 24 20 41 56 48 83 EC 60 48 8B D9 0F 57 DB ?? ?? ?? ?? ?? ?? ?? 0F 57 D2 33 D2")); +#if ADDR_ASSERTS + Trace.Assert(addr == 0x142106450); // nint +#endif + setCameraTentHook = Hook.Create(addr, SetCameraTentHook); // 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")); + 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); + Trace.Assert(addr == 0x141fa5380); // nint #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")); + // Hook where we can set Camera.Up before it's used. + addr = PatternScanner.FindFirst(Pattern.FromString("40 53 48 81 EC 80 00 00 00 8B 81 B0 17 00 00 48 8B D9 85 C0 ?? ??")); #if ADDR_ASSERTS - Trace.Assert(addr == 0x14228eb60); + Trace.Assert(addr == 0x141fa11a0); // nint #endif - calculateViewHook = Hook.Create(addr, CalculateViewHook); + cameraUpHook = Hook.Create(addr, CameraUpHook); - // Hook where we can adjust the analog stick value the game uses. + // Hook where we can change the analog stick value the game uses for character movement. addr = PatternScanner.FindFirst(Pattern.FromString("66 0F 6E C1 0F 5B C0 85 C9 78 11")); #if ADDR_ASSERTS - Trace.Assert(addr == 0x142107cb0); + Trace.Assert(addr == 0x142107cb0); // int #endif - calculateMovementHook = Hook.Create(addr, CalculateMovementHook); + checkMovementHook = Hook.Create(addr, CheckMovementHook); unchecked { + addr = PatternScanner.FindFirst(Pattern.FromString("40 53 48 81 EC B0 00 00 00 48 8B D9 48 8B D1 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 48 8B 03 48 8D 54 24 30")); +#if ADDR_ASSERTS + Trace.Assert(addr == 0x1423b1420); // nint +#endif + disableCameraAnimation = new Patch(addr + 0x50, [ + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, + 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 + ]); + // 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 @@ -267,277 +325,429 @@ namespace MHWNewCamera 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 ]); - /* - // +0xac0 - - // 141325C19 - F3 0F11 83 80000000 - movss [rbx+00000080],xmm0 - // 141325C36 - F3 0F11 83 84000000 - movss [rbx+00000084],xmm0 - // 141325C54 - F3 0F11 83 88000000 - movss [rbx+00000088],xmm0 - addr = PatternScanner.FindFirst(Pattern.FromString("F3 0F 59 DB F3 0F 59 D2 F3 0F 11 83 80 00 00 00 F3 0F 58 E3 F3 0F 10 83 84 00 00 00 F3 41 0F 5C C4 F3 0F 58 E2 F3 0F 11 83 84 00 00 00 F3 0F 10 83 88 00 00 00 F3 41 0F 5C C5 44 0F 28 AC 24 C0 01 00 00 F3 0F 11 83 88 00 00 00")); -#if ADDR_ASSERTS - Trace.Assert(addr + 0x8 == 0x141325c19); -#endif - disableRotationGravity = new Patch(addr + 0x8, [0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90]); - disableRotationGravity2 = new Patch(addr + 0x25, [0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90]); - disableRotationGravity3 = new Patch(addr + 0x43, [0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90]); - - // 1425D94E4 - F3 0F11 86 C0000000 - movss [rsi+000000C0],xmm0 - // 1425D94F4 - F3 0F11 86 C8000000 - movss [rsi+000000C8],xmm0 - addr = PatternScanner.FindFirst(Pattern.FromString("F3 0F 11 86 C0 00 00 00 F3 0F 10 85 48 02 00 00 F3 0F 11 86 C8 00 00 00 F3 0F 10 85 50 02 00 00 F3 0F 11 8E C4 00 00 00 F3 0F 10 8D 54 02 00 00 89 BE CC 00 00 00 F3 0F 11 86 D0 00 00 00 F3 0F 10 85 58 02 00 00 F3 0F 11 86 D8 00 00 00 F3 0F 10 85 68 02 00 00")); -#if ADDR_ASSERTS - Trace.Assert(addr == 0x1425D94E4); -#endif - disableRotationGravity4 = new Patch(addr, [0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90]); - disableRotationGravity5 = new Patch(addr + 0x10, [0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90]); - */ - - // Stop falling from moving player. - // 141BFFEFC - F3 0F11 7E 04 - movss [rsi+04],xmm7 - // - // Instance (of many?) of Y position evaluation for falling? - // 141325E2A - 44 0F2F 9B 80010000 - comiss xmm11,[rbx+00000180] - // 141BFFF75 - E8 965872FF - call MonsterHunterWorld.exe+1325810 addr = PatternScanner.FindFirst(Pattern.FromString("F3 0F 11 97 54 15 00 00 F3 0F 11 8F 58 15 00 00 F3 0F 10 47 68 F3 0F 5E D8 44 89 B7 6C 15 00 00 F3 0F 5E D0 F3 0F 5E C8 F3 0F 11 9F 60 15 00 00 F3 0F 11 97 64 15 00 00 F3 0F 11 8F 68 15 00 00")); #if ADDR_ASSERTS - Trace.Assert(addr + 0x40 == 0x141BFFF75); + Trace.Assert(addr + 0x40 == 0x141bfff75); #endif disableGravityEval = new Patch(addr + 0x40, [0x90, 0x90, 0x90, 0x90, 0x90]); } } - // These "assume" functions are really scuffed and almost certainly could have - // more proper checks, I just haven't gotten around to it. + 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 setCameraRoll(Camera camera) + { + // Don't test our luck with precision weirdness if we know we can be exact. + if (cameraRoll == 0.0) + { + camera.Up.X = 0.0f; + camera.Up.Y = 1.0f; + camera.Up.Z = 0.0f; + } + else if (cameraRoll == 180.0) + { + camera.Up.X = 0.0f; + camera.Up.Y = -1.0f; + camera.Up.Z = 0.0f; + } + else + { + // yaw + 90.0 = point right. + camera.Up.X = (float)(Math.Sin(Double.DegreesToRadians(cameraRoll)) * Math.Cos(Double.DegreesToRadians(cameraPitch)) * Math.Cos(Double.DegreesToRadians(cameraYaw + 90.0))); + camera.Up.Y = (float)(Math.Cos(Double.DegreesToRadians(cameraRoll))); + camera.Up.Z = (float)(Math.Sin(Double.DegreesToRadians(cameraRoll)) * Math.Cos(Double.DegreesToRadians(cameraPitch)) * Math.Sin(Double.DegreesToRadians(cameraYaw + 90.0))); + } + } + + private void setPerspective(Camera camera) + { + Quaternion forward = getForward(camera.Position, camera.Target); + Quaternion right = forward * Quaternion.CreateFromRotationMatrix(Matrix4x4.CreateRotationY(Single.DegreesToRadians(180.0f))); + forward = Quaternion.Normalize(forward); + right = Quaternion.Normalize(right); + Vector3 pos = camera.Position; + pos.X += forward.X * cameraForward; + pos.Y += forward.Y * cameraForward; + pos.Z += forward.Z * cameraForward; + pos.X += right.X * cameraRight; + pos.Z += right.Z * cameraRight; + pos.X += cameraXOffset; + if (debugButton) pos.Y += cameraYOffset; + pos.Z += cameraZOffset; + camera.Position = pos; + if (debugButton && cameraFov != DEFAULT_FOV) + { + camera.FieldOfView = (float)((cameraFov * previousFov) / DEFAULT_FOV); + } + } - // Quest board menu. - private static bool assumeInQuestBoard(float fov) + private void setTentBasePos(Vector3 pos, Vector3 target) { - return fov == 20.000038f || fov == 20.017189f || fov == 20.00004f; + // @TODO: This can be picked out of the code. + nint baseAddr = MemoryUtil.Read(0x145011f58); // Default: + MemoryUtil.WriteBytes(baseAddr + 0x3e20, BitConverter.GetBytes(pos.X)); // 0.0 + MemoryUtil.WriteBytes(baseAddr + 0x3e24, BitConverter.GetBytes(pos.Y)); // -19850.0 + MemoryUtil.WriteBytes(baseAddr + 0x3e28, BitConverter.GetBytes(pos.Z)); // 270.0 + MemoryUtil.WriteBytes(baseAddr + 0x3e30, BitConverter.GetBytes(target.X)); // 0.0 + MemoryUtil.WriteBytes(baseAddr + 0x3e34, BitConverter.GetBytes(target.Y)); // -19830.0 + MemoryUtil.WriteBytes(baseAddr + 0x3e38, BitConverter.GetBytes(target.Z)); // 0.0 } - private bool assumeOnRails(Viewport vp, Camera camera) + private void disableFreeCamera() { - // 1. Seasonal gathering hub cutscene. - // 2. Zoom into quest board or the handler's book. - // 3. Leaving tent. - // 4. Getting up from carting. - // 5. Leaving dialoge with NPC. - // Checking Up.Y != 1.0f is an attempt to include only #2. - return vp.Camera != null && vp.Camera.Instance != camera.Instance && vp.Camera.FarClip == 4000000 && - vp.Camera.Fix && vp.Camera.Up.Y != 1.0f; + freeCamera = false; + if (preDetachFov != null) + { + cameraFov = (double)preDetachFov; + preDetachFov = null; + } + if (preDetachRoll != null) + { + cameraRoll = (double)preDetachRoll; + preDetachRoll = null; + } + cameraWrapState = 0; + setTentBasePos(new Vector3(0.0f, -19850.0f, 270.0f), new Vector3(0.0f, -19830.0f, 0.0f)); + cameraSetFromAlt = false; } - private bool assumeInOpenMap(float fov) + private void updateFreecam(Camera camera) { - if (fov == 50.942066f) + float deltaTime = camera.DeltaTime; + + if (Input.IsDown(Button.L2) && Input.IsDown(Button.R2)) // Left stick zoom. + { + int PadLy = MemoryUtil.Read(controllerAddr() + 0x1bc); + if (Math.Abs(PadLy) < stickDeadzone) PadLy = 0; + double adjustedZoomSpeed = cameraZoomSpeed * deltaTime * cameraFov; + double Ly = PadLy / (Int16.MaxValue / adjustedZoomSpeed); + cameraFov = Math.Clamp(cameraFov - Ly, 1.0, 179.0); + } + else if (!(unlockMovementHeld || unlockMovementToggled)) // Move camera. + { + int PadLy = MemoryUtil.Read(controllerAddr() + 0x1bc); + if (Math.Abs(PadLy) < stickDeadzone) PadLy = 0; + int PadLx = MemoryUtil.Read(controllerAddr() + 0x1b8); + if (Math.Abs(PadLx) < stickDeadzone) PadLx = 0; + double adjustedSpeed = cameraSpeed * deltaTime; + double Lx = PadLx / (Int16.MaxValue / adjustedSpeed); + double Ly = PadLy / (Int16.MaxValue / adjustedSpeed); + + Quaternion forward = getForward(cameraPosition, cameraTarget); + + if (Input.IsDown(Button.L2)) + { + forward.Y = 0.0f; + Lx *= cameraSpeedModifier; + Ly *= cameraSpeedModifier; + double buttonOffset = adjustedSpeed / 2.0; + buttonOffset *= cameraSpeedModifier; + if (Input.IsDown(Button.Up)) + { + cameraPosition.Y += (float)buttonOffset; + } + if (Input.IsDown(Button.Down)) + { + cameraPosition.Y -= (float)buttonOffset; + } + if (Input.IsDown(Button.Left)) + { + cameraRoll -= buttonOffset; + } + if (Input.IsDown(Button.Right)) + { + cameraRoll += buttonOffset; + } + } + + Quaternion right = forward; + right.Y = 0.0f; + right = right * Quaternion.CreateFromRotationMatrix(Matrix4x4.CreateRotationY(Single.DegreesToRadians(180.0f))); + forward = Quaternion.Normalize(forward); + right = Quaternion.Normalize(right); + cameraPosition.X += (float)(right.X * Lx); + cameraPosition.Z += (float)(right.Z * Lx); + cameraPosition.X += (float)(forward.X * Ly); + cameraPosition.Y += (float)(forward.Y * Ly); + cameraPosition.Z += (float)(forward.Z * Ly); + + if (plusForward != 0.0f) + { + cameraPosition.X += (float)(forward.X * plusForward * deltaTime); + cameraPosition.Y += (float)(forward.Y * plusForward * deltaTime); + cameraPosition.Z += (float)(forward.Z * plusForward * deltaTime); + } + } + + // Camera look. + int PadRx = MemoryUtil.Read(controllerAddr() + 0x1b0); + if (Math.Abs(PadRx) < stickDeadzone) PadRx = 0; + int PadRy = MemoryUtil.Read(controllerAddr() + 0x1b4); + if (Math.Abs(PadRy) < stickDeadzone) PadRy = 0; + double adjustedSensitivity = cameraSensitivity * deltaTime * cameraFov; + double Rx = PadRx / (Int16.MaxValue / adjustedSensitivity); + double Ry = PadRy / (Int16.MaxValue / adjustedSensitivity); + cameraYaw += Rx; + if (cameraYaw >= 180.0) + { + cameraYaw -= 360.0; + } + else if (cameraYaw < -180.0) + { + cameraYaw += 360.0; + } + if (cameraPitchLimit >= 0.0) + { + cameraPitch = Math.Clamp(cameraPitch + Ry, -cameraPitchLimit, cameraPitchLimit); + } + else { - if (!changeAfterMapOpened) + cameraPitch += Ry; + if (cameraPitch >= 90.0 && cameraWrapState == 0) + { + cameraRoll += 180.0; + cameraWrapState = 1; + } + else if (cameraPitch < -90.0 && cameraWrapState == 0) + { + cameraPitch += 360.0; + cameraRoll += 180.0; + cameraWrapState = 1; + } + else if (cameraPitch < 90.0 && cameraWrapState == 1) { - return true; + cameraRoll -= 180.0; + cameraWrapState = 0; } - // 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 or press A at departure location). - if (Input.IsReleased(Button.Share) || Input.IsPressed(Button.Cross)) + else if (cameraPitch >= 270.0 && cameraWrapState == 1) { - changeAfterMapOpened = false; - return true; + cameraPitch -= 360.0; + cameraRoll -= 180.0; + cameraWrapState = 0; } } + if (cameraRoll >= 360.0) + { + cameraRoll -= 360.0; + } + else if (cameraRoll < 0.0) + { + cameraRoll += 360.0; + } - if (!changeAfterMapOpened) changeAfterMapOpened = true; + if (plusRight != 0.0) + { + cameraYaw += plusRight * deltaTime; + } - return false; + // 700.0 is the same value the game uses for the player camera. The in-game view mode + // uses 1.0 like I did here before, which is really bad for precision. + double dist = 700.0 - cameraForward; + cameraTarget.X = cameraPosition.X + (float)(dist * Math.Cos(Double.DegreesToRadians(cameraPitch)) * Math.Cos(Double.DegreesToRadians(cameraYaw))); + cameraTarget.Y = cameraPosition.Y + (float)(dist * Math.Sin(Double.DegreesToRadians(cameraPitch))); + cameraTarget.Z = cameraPosition.Z + (float)(dist * Math.Cos(Double.DegreesToRadians(cameraPitch)) * Math.Sin(Double.DegreesToRadians(cameraYaw))); + + camera.Position = cameraPosition; + camera.Target = cameraTarget; + camera.FieldOfView = (float)cameraFov; } - private bool assumeInCanteen(float fov, float previousFov) + private Camera? getVisibleCamera() { - if (fov == 50.0f) + for (int i = 0; i < 8; i++) { - if (!changeAfterGetUp) - { - return true; - } - // Assume that FOV will be greater than or equal to 51.0 before sitting at the canteen. - // Also, assume that if FOV is equal to 50.0 in a different situation, previousFov - // will be less than 51.0 because it would have been lowering last frame. - // This is easily the most fragile check here. - if (previousFov >= 51.0f) + Viewport vp = CameraSystem.GetViewport(i); + // A viewport can be visible with a null camera. + if (vp.Visible && vp.Camera != null) { - if (Input.IsPressed(Button.Cross) || (Input.IsDown(Button.Triangle) || Input.IsReleased(Button.Triangle))) + if (freeCamera && vp.Camera != vCamera) { - changeAfterGetUp = false; - return true; + // This will still be broken in most cases, just less broken. + cameraPosition = vp.Camera.Position; + cameraTarget = vp.Camera.Target; } + return vp.Camera; } } - - if (!changeAfterGetUp) changeAfterGetUp = true; - - return false; + return null; } - private bool assumeInEnterTent(Camera camera, int animState) + private void SetCameraHook(nint unknownPtr) { - if (camera.Up.Y != 1.0f && animState == 5) + if (!disableMod) { - if (!changeAfterInsideTent) + vCamera = getVisibleCamera(); + if (pCamera == null && lastPlayer != null) { - return true; + // This assume the visible camera when, or right after, MainPlayer is set is the player camera. + pCamera = vCamera; } - // This is possible to trip if you spam A while loading into a level before - // seeing the wingdrake animation. - if (previousCameraAnimState != 5) + if (pCamera != null) { - if (Input.IsPressed(Button.Cross)) + previousCameraAnimState = MemoryUtil.Read(pCamera.Instance + 0x240); + if (previousCameraAnimState == 5) + { + // 1:314 = Wingdrake landing on area enter. + // 1:319 = Scared wingdrake landing next to monster. + Player player = Player.MainPlayer!; + ActionInfo currentActionInfo = player.ActionController.CurrentAction; + if (currentActionInfo.ActionSet == 1 && (currentActionInfo.ActionId == 314 || currentActionInfo.ActionId == 319)) + { + // Temporarily disable evaluation of cameraAnimState != 5. + ignoreAnimState = true; + } + } + else if (ignoreAnimState) { - changeAfterInsideTent = false; - return true; + // Once cameraAnimState is no longer 5, resume default logic. + ignoreAnimState = false; } } + else + { + previousCameraAnimState = 0; + } } - - if (!changeAfterInsideTent) changeAfterInsideTent = true; - - return false; + setCameraHook!.Original(unknownPtr); } - private void maybeSetFovForCulling(Viewport vp, Camera camera) + private void SetCameraTentHook(nint unknownPtr) { - /* I don't think this would be correct. As of now I'm pretty sure roll is only - * considered after this point, in CalculateViewHook. - if (cameraRoll == 90.0 || cameraRoll == 270.0) - { - camera.AspectRatio = vp.Region.Height / (float)vp.Region.Width; - } - */ - if (cameraRoll != 0.0 && camera.FieldOfView < 130.0f) + if (!disableMod && freeCamera) { - // The closer the FOV is to 180, the more odd behavior occurs. - // 130 seems like a OK default for when the camera is rolled. - // This will break HUD overlays like "!" on top of NPCs and the tent exit cutscene. - resetFovInView = camera.FieldOfView; - camera.FieldOfView = 130.0f; + setTentBasePos(cameraPosition, cameraTarget); } - /* - else if (camera.FieldOfView < 60.0f) + setCameraTentHook!.Original(unknownPtr); + if (!disableMod && freeCamera && vCamera != null) { - resetFovInView = camera.FieldOfView; - camera.FieldOfView = 60.0f; + updateFreecam(vCamera); + setCameraRoll(vCamera); + cameraSetFromAlt = true; } - */ - } - - 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; } - private static Quaternion getForward(Vector3 pos, Vector3 target) + private static bool assumeInQuestBoard(float fov) { - return new Quaternion(target.X - pos.X, target.Y - pos.Y, target.Z - pos.Z, 0.0f); + return fov == 20.000038f || fov == 20.017189f || fov == 20.00004f; } - // If freeCamera (camera.Move = false), this hook won't run. + // If camera.Move = false, this hook won't run. private void CalculateCameraHook(nint unknownPtr) { - if (modDisabled) + if (disableMod) { calculateCameraHook!.Original(unknownPtr); return; } - Viewport vp = CameraSystem.MainViewport; - Camera? camera = offsetPerspective ? targetPlayerCamera : vp.Camera; - if (camera == null) return; - - bool applyPerspective = offsetPerspective; - if (offsetPerspective) + if (enableOffsetPerspective && !offsetPerspective) { - int cameraAnimState = MemoryUtil.Read(camera.Instance + 0x240); - applyPerspective = camera.Fix && camera.Move; - applyPerspective &= !assumeOnRails(vp, camera); - applyPerspective &= !assumeInQuestBoard(camera.FieldOfView); - applyPerspective &= !assumeInOpenMap(camera.FieldOfView); - applyPerspective &= !assumeInCanteen(camera.FieldOfView, previousFov); - applyPerspective &= !assumeInEnterTent(camera, cameraAnimState); - previousCameraAnimState = cameraAnimState; + offsetPerspective = true; + } + else if (!enableOffsetPerspective && offsetPerspective) + { + offsetPerspective = false; } - previousFov = camera.FieldOfView; - - calculateCameraHook!.Original(unknownPtr); + if (pCamera != null) + { + // previousFov is the pre-offset FOV value. + previousFov = pCamera.FieldOfView; + } + applyPerspective = offsetPerspective && !freeCamera && pCamera != null; if (applyPerspective) { - Quaternion forward = Quaternion.Normalize(getForward(camera.Position, camera.Target)); - setPerspective(camera, camera.Position, forward); - camera.FieldOfView = (float)((cameraFov * previousFov) / 60.0); + applyPerspective &= (previousCameraAnimState != 5 || ignoreAnimState); + applyPerspective &= !assumeInQuestBoard(previousFov); } - if (toggleFreeCamera && !freeCamera) + if (applyPerspective && !debugButton) { - freeCamera = true; - camera.Move = false; - preZoomFov = cameraFov; - preZoomRoll = cameraRoll; - if (applyPerspective) + // Applying Y offset here keeps crosshair UI centered, but affects the angle of the camera. + pCamera!.Position.Y += cameraYOffset; + if (cameraFov != DEFAULT_FOV) { - cameraFov = camera.FieldOfView; + pCamera!.FieldOfView = (float)((cameraFov * previousFov) / DEFAULT_FOV); } - // Camera position might have changed. - Quaternion forward = Quaternion.Normalize(getForward(camera.Position, camera.Target)); - cameraPitch = Double.RadiansToDegrees(Math.Asin(forward.Y)); - cameraYaw = Double.RadiansToDegrees(Math.Atan2(forward.Z, forward.X)); } - if (applyPerspective) maybeSetFovForCulling(vp, camera); - } + calculateCameraHook!.Original(unknownPtr); - private void CalculateViewHook(nint unknownPtr) - { - if (modDisabled) + // Setting the perspective at this point (after Original()), allows the offset to not + // negatively affect the right stick camera movement. If set earlier, camera movement + // would be too snappy and incorrectly smoothed. If set too much later, it may not + // be considered by lower-level functions like culling. + if (applyPerspective) { - calculateViewHook!.Original(unknownPtr); - return; + setPerspective(pCamera!); } - Viewport vp = CameraSystem.MainViewport; - bool notFreecam = offsetPerspective && !freeCamera; - Camera? camera = notFreecam ? targetPlayerCamera : vp.Camera; - - bool updateView = freeCamera || offsetPerspective; - if (resetFovInView != null && updateView && camera != null) + if (vCamera != null) { - camera.FieldOfView = (float)resetFovInView; - resetFovInView = null; + if (!freeCamera) + { + // We need pitch and yaw to apply cameraRoll and avoid a jump if applyPerspective = false. + // @TODO: These values could just be read from the in-game camera. + Quaternion forward = Quaternion.Normalize(getForward(vCamera.Position, vCamera.Target)); + cameraYaw = Double.RadiansToDegrees(Math.Atan2(forward.Z, forward.X)); + cameraPitch = Double.RadiansToDegrees(Math.Asin(forward.Y)); + if (enableFreeCamera) + { + freeCamera = true; + cameraPosition = vCamera.Position; + cameraTarget = vCamera.Target; + preDetachFov = cameraFov; + preDetachRoll = cameraRoll; + // The value of cameraFov is effectively a new default and scales accordingly + // based on the in-game FOV. Take the calculated FOV here because that's what is + // actually shown, which avoids a jump when toggling freecam. This is also why + // we don't enable freecam until after applying a offset on this update. + cameraFov = vCamera.FieldOfView; + } + } + // No else if because we wan't to updateFreecam() if freeCamera was enabled above. + if (freeCamera) + { + if (!cameraSetFromAlt) + { + updateFreecam(vCamera); + } + cameraSetFromAlt = false; + if (!enableFreeCamera) + { + disableFreeCamera(); + } + } } + } - calculateViewHook!.Original(unknownPtr); - - if (updateView && cameraRoll != 0.0) + private void CameraUpHook(nint unknownPtr) + { + cameraUpHook!.Original(unknownPtr); + if (disableMod) return; + // This is still runs before culling. Checking Camera.Fix for tent idle, at least. + if ((applyPerspective || freeCamera) && vCamera != null && vCamera.Fix) + { + setCameraRoll(vCamera); + } + if (applyPerspective) { - Matrix4x4 rollRotation = Matrix4x4.CreateRotationZ((float)Double.DegreesToRadians(cameraRoll)); - vp.ViewMatrix *= rollRotation; + setCameraRoll(pCamera!); } } - private float CalculateMovementHook(int stickValue) + private float CheckMovementHook(int stickValue) { - if (!modDisabled && freeCamera && !(unlockMovementHeld || unlockMovementToggled)) + if (!disableMod && freeCamera && !(unlockMovementHeld || unlockMovementToggled)) { stickValue = 0; } - return calculateMovementHook!.Original(stickValue); + return checkMovementHook!.Original(stickValue); } private void disableAllHooks() @@ -569,24 +779,22 @@ namespace MHWNewCamera Config config = ConfigManager.GetConfig(this); float width = ImGui.GetWindowWidth(); - if (ImGui.Checkbox("Disable Mod", ref modDisabled)) + if (ImGui.Checkbox("Disable Mod", ref disableMod)) { - if (modDisabled) + if (disableMod) { disableAllHooks(); if (freeCamera) { - freeCamera = false; - if (preZoomRoll != null) - { - cameraRoll = (double)preZoomRoll; - preZoomRoll = null; - } + disableFreeCamera(); } for (int i = 0; i < 8; i++) { Viewport vp = CameraSystem.GetViewport(i); - if (vp.Camera != null) vp.Camera.Move = true; + if (vp.Camera != null) + { + vp.Camera.Move = true; + } } Player? player = Player.MainPlayer; if (player != null) @@ -595,59 +803,71 @@ namespace MHWNewCamera player.Rotation.Z = 0.0f; } } + config.DisableMod = disableMod; + ConfigManager.SaveConfig(this); } if (ImGui.BeginItemTooltip()) { ImGui.Text("Try to disable as much of the mod as possible."); ImGui.EndTooltip(); } - if (modDisabled) return; - ImGui.Checkbox("Disable Toggle Bind", ref modToggleDisabled); - if (ImGui.BeginItemTooltip()) - { - ImGui.Text("Disable button combination (Hold RStick + Press LT) for toggling free camera."); - ImGui.EndTooltip(); - } + if (disableMod) return; + + ImGui.Separator(); - ImGui.Checkbox("Enable Free Camera", ref toggleFreeCamera); - if (toggleFreeCamera) + ImGui.Checkbox("Enable Free Camera", ref enableFreeCamera); + if (enableFreeCamera) { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); } - if (ImGui.Checkbox("Enable Perspective Camera", ref toggleOffsetPerspective)) + if (ImGui.Checkbox("Enable Perspective Camera", ref enableOffsetPerspective)) { - // Take the opposite because it's yet to be toggled. - config.PerspectiveCameraEnabled = !offsetPerspective; + config.PerspectiveCameraEnabled = enableOffsetPerspective; ConfigManager.SaveConfig(this); } - if (toggleFreeCamera) + if (enableFreeCamera) { ImGui.PopItemFlag(); ImGui.PopStyleVar(); } + ImGui.Separator(); + ImGui.PushID("Perspective"); float singleFov = (float)cameraFov; if (ImGui.DragFloat("Field of View", ref singleFov, 0.4f, 1.0f, 179.0f)) { cameraFov = singleFov; } - ImGui.DragFloat("Forward", ref cameraForward, 0.4f); - ImGui.DragFloat("Left", ref cameraLeft, 0.4f); - ImGui.DragFloat("Up", ref cameraYOffset, 0.4f); + ImGui.DragFloat("Forward", ref cameraForward, 0.25f); + ImGui.DragFloat("Right", ref cameraRight, 0.25f); + ImGui.DragFloat("Up", ref cameraYOffset, 0.025f); + float roll = (float)cameraRoll; + if (ImGui.DragFloat("Roll", ref roll, 0.25f)) + { + cameraRoll = roll; + if (preDetachRoll != null) + { + preDetachRoll = roll; + } + } if (ImGui.Button("Default")) { cameraFov = DEFAULT_FOV; cameraForward = 0.0f; - cameraLeft = 0.0f; + cameraRight = 0.0f; cameraXOffset = 0.0f; cameraYOffset = 0.0f; cameraZOffset = 0.0f; + cameraRoll = 0.0f; config.Selected = ""; ConfigManager.SaveConfig(this); } ImGui.SameLine(); + ImGui.PushItemWidth(width * 0.15f); + ImGui.InputText("##Preset Name", ref typedPresetName, 99); + ImGui.SameLine(); if (ImGui.Button("Save")) { string name = typedPresetName; @@ -656,7 +876,7 @@ namespace MHWNewCamera Config.Preset preset = new Config.Preset(); preset.FOV = cameraFov; preset.Forward = cameraForward; - preset.Left = cameraLeft; + preset.Right = cameraRight; preset.Up = cameraYOffset; preset.Roll = cameraRoll; if (config.Presets.ContainsKey(name)) @@ -672,19 +892,6 @@ namespace MHWNewCamera } } 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; @@ -699,7 +906,7 @@ namespace MHWNewCamera Config.Preset preset = config.Presets[config.Selected]; cameraFov = preset.FOV; cameraForward = preset.Forward; - cameraLeft = preset.Left; + cameraRight = preset.Right; cameraYOffset = preset.Up; cameraRoll = preset.Roll; } @@ -708,6 +915,16 @@ namespace MHWNewCamera ImGui.EndCombo(); } ImGui.PopItemWidth(); + ImGui.SameLine(); + if (ImGui.Button("Delete")) + { + if (config.Selected != "") + { + config.Presets.Remove(config.Selected); + config.Selected = ""; + ConfigManager.SaveConfig(this); + } + } ImGui.PopID(); ImGui.PushID("Settings"); @@ -735,6 +952,20 @@ namespace MHWNewCamera ImGui.Text("Camera look sensitivity in freecam."); ImGui.EndTooltip(); } + float pitchLimit = (float)cameraPitchLimit; + if (ImGui.InputFloat("Pitch Limit", ref pitchLimit, 0.0f, 0.0f, null, ImGuiInputTextFlags.EnterReturnsTrue)) + { + cameraPitchLimit = pitchLimit; + if (cameraPitchLimit != -1.0f) + { + cameraPitchLimit = Math.Clamp(cameraPitchLimit, 0.0f, 89.95f); + } + } + if (ImGui.BeginItemTooltip()) + { + ImGui.Text("-1.0 = Wrap around (go upside down)."); + ImGui.EndTooltip(); + } ImGui.InputInt("Stick Deadzone", ref stickDeadzone, 10); if (ImGui.Button("Default")) { @@ -742,8 +973,8 @@ namespace MHWNewCamera cameraZoomSpeed = Config.Settings.DEFAULT_ZOOM_SPEED; cameraSpeedModifier = Config.Settings.DEFAULT_SPEED_MODIFIER; cameraSensitivity = Config.Settings.DEFAULT_SENSITIVITY; - stickDeadzone = Config.Settings.DEFAULT_DEADZONE; cameraPitchLimit = Config.Settings.DEFAULT_PITCH_LIMIT; + stickDeadzone = Config.Settings.DEFAULT_DEADZONE; } ImGui.SameLine(); if (ImGui.Button("Reset")) @@ -753,8 +984,8 @@ namespace MHWNewCamera cameraZoomSpeed = settings.CameraZoomSpeed; cameraSpeedModifier = settings.CameraSpeedModifier; cameraSensitivity = settings.CameraSensitivity; - stickDeadzone = settings.StickDeadzone; cameraPitchLimit = settings.CameraPitchLimit; + stickDeadzone = settings.StickDeadzone; } ImGui.SameLine(); if (ImGui.Button("Save")) @@ -764,29 +995,84 @@ namespace MHWNewCamera settings.CameraZoomSpeed = cameraZoomSpeed; settings.CameraSpeedModifier = cameraSpeedModifier; settings.CameraSensitivity = cameraSensitivity; - settings.StickDeadzone = stickDeadzone; settings.CameraPitchLimit = cameraPitchLimit; + settings.StickDeadzone = stickDeadzone; config.CameraSettings = settings; ConfigManager.SaveConfig(this); } ImGui.PopID(); + ImGui.PushID("Binds"); + if (ImGui.CollapsingHeader("Binds")) + { + if (ImGui.Checkbox("Toggle Free Camera", ref enableCombo)) + { + config.EnableCombo = enableCombo; + ConfigManager.SaveConfig(this); + } + if (ImGui.BeginItemTooltip()) + { + ImGui.Text("Hold the first button and press the second."); + ImGui.EndTooltip(); + } + ImGui.SameLine(); + if (freeCameraCombo == null) + { + ImGui.PushStyleColor(ImGuiCol.Text, 0xff0000ff); + } + ImGui.PushItemWidth(width * 0.15f); + ImGui.InputText("##Toggle Free Camera", ref typedCombo, 12); + if (freeCameraCombo == null) + { + ImGui.PopStyleColor(); + } + if (ImGui.BeginItemTooltip()) + { + ImGui.Text("Form: \"RT+LB\", \"R3+L2\", \"A+LStick\", \"Up+RT\", \"Cross+Triangle\", etc."); + ImGui.EndTooltip(); + } + ImGui.PopItemWidth(); + ImGui.SameLine(); + if (ImGui.Button("Save")) + { + string comboString = typedCombo.Replace("+", ","); + freeCameraCombo = Config.ParseCombo(comboString); + if (freeCameraCombo != null) + { + config.FreeCameraCombo = comboString; + ConfigManager.SaveConfig(this); + } + } + } + ImGui.PopID(); + ImGui.PushID("Debug"); - if (ImGui.CollapsingHeader("DEBUG")) + if (ImGui.CollapsingHeader("DEBUG/Extras")) { + ImGui.Checkbox("Debug Button", ref debugButton); + if (ImGui.BeginItemTooltip()) + { + ImGui.Text("Currently toggles how early camera Y position and FOV are set (off = earlier).\nIf you want to see why: go out, set a big \"Up\" offset, aim in your slinger and click this button on and off."); + ImGui.EndTooltip(); + } + ImGui.PushID("Camera"); - if (targetPlayerCamera != null) + if (pCamera != null) { - ImGui.Text($"Target Camera: {targetPlayerCamera.Instance:x}"); + ImGui.Text($"Player Camera: {pCamera.Instance:x}"); + ImGui.Text($"Applying Offset: {applyPerspective}"); + ImGui.Text($"previousCameraAnimState: {previousCameraAnimState}"); } + + ImGui.Separator(); + ImGui.Text("Camera/Viewport:"); - Viewport vp; for (int i = 0; i < 8; i++) { - if (ImGui.CollapsingHeader($"Camera #{i}")) + ImGui.PushID($"Viewport{i}"); + Viewport vp = CameraSystem.GetViewport(i); + if (ImGui.CollapsingHeader($"Viewport #{i} ({(vp.Visible ? "visible" : "inactive")})")) { - 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}"); @@ -798,11 +1084,14 @@ namespace MHWNewCamera 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()) + if (camera == pCamera) { - ImGui.Text("What the FOV would be if not set by us."); - ImGui.EndTooltip(); + 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); @@ -810,51 +1099,113 @@ namespace MHWNewCamera 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)) + ImGui.PushItemWidth(width * 0.15f); + ImGui.InputText("##Position Name", ref typedPositionName, 99); + ImGui.SameLine(); + if (ImGui.Button("Save")) { - 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; + string name = typedPositionName; + if (name != "") + { + Config.SavedPosition pos = new Config.SavedPosition(); + pos.PosX = camera.Position.X; + pos.PosY = camera.Position.Y; + pos.PosZ = camera.Position.Z; + pos.TargetX = camera.Target.X; + pos.TargetY = camera.Target.Y; + pos.TargetZ = camera.Target.Z; + pos.Roll = cameraRoll; + if (config.Positions.ContainsKey(name)) + { + config.Positions[name] = pos; + } + else + { + config.Positions.Add(name, pos); + } + ConfigManager.SaveConfig(this); + } } - float offsetLeft = 0.0f; - if (ImGui.DragFloat("Left", ref offsetLeft, 0.45f)) + ImGui.SameLine(); + if (ImGui.BeginCombo("##Positions", "")) { - 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; + Dictionary.KeyCollection positionKeys = config.Positions.Keys; + for (int j = 0; j < positionKeys.Count; j++) + { + string positionKey = positionKeys.ElementAt(j); + if (ImGui.Selectable(positionKey)) + { + Config.SavedPosition pos = config.Positions[positionKey]; + selectedPositionName = positionKey; + cameraPosition = new Vector3(pos.PosX, pos.PosY, pos.PosZ); + cameraTarget = new Vector3(pos.TargetX, pos.TargetY, pos.TargetZ); + Quaternion forward = Quaternion.Normalize(getForward(cameraPosition, cameraTarget)); + cameraYaw = Double.RadiansToDegrees(Math.Atan2(forward.Z, forward.X)); + cameraPitch = Double.RadiansToDegrees(Math.Asin(forward.Y)); + camera.Position = cameraPosition; + camera.Target = cameraTarget; + cameraRoll = pos.Roll; + } + } + ImGui.EndCombo(); } - if (i == 0) + ImGui.PopItemWidth(); + ImGui.SameLine(); + if (ImGui.Button("Delete")) { - ImGui.Text("Rotation:"); - double pitch = cameraPitch; - if (ImGui.InputDouble("Pitch", ref pitch, 0.25)) + if (selectedPositionName != "") { - cameraPitch = pitch; + config.Positions.Remove(selectedPositionName); + selectedPositionName = ""; + ConfigManager.SaveConfig(this); } - double yaw = cameraYaw; - if (ImGui.InputDouble("Yaw", ref yaw, 0.25)) + } + if (camera == vCamera) + { + float yaw = (float)cameraYaw; + if (ImGui.DragFloat("Yaw", ref yaw, 0.25f)) { cameraYaw = yaw; } - float roll = (float)cameraRoll; - if (ImGui.DragFloat("Roll", ref roll, 0.25f)) + float pitch = (float)cameraPitch; + if (ImGui.DragFloat("Pitch", ref pitch, 0.25f)) { - cameraRoll = roll; - if (preZoomRoll != null) - { - preZoomRoll = roll; - } + cameraPitch = pitch; + } + float offsetRoll = (float)cameraRoll; + if (ImGui.DragFloat("Roll", ref offsetRoll, 0.25f)) + { + cameraRoll = offsetRoll; + } + if (camera == pCamera && !freeCamera) + { + ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); + } + 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; + cameraPosition = camera.Position; } - float pitchLimit = (float)cameraPitchLimit; - if (ImGui.InputFloat("Pitch Limit", ref pitchLimit, 0.0f, 0.0f, null, ImGuiInputTextFlags.EnterReturnsTrue)) + float offsetRight = 0.0f; + if (ImGui.DragFloat("Right", ref offsetRight, 0.45f)) { - cameraPitchLimit = pitchLimit; + Quaternion forward = getForward(camera.Position, camera.Target); + Quaternion right = forward * Quaternion.CreateFromRotationMatrix(Matrix4x4.CreateRotationY(Single.DegreesToRadians(180.0f))); + forward = Quaternion.Normalize(forward); + right = Quaternion.Normalize(right); + camera.Position.X += right.X * offsetRight; + camera.Position.Z += right.Z * offsetRight; + cameraPosition = camera.Position; + } + if (camera == pCamera && !freeCamera) + { + ImGui.PopStyleVar(); } } bool move = camera.Move; @@ -869,8 +1220,8 @@ namespace MHWNewCamera camera.Fix = fix; } } - ImGui.PopID(); } + ImGui.PopID(); } ImGui.PopID(); @@ -882,6 +1233,11 @@ namespace MHWNewCamera ImGui.Text("Player:"); ImGui.Text($"Pointer: {player.Instance:x}"); ImGui.DragFloat3("Position", ref player.Position, 0.5f); + Vector3 forward; + forward.X = player.Forward.X; + forward.Y = player.Forward.Y; + forward.Z = player.Forward.Z; + ImGui.DragFloat3("Forward", ref forward, 0.0f); if (ImGui.Checkbox("Disable Collision", ref disableCollision)) { if (disableCollision) @@ -1018,11 +1374,6 @@ namespace MHWNewCamera 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) @@ -1036,7 +1387,7 @@ namespace MHWNewCamera AnimationLayerComponent? animationLayer = player.AnimationLayer; AnimationId currentAnimation = player.CurrentAnimation; ImGui.Text("Action/Animation:"); - ImGui.Text($" Current: {currentAction}, {currentAnimation}"); + ImGui.Text($" Current: {currentAction} {currentActionInfo}, {currentAnimation}"); if (animationLayer != null) { ImGui.Text($" Speed: {animationLayer.Speed:0.000}"); @@ -1064,21 +1415,38 @@ namespace MHWNewCamera } ImGui.Text($"In Menu: {playerInMenu}"); */ - ImGui.Text($"Move: {player.Move}, Fix: {player.Fix}"); + bool move = player.Move; + if (ImGui.Checkbox("Move", ref move)) + { + player.Move = move; + } + ImGui.SameLine(); + bool fix = player.Fix; + if (ImGui.Checkbox("Fix", ref fix)) + { + player.Fix = fix; + } } ImGui.PopID(); ImGui.Separator(); + ImGui.PushID("Palico"); + ImGui.Text("Palico/Otomo:"); + ImGui.Text($"Pointer: {sOtomo.Instance:x}"); + 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($"Pointer: {controllerAddr():x}"); + int PadRx = MemoryUtil.Read(controllerAddr() + 0x1b0); + int PadRy = MemoryUtil.Read(controllerAddr() + 0x1b4); + int PadLx = MemoryUtil.Read(controllerAddr() + 0x1b8); + int PadLy = MemoryUtil.Read(controllerAddr() + 0x1bc); + byte PadRz = MemoryUtil.Read(controllerAddr() + 0x1c0); + byte PadLz = MemoryUtil.Read(controllerAddr() + 0x1c1); ImGui.Text("Left Stick:"); ImGui.Text($" X: {PadLx}"); ImGui.Text($" Y: {PadLy}"); @@ -1087,164 +1455,77 @@ namespace MHWNewCamera ImGui.Text($" X: {PadRx}"); ImGui.Text($" Y: {PadRy}"); ImGui.Text($" R2: {PadRz}"); + ImGui.DragFloat("+right", ref plusRight, 0.05f); + ImGui.DragFloat("+forward", ref plusForward, 0.05f); ImGui.PopID(); } ImGui.PopID(); } - public void OnUpdate(float deltaTime) + private void checkUnlockMovement() { - if (modDisabled) return; - - Viewport vp = CameraSystem.MainViewport; - Camera? camera = vp.Camera; - if (camera == null) return; - - Player? player = Player.MainPlayer; - if (player != null) + if (!unlockMovementHeld && Input.IsPressed(Button.R2)) { - if (offsetPerspective && player != lastPlayer) - { - // Update target camera on player change. - targetPlayerCamera = camera; - previousFov = -1.0f; - } - lastPlayer = player; - if (toggleOffsetPerspective && !offsetPerspective) - { - offsetPerspective = true; - targetPlayerCamera = camera; - previousFov = -1.0f; - } - else if (!toggleOffsetPerspective && offsetPerspective) - { - offsetPerspective = false; - targetPlayerCamera = null; - } + unlockMovementHeld = true; } - - if (!modToggleDisabled && (!Input.IsPressed(Button.R3) && Input.IsDown(Button.R3)) && Input.IsPressed(Button.L2)) + else if (unlockMovementHeld && Input.IsReleased(Button.R2)) { - toggleFreeCamera = !toggleFreeCamera; + unlockMovementHeld = false; } - - if (freeCamera) + if (unlockMovementHeld && Input.IsPressed(Button.L1)) { - if (!unlockMovementHeld && Input.IsPressed(Button.R2)) - { - unlockMovementHeld = true; - } - else if (unlockMovementHeld && Input.IsReleased(Button.R2)) - { - unlockMovementHeld = false; - } - - if (unlockMovementHeld && Input.IsPressed(Button.L1)) - { - unlockMovementToggled = !unlockMovementToggled; - } - - if (Input.IsDown(Button.R2)) + unlockMovementToggled = !unlockMovementToggled; + } + if (Input.IsDown(Button.R2)) + { + if (Input.IsDown(Button.L2)) { - if (Input.IsDown(Button.L2)) + if (unlockMovementHeld) { - if (unlockMovementHeld) - { - unlockMovementHeld = false; - } - if (unlockMovementToggled) - { - unlockMovementToggled = false; - } + unlockMovementHeld = false; } - else if (Input.IsReleased(Button.L2)) + if (unlockMovementToggled) { - unlockMovementHeld = true; + unlockMovementToggled = false; } } - - if (Input.IsDown(Button.L2) && Input.IsDown(Button.R2)) - { - int PadLy = MemoryUtil.Read(sMhSteamController.Instance + 0x1bc); - if (Math.Abs(PadLy) < stickDeadzone) PadLy = 0; - double adjustedZoomSpeed = cameraZoomSpeed * (deltaTime * cameraFov); - double Ly = PadLy / (Int16.MaxValue / adjustedZoomSpeed); - cameraFov = Math.Clamp(cameraFov - Ly, 1.0, 179.0); - } - else if (!(unlockMovementHeld || unlockMovementToggled)) + else if (Input.IsReleased(Button.L2)) { - int PadLy = MemoryUtil.Read(sMhSteamController.Instance + 0x1bc); - if (Math.Abs(PadLy) < stickDeadzone) PadLy = 0; - int PadLx = MemoryUtil.Read(sMhSteamController.Instance + 0x1b8); - if (Math.Abs(PadLx) < stickDeadzone) PadLx = 0; - double adjustedSpeed = cameraSpeed * (deltaTime * 100.0); - double Lx = PadLx / (Int16.MaxValue / adjustedSpeed); - double Ly = PadLy / (Int16.MaxValue / adjustedSpeed); - Quaternion forward = getForward(camera.Position, camera.Target); - double buttonOffset = adjustedSpeed / 2.0; - if (Input.IsDown(Button.L2)) - { - buttonOffset *= cameraSpeedModifier; - Lx *= cameraSpeedModifier; - Ly *= cameraSpeedModifier; - forward.Y = 0.0f; - } - if (Input.IsDown(Button.Down)) - { - camera.Position.Y -= (float)buttonOffset; - } - if (Input.IsDown(Button.Up)) - { - camera.Position.Y += (float)buttonOffset; - } - if (Input.IsDown(Button.Left)) - { - cameraRoll -= buttonOffset; - } - if (Input.IsDown(Button.Right)) - { - cameraRoll += buttonOffset; - } - forward = Quaternion.Normalize(forward); - Quaternion left = forward * Quaternion.CreateFromRotationMatrix(Matrix4x4.CreateRotationY(Single.DegreesToRadians(180.0f))); - camera.Position.X += (float)(left.X * Lx); - camera.Position.Z += (float)(left.Z * Lx); - camera.Position.X += (float)(forward.X * Ly); - camera.Position.Y += (float)(forward.Y * Ly); - camera.Position.Z += (float)(forward.Z * Ly); + unlockMovementHeld = true; } - 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; - double adjustedSensitivity = cameraSensitivity * (deltaTime * cameraFov); - double Rx = PadRx / (Int16.MaxValue / adjustedSensitivity); - double Ry = PadRy / (Int16.MaxValue / adjustedSensitivity); - cameraYaw += Rx; - cameraPitch = Math.Clamp(cameraPitch + Ry, -cameraPitchLimit, cameraPitchLimit); - // This is the same value the game uses for the player camera. Oddly the in-game view mode - // uses 1.0 like I did here before, which is really bad for precision. - double dist = 700.0 - cameraForward; - camera.Target.X = camera.Position.X + (float)(dist * Math.Cos(Double.DegreesToRadians(cameraPitch)) * Math.Cos(Double.DegreesToRadians(cameraYaw))); - camera.Target.Y = camera.Position.Y + (float)(dist * Math.Sin(Double.DegreesToRadians(cameraPitch))); - camera.Target.Z = camera.Position.Z + (float)(dist * Math.Cos(Double.DegreesToRadians(cameraPitch)) * Math.Sin(Double.DegreesToRadians(cameraYaw))); - camera.FieldOfView = (float)cameraFov; - if (!toggleFreeCamera) + } + } + + private void checkPlayerChange() + { + Player? player = Player.MainPlayer; + if (player != lastPlayer) + { + pCamera = null; + lastPlayer = player; + } + } + + public void OnUpdate(float deltaTime) + { + if (disableMod) return; + + // Consider that pCamera could become an invalid pointer. + checkPlayerChange(); + + if (enableCombo && freeCameraCombo != null) + { + Button b1 = freeCameraCombo[0]; + Button b2 = freeCameraCombo[1]; + if ((!Input.IsPressed(b1) && Input.IsDown(b1)) && Input.IsPressed(b2)) { - freeCamera = false; - camera.Move = true; - if (preZoomFov != null) - { - cameraFov = (double)preZoomFov; - preZoomFov = null; - } - if (preZoomRoll != null) - { - cameraRoll = (double)preZoomRoll; - preZoomRoll = null; - } + enableFreeCamera = !enableFreeCamera; } - maybeSetFovForCulling(vp, camera); + } + + if (freeCamera) + { + checkUnlockMovement(); } } } -- cgit v1.2.3-101-g0448