//#define ADDR_ASSERTS //#define HOOK_ORDER_ASSERTS //#define LOG_DEBUG_MESSAGES 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.Configuration; #if ADDR_ASSERTS || HOOK_ORDER_ASSERTS using System.Diagnostics; #endif // @TODO: // - Thoroughly test "Disable Mod". // - Improve AOB scans. // - White vase thing in research base still fades when close. // - How to change closer shadow "fade" range. // - Display equiped armor in the debug UI. // - Manully set freecam viewport index. // - Override in-game viewmode. // - +right and +forward to move character. // - Special case freecam for cutscenes and canteen animations. // Steam proton launch command: // env DOTNET_ROOT="" WINEDLLOVERRIDES="msvcrt,dinput8=n,b" %command% // 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. // // Disable Character Fade: // - NPCs that spawn invisible and fade-in have to actually fade-in. // - A night time in Astera, there will eventually be 3 hunters eating at the table next to the handler. // If this is broken, two of them will be invisible. // // 2x Shadow Resolution: // - The shadow of the \"Waterfall Bridge\" lift in Astera. // - Walk up the stairs towards the canteen and the shadow of the lift will fade-out. Move the camera // back and forth and make sure the lower resolution shadow aligns with the higher res one. namespace MHWNewCamera { public unsafe class Plugin : IPlugin { public string Name => "MHW New Camera"; public string Author => "Akon City Software"; private const double DEFAULT_FOV = 60.0; private bool disableMod = false; private float cameraSpeed; private float cameraZoomSpeed; private float cameraSpeedModifier; private float cameraSensitivity; private double cameraPitchLimit; private int stickDeadzone; private bool enableCombo = false; private Button[]? freeCameraCombo = null; private bool disableComboButton1 = false; private bool comboButton1Down = false; private uint lastPadDown = 0; private bool buttonWasDown(Button button) { return (lastPadDown & (uint)button) == (uint)button; } private double cameraFov = DEFAULT_FOV; private float cameraForward = 0.0f; private float cameraRight = 0.0f; private float cameraXOffset = 0.0f; private float cameraYOffset = 0.0f; private float cameraZOffset = 0.0f; private double cameraYaw = 0.0; private double cameraPitch = 0.0; private double cameraRoll = 0.0; private bool fadeObjects = true; private int cameraWrapState = 0; private bool freeCamera = false; private bool enableFreeCamera = false; private Camera? vCamera = null; private int vCameraViewportIndex = -1; private Vector3 cameraPosition; private Vector3 cameraTarget; private double? preDetachFov = null; private double? preDetachRoll = null; private bool unlockMovementHeld = false; private bool unlockMovementToggled = false; private bool unlockMovementPause = false; private bool unlockInputToggled = false; private bool offsetPerspective = false; private bool enableOffsetPerspective = false; private Player? lastPlayer = null; private Camera? pCamera = null; private int pCameraViewportIndex = -1; private bool applyPerspective = false; private float previousFov = -1.0f; private int previousCameraAnimState = 0; 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 CalculateCameraDelegate(nint unknownPtr); private Hook? calculateCameraHook; private delegate void SetCameraTentDelegate(nint unknownPtr); private Hook? setCameraTentHook; private delegate void WritePadInputDelegate(nint unknownPtr, nint unknownPtr2, nint unknownPtr3); private Hook? writePadInputHook; private delegate float CheckMovementDelegate(int stickValue); private Hook? checkMovementHook; private Patch swapMinimapFollowsCamera; private bool uiToggled = false; private Patch jmpOverUi; private bool disableCharacterFade = false; private Patch noopCharacterFade; private bool doubleShadowRes = false; private bool largerShadowRange = false; private Patch shadowRes1; private Patch shadowRes1_1; private Patch shadowRes1_2; private Patch shadowRes1_3; private Patch shadowRes2; private Patch shadowRes3; private Patch shadowRes4; private bool softerShadows = false; private bool slightlySofterShadows = false; private Patch higherMinThreshold; private Patch slightlyHigherMinThreshold; private void doubleShadowResEnable() { shadowRes1.Enable(); shadowRes1_1.Enable(); shadowRes1_2.Enable(); shadowRes1_3.Enable(); shadowRes2.Enable(); shadowRes3.Enable(); } private void doubleShadowResDisable() { shadowRes1.Disable(); shadowRes1_1.Disable(); shadowRes1_2.Disable(); shadowRes1_3.Disable(); shadowRes2.Disable(); shadowRes3.Disable(); } private float unknownLod1 = 1.0f; private float unknownLod2 = 1.0f; private float foliageLodFactor = 1.0f; private float terrainLodFactor = 1.0f; private bool largerFoliageSwayRange = false; private Patch addressHigherValueForFoliageSway; private bool disableCollision = false; private bool disableExtraCollision = false; private bool disableGravity = false; private bool disableExtraGravity = false; private Patch disableXCollision; private Patch disableYCollision; private Patch disableSecondaryYCollision; private Patch disableZCollision; private Patch disableExtraYCollision; private Patch disableGravityYUpdate; private Patch disableGravityXZUpdate; private Patch disableGravityEval; private void disableCollisionEnable() { disableXCollision.Enable(); disableYCollision.Enable(); disableSecondaryYCollision.Enable(); disableZCollision.Enable(); } private void disableCollisionDisable() { disableXCollision.Disable(); disableYCollision.Disable(); disableSecondaryYCollision.Disable(); disableZCollision.Disable(); } private void disableExtraCollisionEnable() { disableExtraYCollision.Enable(); } private void disableExtraCollisionDisable() { disableExtraYCollision.Disable(); } private void disableGravityEnable() { disableGravityYUpdate.Enable(); } private void disableGravityDisable() { disableGravityYUpdate.Disable(); } private void disableExtraGravityEnable() { disableGravityEval.Enable(); } private void disableExtraGravityDisable() { disableGravityEval.Disable(); } 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 = ""; #if HOOK_ORDER_ASSERTS private int hookOrder = 0; private int frameTick = 0; #endif private void debugLog(string message) { #if LOG_DEBUG_MESSAGES Log.Debug(message); #endif } private Config loadConfig() { Config config = ConfigManager.GetConfig(this); disableMod = config.DisableMod; enableCombo = config.EnableCombo; freeCameraCombo = Config.ParseCombo(config.FreeCameraCombo); typedCombo = config.FreeCameraCombo.Replace(",", "+"); disableComboButton1 = config.DisableComboButton1UnlessButton2Held; Config.Settings settings = config.CameraSettings; cameraSpeed = settings.CameraSpeed; cameraZoomSpeed = settings.CameraZoomSpeed; cameraSpeedModifier = settings.CameraSpeedModifier; cameraSensitivity = settings.CameraSensitivity; cameraPitchLimit = settings.CameraPitchLimit; if (cameraPitchLimit != -1.0) { cameraPitchLimit = Math.Clamp(cameraPitchLimit, 0.0, Config.Settings.MAX_PITCH_LIMIT); } stickDeadzone = settings.StickDeadzone; enableOffsetPerspective = config.PerspectiveCameraEnabled; if (config.Selected != "") { Config.Preset preset = config.Presets[config.Selected]; cameraFov = preset.FOV; cameraForward = preset.Forward; cameraRight = preset.Right; cameraYOffset = preset.Up; cameraRoll = preset.Roll; fadeObjects = preset.FadeObjects; } doubleShadowRes = config.DoubleShadowResolution; largerShadowRange = config.LargerShadowRange; slightlySofterShadows = config.SlightlySofterShadows; if (slightlySofterShadows && config.SofterShadows) { config.SofterShadows = false; } softerShadows = config.SofterShadows; largerFoliageSwayRange = config.LargerFoliageSwayRange; ConfigManager.SaveConfig(this); return config; } public void OnPreMain() { Config config = loadConfig(); // Asserts based on version 15.23.00. // @TODO: Handle addr not found. // Place where we can check camera state early. nint 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 == 0x141FA11A0); // nint #endif setCameraHook = Hook.Create(addr, SetCameraHook); // Place where we can adjust the camera position. 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); // nint #endif calculateCameraHook = Hook.Create(addr, CalculateCameraHook); // 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); addr = PatternScanner.FindFirst(Pattern.FromString("48 89 5C 24 08 57 44 8B 9A 60 01 00 00 48 8B DA 41 0F BF 40 08 4D 8B D0 89 82 80 01 00 00 BF 00 10 00 00 41 0F BF 40 0A 89 82 84 01 00 00 41 0F BF 40 0C 89 82 78 01 00 00 41 0F BF 40 0E 89 82 7C 01 00 00")); #if ADDR_ASSERTS Trace.Assert(addr == 0x1422A1280); // nint, nint, nint #endif writePadInputHook = Hook.Create(addr, WritePadInputHook); // Place where we can change the analog stick value the game uses for player movement. addr = PatternScanner.FindFirst(Pattern.FromString("66 0F 6E C1 0F 5B C0 85 C9 78 11")); #if ADDR_ASSERTS Trace.Assert(addr == 0x142107CB0); // int #endif checkMovementHook = Hook.Create(addr, CheckMovementHook); unchecked { addr = PatternScanner.FindFirst(Pattern.FromString("84 C0 0F 84 ?? ?? ?? ?? 48 8B 57 28 45 33 C0 48 8B CE E8 ?? ?? ?? ?? 48 8B 97 E0 01 00 00 41 B0 01 48 8B CE E8 ?? ?? ?? ?? 48 8B 0D ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 8B D8")); #if ADDR_ASSERTS Trace.Assert(addr == 0x141E55D9E); #endif swapMinimapFollowsCamera = new Patch(addr + 0x3, [0x85]); // je -> jne addr = PatternScanner.FindFirst(Pattern.FromString("48 89 5C 24 18 48 89 6C 24 20 57 48 83 EC 20 48 8B 59 68 48 8B FA 48 8B E9 48 85 DB 0F 84 ?? ?? ?? ?? 4C 89 74 24 38 4C 8B B3 80 00 00 00 4D 85 F6")); #if ADDR_ASSERTS Trace.Assert(addr == 0x14234DD60); #endif jmpOverUi = new Patch(addr + 0x1c, [0xE9, 0x65, 0x01, 0x00, 0x00, 0x90]); addr = PatternScanner.FindFirst(Pattern.FromString("CC 48 89 5C 24 18 57 48 83 EC 30 48 89 74 24 48 48 8B D9 E8 ?? ?? ?? ?? 48 8B 8B B0 0D 00 00 8B 81 54 89 00 00 C1 E8 0D A8 01")); #if ADDR_ASSERTS Trace.Assert(addr == 0x1411A6A9F); #endif noopCharacterFade = new Patch(addr + 0x13, [0x90, 0x90, 0x90, 0x90, 0x90]); // call MonsterHunterWorld.exe+11A6D50 if (disableCharacterFade && !disableMod) { noopCharacterFade.Enable(); } addr = PatternScanner.FindFirst(Pattern.FromString("48 83 EC 28 F3 0F 10 0D ?? ?? ?? ?? 80 F9 04 75 2F 48 8B 0D ?? ?? ?? ?? E8 D3 8A E4 01 48 8B 0D ?? ?? ?? ?? 33 D2 E8 ?? ?? ?? ??")); #if ADDR_ASSERTS Trace.Assert(addr == 0x14043FF80); Trace.Assert(MemoryUtil.Read(0x142F0F358) == 2.0f); #endif shadowRes1 = new Patch(addr + 0x8, [0xCC, 0xF3, 0xAC, 0x02]); shadowRes1_1 = new Patch(addr + 0x65, [0x17]); // Always set "Value is not 1.0" flag. shadowRes1_2 = new Patch(addr + 0x68, [0x59]); // movss -> mulss shadowRes1_3 = new Patch(addr + 0x77, [0x59]); // movss -> mulss addr = PatternScanner.FindFirst(Pattern.FromString("83 FA FF 74 06 89 91 1C 55 00 00 8B 91 30 55 00 00 83 FA FF 7F 06 8B 91 1C 55 00 00 85 D2 74 4B 83 EA 01 74 3B 83 EA 01 74 2B 83 EA 01 74 1B 83 FA 01 74 0B C7 81 20 55 00 00 01 00 00 00 C3 C7 81 20 55 00 00 00 10 00 00")); #if ADDR_ASSERTS Trace.Assert(addr == 0x142287A50); #endif shadowRes2 = new Patch(addr + 0x3F, [ 0xC7, 0x81, 0x20, 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0xC3, 0xC7, 0x81, 0x20, 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0xC3, 0xC7, 0x81, 0x20, 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0xC3, // Has to be 8192 for the fallback lower res shadow to be aligned. 0xC7, 0x81, 0x20, 0x55, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0xC3, 0xC7, 0x81, 0x20, 0x55, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0xC3 ]); addr = PatternScanner.FindFirst(Pattern.FromString("89 91 30 55 00 00 83 FA FF 7F 06 8B 91 1C 55 00 00 85 D2 74 4B 83 EA 01 74 3B 83 EA 01 74 2B 83 EA 01 74 1B 83 FA 01 74 0B C7 81 20 55 00 00 01 00 00 00 C3 C7 81 20 55 00 00 00 10 00 00")); #if ADDR_ASSERTS Trace.Assert(addr == 0x142287AD0); #endif shadowRes3 = new Patch(addr + 0x34, [ 0xC7, 0x81, 0x20, 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0xC3, 0xC7, 0x81, 0x20, 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0xC3, 0xC7, 0x81, 0x20, 0x55, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0xC3, 0xC7, 0x81, 0x20, 0x55, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0xC3, 0xC7, 0x81, 0x20, 0x55, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0xC3 ]); addr = PatternScanner.FindFirst(Pattern.FromString("89 91 00 55 00 00 83 FA 05 77 41 48 63 C2 4C 8D 05 ?? ?? ?? ?? 41 8B 94 80 84 8B 28 02 49 03 D0 FF E2 B8 00 08 00 00")); #if ADDR_ASSERTS Trace.Assert(addr == 0x142288B00); #endif shadowRes4 = new Patch(addr + 0x29, [ 0xB8, 0x00, 0x16, 0x00, 0x00, 0xEB, 0x21 // "Primary shadow range". This value is used for Low, Medium and High in-game. ]); if (doubleShadowRes && !disableMod) { doubleShadowResEnable(); } if (largerShadowRange && !disableMod) { shadowRes4.Enable(); } addr = PatternScanner.FindFirst(Pattern.FromString("F3 0F 10 8F D0 E5 00 00 0F 57 C0 F3 0F 59 0D ?? ?? ?? ?? 8B C1 48 C1 E8 0C 25 FF 03 00 00 F3 48 0F 2A C3 F3 0F 5E C8 F3 41 0F 11 0C 87")); #if ADDR_ASSERTS Trace.Assert(addr == 0x142286B1B); Trace.Assert(MemoryUtil.Read(0x14239CF98) == 4096.0f); Trace.Assert(MemoryUtil.Read(0x140598864) == 3072.0f); #endif slightlyHigherMinThreshold = new Patch(addr + 0xF, [0x36, 0x1D, 0x31, 0xFE]); if (slightlySofterShadows && !disableMod) { slightlyHigherMinThreshold.Enable(); } higherMinThreshold = new Patch(addr + 0xF, [0x6A, 0x64, 0x11, 0x00]); if (softerShadows && !disableMod) { higherMinThreshold.Enable(); } addr = PatternScanner.FindFirst(Pattern.FromString("74 4D 33 C0 48 81 C1 00 02 00 00 F3 0F 10 11 0F 2F CA 72 0C FF C0 48 83 C1 04 83 F8 03 72 EC C3")); #if ADDR_ASSERTS Trace.Assert(addr == 0x1426D89CA); #endif addressHigherValueForFoliageSway = new Patch(addr + 0x7, [0x04]); if (largerFoliageSwayRange && !disableMod) { addressHigherValueForFoliageSway.Enable(); } // 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]); addr = PatternScanner.FindFirst(Pattern.FromString("F3 0F 11 83 80 00 00 00 F3 0F 11 8B 84 00 00 00 F3 0F 11 93 88 00 00 00 89 B3 8C 00 00 00 8B 83 A4 01 00 00 C1 E8 05 44 0F 29 A4 24 D0 01 00 00 44 0F 29 B4 24 B0 01 00 00 A8 01")); #if ADDR_ASSERTS Trace.Assert(addr + 0x8 == 0x1413259ED); #endif disableGravityYUpdate = new Patch(addr + 0x8, [ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, ]); disableGravityXZUpdate = new Patch(addr, [ 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, // Will already be applied. 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90 ]); 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); #endif disableGravityEval = new Patch(addr + 0x40, [0x90, 0x90, 0x90, 0x90, 0x90]); // call MonsterHunterWorld.exe+1325810 } } public void OnLoad() { Config config = ConfigManager.GetConfig(this); unknownLod1 = config.LodFactor1; unknownLod2 = config.LodFactor2; foliageLodFactor = config.FoliageLodFactor; terrainLodFactor = config.TerrainLodFactor; if (config.ApplyLodFactors) { setLodFactors(true); } } // Credit to Otis_Inf. private bool getFadeObjects(int viewportIndex) { Viewport vp = CameraSystem.GetViewport(viewportIndex); return MemoryUtil.Read(vp.Instance + 0x21) == 1; } private void setFadeObjects(int viewportIndex, bool fadeOut) { Viewport vp = CameraSystem.GetViewport(viewportIndex); MemoryUtil.WriteBytes(vp.Instance + 0x21, fadeOut ? [0x1] : [0x0]); } private void setCharacterFade(bool fadeOut) { if (fadeOut && disableCharacterFade) { noopCharacterFade.Disable(); disableCharacterFade = false; } else if (!fadeOut && !disableCharacterFade) { noopCharacterFade.Enable(); disableCharacterFade = true; } } private bool areLodFactorsDefault() { return unknownLod1 == 1.0f && unknownLod2 == 1.0f && foliageLodFactor == 1.0f && terrainLodFactor == 1.0f; } private bool areLodFactorsSet() { nint baseAddr = MemoryUtil.Read(0x1451C4368); float lod1 = MemoryUtil.Read(baseAddr + 0x224); float lod2 = MemoryUtil.Read(baseAddr + 0x228); float lod3 = MemoryUtil.Read(baseAddr + 0x234); float lod4 = MemoryUtil.Read(baseAddr + 0x24C); return lod1 == unknownLod1 && lod2 == unknownLod2 && lod3 == foliageLodFactor && lod4 == terrainLodFactor; } private void setLodFactors(bool toggleOn) { nint baseAddr = MemoryUtil.Read(0x1451C4368); if (toggleOn) { MemoryUtil.WriteBytes(baseAddr + 0x224, BitConverter.GetBytes(unknownLod1)); // Something with lighting? MemoryUtil.WriteBytes(baseAddr + 0x228, BitConverter.GetBytes(unknownLod2)); MemoryUtil.WriteBytes(baseAddr + 0x234, BitConverter.GetBytes(foliageLodFactor)); MemoryUtil.WriteBytes(baseAddr + 0x24C, BitConverter.GetBytes(terrainLodFactor)); } else { MemoryUtil.WriteBytes(baseAddr + 0x224, BitConverter.GetBytes(1.0f)); MemoryUtil.WriteBytes(baseAddr + 0x228, BitConverter.GetBytes(1.0f)); MemoryUtil.WriteBytes(baseAddr + 0x234, BitConverter.GetBytes(1.0f)); MemoryUtil.WriteBytes(baseAddr + 0x24C, BitConverter.GetBytes(1.0f)); } } private void checkPlayerChange() { Player? player = Player.MainPlayer; if (player != lastPlayer) { pCamera = null; pCameraViewportIndex = -1; lastPlayer = player; } } public void OnUpdate(float deltaTime) { if (disableMod) return; #if HOOK_ORDER_ASSERTS Trace.Assert(hookOrder == 0 || hookOrder == 3); hookOrder = 0; frameTick++; #endif // Consider that pCamera could become an invalid pointer. checkPlayerChange(); } 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)Math.Clamp((cameraFov * previousFov) / DEFAULT_FOV, 1.0, 179.0); } } private void setTentBasePos(Vector3 pos, Vector3 target) { // @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 void disableFreeCamera() { 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)); if (vCameraViewportIndex != pCameraViewportIndex) { setFadeObjects(vCameraViewportIndex, true); } else { setFadeObjects(pCameraViewportIndex, fadeObjects); } setCharacterFade(fadeObjects); swapMinimapFollowsCamera.Disable(); } private void updateFreeCamera(Camera camera) { float deltaTime = camera.DeltaTime; double adjustedSpeed = cameraSpeed * deltaTime; double buttonOffset = adjustedSpeed / 2.0; if (buttonWasDown(Button.L2) && buttonWasDown(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 Lx = PadLx / (Int16.MaxValue / adjustedSpeed); double Ly = PadLy / (Int16.MaxValue / adjustedSpeed); Quaternion forward = getForward(cameraPosition, cameraTarget); if (buttonWasDown(Button.R2)) { forward.Y = 0.0f; Lx *= cameraSpeedModifier; Ly *= cameraSpeedModifier; buttonOffset *= cameraSpeedModifier; } 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); } } if (!unlockInputToggled) { if (buttonWasDown(Button.Up)) { cameraPosition.Y += (float)buttonOffset; } if (buttonWasDown(Button.Down)) { cameraPosition.Y -= (float)buttonOffset; } if (buttonWasDown(Button.Left)) { cameraRoll -= buttonOffset / 2.0; } if (buttonWasDown(Button.Right)) { cameraRoll += buttonOffset / 2.0; } } // 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 { 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) { cameraRoll -= 180.0; cameraWrapState = 0; } else if (cameraPitch >= 270.0 && cameraWrapState == 1) { cameraPitch -= 360.0; cameraRoll -= 180.0; cameraWrapState = 0; } } if (cameraRoll >= 360.0) { cameraRoll -= 360.0; } else if (cameraRoll < 0.0) { cameraRoll += 360.0; } if (plusRight != 0.0) { cameraYaw += plusRight * deltaTime; } // 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 int getVisibleCamera() { for (int i = 0; i < 8; i++) { Viewport vp = CameraSystem.GetViewport(i); // A viewport can be visible with a null camera. if (vp.Visible && vp.Camera != null) { return i; } } return -1; } private void checkCurrentVisibleCamera() { vCameraViewportIndex = getVisibleCamera(); if (vCameraViewportIndex >= 0) { Camera camera = CameraSystem.GetViewport(vCameraViewportIndex).Camera!; // Free camera target change. if (freeCamera && camera != vCamera) { Player? player = Player.MainPlayer; if (player != null) { // Try to position the camera behind the player. cameraPosition = player.Position; cameraPosition.X -= player.Forward.X * 250.0f; cameraPosition.Y += 200.0f; cameraPosition.Z -= player.Forward.Z * 250.0f; cameraTarget = player.Position; cameraTarget.Y += 150.0f; } else { cameraPosition = camera.Position; cameraTarget = camera.Target; } Quaternion forward = Quaternion.Normalize(getForward(cameraPosition, cameraTarget)); cameraYaw = Double.RadiansToDegrees(Math.Atan2(forward.Z, forward.X)); cameraPitch = Double.RadiansToDegrees(Math.Asin(forward.Y)); } vCamera = camera; // pCamera is the first visible camera after a player is set. // We assume it is the player camera. if (pCamera == null && lastPlayer != null) { pCamera = vCamera; pCameraViewportIndex = vCameraViewportIndex; setFadeObjects(pCameraViewportIndex, fadeObjects); } } } private void checkCameraAnimState() { if (pCamera != null) { previousCameraAnimState = MemoryUtil.Read(pCamera.Instance + 0x240); if (previousCameraAnimState == 5) { // 1:314 = Wingdrake landing on area enter. // 1:319 = Disoriented 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) { // Once cameraAnimState is no longer 5, resume default logic. ignoreAnimState = false; } } else { previousCameraAnimState = 0; } } private void SetCameraHook(nint unknownPtr) { if (disableMod) { setCameraHook!.Original(unknownPtr); return; } #if HOOK_ORDER_ASSERTS debugLog($"SetCameraHook() @ {frameTick}"); Trace.Assert(hookOrder == 0); hookOrder = 1; #endif checkCurrentVisibleCamera(); checkCameraAnimState(); // CalculateCameraHook() within this function. setCameraHook!.Original(unknownPtr); #if HOOK_ORDER_ASSERTS Trace.Assert(hookOrder == 2); hookOrder = 3; #endif if (applyPerspective) { setCameraRoll(pCamera!); } if (freeCamera && vCamera != null) { setCameraRoll(vCamera); if (!enableFreeCamera) { // Disable free camera only after applying all possible // offsets for this frame. This is to hopefully avoid any // visual jumps. disableFreeCamera(); } } } private static bool assumeInQuestBoard(float fov) { return fov == 20.000038f || fov == 20.017189f || fov == 20.00004f; } // If camera.Move = false, this hook won't run. private void CalculateCameraHook(nint unknownPtr) { if (disableMod) { calculateCameraHook!.Original(unknownPtr); return; } #if HOOK_ORDER_ASSERTS debugLog($"CalculateCameraHook() @ {frameTick}"); Trace.Assert(hookOrder == 1); hookOrder = 2; #endif if (enableOffsetPerspective && !offsetPerspective) { offsetPerspective = true; } else if (!enableOffsetPerspective && offsetPerspective) { offsetPerspective = false; } if (pCamera != null) { // previousFov is the pre-offset FOV value. previousFov = pCamera.FieldOfView; } applyPerspective = offsetPerspective && !freeCamera && pCamera != null; if (applyPerspective) { applyPerspective &= (previousCameraAnimState != 5 || ignoreAnimState); applyPerspective &= !assumeInQuestBoard(previousFov); } if (applyPerspective && !debugButton) { // Applying Y offset here keeps crosshair UI centered, but affects the angle of the camera. pCamera!.Position.Y += cameraYOffset; if (cameraFov != DEFAULT_FOV) { pCamera!.FieldOfView = (float)Math.Clamp((cameraFov * previousFov) / DEFAULT_FOV, 1.0, 179.0); } } calculateCameraHook!.Original(unknownPtr); // 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) { setPerspective(pCamera!); } if (vCamera != 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; setFadeObjects(vCameraViewportIndex, false); setCharacterFade(false); swapMinimapFollowsCamera.Enable(); } } // No else if because we want to updateFreeCamera() if freeCamera was enabled above. if (freeCamera) { updateFreeCamera(vCamera); } } } private void SetCameraTentHook(nint unknownPtr) { if (disableMod) { setCameraTentHook!.Original(unknownPtr); return; } #if HOOK_ORDER_ASSERTS // The order of this hook between SetCameraHook() and CalculateCameraHook() is inconsistent. debugLog($"SetCameraTentHook() @ {frameTick}"); #endif if (freeCamera && vCamera != null) { setTentBasePos(cameraPosition, cameraTarget); } setCameraTentHook!.Original(unknownPtr); if (freeCamera && vCamera != null) { vCamera.Position = cameraPosition; vCamera.Target = cameraTarget; vCamera.FieldOfView = (float)cameraFov; setCameraRoll(vCamera); } } // This can undoubtedly be simplified. I'm being conservative about MemoryUtil.Read() because // I haven't thought about or tested enough to know if it should be a performance consideration. private void WritePadInputHook(nint unknownPtr, nint unknownPtr2, nint unknownPtr3) { if (disableMod) { writePadInputHook!.Original(unknownPtr, unknownPtr2, unknownPtr3); return; } uint PadDown = 0, PadTrg = 0, PadRel = 0, PadChg = 0; if ((enableCombo && freeCameraCombo != null && disableComboButton1) && comboButton1Down) { PadDown = MemoryUtil.Read(controllerAddr() + 0x198); uint b1 = (uint)freeCameraCombo[0]; PadDown |= b1; MemoryUtil.WriteBytes(controllerAddr() + 0x198, BitConverter.GetBytes(PadDown)); } writePadInputHook!.Original(unknownPtr, unknownPtr2, unknownPtr3); bool readInputsPostHook = false; if (enableCombo && freeCameraCombo != null) { Button b1 = freeCameraCombo[0]; Button b2 = freeCameraCombo[1]; if (disableComboButton1) { if (!readInputsPostHook) { PadDown = MemoryUtil.Read(controllerAddr() + 0x198); PadTrg = MemoryUtil.Read(controllerAddr() + 0x1A0); PadRel = MemoryUtil.Read(controllerAddr() + 0x1A4); PadChg = MemoryUtil.Read(controllerAddr() + 0x1A8); lastPadDown = PadDown; readInputsPostHook = true; } uint b1u = (uint)b1; if (comboButton1Down || enableFreeCamera || !Input.IsDown(b2)) { if ((PadDown & b1u) == b1u) { comboButton1Down = true; } PadDown &= ~b1u; MemoryUtil.WriteBytes(controllerAddr() + 0x198, BitConverter.GetBytes(PadDown)); } if (comboButton1Down) { if (((PadRel & b1u) == b1u)) { comboButton1Down = false; } PadTrg &= ~b1u; PadRel &= ~b1u; PadChg &= ~b1u; MemoryUtil.WriteBytes(controllerAddr() + 0x1A0, BitConverter.GetBytes(PadTrg)); MemoryUtil.WriteBytes(controllerAddr() + 0x1A4, BitConverter.GetBytes(PadRel)); MemoryUtil.WriteBytes(controllerAddr() + 0x1A8, BitConverter.GetBytes(PadChg)); } } if ((comboButton1Down || (!disableComboButton1 && Input.IsDown(b1))) && Input.IsPressed(b2)) { enableFreeCamera = !enableFreeCamera; } } if (enableFreeCamera) { if (!unlockMovementHeld && Input.IsPressed(Button.L2)) { unlockMovementHeld = true; } else if (unlockMovementHeld && Input.IsReleased(Button.L2)) { unlockMovementHeld = false; unlockMovementPause = false; } if (unlockMovementHeld) { if (Input.IsPressed(Button.L1)) { unlockMovementToggled = !unlockMovementToggled; } // Temporarily disable for zoom mode. if (Input.IsDown(Button.R2)) { unlockMovementPause = true; } else if (Input.IsReleased(Button.R2)) { unlockMovementPause = false; } } if (Input.IsDown(Button.L2) && Input.IsPressed(Button.R1)) { unlockInputToggled = !unlockInputToggled; } if (Input.IsDown(Button.R2) && Input.IsPressed(Button.R1)) { if (uiToggled) { jmpOverUi.Disable(); } else { jmpOverUi.Enable(); } uiToggled = !uiToggled; } if (!readInputsPostHook) { PadDown = MemoryUtil.Read(controllerAddr() + 0x198); lastPadDown = PadDown; } if (!unlockInputToggled) { if (!readInputsPostHook) { PadTrg = MemoryUtil.Read(controllerAddr() + 0x1A0); PadRel = MemoryUtil.Read(controllerAddr() + 0x1A4); PadChg = MemoryUtil.Read(controllerAddr() + 0x1A8); readInputsPostHook = true; } uint FaceButtonMask = (uint)Button.Cross | (uint)Button.Circle | (uint)Button.Square | (uint)Button.Triangle; uint DPadMask = (uint)Button.Up | (uint)Button.Down | (uint)Button.Left | (uint)Button.Right; if (enableCombo && freeCameraCombo != null) { uint b1 = (uint)freeCameraCombo[0]; uint b2 = (uint)freeCameraCombo[1]; FaceButtonMask &= ~(b1 | b2); DPadMask &= ~(b1 | b2); } PadDown &= ~DPadMask; PadDown &= ~FaceButtonMask; //PadDown &= ~((uint)Button.R1 | (uint)Button.L1); PadTrg &= ~DPadMask; PadTrg &= ~FaceButtonMask; //PadTrg &= ~((uint)Button.R1 | (uint)Button.L1); PadChg &= ~DPadMask; PadChg &= ~FaceButtonMask; //PadChg &= ~((uint)Button.R1 | (uint)Button.L1); MemoryUtil.WriteBytes(controllerAddr() + 0x198, BitConverter.GetBytes(PadDown)); MemoryUtil.WriteBytes(controllerAddr() + 0x1A0, BitConverter.GetBytes(PadTrg)); MemoryUtil.WriteBytes(controllerAddr() + 0x1A8, BitConverter.GetBytes(PadChg)); // Left and right trigger. MemoryUtil.WriteBytes(controllerAddr() + 0x1C0, BitConverter.GetBytes(0)); MemoryUtil.WriteBytes(controllerAddr() + 0x1C1, BitConverter.GetBytes(0)); } } } private float CheckMovementHook(int stickValue) { if (disableMod) { return checkMovementHook!.Original(stickValue); } #if HOOK_ORDER_ASSERTS debugLog($"CheckMovementHook() @ {frameTick}"); #endif if (freeCamera && !((unlockMovementHeld || unlockMovementToggled) && !unlockMovementPause)) { stickValue = 0; } return checkMovementHook!.Original(stickValue); } private void disableAllCollisionHooks() { if (disableExtraGravity) { disableExtraGravity = false; disableExtraGravityDisable(); } if (disableGravity) { disableGravity = false; disableGravityDisable(); } if (disableExtraCollision) { disableExtraCollision = false; disableExtraCollisionDisable(); } if (disableCollision) { disableCollision = false; disableCollisionDisable(); } } public void OnImGuiRender() { Config config = ConfigManager.GetConfig(this); float width = ImGui.GetWindowWidth(); if (ImGui.Checkbox("Disable Mod", ref disableMod)) { if (disableMod) { disableAllCollisionHooks(); if (freeCamera) { disableFreeCamera(); } for (int i = 0; i < 8; i++) { Viewport vp = CameraSystem.GetViewport(i); if (vp.Camera != null) { vp.Camera.Move = true; } setFadeObjects(i, true); } Player? player = Player.MainPlayer; if (player != null) { player.Rotation.X = 0.0f; player.Rotation.Z = 0.0f; } if (areLodFactorsSet() && !areLodFactorsDefault()) { setLodFactors(false); } if (doubleShadowRes) { doubleShadowResDisable(); } if (largerShadowRange) { shadowRes4.Disable(); } if (slightlySofterShadows) { slightlyHigherMinThreshold.Disable(); } if (softerShadows) { higherMinThreshold.Disable(); } if (largerFoliageSwayRange) { addressHigherValueForFoliageSway.Disable(); } if (disableCharacterFade) { noopCharacterFade.Disable(); } } else { if (config.ApplyLodFactors && !areLodFactorsDefault()) { setLodFactors(true); } if (doubleShadowRes) { doubleShadowResEnable(); } if (largerShadowRange) { shadowRes4.Enable(); } if (slightlySofterShadows) { slightlyHigherMinThreshold.Enable(); } if (softerShadows) { higherMinThreshold.Enable(); } if (largerFoliageSwayRange) { addressHigherValueForFoliageSway.Enable(); } if (pCamera != null) { setFadeObjects(pCameraViewportIndex, fadeObjects); } if (disableCharacterFade) { noopCharacterFade.Enable(); } } config.DisableMod = disableMod; ConfigManager.SaveConfig(this); } if (ImGui.BeginItemTooltip()) { ImGui.Text("Try to disable as much as possible."); ImGui.EndTooltip(); } if (disableMod) return; ImGui.Separator(); 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 enableOffsetPerspective)) { config.PerspectiveCameraEnabled = enableOffsetPerspective; ConfigManager.SaveConfig(this); } if (enableFreeCamera) { ImGui.PopItemFlag(); ImGui.PopStyleVar(); } 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.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.Checkbox("Fade Objects When Close to Camera", ref fadeObjects)) { if (pCamera != null) { setFadeObjects(pCameraViewportIndex, fadeObjects); } setCharacterFade(fadeObjects); } if (ImGui.BeginItemTooltip()) { ImGui.Text("Applies to Objects, Characters and Palicos. Credit to Otis_Inf for Objects."); ImGui.EndTooltip(); } if (ImGui.Button("Default")) { cameraFov = DEFAULT_FOV; cameraForward = 0.0f; cameraRight = 0.0f; cameraXOffset = 0.0f; cameraYOffset = 0.0f; cameraZOffset = 0.0f; cameraRoll = 0.0; if (preDetachRoll != null) { preDetachRoll = cameraRoll; } fadeObjects = true; if (pCamera != null) { setFadeObjects(pCameraViewportIndex, fadeObjects); } setCharacterFade(fadeObjects); 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; if (name != "") { Config.Preset preset = new Config.Preset(); preset.FOV = cameraFov; preset.Forward = cameraForward; preset.Right = cameraRight; preset.Up = cameraYOffset; preset.Roll = cameraRoll; preset.FadeObjects = pCamera != null ? getFadeObjects(pCameraViewportIndex) : true; 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.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; cameraRight = preset.Right; cameraYOffset = preset.Up; cameraRoll = preset.Roll; if (preDetachRoll != null) { preDetachRoll = cameraRoll; } fadeObjects = preset.FadeObjects; if (pCamera != null) { setFadeObjects(pCameraViewportIndex, fadeObjects); } setCharacterFade(fadeObjects); } if (isSelected) ImGui.SetItemDefaultFocus(); } 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"); ImGui.DragFloat("Camera Speed", ref cameraSpeed, 0.01f, 0.0f); if (ImGui.BeginItemTooltip()) { ImGui.Text("Movement speed in free camera."); ImGui.EndTooltip(); } ImGui.DragFloat("Zoom Speed", ref cameraZoomSpeed, 0.01f, 0.0f); if (ImGui.BeginItemTooltip()) { ImGui.Text("Rate of camera zoom."); ImGui.EndTooltip(); } ImGui.DragFloat("Speed Modifier", ref cameraSpeedModifier, 0.01f, 0.0f); if (ImGui.BeginItemTooltip()) { ImGui.Text("Value to multiply speed by when R2 is held."); ImGui.EndTooltip(); } ImGui.DragFloat("Camera Sensitivity", ref cameraSensitivity, 0.0025f, 0.0f); if (ImGui.BeginItemTooltip()) { ImGui.Text("Camera look sensitivity in free camera."); 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.0) { cameraPitchLimit = Math.Clamp(cameraPitchLimit, 0.0, Config.Settings.MAX_PITCH_LIMIT); } } 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")) { cameraSpeed = Config.Settings.DEFAULT_SPEED; cameraZoomSpeed = Config.Settings.DEFAULT_ZOOM_SPEED; cameraSpeedModifier = Config.Settings.DEFAULT_SPEED_MODIFIER; cameraSensitivity = Config.Settings.DEFAULT_SENSITIVITY; cameraPitchLimit = Config.Settings.DEFAULT_PITCH_LIMIT; stickDeadzone = Config.Settings.DEFAULT_DEADZONE; } ImGui.SameLine(); if (ImGui.Button("Reset")) { Config.Settings settings = config.CameraSettings; cameraSpeed = settings.CameraSpeed; cameraZoomSpeed = settings.CameraZoomSpeed; cameraSpeedModifier = settings.CameraSpeedModifier; cameraSensitivity = settings.CameraSensitivity; cameraPitchLimit = settings.CameraPitchLimit; stickDeadzone = settings.StickDeadzone; } ImGui.SameLine(); if (ImGui.Button("Save")) { Config.Settings settings = config.CameraSettings; settings.CameraSpeed = cameraSpeed; settings.CameraZoomSpeed = cameraZoomSpeed; settings.CameraSpeedModifier = cameraSpeedModifier; settings.CameraSensitivity = cameraSensitivity; settings.CameraPitchLimit = cameraPitchLimit; settings.StickDeadzone = stickDeadzone; config.CameraSettings = settings; ConfigManager.SaveConfig(this); } ImGui.PopID(); ImGui.PushID("Toggles"); if (!freeCamera) { ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); } ImGui.Checkbox("Unlock Player Movement", ref unlockMovementToggled); if (ImGui.BeginItemTooltip()) { ImGui.Text("LT+LB"); ImGui.EndTooltip(); } ImGui.Checkbox("Unlock Input", ref unlockInputToggled); if (ImGui.BeginItemTooltip()) { ImGui.Text("LT+RB"); ImGui.EndTooltip(); } if (!freeCamera) { ImGui.PopStyleVar(); } if (ImGui.Checkbox("Disable UI", ref uiToggled)) { if (uiToggled) { jmpOverUi.Enable(); } else { jmpOverUi.Disable(); } } if (ImGui.BeginItemTooltip()) { ImGui.Text("RT+RB. This will still show the scoutfly marker on menus."); ImGui.EndTooltip(); } 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); } } if (ImGui.Checkbox("Disable Button 1 Unless Button 2 is Held", ref disableComboButton1)) { comboButton1Down = false; config.DisableComboButton1UnlessButton2Held = disableComboButton1; ConfigManager.SaveConfig(this); } } ImGui.PopID(); ImGui.PushID("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 large \"Up\" offset, aim in your slinger and click this button on and off."); ImGui.EndTooltip(); } if (pCamera != null) { ImGui.Separator(); ImGui.Text($"Player Camera: {pCamera.Instance:x}"); ImGui.Text($"Applying Offset: {applyPerspective}"); ImGui.Text($"previousCameraAnimState: {previousCameraAnimState}"); } ImGui.Separator(); if (ImGui.Checkbox("2x Shadow Resolution (Requires Reload)", ref doubleShadowRes)) { if (doubleShadowRes) { doubleShadowResEnable(); } else { doubleShadowResDisable(); } config.DoubleShadowResolution = doubleShadowRes; ConfigManager.SaveConfig(this); } if (ImGui.BeginItemTooltip()) { ImGui.Text("Reload shadows by toggling \"Shadow Quality\" in the options."); ImGui.EndTooltip(); } if (ImGui.Checkbox("Larger Shadow Range (Requires Reload)", ref largerShadowRange)) { if (largerShadowRange) { shadowRes4.Enable(); } else { shadowRes4.Disable(); } config.LargerShadowRange = largerShadowRange; ConfigManager.SaveConfig(this); } if (ImGui.BeginItemTooltip()) { ImGui.Text("This only seems to affect far shadows and not the very close fade to lower resolution range."); ImGui.EndTooltip(); } if (softerShadows) { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); } if (ImGui.Checkbox("Slightly Softer Shadows", ref slightlySofterShadows)) { if (slightlySofterShadows) { slightlyHigherMinThreshold.Enable(); } else { slightlyHigherMinThreshold.Disable(); } config.SlightlySofterShadows = slightlySofterShadows; ConfigManager.SaveConfig(this); } if (softerShadows) { ImGui.PopItemFlag(); ImGui.PopStyleVar(); } if (slightlySofterShadows) { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); } if (ImGui.Checkbox("Softer Shadows", ref softerShadows)) { if (softerShadows) { higherMinThreshold.Enable(); } else { higherMinThreshold.Disable(); } config.SofterShadows = softerShadows; ConfigManager.SaveConfig(this); } if (slightlySofterShadows) { ImGui.PopItemFlag(); ImGui.PopStyleVar(); } if (ImGui.BeginItemTooltip()) { ImGui.Text("When 2x shadow resolution is enabled and \"Shadow Quality\" is set to \"High\", this matches the original \"High\" setting."); ImGui.EndTooltip(); } ImGui.InputFloat("Unknown LOD Factor 1", ref unknownLod1); if (ImGui.BeginItemTooltip()) { ImGui.Text("Default: 1.0, Best Quality: 0.0."); ImGui.EndTooltip(); } ImGui.InputFloat("Unknown LOD Factor 2", ref unknownLod2); if (ImGui.BeginItemTooltip()) { ImGui.Text("Seems to control whether some objects are still drawn when very far from the camera.\nDefault: 1.0, Best Quality: 0.0."); ImGui.EndTooltip(); } ImGui.InputFloat("Foliage LOD Factor", ref foliageLodFactor); if (ImGui.BeginItemTooltip()) { ImGui.Text("This can sometimes look a bit off when set >1.0.\nDefault: 1.0, Best Quality: 64.0."); ImGui.EndTooltip(); } ImGui.InputFloat("Terrain/Object LOD Factor", ref terrainLodFactor); if (ImGui.BeginItemTooltip()) { ImGui.Text("Default: 1.0, Best Quality: 64.0."); ImGui.EndTooltip(); } bool lodsSet = areLodFactorsSet(); bool disableLodButton = lodsSet && areLodFactorsDefault(); if (disableLodButton) { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); lodsSet = false; } if (ImGui.Checkbox("Apply LOD Factors", ref lodsSet)) { setLodFactors(lodsSet); config.ApplyLodFactors = lodsSet && !areLodFactorsDefault(); if (lodsSet) { config.LodFactor1 = unknownLod1; config.LodFactor2 = unknownLod2; config.FoliageLodFactor = foliageLodFactor; config.TerrainLodFactor = terrainLodFactor; } ConfigManager.SaveConfig(this); } if (disableLodButton) { ImGui.PopItemFlag(); ImGui.PopStyleVar(); } if (ImGui.BeginItemTooltip()) { ImGui.Text("Credit to Otis_Inf."); ImGui.EndTooltip(); } if (ImGui.Checkbox("Larger Foliage Sway Range", ref largerFoliageSwayRange)) { if (largerFoliageSwayRange) { addressHigherValueForFoliageSway.Enable(); } else { addressHigherValueForFoliageSway.Disable(); } config.LargerFoliageSwayRange = largerFoliageSwayRange; ConfigManager.SaveConfig(this); } ImGui.Separator(); if (ImGui.Checkbox("Disable Character/Palico Fade", ref disableCharacterFade)) { if (disableCharacterFade) { noopCharacterFade.Enable(); } else { noopCharacterFade.Disable(); } } ImGui.Separator(); ImGui.PushID("Player"); Player? player = Player.MainPlayer; if (ImGui.CollapsingHeader("Player") && player != null) { 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) { disableCollisionEnable(); } else { if (disableExtraGravity) { disableExtraGravity = false; disableExtraGravityDisable(); } if (disableGravity) { disableGravity = false; disableGravityDisable(); } if (disableExtraCollision) { disableExtraCollision = false; disableExtraCollisionDisable(); } disableCollisionDisable(); } } if (!disableCollision) { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); } if (ImGui.Checkbox("Disable Another Y Collision", ref disableExtraCollision)) { if (disableExtraCollision) { disableExtraCollisionEnable(); } else { if (disableExtraGravity) { disableExtraGravity = false; disableExtraGravityDisable(); } if (disableGravity) { disableGravity = false; disableGravityDisable(); } disableExtraCollisionDisable(); } } if (ImGui.BeginItemTooltip()) { ImGui.Text("This will make your legs goofy, but it's needed to get on top of some things."); ImGui.EndTooltip(); } if (!disableCollision) { ImGui.PopItemFlag(); ImGui.PopStyleVar(); } if (!disableExtraCollision) { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); } if (ImGui.Checkbox("Disable Y Gravity", ref disableGravity)) { if (disableGravity) { disableGravityEnable(); } else { if (disableExtraGravity) { disableExtraGravity = false; disableExtraGravityDisable(); } disableGravityDisable(); } } if (ImGui.BeginItemTooltip()) { ImGui.Text("Avoid falling when going too high (Y axis)."); ImGui.EndTooltip(); } if (!disableExtraCollision) { ImGui.PopItemFlag(); ImGui.PopStyleVar(); } if (!disableGravity) { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); } if (ImGui.Checkbox("Disable More Gravity", ref disableExtraGravity)) { if (disableExtraGravity) { disableExtraGravityEnable(); } else { disableExtraGravityDisable(); } } if (ImGui.BeginItemTooltip()) { ImGui.Text("This can stop you from starting to fall when going out of bounds and/or rotating."); ImGui.EndTooltip(); } if (!disableGravity) { ImGui.PopItemFlag(); ImGui.PopStyleVar(); } 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; } 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} {currentActionInfo}, {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}"); */ 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("Camera"); ImGui.Text("Camera/Viewport:"); for (int i = 0; i < 8; i++) { ImGui.PushID($"Viewport{i}"); Viewport vp = CameraSystem.GetViewport(i); if (ImGui.CollapsingHeader($"Viewport #{i} ({(vp.Visible ? "visible" : "inactive")})")) { 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}"); bool fadingObjects = getFadeObjects(i); if (ImGui.Checkbox("Fade Objects When Close to Camera", ref fadingObjects)) { setFadeObjects(i, fadingObjects); } if (ImGui.BeginItemTooltip()) { ImGui.Text("Credit to Otis_Inf."); ImGui.EndTooltip(); } 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); if (camera == pCamera) { 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.PushItemWidth(width * 0.15f); ImGui.InputText("##Position Name", ref typedPositionName, 99); ImGui.SameLine(); if (ImGui.Button("Save")) { 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); } } ImGui.SameLine(); if (ImGui.BeginCombo("##Positions", selectedPositionName)) { Dictionary.KeyCollection positionKeys = config.Positions.Keys; for (int j = 0; j < positionKeys.Count + 1; j++) { if (j == 0) { if (ImGui.Selectable("(Deselect)")) { selectedPositionName = ""; } continue; } string positionKey = positionKeys.ElementAt(j - 1); 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(); } ImGui.PopItemWidth(); ImGui.SameLine(); if (ImGui.Button("Delete")) { if (selectedPositionName != "") { config.Positions.Remove(selectedPositionName); selectedPositionName = ""; ConfigManager.SaveConfig(this); } } if (camera == vCamera) { float yaw = (float)cameraYaw; if (ImGui.DragFloat("Yaw", ref yaw, 0.25f)) { cameraYaw = yaw; } float pitch = (float)cameraPitch; if (ImGui.DragFloat("Pitch", ref pitch, 0.25f)) { 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 offsetRight = 0.0f; if (ImGui.DragFloat("Right", ref offsetRight, 0.45f)) { 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; 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.Separator(); ImGui.PushID("Pad"); ImGui.Text("Pad:"); 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}"); ImGui.Text($" L2: {PadLz}"); ImGui.Text("Right Stick:"); 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(); } } }