mirror of
https://github.com/originalnicodr/CinematicUnityExplorer.git
synced 2026-10-11 01:48:07 +08:00
281 lines
9.6 KiB
C#
281 lines
9.6 KiB
C#
using HarmonyLib;
|
|
using System;
|
|
using System.Collections;
|
|
using UnityEngine;
|
|
using UnityEngine.EventSystems;
|
|
using UnityExplorer.Core;
|
|
using UnityExplorer.Core.Config;
|
|
using UnityExplorer.Core.Input;
|
|
using UnityExplorer.UI;
|
|
|
|
namespace UnityExplorer.Core.Input
|
|
{
|
|
public class CursorUnlocker
|
|
{
|
|
public static bool Unlock
|
|
{
|
|
get => m_forceUnlock;
|
|
set
|
|
{
|
|
m_forceUnlock = value;
|
|
UpdateCursorControl();
|
|
}
|
|
}
|
|
private static bool m_forceUnlock;
|
|
|
|
public static bool ShouldActuallyUnlock => UIManager.ShowMenu && Unlock;
|
|
|
|
private static CursorLockMode lastLockMode;
|
|
private static bool lastVisibleState;
|
|
|
|
private static bool currentlySettingCursor = false;
|
|
|
|
public static void Init()
|
|
{
|
|
lastLockMode = Cursor.lockState;
|
|
lastVisibleState = Cursor.visible;
|
|
|
|
SetupPatches();
|
|
UpdateCursorControl();
|
|
|
|
// Hook up config values
|
|
|
|
// Force Unlock Mouse
|
|
Unlock = ConfigManager.Force_Unlock_Mouse.Value;
|
|
ConfigManager.Force_Unlock_Mouse.OnValueChanged += (bool val) => { Unlock = val; };
|
|
|
|
// Aggressive Mouse Unlock
|
|
if (ConfigManager.Aggressive_Mouse_Unlock.Value)
|
|
SetupAggressiveUnlock();
|
|
}
|
|
|
|
public static void SetupAggressiveUnlock()
|
|
{
|
|
try
|
|
{
|
|
RuntimeProvider.Instance.StartCoroutine(AggressiveUnlockCoroutine());
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ExplorerCore.LogWarning($"Exception setting up Aggressive Mouse Unlock: {ex}");
|
|
}
|
|
}
|
|
|
|
private static WaitForEndOfFrame _waitForEndOfFrame = new WaitForEndOfFrame();
|
|
|
|
private static IEnumerator AggressiveUnlockCoroutine()
|
|
{
|
|
while (true)
|
|
{
|
|
yield return _waitForEndOfFrame ?? (_waitForEndOfFrame = new WaitForEndOfFrame());
|
|
|
|
if (UIManager.ShowMenu)
|
|
UpdateCursorControl();
|
|
}
|
|
}
|
|
|
|
public static void UpdateCursorControl()
|
|
{
|
|
try
|
|
{
|
|
currentlySettingCursor = true;
|
|
|
|
if (ShouldActuallyUnlock)
|
|
{
|
|
Cursor.lockState = CursorLockMode.None;
|
|
Cursor.visible = true;
|
|
|
|
if (!ConfigManager.Disable_EventSystem_Override.Value && UIManager.EventSys)
|
|
SetEventSystem();
|
|
}
|
|
else
|
|
{
|
|
Cursor.lockState = lastLockMode;
|
|
Cursor.visible = lastVisibleState;
|
|
|
|
if (!ConfigManager.Disable_EventSystem_Override.Value && UIManager.EventSys)
|
|
ReleaseEventSystem();
|
|
}
|
|
|
|
currentlySettingCursor = false;
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
ExplorerCore.Log($"Exception setting Cursor state: {e.GetType()}, {e.Message}");
|
|
}
|
|
}
|
|
|
|
// Event system overrides
|
|
|
|
private static bool settingEventSystem;
|
|
private static EventSystem lastEventSystem;
|
|
private static BaseInputModule lastInputModule;
|
|
|
|
public static void SetEventSystem()
|
|
{
|
|
if (EventSystem.current && EventSystem.current != UIManager.EventSys)
|
|
{
|
|
lastEventSystem = EventSystem.current;
|
|
lastEventSystem.enabled = false;
|
|
}
|
|
|
|
// Set to our current system
|
|
settingEventSystem = true;
|
|
UIManager.EventSys.enabled = true;
|
|
EventSystem.current = UIManager.EventSys;
|
|
InputManager.ActivateUIModule();
|
|
settingEventSystem = false;
|
|
}
|
|
|
|
public static void ReleaseEventSystem()
|
|
{
|
|
if (lastEventSystem && lastEventSystem.gameObject.activeSelf)
|
|
{
|
|
lastEventSystem.enabled = true;
|
|
|
|
settingEventSystem = true;
|
|
UIManager.EventSys.enabled = false;
|
|
EventSystem.current = lastEventSystem;
|
|
lastInputModule?.ActivateModule();
|
|
settingEventSystem = false;
|
|
}
|
|
}
|
|
|
|
// Patches
|
|
|
|
public static void SetupPatches()
|
|
{
|
|
try
|
|
{
|
|
PrefixPropertySetter(typeof(Cursor),
|
|
"lockState",
|
|
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_set_lockState))));
|
|
|
|
PrefixPropertySetter(typeof(Cursor),
|
|
"visible",
|
|
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_set_visible))));
|
|
|
|
PrefixPropertySetter(typeof(EventSystem),
|
|
"current",
|
|
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_EventSystem_set_current))));
|
|
|
|
PrefixMethod(typeof(EventSystem),
|
|
"SetSelectedGameObject",
|
|
// some games use a modified version of uGUI that includes this extra int argument on this method.
|
|
new Type[] { typeof(GameObject), typeof(BaseEventData), typeof(int) },
|
|
new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_EventSystem_SetSelectedGameObject))),
|
|
// most games use these arguments, we'll use them as our "backup".
|
|
new Type[] { typeof(GameObject), typeof(BaseEventData) });
|
|
|
|
//// Not sure if this one is needed.
|
|
//PrefixMethod(typeof(PointerInputModule),
|
|
// "ClearSelection",
|
|
// new Type[0],
|
|
// new HarmonyMethod(typeof(CursorUnlocker).GetMethod(nameof(CursorUnlocker.Prefix_PointerInputModule_ClearSelection))));
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
ExplorerCore.Log($"Exception setting up Harmony patches:\r\n{ex.ReflectionExToString()}");
|
|
}
|
|
}
|
|
|
|
private static void PrefixMethod(Type type, string method, Type[] arguments, HarmonyMethod prefix, Type[] backupArgs = null)
|
|
{
|
|
try
|
|
{
|
|
var methodInfo = type.GetMethod(method, ReflectionUtility.FLAGS, null, arguments, null);
|
|
if (methodInfo == null)
|
|
{
|
|
if (backupArgs != null)
|
|
methodInfo = type.GetMethod(method, ReflectionUtility.FLAGS, null, backupArgs, null);
|
|
|
|
if (methodInfo == null)
|
|
throw new MissingMethodException($"Could not find method for patching - '{type.FullName}.{method}'!");
|
|
}
|
|
|
|
var processor = ExplorerCore.Harmony.CreateProcessor(methodInfo);
|
|
processor.AddPrefix(prefix);
|
|
processor.Patch();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
ExplorerCore.LogWarning($"Unable to patch {type.Name}.{method}: {e.Message}");
|
|
}
|
|
}
|
|
|
|
private static void PrefixPropertySetter(Type type, string property, HarmonyMethod prefix)
|
|
{
|
|
try
|
|
{
|
|
var processor = ExplorerCore.Harmony.CreateProcessor(type.GetProperty(property, ReflectionUtility.FLAGS).GetSetMethod());
|
|
processor.AddPrefix(prefix);
|
|
processor.Patch();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
ExplorerCore.Log($"Unable to patch {type.Name}.set_{property}: {e.Message}");
|
|
}
|
|
}
|
|
|
|
// Prevent setting non-UnityExplorer objects as selected when menu is open
|
|
|
|
public static bool Prefix_EventSystem_SetSelectedGameObject(GameObject __0)
|
|
{
|
|
if (!UIManager.ShowMenu || !UIManager.CanvasRoot)
|
|
return true;
|
|
|
|
return __0 && __0.transform.root.gameObject.GetInstanceID() == UIManager.CanvasRoot.GetInstanceID();
|
|
}
|
|
|
|
//public static bool Prefix_PointerInputModule_ClearSelection()
|
|
//{
|
|
// return !(UIManager.ShowMenu && UIManager.CanvasRoot);
|
|
//}
|
|
|
|
// Force EventSystem.current to be UnityExplorer's when menu is open
|
|
|
|
public static void Prefix_EventSystem_set_current(ref EventSystem value)
|
|
{
|
|
if (!settingEventSystem && value)
|
|
{
|
|
lastEventSystem = value;
|
|
lastInputModule = value.currentInputModule;
|
|
}
|
|
|
|
if (!UIManager.EventSys)
|
|
return;
|
|
|
|
if (!settingEventSystem && ShouldActuallyUnlock && !ConfigManager.Disable_EventSystem_Override.Value)
|
|
{
|
|
value = UIManager.EventSys;
|
|
value.enabled = true;
|
|
}
|
|
}
|
|
|
|
// 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.
|
|
|
|
public static void Prefix_set_lockState(ref CursorLockMode value)
|
|
{
|
|
if (!currentlySettingCursor)
|
|
{
|
|
lastLockMode = value;
|
|
|
|
if (ShouldActuallyUnlock)
|
|
value = CursorLockMode.None;
|
|
}
|
|
}
|
|
|
|
public static void Prefix_set_visible(ref bool value)
|
|
{
|
|
if (!currentlySettingCursor)
|
|
{
|
|
lastVisibleState = value;
|
|
|
|
if (ShouldActuallyUnlock)
|
|
value = true;
|
|
}
|
|
}
|
|
}
|
|
} |