//#define ADDR_ASSERTS //#define HOOK_ORDER_ASSERTS //#define LOG_DEBUG_MESSAGES #define SIMPLE_KEYBOARD_LAYER 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: // - Find a way to enable crouching and crawling on command. // - Option to ignore camera change (update position, test open book and close). // - Simplify and document input handling/blocking logic. // - Find a way to block other inputs while button1 is down (and blocked). // - More multiple controller testing (3 controllers, steam input, windows). // - Thoroughly test "Disable Mod". // - Improve AOB scans. // - Some objects still fade. // - Glass objects in research base and on the botanical research center table. // - Decals on floor near Astera lift. // - Snow LOD pop-in. // - Stop animation rate lowering at distance. // - Manully set freecam viewport index. // - Option to override in-game viewmode. // - +right and +forward to move character. // Known Issues: // - Camera pitch wrap around while in the tent will sometimes flicker at the point of wrapping. // - Likely due to the unpredictable position of SetCameraTentHook() in the chain of hooks. Updating // free camera state within SetCameraTentHook() is not a solution because it comes out laggy. // 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 const float DEFAULT_NEAR_CLIP = 16.0f; private bool disableMod = false; #if SIMPLE_KEYBOARD_LAYER private bool keyboardEnabled = false; private int keyboardLookValue; #endif private float cameraSpeed; private float cameraSpeedModifier; private float cameraSensitivity; private float cameraZoomSpeed; private double cameraPitchLimit; private int stickDeadzone; 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 disableFading = false; private int cameraWrapState = 0; private bool freeCamera = false; private bool enableFreeCamera = false; private Camera? vCamera = null; private int vCameraViewportIndex = -1; private double cameraFrameX; private double cameraFrameY; private double cameraFrameZ; 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 lockVerticalToggled = 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 enableCombo = false; private Button[]? freeCameraCombo = null; private bool disableComboButton1 = false; private bool comboButton1Down = false; private static readonly MtObject sMhSteamController = SingletonManager.GetSingleton("sMhSteamController")!; private nint controllerAddr() => sMhSteamController.Instance; private nint primaryPad = 0x0; private uint lastPadDown = 0; private uint? prevPadDown = null; private bool unlockInputToggled = false; private bool inputBlockedForToggle = false; private bool buttonWasDown(Button button) { return (lastPadDown & (uint)button) == (uint)button; } private bool buttonWasPressed(Button button) { return (lastPadDown & (uint)button) == (uint)button && (prevPadDown & (uint)button) != (uint)button; } private bool buttonWasReleased(Button button) { return (lastPadDown & (uint)button) != (uint)button && (prevPadDown & (uint)button) == (uint)button; } private bool skipDofHook = true; private bool forceOffDof = false; private bool? wasDofAllowed = null; private nint dofPointer = 0x0; 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 SetCameraCutsceneDelegate(nint unknownPtr); private Hook? setCameraCutsceneHook; private delegate void WritePadInputDelegate(nint unknownPtr, nint unknownPtr2, nint unknownPtr3); private Hook? writePadInputHook; private delegate float CheckMovementDelegate(int stickValue); private Hook? checkMovementHook; private delegate void ShadowCascadeDelegate(nint unknownPtr, nint unknownPtr2); private Hook? shadowCascadeHook; private delegate void EvalDepthOfFieldDelegate(nint unknownPtr, nint unknownPtr2); private Hook? evalDepthOfFieldHook; private Patch swapMinimapFollowsCamera; private bool uiToggled = false; private Patch jmpOverUi; private bool disableCharacterFade = false; private Patch noopCharacterFade; private bool tripleShadowRes = false; private bool lowShadowDetailOverride = false; private Patch shadowRes1_3x; private Patch shadowRes1_1; private Patch shadowRes1_2; private Patch shadowRes1_3; private Patch shadowRes2; private Patch shadowRes3; private Patch shadowRes4_3x; private float offsetBiasBy = 0.0f; private float offsetRadiusBy = 0.0f; //private float offsetSampleNumBy = 0.0f; private bool applyShadowBias = false; private bool applyShadowRadius = false; //private bool applyShadowSampleNum = false; private float lastShadowBias = -1.0f; private float lastShadowRadius = -1.0f; private float lastShadowSampleNum = -1.0f; private nint lastShadowCascadeValue = -1; private void tripleShadowResEnable() { shadowRes1_3x.Enable(); shadowRes1_1.Enable(); shadowRes1_2.Enable(); shadowRes1_3.Enable(); shadowRes2.Enable(); shadowRes3.Enable(); shadowRes4_3x.Enable(); } private void tripleShadowResDisable() { shadowRes1_3x.Disable(); shadowRes1_1.Disable(); shadowRes1_2.Disable(); shadowRes1_3.Disable(); shadowRes2.Disable(); shadowRes3.Disable(); shadowRes4_3x.Disable(); } private float foliageLodBias; private float terrainLodBias; private float prevFoliageLodBias = 0.0f; private float prevTerrainLodBias = 0.0f; private float foliageLodFactor; private float terrainLodFactor; private bool disableLodLimits = false; private Patch defaultViewModeLodLimit; private Patch defaultViewModeLodLimit_1; private Patch defaultViewModeLodLimit_2; private void disableLodLimitsEnable() { defaultViewModeLodLimit.Enable(); defaultViewModeLodLimit_1.Enable(); defaultViewModeLodLimit_2.Enable(); } private void disableLodLimitsDisable() { defaultViewModeLodLimit.Disable(); defaultViewModeLodLimit_1.Disable(); defaultViewModeLodLimit_2.Disable(); } 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 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; #if SIMPLE_KEYBOARD_LAYER keyboardEnabled = config.EnableKeyboard; keyboardLookValue = config.KeyboardLookSensitivity; #endif enableCombo = config.EnableCombo; freeCameraCombo = Config.ParseCombo(config.FreeCameraCombo); typedCombo = config.FreeCameraCombo.Replace(",", "+"); disableComboButton1 = config.DisableComboButton1UnlessButton2Held; Config.Settings settings = config.CameraSettings; cameraSpeed = settings.CameraSpeed; cameraSpeedModifier = settings.CameraSpeedModifier; cameraSensitivity = settings.CameraSensitivity; cameraZoomSpeed = settings.CameraZoomSpeed; 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; disableFading = preset.DisableFading; } tripleShadowRes = config.TripleShadowResolution; offsetBiasBy = config.ShadowRangeOffset; offsetRadiusBy = config.ShadowRadiusOffset; applyShadowBias = config.ApplyShadowRange; applyShadowRadius = config.ApplyShadowRadius; lowShadowDetailOverride = config.HigherShadowDetailInHoarfrost; disableLodLimits = config.DisableLodLimits; 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 8B C4 48 89 58 10 48 89 70 18 55 57 41 54 41 56 41 57 48 8D 6C 24 80 48 81 EC 80 01 00 00 0F 29 70 C8 48 8B F9 0F 29 78 B8 0F 57 C9 44 0F 29 40 A8 0F 57 F6 44 0F 29 48 98 44 0F 29 50 88")); #if ADDR_ASSERTS Trace.Assert(addr == 0x141FB12D0); // nint #endif setCameraCutsceneHook = Hook.Create(addr, SetCameraCutsceneHook); 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); addr = PatternScanner.FindFirst(Pattern.FromString("48 89 5C 24 10 48 89 74 24 18 48 89 7C 24 20 55 41 56 41 57 48 8B EC 48 83 EC 30 48 8B FA 48 8B D9 E8 AA B6 C1 FF 48 8B 47 10 48 8D 57 50")); #if ADDR_ASSERTS Trace.Assert(addr == 0x141AB2260); // nint, nint #endif shadowCascadeHook = Hook.Create(addr, ShadowCascadeHook); addr = PatternScanner.FindFirst(Pattern.FromString("40 53 57 41 57 48 83 EC 20 80 B9 DD 01 00 00 00 4C 8B FA 48 8B D9 74 2B 48 8B 0D 99 0F DA 02 48 8B 01 FF 90 A8 00 00 00 84 C0 74 17 80 BB DE 01 00 00 00")); #if ADDR_ASSERTS Trace.Assert(addr == 0x1424233B0); // nint, nint #endif evalDepthOfFieldHook = Hook.Create(addr, EvalDepthOfFieldHook); 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, [0xE9]); // je -> jmp 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(0x142E4FE5C) == 3.0f); #endif shadowRes1_3x = new Patch(addr + 0x8, [0xD0, 0xFE, 0xA0, 0x02]); // 3.0 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 // It's hard to tell exactly what this value does. It appears to be labeled "iPrimaryShadowSampleNum", // but I may be reading the code wrong. Nevertheless, shadows in the Seliana smithy break if this value // isn't increased. The value is already multiplied by the shadow resolution so directly increasing // it here will result in an even bigger multiplier. Original value is 2816, with this offset and // Shadow Quality: High the result will be 15360. Going past some number around 16384 will crash // the game. I think it's related to N/64 >= 256. shadowRes4_3x = new Patch(addr + 0x29, [ 0xB8, 0x00, 0x14, 0x00, 0x00, 0xEB, 0x21 // This case is used for Low, Medium and High in-game. ]); if (tripleShadowRes && !disableMod) { tripleShadowResEnable(); } // Gameplay. addr = PatternScanner.FindFirst(Pattern.FromString("80 BB 34 EC 00 00 00 88 8B 31 EC 00 00 C6 83 30 EC 00 00 00 74 1C C6 83 34 EC 00 00 00 48 8B 0D 68 4B 4F 03 80 B9 58 02 00 00 00 75 05 E8 5A F5 01 00 48 8B CB E8 E2 05 00 00 83 BB DC EB 00 00 06 74 0A C7 83 DC EB 00 00 06 00 00 00")); #if ADDR_ASSERTS Trace.Assert(addr == 0x141B1A0B4); #endif defaultViewModeLodLimit = new Patch(addr + 0x40, [0x07, 0x74, 0x0A, 0xC7, 0x83, 0xDC, 0xEB, 0x00, 0x00, 0x07]); // In room. addr = PatternScanner.FindFirst(Pattern.FromString("8B 9F CC 00 00 00 81 FB F9 01 00 00 74 0C 8B CB E8 95 3F 65 01 83 F8 05 75 11 48 8B 0D 61 C8 F6 04 BA 02 00 00 00 E8 DF 2D 8C 01 48 8B 0D A0 C7 F6 04 8B D3")); #if ADDR_ASSERTS Trace.Assert(addr == 0x140257AE6); #endif defaultViewModeLodLimit_1 = new Patch(addr + 0x22, [0x07]); // Forging/Changing Equipment. addr = PatternScanner.FindFirst(Pattern.FromString("BA 02 00 00 00 E8 6E C0 80 00 48 8B 74 24 38 48 8B 57 28 48 8D 4F 30 E8 4C 79 B4 FE 48 8B 0D 0D FF A6 03 BA 04 00 00 00 E8 8B 64 3C 00 48 8B 05 D4 01 A7 03 80 B8 FD 46 01 00 00")); #if ADDR_ASSERTS Trace.Assert(addr == 0x141754438); #endif defaultViewModeLodLimit_2 = new Patch(addr + 0x24, [0x07]); if (disableLodLimits && !disableMod) { disableLodLimitsEnable(); } 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 } } // Credit to Otis_Inf. private bool getDisableFadingObjects(int viewportIndex) { Viewport vp = CameraSystem.GetViewport(viewportIndex); return MemoryUtil.Read(vp.Instance + 0x21) == 0; } private void setDisableFadingObjects(int viewportIndex, bool disable) { Viewport vp = CameraSystem.GetViewport(viewportIndex); MemoryUtil.WriteBytes(vp.Instance + 0x21, disable ? [0x0] : [0x1]); } private void setDisableCharacterFade(bool disable) { if (disable && !disableCharacterFade) { noopCharacterFade.Enable(); disableCharacterFade = true; } else if (!disable && disableCharacterFade) { noopCharacterFade.Disable(); disableCharacterFade = false; } } private bool areLodFactorsDefault() { return foliageLodBias == 3.0f && terrainLodBias == 3.0f && foliageLodFactor == 1.0f && terrainLodFactor == 1.0f; } private bool areLodFactorsSet() { nint baseAddr = MemoryUtil.Read(0x1451C4368); float foliageLod1 = MemoryUtil.Read(baseAddr + 0x21C); float terrainLod1 = MemoryUtil.Read(baseAddr + 0x220); float foliageLod2 = MemoryUtil.Read(baseAddr + 0x224); float terrainLod2 = MemoryUtil.Read(baseAddr + 0x228); return foliageLod1 == foliageLodBias && terrainLod1 == terrainLodBias && foliageLod2 == foliageLodFactor && terrainLod2 == terrainLodFactor; } private void setLodFactors(bool toggleOn) { nint baseAddr = MemoryUtil.Read(0x1451C4368); if (toggleOn) { if (prevFoliageLodBias <= 0.0f) { prevFoliageLodBias = MemoryUtil.Read(baseAddr + 0x21C); if (prevFoliageLodBias > 3.0f) prevFoliageLodBias = 3.0f; } MemoryUtil.WriteBytes(baseAddr + 0x21C, BitConverter.GetBytes(foliageLodBias)); if (prevTerrainLodBias <= 0.0f) { prevTerrainLodBias = MemoryUtil.Read(baseAddr + 0x220); if (prevTerrainLodBias > 3.0f) prevTerrainLodBias = 3.0f; } MemoryUtil.WriteBytes(baseAddr + 0x220, BitConverter.GetBytes(terrainLodBias)); MemoryUtil.WriteBytes(baseAddr + 0x224, BitConverter.GetBytes(foliageLodFactor)); MemoryUtil.WriteBytes(baseAddr + 0x228, BitConverter.GetBytes(terrainLodFactor)); } else { MemoryUtil.WriteBytes(baseAddr + 0x21C, BitConverter.GetBytes(prevFoliageLodBias)); MemoryUtil.WriteBytes(baseAddr + 0x220, BitConverter.GetBytes(prevTerrainLodBias)); MemoryUtil.WriteBytes(baseAddr + 0x224, BitConverter.GetBytes(1.0f)); MemoryUtil.WriteBytes(baseAddr + 0x228, BitConverter.GetBytes(1.0f)); } } public void OnLoad() { Config config = ConfigManager.GetConfig(this); foliageLodBias = config.FoliageLodBias; terrainLodBias = config.TerrainLodBias; foliageLodFactor = config.FoliageLodFactor; terrainLodFactor = config.TerrainLodFactor; if (config.ApplyLodFactors) { setLodFactors(true); } } public void OnUpdate(float deltaTime) { if (disableMod) return; #if HOOK_ORDER_ASSERTS Trace.Assert(hookOrder == 0 || hookOrder == 3); hookOrder = 0; if (frameTick == int.MaxValue) frameTick = 0; else frameTick++; debugLog($"OnUpdate() @ {frameTick}"); #endif #if SIMPLE_KEYBOARD_LAYER if (keyboardEnabled) { if (Input.IsPressed(Key.NumPad0)) { enableFreeCamera = !enableFreeCamera; } if (Input.IsPressed(Key.NumPadPeriod)) { if (uiToggled) { jmpOverUi.Disable(); } else { jmpOverUi.Enable(); } uiToggled = !uiToggled; } if (Input.IsPressed(Key.NumPadSlash)) { forceOffDof = !forceOffDof; } } #endif primaryPad = 0x0; } 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 static Quaternion getRight(Quaternion q) { return q * Quaternion.CreateFromYawPitchRoll((float)Math.PI, 0.0f, 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 = getRight(forward); 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; pos.Z += cameraZOffset; camera.Position = pos; if (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; unlockInputToggled = 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)); setDisableFadingObjects(vCameraViewportIndex, (vCameraViewportIndex == pCameraViewportIndex) ? disableFading : false); setDisableCharacterFade(disableFading); if (vCamera != null) { vCamera.NearClip = DEFAULT_NEAR_CLIP; } swapMinimapFollowsCamera.Disable(); } private double adjustedZoomSpeed(float deltaTime) { return cameraZoomSpeed * deltaTime * cameraFov; } private void updateFreeCamera(Camera camera) { float deltaTime = camera.DeltaTime; bool lockVerticalAndModifySpeed = buttonWasDown(Button.L2) || lockVerticalToggled; double adjustedSpeed = cameraSpeed * deltaTime * (lockVerticalAndModifySpeed ? cameraSpeedModifier : 1.0f); cameraFrameX = 0.0; cameraFrameY = 0.0; cameraFrameZ = 0.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 Ly = PadLy / (Int16.MaxValue / adjustedZoomSpeed(deltaTime)); 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; #if SIMPLE_KEYBOARD_LAYER if (keyboardEnabled) { if (Input.IsDown(Key.Up)) PadLy += Int16.MaxValue; if (Input.IsDown(Key.Down)) PadLy -= Int16.MaxValue; if (Input.IsDown(Key.Left)) PadLx -= Int16.MaxValue; if (Input.IsDown(Key.Right)) PadLx += Int16.MaxValue; } #endif double Lx = PadLx / (Int16.MaxValue / adjustedSpeed); double Ly = PadLy / (Int16.MaxValue / adjustedSpeed); Quaternion forward = getForward(cameraPosition, cameraTarget); // Invert diagonal directions when upside down for movement consistency. if (cameraWrapState == 1) { forward.X = -forward.X; forward.Z = -forward.Z; } if (lockVerticalAndModifySpeed) { forward.Y = 0.0f; } Quaternion right = forward; right.Y = 0.0f; right = getRight(right); forward = Quaternion.Normalize(forward); right = Quaternion.Normalize(right); cameraFrameX += right.X * Lx; cameraFrameZ += right.Z * Lx; cameraFrameX += forward.X * Ly; cameraFrameY += forward.Y * Ly; cameraFrameZ += forward.Z * Ly; if (plusForward != 0.0f) { cameraFrameX += forward.X * plusForward * deltaTime; cameraFrameY += forward.Y * plusForward * deltaTime; cameraFrameZ += forward.Z * plusForward * deltaTime; } } if (!unlockInputToggled) { if (buttonWasDown(Button.Share)) { if (buttonWasPressed(Button.Square)) { if (camera.NearClip == 0.5f) { camera.NearClip = DEFAULT_NEAR_CLIP; } else { camera.NearClip = 0.5f; } } } if (buttonWasDown(Button.Triangle)) { if (buttonWasPressed(Button.Left) || buttonWasPressed(Button.Right)) { cameraRoll = (preDetachRoll != null) ? (double)preDetachRoll : 0.0f; } if (buttonWasPressed(Button.Up)) { cameraFov = (preDetachFov != null) ? (double)preDetachFov : DEFAULT_FOV; } } else { if (buttonWasDown(Button.Up)) { cameraFrameY += adjustedSpeed / 2.0; } if (buttonWasDown(Button.Down)) { cameraFrameY -= adjustedSpeed / 2.0; } if (buttonWasDown(Button.Left)) { cameraRoll -= adjustedSpeed / 4.0; } if (buttonWasDown(Button.Right)) { cameraRoll += adjustedSpeed / 4.0; } } } #if SIMPLE_KEYBOARD_LAYER if (keyboardEnabled) { if (Input.IsDown(Key.NumPadMinus)) { cameraFov = Math.Clamp(cameraFov - adjustedZoomSpeed(deltaTime), 1.0, 179.0); } if (Input.IsDown(Key.NumPadPlus)) { cameraFov = Math.Clamp(cameraFov + adjustedZoomSpeed(deltaTime), 1.0, 179.0); } if (Input.IsPressed(Key.NumPadStar)) { cameraRoll = (preDetachRoll != null) ? (double)preDetachRoll : 0.0f; } if (Input.IsDown(Key.NumPad7)) { cameraFrameY += adjustedSpeed / 2.0; } if (Input.IsDown(Key.NumPad1)) { cameraFrameY -= adjustedSpeed / 2.0; } if (Input.IsDown(Key.NumPad9)) { cameraRoll += adjustedSpeed / 4.0; } if (Input.IsDown(Key.NumPad3)) { cameraRoll -= adjustedSpeed / 4.0; } } #endif // 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; #if SIMPLE_KEYBOARD_LAYER if (keyboardEnabled) { if (Input.IsDown(Key.NumPad8)) PadRy += keyboardLookValue; if (Input.IsDown(Key.NumPad2)) PadRy -= keyboardLookValue; if (Input.IsDown(Key.NumPad4)) PadRx -= keyboardLookValue; if (Input.IsDown(Key.NumPad6)) PadRx += keyboardLookValue; } #endif 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 < 0.0) { cameraRoll += 360.0; } else if (cameraRoll >= 360.0) { cameraRoll -= 360.0; } if (plusRight != 0.0) { cameraYaw += plusRight * deltaTime; } cameraPosition.X += (float)cameraFrameX; cameraPosition.Y += (float)cameraFrameY; cameraPosition.Z += (float)cameraFrameZ; // 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 Player? checkPlayerChange() { Player? player = Player.MainPlayer; if (player != lastPlayer) { pCamera = null; pCameraViewportIndex = -1; lastPlayer = player; } return player; } 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(Player? player) { int prevIndex = vCameraViewportIndex; vCameraViewportIndex = getVisibleCamera(); if (vCameraViewportIndex >= 0) { Camera camera = CameraSystem.GetViewport(vCameraViewportIndex).Camera!; // Evaluate potential free camera target change. if (freeCamera) { if (prevIndex != vCameraViewportIndex) { if (prevIndex >= 0) { setDisableFadingObjects(prevIndex, (prevIndex == pCameraViewportIndex) ? disableFading : false); } setDisableFadingObjects(vCameraViewportIndex, true); } if (vCamera != camera) { // We're assuming vCamera isn't an invalid pointer, but there's no assurance of that. if (vCamera != null) { camera.NearClip = vCamera.NearClip; vCamera.NearClip = DEFAULT_NEAR_CLIP; } 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; // Assume that after a player is set the visible camera is the player camera. if (pCamera == null && player != null) { pCamera = vCamera; pCameraViewportIndex = vCameraViewportIndex; setDisableFadingObjects(pCameraViewportIndex, disableFading); } } else { vCamera = null; pCamera = null; pCameraViewportIndex = -1; } } 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({unknownPtr:x}) @ {frameTick}"); Trace.Assert(hookOrder == 0); hookOrder = 1; #endif Player? player = checkPlayerChange(); checkCurrentVisibleCamera(player); 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({unknownPtr:x}) @ {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) { // 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 after calculateCameraHook.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 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; setDisableFadingObjects(vCameraViewportIndex, true); setDisableCharacterFade(true); 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({unknownPtr:x}) @ {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); } } private void SetCameraCutsceneHook(nint unknownPtr) { if (disableMod) { setCameraCutsceneHook!.Original(unknownPtr); return; } #if HOOK_ORDER_ASSERTS debugLog($"SetCameraCutsceneHook({unknownPtr:x}) @ {frameTick}"); #endif setCameraCutsceneHook!.Original(unknownPtr); if (freeCamera && vCamera != null) { vCamera.Position = cameraPosition; vCamera.Target = cameraTarget; vCamera.FieldOfView = (float)cameraFov; setCameraRoll(vCamera); } } private void setPerspectivePreset(Config.Preset preset) { cameraFov = preset.FOV; cameraForward = preset.Forward; cameraRight = preset.Right; cameraXOffset = 0.0f; cameraYOffset = preset.Up; cameraZOffset = 0.0f; cameraRoll = preset.Roll; if (preDetachRoll != null) { preDetachRoll = cameraRoll; } disableFading = preset.DisableFading; if (pCameraViewportIndex >= 0) { setDisableFadingObjects(pCameraViewportIndex, disableFading); } setDisableCharacterFade(disableFading); } // This can undoubtedly be simplified. private void WritePadInputHook(nint unknownPtr, nint unknownPtr2, nint unknownPtr3) { // Assumes the primary pad always comes first. if (primaryPad == 0x0) { primaryPad = unknownPtr2; } if (disableMod || unknownPtr2 != primaryPad) { writePadInputHook!.Original(unknownPtr, unknownPtr2, unknownPtr3); return; } #if HOOK_ORDER_ASSERTS debugLog($"WritePadInputHook({unknownPtr:x}, {unknownPtr2:x}, {unknownPtr3:x}) @ {frameTick}"); #endif Button b1 = 0u, b2 = 0u; uint PadDown = 0u, PadTrg = 0u, PadRel = 0u, PadChg = 0u; // PadOld = 0x19C if (enableCombo && freeCameraCombo != null) { b1 = freeCameraCombo[0]; b2 = freeCameraCombo[1]; if (disableComboButton1 && comboButton1Down) { PadDown = MemoryUtil.Read(controllerAddr() + 0x198); PadDown |= (uint)b1; MemoryUtil.WriteBytes(controllerAddr() + 0x198, BitConverter.GetBytes(PadDown)); } } writePadInputHook!.Original(unknownPtr, unknownPtr2, unknownPtr3); bool readInputsPostHook = false; if (enableCombo && freeCameraCombo != null) { if (disableComboButton1) { if (!readInputsPostHook) { PadDown = MemoryUtil.Read(controllerAddr() + 0x198); PadTrg = MemoryUtil.Read(controllerAddr() + 0x1A0); PadRel = MemoryUtil.Read(controllerAddr() + 0x1A4); PadChg = MemoryUtil.Read(controllerAddr() + 0x1A8); prevPadDown = (prevPadDown == null) ? PadDown : lastPadDown; lastPadDown = PadDown; readInputsPostHook = true; } uint b1u = (uint)b1; if (comboButton1Down || enableFreeCamera || !buttonWasDown(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 (!readInputsPostHook) { PadDown = MemoryUtil.Read(controllerAddr() + 0x198); prevPadDown = (prevPadDown == null) ? PadDown : lastPadDown; lastPadDown = PadDown; } if ((comboButton1Down || (!disableComboButton1 && buttonWasDown(b1)))) { if (buttonWasPressed(b2)) { enableFreeCamera = !enableFreeCamera; } int presetSelect = (buttonWasPressed(Button.Up) ? -1 : 0) + (buttonWasPressed(Button.Down) ? 1 : 0); if (presetSelect != 0) { Config config = ConfigManager.GetConfig(this); Dictionary.KeyCollection presetKeys = config.Presets.Keys; if (presetKeys.Count > 0) { if (config.Selected != "") { int selectedIndex; for (selectedIndex = 0; selectedIndex < presetKeys.Count; selectedIndex++) { string presetKey = presetKeys.ElementAt(selectedIndex); if (presetKey == config.Selected) break; } selectedIndex += presetSelect; if (selectedIndex >= presetKeys.Count) { selectedIndex -= presetKeys.Count; } else if (selectedIndex < 0) { selectedIndex += presetKeys.Count; } config.Selected = presetKeys.ElementAt(selectedIndex); } else { config.Selected = presetKeys.ElementAt((presetSelect == 1) ? 0 : presetKeys.Count - 1); } setPerspectivePreset(config.Presets[config.Selected]); ConfigManager.SaveConfig(this); } } } if (unlockMovementHeld && buttonWasReleased(Button.R2)) { unlockMovementHeld = false; unlockMovementPause = false; } if (enableFreeCamera) { bool blockInput = !unlockInputToggled; // Hold both buttons and release either. We have to block until // release or it will get annoying to toggle input while on a menu. if (buttonWasDown(Button.R2)) { if (buttonWasReleased(Button.R1)) { unlockInputToggled = !unlockInputToggled; inputBlockedForToggle = false; blockInput = true; } else if (buttonWasDown(Button.R1)) { inputBlockedForToggle = true; blockInput = true; } } else if (buttonWasReleased(Button.R2) && inputBlockedForToggle) { unlockInputToggled = !unlockInputToggled; inputBlockedForToggle = false; blockInput = true; } if (blockInput) { 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; uint StartSelectMask = (uint)Button.Options | (uint)Button.Share; uint BumperMask = (uint)Button.L1 | (uint)Button.R1; uint StickMask = (uint)Button.LsUp | (uint)Button.LsDown | (uint)Button.LsLeft | (uint)Button.LsRight | (uint)Button.RsUp | (uint)Button.RsDown | (uint)Button.RsLeft | (uint)Button.RsRight; uint Mask = FaceButtonMask | DPadMask | StartSelectMask | BumperMask | StickMask; if (enableCombo && freeCameraCombo != null) { Mask &= ~((uint)b1 | (uint)b2); } PadDown &= ~Mask; PadTrg &= ~Mask; PadRel &= ~Mask; PadChg &= ~Mask; MemoryUtil.WriteBytes(controllerAddr() + 0x198, BitConverter.GetBytes(PadDown)); MemoryUtil.WriteBytes(controllerAddr() + 0x1A0, BitConverter.GetBytes(PadTrg)); MemoryUtil.WriteBytes(controllerAddr() + 0x1A0, BitConverter.GetBytes(PadRel)); 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)); } if (!unlockMovementHeld && buttonWasPressed(Button.R2)) { unlockMovementHeld = true; } if (unlockMovementHeld) { if (buttonWasPressed(Button.L1)) { unlockMovementToggled = !unlockMovementToggled; } // Temporarily disable for zoom mode. if (buttonWasDown(Button.L2)) { unlockMovementPause = true; } else if (buttonWasReleased(Button.L2)) { unlockMovementPause = false; } } if (buttonWasDown(Button.L2) && buttonWasPressed(Button.L1)) { lockVerticalToggled = !lockVerticalToggled; } if (blockInput) { if (buttonWasDown(Button.Triangle)) { if (buttonWasPressed(Button.Down)) { if (Player.MainPlayer != null) { // Y - 150 to approximately align the players head with the camera. Player.MainPlayer.Position = new Vector3(cameraPosition.X, cameraPosition.Y - 150.0f, cameraPosition.Z); } } } if (buttonWasDown(Button.Share)) { if (buttonWasPressed(Button.Cross)) { if (uiToggled) { jmpOverUi.Disable(); } else { jmpOverUi.Enable(); } uiToggled = !uiToggled; } if (buttonWasPressed(Button.Circle)) { forceOffDof = !forceOffDof; } } } } } private float CheckMovementHook(int stickValue) { if (disableMod) { return checkMovementHook!.Original(stickValue); } #if HOOK_ORDER_ASSERTS debugLog($"CheckMovementHook({stickValue}) @ {frameTick}"); #endif if (freeCamera && !((unlockMovementHeld || unlockMovementToggled) && !unlockMovementPause)) { stickValue = 0; } return checkMovementHook!.Original(stickValue); } private void ShadowCascadeHook(nint unknownPtr, nint unknownPtr2) { if (disableMod) { shadowCascadeHook!.Original(unknownPtr, unknownPtr2); return; } #if HOOK_ORDER_ASSERTS debugLog($"ShadowCascadeHook({unknownPtr:x}, {unknownPtr2:x}) @ {frameTick}"); #endif nint rax = MemoryUtil.Read(unknownPtr2 + 0x170); nint shadowCascadeValue = rax >> 32; float biasOverride = MemoryUtil.Read(unknownPtr2 + 0x17C); lastShadowBias = biasOverride; biasOverride += offsetBiasBy; if (lowShadowDetailOverride) { if (shadowCascadeValue == 1) { biasOverride *= 2.0f; MemoryUtil.WriteBytes(unknownPtr2 + 0x170, BitConverter.GetBytes((shadowCascadeValue + 1) << 32 | (rax & 0x00000000FFFFFFFF))); } } if (shadowCascadeValue != 3) { lastShadowCascadeValue = shadowCascadeValue; lastShadowBias = MemoryUtil.Read(unknownPtr2 + 0x17C); if (applyShadowBias) { MemoryUtil.WriteBytes(unknownPtr2 + 0x17C, BitConverter.GetBytes(biasOverride)); } lastShadowRadius = MemoryUtil.Read(unknownPtr2 + 0x1EC); if (applyShadowRadius) { MemoryUtil.WriteBytes(unknownPtr2 + 0x1EC, BitConverter.GetBytes(lastShadowRadius + offsetRadiusBy)); } lastShadowSampleNum = MemoryUtil.Read(unknownPtr2 + 0x1E4); /* if (applyShadowSampleNum) { MemoryUtil.WriteBytes(unknownPtr2 + 0x1E4, BitConverter.GetBytes(lastShadowSampleNum + offsetSampleNumBy)); } */ } shadowCascadeHook!.Original(unknownPtr, unknownPtr2); } private void EvalDepthOfFieldHook(nint unknownPtr, nint unknownPtr2) { if (disableMod || (skipDofHook = !skipDofHook)) { evalDepthOfFieldHook!.Original(unknownPtr, unknownPtr2); return; } #if HOOK_ORDER_ASSERTS debugLog($"EvalDepthOfFieldHook({unknownPtr}, {unknownPtr2}) @ {frameTick}"); #endif // Possibly of interest: +0x1DE, +0x1DF. // We're assuming the previous dofPointer will still be valid, but that might not be true. if (dofPointer != 0x0 && unknownPtr != dofPointer && wasDofAllowed != null) { MemoryUtil.WriteBytes(dofPointer + 0x1DD, ((bool)wasDofAllowed) ? [0x1] : [0x0]); wasDofAllowed = null; } dofPointer = unknownPtr; if (!forceOffDof) { if (wasDofAllowed != null) { MemoryUtil.WriteBytes(dofPointer + 0x1DD, ((bool)wasDofAllowed) ? [0x1] : [0x0]); wasDofAllowed = null; } } else { if (wasDofAllowed == null) { wasDofAllowed = MemoryUtil.Read(dofPointer + 0x1DD) == 0x1; } MemoryUtil.WriteBytes(dofPointer + 0x1DD, [0x0]); } evalDepthOfFieldHook!.Original(unknownPtr, unknownPtr2); } private void disableAllCollisionHooks() { if (disableExtraGravity) { disableExtraGravity = false; disableExtraGravityDisable(); } if (disableGravity) { disableGravity = false; disableGravityDisable(); } if (disableExtraCollision) { disableExtraCollision = false; disableExtraCollisionDisable(); } if (disableCollision) { disableCollision = false; disableCollisionDisable(); } } private void drawViewportInfo(int i, float width, Config config, bool debug) { Viewport vp = CameraSystem.GetViewport(i); if (debug) { 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}"); } else { ImGui.Text($"Resolution: {vp.Region.Width}x{vp.Region.Height}"); } if (vp.Visible) { bool disableFadingObjects = getDisableFadingObjects(i); if (ImGui.Checkbox("Disable Fading Objects When Close to Camera", ref disableFadingObjects)) { setDisableFadingObjects(i, disableFadingObjects); } if (ImGui.BeginItemTooltip()) { ImGui.Text("Credit to Otis_Inf."); ImGui.EndTooltip(); } } if (vp.Camera != null) { Camera camera = vp.Camera; bool usedByFreeCamera = freeCamera && enableFreeCamera && camera == vCamera; if (debug) ImGui.Text($"Camera Pointer: {camera.Instance:x}"); if (ImGui.DragFloat("FOV", ref camera.FieldOfView, 0.1f)) { if (usedByFreeCamera) { cameraFov = camera.FieldOfView; } } 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(); } } if (debug) ImGui.InputFloat("Aspect Ratio", ref camera.AspectRatio); ImGui.InputFloat("Near Clip", ref camera.NearClip); ImGui.InputFloat("Far Clip", ref camera.FarClip); if (usedByFreeCamera) { ImGui.DragFloat3("Position", ref cameraPosition, 0.425f); } else { ImGui.DragFloat3("Position", ref camera.Position, 0.425f); } ImGui.DragFloat3("Target", ref camera.Target, 0.425f); ImGui.DragFloat3("Up", ref camera.Up, 0.425f); 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 (debug) { bool move = camera.Move; if (ImGui.Checkbox("Move", ref move)) { camera.Move = move; } if (ImGui.BeginItemTooltip()) { ImGui.Text("Uncheck this to allow manually overriding the camera position.\nIf your camera is frozen, make sure this is checked."); ImGui.EndTooltip(); } ImGui.SameLine(); bool fix = camera.Fix; if (ImGui.Checkbox("Fix", ref fix)) { camera.Fix = fix; } } if (camera == vCamera) { if (!usedByFreeCamera) { ImGui.PushItemFlag(ImGuiItemFlags.Disabled, true); } // If our camera hooks aren't being run, these values won't be updated. float yaw = (float)cameraYaw; if (ImGui.DragFloat("Yaw", ref yaw, 0.2f)) { cameraYaw = yaw; } float pitch = (float)cameraPitch; if (ImGui.DragFloat("Pitch", ref pitch, 0.2f)) { cameraPitch = pitch; } float offsetRoll = (float)cameraRoll; if (ImGui.DragFloat("Roll", ref offsetRoll, 0.2f)) { cameraRoll = offsetRoll; } if (!usedByFreeCamera) { ImGui.PopItemFlag(); } // Try to grey out offsets when they probably have no effect. if ((camera == pCamera && !usedByFreeCamera) && camera.Move) { ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); } ImGui.Text("Offsets"); float offsetForward = 0.0f; if (ImGui.DragFloat("Forward", ref offsetForward, 0.425f)) { 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; if (usedByFreeCamera) cameraPosition = camera.Position; } float offsetRight = 0.0f; if (ImGui.DragFloat("Right", ref offsetRight, 0.425f)) { Quaternion forward = getForward(camera.Position, camera.Target); Quaternion right = getRight(forward); forward = Quaternion.Normalize(forward); right = Quaternion.Normalize(right); camera.Position.X += right.X * offsetRight; camera.Position.Z += right.Z * offsetRight; if (usedByFreeCamera) cameraPosition = camera.Position; } float offsetUp = 0.0f; if (ImGui.DragFloat("Up", ref offsetUp, 0.425f)) { camera.Position.Y += offsetUp; if (usedByFreeCamera) cameraPosition = camera.Position; } if (camera == pCamera && !(freeCamera || enableFreeCamera)) { ImGui.PopStyleVar(); } } } } public void OnImGuiRender() { #if HOOK_ORDER_ASSERTS debugLog($"OnImGuiRender() @ {frameTick}"); #endif Config config = ConfigManager.GetConfig(this); float width = ImGui.GetWindowWidth(); // If we use vCamera or pCamera here, we need to verify they're not invalid. Player? player = checkPlayerChange(); checkCurrentVisibleCamera(player); 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; } setDisableFadingObjects(i, false); } if (player != null) { player.Rotation.X = 0.0f; player.Rotation.Z = 0.0f; } if (areLodFactorsSet() && !areLodFactorsDefault()) { setLodFactors(false); } if (tripleShadowRes) { tripleShadowResDisable(); } if (disableLodLimits) { disableLodLimitsDisable(); } if (largerFoliageSwayRange) { addressHigherValueForFoliageSway.Disable(); } if (disableCharacterFade) { noopCharacterFade.Disable(); } } else { if (config.ApplyLodFactors && !areLodFactorsDefault()) { setLodFactors(true); } if (tripleShadowRes) { tripleShadowResEnable(); } if (disableLodLimits) { disableLodLimitsEnable(); } if (largerFoliageSwayRange) { addressHigherValueForFoliageSway.Enable(); } if (pCameraViewportIndex >= 0) { setDisableFadingObjects(pCameraViewportIndex, disableFading); } 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"); ImGui.PushItemWidth(width * 0.575f); 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 (freeCamera) { ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); } ImGui.PopItemWidth(); if (ImGui.Checkbox("Disable Fading", ref disableFading)) { if (pCameraViewportIndex >= 0) { setDisableFadingObjects(pCameraViewportIndex, disableFading); } setDisableCharacterFade(disableFading); } if (ImGui.BeginItemTooltip()) { ImGui.Text("Disable transparency fading when the camera gets close to something.\nApplies to Objects, Players, Palicos and NPCs. Credit to Otis_Inf for Objects.\nEnabled by default while in free camera."); ImGui.EndTooltip(); } if (freeCamera) { ImGui.PopStyleVar(); } 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; } disableFading = false; if (pCameraViewportIndex >= 0) { setDisableFadingObjects(pCameraViewportIndex, false); } setDisableCharacterFade(false); 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.DisableFading = (pCameraViewportIndex >= 0) ? getDisableFadingObjects(pCameraViewportIndex) : false; 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; setPerspectivePreset(config.Presets[config.Selected]); ConfigManager.SaveConfig(this); } 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.PushItemWidth(width * 0.575f); ImGui.DragFloat("Camera Speed", ref cameraSpeed, 0.01f, 0.0f); if (ImGui.BeginItemTooltip()) { ImGui.Text("Movement speed in free camera."); ImGui.EndTooltip(); } ImGui.DragFloat("Speed Modifier", ref cameraSpeedModifier, 0.01f, 0.0f); if (ImGui.BeginItemTooltip()) { ImGui.Text("Value to multiply speed by when LT is held."); ImGui.EndTooltip(); } ImGui.DragFloat("Camera Sensitivity", ref cameraSensitivity, 0.0025f, 0.0f); if (ImGui.BeginItemTooltip()) { ImGui.Text("Look sensitivity in free camera."); ImGui.EndTooltip(); } ImGui.DragFloat("Zoom Speed", ref cameraZoomSpeed, 0.01f, 0.0f); 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.DragInt("Stick Deadzone", ref stickDeadzone, 5, 0); ImGui.PopItemWidth(); if (ImGui.Button("Default")) { cameraSpeed = Config.Settings.DEFAULT_SPEED; cameraSpeedModifier = Config.Settings.DEFAULT_SPEED_MODIFIER; cameraSensitivity = Config.Settings.DEFAULT_SENSITIVITY; cameraZoomSpeed = Config.Settings.DEFAULT_ZOOM_SPEED; 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; cameraSpeedModifier = settings.CameraSpeedModifier; cameraSensitivity = settings.CameraSensitivity; cameraZoomSpeed = settings.CameraZoomSpeed; cameraPitchLimit = settings.CameraPitchLimit; stickDeadzone = settings.StickDeadzone; } ImGui.SameLine(); if (ImGui.Button("Save")) { Config.Settings settings = config.CameraSettings; settings.CameraSpeed = cameraSpeed; settings.CameraSpeedModifier = cameraSpeedModifier; settings.CameraSensitivity = cameraSensitivity; settings.CameraZoomSpeed = cameraZoomSpeed; settings.CameraPitchLimit = cameraPitchLimit; settings.StickDeadzone = stickDeadzone; config.CameraSettings = settings; ConfigManager.SaveConfig(this); } ImGui.PopID(); ImGui.Separator(); ImGui.PushID("Toggles"); ImGui.Text("Toggles"); if (!freeCamera) { ImGui.PushStyleVar(ImGuiStyleVar.Alpha, ImGui.GetStyle().Alpha * 0.5f); } ImGui.Checkbox("Unlock Input to the Game", ref unlockInputToggled); if (ImGui.BeginItemTooltip()) { ImGui.Text("Hold RT + RB, Release Either."); ImGui.EndTooltip(); } ImGui.Checkbox("Unlock Player Movement", ref unlockMovementToggled); if (ImGui.BeginItemTooltip()) { ImGui.Text("Hold RT + Press LB."); ImGui.EndTooltip(); } ImGui.Checkbox("Lock Vertical Movement and Apply Speed Modifier", ref lockVerticalToggled); if (ImGui.BeginItemTooltip()) { ImGui.Text("Hold LT + Press LB."); 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("Hold Select + Press A. The scoutfly marker on menus will still be visible."); ImGui.EndTooltip(); } ImGui.Checkbox("Force Depth of Field Off", ref forceOffDof); if (ImGui.BeginItemTooltip()) { ImGui.Text("Hold Select + Press B. Works to disable depth of field in cutscenes. Credit to Otis_Inf."); ImGui.EndTooltip(); } if (vCamera != null) { bool reducedNearClip = vCamera.NearClip == 0.5f; if (ImGui.Checkbox("Reduce Near Clip", ref reducedNearClip)) { if (reducedNearClip) { vCamera.NearClip = 0.5f; } else { vCamera.NearClip = DEFAULT_NEAR_CLIP; } } if (ImGui.BeginItemTooltip()) { ImGui.Text("Hold Select + Press X. Allows you to get the camera closer to things without clipping but breaks rendering farther from the camera."); ImGui.EndTooltip(); } } ImGui.PopID(); ImGui.Separator(); ImGui.Text("When Close to the Camera"); if (vCameraViewportIndex >= 0) { bool disableFadingObjects = getDisableFadingObjects(vCameraViewportIndex); if (ImGui.Checkbox("Disable Fading Objects", ref disableFadingObjects)) { setDisableFadingObjects(vCameraViewportIndex, disableFadingObjects); } } if (ImGui.Checkbox("Disable Fading Player/Palico/NPCs", ref disableCharacterFade)) { if (disableCharacterFade) { noopCharacterFade.Enable(); } else { noopCharacterFade.Disable(); } } ImGui.Separator(); 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.SameLine(); if (ImGui.Checkbox("Disable Button 1 Unless Button 2 is Held", ref disableComboButton1)) { comboButton1Down = false; config.DisableComboButton1UnlessButton2Held = disableComboButton1; ConfigManager.SaveConfig(this); } if (ImGui.BeginItemTooltip()) { ImGui.Text("For example, if bound to \"RStick+LT\", right stick presses will ignored by the game unless you're holding the left trigger.\nIn that case, it can let you avoid switching the targeted monster when toggling free camera."); ImGui.EndTooltip(); } ImGui.Text("While in Free Camera"); if (ImGui.BeginItemTooltip()) { ImGui.Text("Set Camera Mouse Controls to Off in Options -> Camera for a better experience."); ImGui.EndTooltip(); } if (ImGui.BeginTable("Binds", 2, ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.Borders)) { ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); ImGui.Text("Hold RT (+ Press LB; Toggle)"); ImGui.TableSetColumnIndex(1); ImGui.Text("Unlock Player Movement and Freeze Camera Movement"); ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); ImGui.Text("Hold LT (+ Press LB; Toggle)"); ImGui.TableSetColumnIndex(1); ImGui.Text("Lock Vertical Camera Movement and Apply Speed Modifier"); ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); ImGui.Text("Hold RT + RB, Release Either"); if (ImGui.BeginItemTooltip()) { ImGui.Text("Can effectively be treated as press RT + RB, but you probably want to press RT first in you're on a menu."); ImGui.EndTooltip(); } ImGui.TableSetColumnIndex(1); ImGui.Text("Toggle Unlock Input to the Game"); if (ImGui.BeginItemTooltip()) { ImGui.Text("Applies to every controller input except left stick player movement."); ImGui.EndTooltip(); } ImGui.EndTable(); } ImGui.Text("Disabled While Input to the Game is Unlocked"); if (ImGui.BeginTable("More Binds", 2, ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.Borders)) { ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); ImGui.Text("Hold Select + Press A"); ImGui.TableSetColumnIndex(1); ImGui.Text("Toggle UI"); ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); ImGui.Text("Hold Select + Press B"); ImGui.TableSetColumnIndex(1); ImGui.Text("Toggle Depth of Field"); ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); ImGui.Text("Hold Select + Press X"); ImGui.TableSetColumnIndex(1); ImGui.Text("Toggle Reduced Near Clip (16.0 -> 0.5)"); ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); ImGui.Text("D-Pad Up/Down"); ImGui.TableSetColumnIndex(1); ImGui.Text("Translate Camera Up/Down"); ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); ImGui.Text("D-Pad Left/Right"); ImGui.TableSetColumnIndex(1); ImGui.Text("Roll Camera"); ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); ImGui.Text("Hold Y + Press D-Pad Left/Right"); ImGui.TableSetColumnIndex(1); ImGui.Text("Reset Roll"); ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); ImGui.Text("Hold LT + RT"); ImGui.TableSetColumnIndex(1); ImGui.Text("Zoom with Left Stick Up/Down"); if (ImGui.BeginItemTooltip()) { ImGui.Text("If you have DOF (Depth of Field) enabled in Advanced Graphics Settings, this will also move the focal point."); ImGui.EndTooltip(); } ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); ImGui.Text("Hold Y + Press D-Pad Up"); ImGui.TableSetColumnIndex(1); ImGui.Text("Reset Zoom"); ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); ImGui.Text("Hold Y + Press D-Pad Down"); ImGui.TableSetColumnIndex(1); ImGui.Text("Teleport Player to Camera Position"); ImGui.EndTable(); } } ImGui.PopID(); #if SIMPLE_KEYBOARD_LAYER ImGui.PushID("Keyboard"); if (ImGui.CollapsingHeader("Keyboard")) { if (ImGui.Checkbox("Enable Keyboard", ref keyboardEnabled)) { config.EnableKeyboard = keyboardEnabled; ConfigManager.SaveConfig(this); } ImGui.PushItemWidth(width * 0.20f); if (ImGui.DragInt("Keyboard Look Sensitivity", ref keyboardLookValue, 20, 0, Int16.MaxValue)) { config.KeyboardLookSensitivity = keyboardLookValue; ConfigManager.SaveConfig(this); } if (ImGui.BeginItemTooltip()) { ImGui.Text("Value that gets sent as a simulated joystick movement. Range: [0, 32767]."); ImGui.EndTooltip(); } ImGui.PopItemWidth(); if (ImGui.BeginTable("Keyboard Binds", 2, ImGuiTableFlags.SizingStretchProp | ImGuiTableFlags.Borders)) { ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); ImGui.Text("Num 0"); ImGui.TableSetColumnIndex(1); ImGui.Text("Toggle Free Camera"); ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); ImGui.Text("Num 8/2/4/6"); ImGui.TableSetColumnIndex(1); ImGui.Text("Look"); ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); ImGui.Text("Up/Down/Left/Right"); ImGui.TableSetColumnIndex(1); ImGui.Text("Move Camera"); ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); ImGui.Text("Num 7/1"); ImGui.TableSetColumnIndex(1); ImGui.Text("Translate Up/Down"); ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); ImGui.Text("Num 9/3"); ImGui.TableSetColumnIndex(1); ImGui.Text("Roll Camera"); ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); ImGui.Text("Num *"); ImGui.TableSetColumnIndex(1); ImGui.Text("Reset Roll"); ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); ImGui.Text("Num -/+"); ImGui.TableSetColumnIndex(1); ImGui.Text("Zoom"); ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); ImGui.Text("Num ."); ImGui.TableSetColumnIndex(1); ImGui.Text("Toggle UI"); ImGui.TableNextRow(); ImGui.TableSetColumnIndex(0); ImGui.Text("Num /"); ImGui.TableSetColumnIndex(1); ImGui.Text("Toggle Depth of Field"); ImGui.EndTable(); } } ImGui.PopID(); #endif ImGui.PushID("Player"); if (ImGui.CollapsingHeader("Player") && player != null) { 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); 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; } 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 is 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 falling when going out of bounds and/or rotating."); ImGui.EndTooltip(); } if (!disableGravity) { ImGui.PopItemFlag(); ImGui.PopStyleVar(); } } ImGui.PopID(); ImGui.PushID("Viewport"); if (ImGui.CollapsingHeader("Viewport") && vCameraViewportIndex >= 0) { drawViewportInfo(vCameraViewportIndex, width, config, false); } ImGui.PopID(); ImGui.PushID("Graphics"); if (ImGui.CollapsingHeader("Graphical Enhancements")) { if (ImGui.Checkbox("3x Shadow Resolution (Requires Reload)", ref tripleShadowRes)) { if (tripleShadowRes) { tripleShadowResEnable(); } else { tripleShadowResDisable(); } config.TripleShadowResolution = tripleShadowRes; ConfigManager.SaveConfig(this); } if (ImGui.BeginItemTooltip()) { ImGui.Text("In-Game Settings:\n - High: 1.0 -> 3.0\n - Mid: 0.7 -> 2.1\n - Low: 0.5 -> 1.5\nReload shadows by toggling Shadow Quality in Options -> Display -> Advanced Graphics Settings."); ImGui.EndTooltip(); } ImGui.PushItemWidth(width * 0.125f); if (ImGui.Checkbox("##Apply Larger Shadow Range", ref applyShadowBias)) { config.ApplyShadowRange = applyShadowBias; ConfigManager.SaveConfig(this); } ImGui.SameLine(); if (ImGui.InputFloat("Shadow Range", ref offsetBiasBy, 0.0f, 0.0f, null, ImGuiInputTextFlags.EnterReturnsTrue)) { config.ShadowRangeOffset = offsetBiasBy; ConfigManager.SaveConfig(this); } if (ImGui.BeginItemTooltip()) { ImGui.Text("This will inversely reduce the detail of shadows closer to the camera (like the player shadow), so it's a trade-off.\nValue is an offset. Recommended: 0.5."); ImGui.EndTooltip(); } if (ImGui.Checkbox("##Apply Radius", ref applyShadowRadius)) { config.ApplyShadowRadius = applyShadowRadius; ConfigManager.SaveConfig(this); } ImGui.SameLine(); if (ImGui.InputFloat("Shadow Radius", ref offsetRadiusBy, 0.0f, 0.0f, "%.5f", ImGuiInputTextFlags.EnterReturnsTrue)) { config.ShadowRadiusOffset = offsetRadiusBy; ConfigManager.SaveConfig(this); } if (ImGui.BeginItemTooltip()) { ImGui.Text("Amount of softening for realtime shadows.\nValue is an offset. Recommended: 0.0005."); ImGui.EndTooltip(); } /* ImGui.Checkbox("##Apply SampleNum", ref applyShadowSampleNum); ImGui.SameLine(); ImGui.InputFloat("Offset Shadow SampleNum", ref offsetSampleNumBy, 0.0f, 0.0f, null, ImGuiInputTextFlags.EnterReturnsTrue); */ ImGui.PopItemWidth(); if (ImGui.Checkbox("Higher Shadow Detail in Hoarfrost Reach/Seliana Gathering Hub", ref lowShadowDetailOverride)) { config.HigherShadowDetailInHoarfrost = lowShadowDetailOverride; ConfigManager.SaveConfig(this); } if (ImGui.BeginItemTooltip()) { ImGui.Text("Disable reduced detail/range shadows in Hoarfrost Reach and Seliana Gathering Hub."); ImGui.EndTooltip(); } if (ImGui.Checkbox("Disable Player/Palico/NPC LOD Limit in Gameplay", ref disableLodLimits)) { if (disableLodLimits) { disableLodLimitsEnable(); } else { disableLodLimitsDisable(); } config.DisableLodLimits = disableLodLimits; ConfigManager.SaveConfig(this); } if (ImGui.BeginItemTooltip()) { ImGui.Text("This will make the default (gameplay) LOD limits equivalent to View Mode. Disabling the limit on:\n - Player Models & Shadows\n - Palico Models & Shadows\n - NPC Models & Shadows\n - Simple NPC Models & Shadows\nAlso equivalent to the settings applied in your room with the addition of disabled \"Simple NPC\" limits."); ImGui.EndTooltip(); } ImGui.PushItemWidth(width * 0.1f); ImGui.InputFloat("##Foliage LOD Bias", ref foliageLodBias, 0.0f, 0.0f, "%.2f"); if (ImGui.BeginItemTooltip()) { ImGui.Text("In-Game Settings:\n - High: 3.0\n - Mid: 1.0\n - Low: 0.9\n - Variable: -1.0\nHigher value = Farther distance before plants switch to a lower LOD."); ImGui.EndTooltip(); } ImGui.SameLine(); ImGui.InputFloat("Foliage LOD Bias", ref foliageLodFactor, 0.0f, 0.0f, "%.2f"); if (ImGui.BeginItemTooltip()) { ImGui.Text("Controls the distance at which the game decides to no longer render some plants.\nCan be effectively overridden by a high first value.\nDefault: 1.0, Best Quality: 0.0."); ImGui.EndTooltip(); } ImGui.InputFloat("##Terrain/Object LOD Bias", ref terrainLodBias, 0.0f, 0.0f, "%.2f"); if (ImGui.BeginItemTooltip()) { ImGui.Text("In-Game Settings:\n - High: 3.0\n - Mid: 1.0\n - Low: 0.9\n - Variable: -1.0\nHigher value = Farther distance before objects switch to a lower LOD."); ImGui.EndTooltip(); } ImGui.SameLine(); ImGui.InputFloat("Terrain/Object LOD Bias", ref terrainLodFactor, 0.0f, 0.0f, "%.2f"); if (ImGui.BeginItemTooltip()) { ImGui.Text("Controls the distance at which the game decides to no longer render some objects.\nCan be effectively overridden by a high first value.\nDefault: 1.0, Best Quality: 0.0."); ImGui.EndTooltip(); } ImGui.PopItemWidth(); 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 Biases", ref lodsSet)) { setLodFactors(lodsSet); config.ApplyLodFactors = lodsSet && !areLodFactorsDefault(); if (lodsSet) { config.FoliageLodBias = foliageLodBias; config.TerrainLodBias = terrainLodBias; 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); } if (ImGui.BeginItemTooltip()) { ImGui.Text("Increase the distance at which Foliage Sway is still applied to lower priority plants."); ImGui.EndTooltip(); } } ImGui.PopID(); ImGui.PushID("Debug"); if (ImGui.CollapsingHeader("DEBUG")) { if (pCamera != null) { ImGui.Text("Perspective Camera"); ImGui.Text($"Player Camera: {pCamera.Instance:x}"); ImGui.Text($"Applying Offset: {applyPerspective}"); ImGui.Text($"previousCameraAnimState: {previousCameraAnimState}"); ImGui.DragFloat("X Offset", ref cameraXOffset, 0.425f); ImGui.DragFloat("Y Offset", ref cameraYOffset, 0.425f); ImGui.DragFloat("Z Offset", ref cameraZOffset, 0.425f); ImGui.Separator(); } if (player != null) { ImGui.PushID("Player"); ImGui.Text("Player"); ImGui.Text($"Pointer: {player.Instance:x}"); 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("Viewports"); ImGui.Text("Viewports"); for (int i = 0; i < 8; i++) { ImGui.PushID($"Viewport{i}"); if (ImGui.CollapsingHeader($"Viewport #{i} ({((i == vCameraViewportIndex) ? "visible" : "inactive")})")) { drawViewportInfo(i, width, config, true); } ImGui.PopID(); } ImGui.PopID(); ImGui.Separator(); ImGui.Text("Graphics"); ImGui.Text($"dofPointer: {dofPointer:x}"); ImGui.Text($"prevFoliageLodBias: {prevFoliageLodBias}"); ImGui.Text($"prevTerrainLodBias: {prevTerrainLodBias}"); ImGui.Text($"shadowCascadeValue: {lastShadowCascadeValue}"); ImGui.Text($"shadowBias: {lastShadowBias}"); ImGui.Text($"shadowRadius: {lastShadowRadius}"); ImGui.Text($"shadowSampleNum: {lastShadowSampleNum}"); 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($" LT: {PadLz}"); ImGui.Text("Right Stick:"); ImGui.Text($" X: {PadRx}"); ImGui.Text($" Y: {PadRy}"); ImGui.Text($" RT: {PadRz}"); ImGui.DragFloat("+right", ref plusRight, 0.05f); ImGui.DragFloat("+forward", ref plusForward, 0.05f); ImGui.PopID(); } ImGui.PopID(); } } }