Files
UnityExplorer_Fix/src/UI/ForceUnlockCursor.cs

251 lines
7.7 KiB
C#

using System;
using UnityEngine;
using UnityExplorer.Helpers;
using UnityEngine.EventSystems;
using UnityExplorer.Input;
using BF = System.Reflection.BindingFlags;
using UnityExplorer.Config;
#if ML
using Harmony;
#else
using HarmonyLib;
#endif
namespace UnityExplorer.UI
{
public class ForceUnlockCursor
{
public static bool Unlock
{
get => m_forceUnlock;
set => SetForceUnlock(value);
}
private static bool m_forceUnlock;
private static void SetForceUnlock(bool unlock)
{
m_forceUnlock = unlock;
UpdateCursorControl();
}
public static bool ShouldForceMouse => ExplorerCore.ShowMenu && Unlock;
private static CursorLockMode m_lastLockMode;
private static bool m_lastVisibleState;
private static bool m_currentlySettingCursor = false;
private static Type CursorType
=> m_cursorType
?? (m_cursorType = ReflectionHelpers.GetTypeByName("UnityEngine.Cursor"));
private static Type m_cursorType;
public static void Init()
{
ModConfig.OnConfigChanged += ModConfig_OnConfigChanged;
SetupPatches();
Unlock = true;
}
internal static void ModConfig_OnConfigChanged()
{
Unlock = ModConfig.Instance.Force_Unlock_Mouse;
}
private static void SetupPatches()
{
try
{
if (CursorType == null)
{
throw new Exception("Could not find Type 'UnityEngine.Cursor'!");
}
// Get current cursor state and enable cursor
try
{
//m_lastLockMode = Cursor.lockState;
m_lastLockMode = (CursorLockMode?)typeof(Cursor).GetProperty("lockState", BF.Public | BF.Static)?.GetValue(null, null)
?? CursorLockMode.None;
//m_lastVisibleState = Cursor.visible;
m_lastVisibleState = (bool?)typeof(Cursor).GetProperty("visible", BF.Public | BF.Static)?.GetValue(null, null)
?? false;
}
catch { }
// Setup Harmony Patches
TryPatch(typeof(Cursor),
"lockState",
new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Prefix_set_lockState))),
true);
TryPatch(typeof(Cursor),
"visible",
new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Prefix_set_visible))),
true);
TryPatch(typeof(EventSystem),
"current",
new HarmonyMethod(typeof(ForceUnlockCursor).GetMethod(nameof(Prefix_EventSystem_set_current))),
true);
}
catch (Exception e)
{
ExplorerCore.Log($"Exception on ForceUnlockCursor.Init! {e.GetType()}, {e.Message}");
}
}
private static void TryPatch(Type type, string property, HarmonyMethod patch, bool setter)
{
try
{
var harmony =
#if ML
ExplorerMelonMod.Instance.Harmony;
#elif BIE
ExplorerBepInPlugin.HarmonyInstance;
#elif STANDALONE
ExplorerStandalone.HarmonyInstance;
#endif
System.Reflection.PropertyInfo prop = type.GetProperty(property);
if (setter) // setter is prefix
{
harmony.Patch(prop.GetSetMethod(), prefix: patch);
}
else // getter is postfix
{
harmony.Patch(prop.GetGetMethod(), postfix: patch);
}
}
catch (Exception e)
{
string suf = setter ? "set_" : "get_";
ExplorerCore.Log($"Unable to patch {type.Name}.{suf}{property}: {e.Message}");
}
}
public static void UpdateCursorControl()
{
try
{
m_currentlySettingCursor = true;
if (ShouldForceMouse)
{
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
}
else
{
Cursor.lockState = m_lastLockMode;
Cursor.visible = m_lastVisibleState;
}
m_currentlySettingCursor = false;
}
catch (Exception e)
{
ExplorerCore.Log($"Exception setting Cursor state: {e.GetType()}, {e.Message}");
}
}
// Event system overrides
private static bool m_settingEventSystem;
private static EventSystem m_lastEventSystem;
private static BaseInputModule m_lastInputModule;
public static void SetEventSystem()
{
// temp disabled for new InputSystem
if (InputManager.CurrentType == InputType.InputSystem)
return;
// Disable current event system object
if (m_lastEventSystem || EventSystem.current)
{
if (!m_lastEventSystem)
m_lastEventSystem = EventSystem.current;
//ExplorerCore.Log("Disabling current event system...");
m_lastEventSystem.enabled = false;
//m_lastEventSystem.gameObject.SetActive(false);
}
// Set to our current system
m_settingEventSystem = true;
EventSystem.current = UIManager.EventSys;
UIManager.EventSys.enabled = true;
InputManager.ActivateUIModule();
m_settingEventSystem = false;
}
public static void ReleaseEventSystem()
{
if (InputManager.CurrentType == InputType.InputSystem)
return;
if (m_lastEventSystem)
{
m_lastEventSystem.enabled = true;
//m_lastEventSystem.gameObject.SetActive(true);
m_settingEventSystem = true;
EventSystem.current = m_lastEventSystem;
m_lastInputModule?.ActivateModule();
m_settingEventSystem = false;
}
}
[HarmonyPrefix]
public static void Prefix_EventSystem_set_current(ref EventSystem value)
{
if (!m_settingEventSystem)
{
m_lastEventSystem = value;
m_lastInputModule = value?.currentInputModule;
if (ExplorerCore.ShowMenu)
{
value = UIManager.EventSys;
}
}
}
// Force mouse to stay unlocked and visible while UnlockMouse and ShowMenu are true.
// Also keep track of when anything else tries to set Cursor state, this will be the
// value that we set back to when we close the menu or disable force-unlock.
[HarmonyPrefix]
public static void Prefix_set_lockState(ref CursorLockMode value)
{
if (!m_currentlySettingCursor)
{
m_lastLockMode = value;
if (ShouldForceMouse)
{
value = CursorLockMode.None;
}
}
}
[HarmonyPrefix]
public static void Prefix_set_visible(ref bool value)
{
if (!m_currentlySettingCursor)
{
m_lastVisibleState = value;
if (ShouldForceMouse)
{
value = true;
}
}
}
}
}