using System; using System.Collections.Generic; using System.Collections.ObjectModel; using JetBrains.Annotations; using Rewired; using Rewired.Integration.UnityUI; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.SceneManagement; using UnityEngine.UI; namespace RoR2.UI; [RequireComponent(typeof(RewiredStandaloneInputModule))] public class MPEventSystem : EventSystem { public enum InputSource { MouseAndKeyboard, Gamepad } private struct PushInfo { public int index; public Vector2 position; public Vector2 pushVector; } private static readonly List instancesList; public static ReadOnlyCollection readOnlyInstancesList; public int playerSlot = -1; [NonSerialized] public bool allowCursorPush = true; [NonSerialized] public bool isCombinedEventSystem; [HideInInspector] public MPButton currentSelectedButton; private CursorIndicatorController cursorIndicatorController; [NotNull] public Player player; [CanBeNull] public LocalUser localUser; public TooltipProvider currentTooltipProvider; public TooltipController currentTooltip; private static PushInfo[] pushInfos; private const float radius = 24f; private const float invRadius = 1f / 24f; private const float radiusSqr = 576f; private const float pushFactor = 10f; public static int activeCount { get; private set; } public int cursorOpenerCount { get; set; } public int cursorOpenerForGamepadCount { get; set; } public bool isHovering { get { if ((bool)base.currentInputModule) { return ((MPInputModule)base.currentInputModule).isHovering; } return false; } } public bool isCursorVisible => cursorIndicatorController.gameObject.activeInHierarchy; public InputSource currentInputSource { get; private set; } = InputSource.Gamepad; public InputMapperHelper inputMapperHelper { get; private set; } public static MPEventSystem FindByPlayer(Player player) { foreach (MPEventSystem instances in instancesList) { if (instances.player == player) { return instances; } } return null; } public static void RefreshAllControllerGlyphs() { foreach (MPEventSystem instances in instancesList) { if (instances.isActiveAndEnabled) { instances.RefreshControlGlyphs(); } } } public void RefreshControlGlyphs() { OnLastActiveControllerChanged(player, player.controllers.GetLastActiveController()); } protected override void Update() { EventSystem eventSystem = EventSystem.current; EventSystem.current = this; base.Update(); EventSystem.current = eventSystem; ValidateCurrentSelectedGameobject(); if (player.GetButtonDown(25) && (PauseScreenController.instancesList.Count == 0 || SimpleDialogBox.instancesList.Count == 0)) { if (!PauseManager.isPaused) { PauseManager.PausingPlayerID = player.id; } if (PauseManager.PausingPlayerID == player.id) { Console.instance.SubmitCmd(null, "pause"); } } } protected override void Awake() { base.Awake(); instancesList.Add(this); cursorIndicatorController = UnityEngine.Object.Instantiate(LegacyResourcesAPI.Load("Prefabs/UI/CursorIndicator"), base.transform).GetComponent(); inputMapperHelper = new InputMapperHelper(this); } private void ValidateCurrentSelectedGameobject() { if ((bool)base.currentSelectedGameObject) { MPButton mPButton = (currentSelectedButton = base.currentSelectedGameObject.GetComponent()); if ((bool)mPButton && (!mPButton.CanBeSelected() || (currentInputSource == InputSource.Gamepad && mPButton.navigation.mode == UnityEngine.UI.Navigation.Mode.None))) { SetSelectedGameObject(null); currentSelectedButton = null; } } else { currentSelectedButton = null; } } public void SetSelectedObject(GameObject o) { SetSelectedGameObject(o); } private static void OnActiveSceneChanged(Scene scene1, Scene scene2) { RecenterCursors(); } private static void RecenterCursors() { foreach (MPEventSystem instances in instancesList) { if (instances.currentInputSource == InputSource.Gamepad && (bool)instances.currentInputModule) { ((MPInput)instances.currentInputModule.input).CenterCursor(); } } } protected override void OnDestroy() { player.controllers.RemoveLastActiveControllerChangedDelegate(OnLastActiveControllerChanged); instancesList.Remove(this); inputMapperHelper.Dispose(); base.OnDestroy(); } protected override void Start() { base.Start(); SetCursorIndicatorEnabled(cursorIndicatorEnabled: false); if ((bool)base.currentInputModule && (bool)base.currentInputModule.input) { ((MPInput)base.currentInputModule.input).CenterCursor(); } player.controllers.AddLastActiveControllerChangedDelegate(OnLastActiveControllerChanged); OnLastActiveControllerChanged(player, player.controllers.GetLastActiveController()); } protected override void OnEnable() { base.OnEnable(); activeCount++; } protected override void OnDisable() { SetCursorIndicatorEnabled(cursorIndicatorEnabled: false); base.OnDisable(); if (activeCount > 0) { activeCount--; } } protected void LateUpdate() { bool flag = false; if ((bool)base.currentInputModule && (bool)base.currentInputModule.input) { flag = ((currentInputSource != InputSource.Gamepad) ? (cursorOpenerCount > 0) : (cursorOpenerForGamepadCount > 0)); } SetCursorIndicatorEnabled(flag); MPInputModule mPInputModule = base.currentInputModule as MPInputModule; if (flag) { CursorIndicatorController.CursorSet cursorSet = cursorIndicatorController.noneCursorSet; switch (currentInputSource) { case InputSource.MouseAndKeyboard: cursorSet = cursorIndicatorController.mouseCursorSet; break; case InputSource.Gamepad: cursorSet = cursorIndicatorController.gamepadCursorSet; break; } cursorIndicatorController.SetCursor(cursorSet, (!isHovering) ? CursorIndicatorController.CursorImage.Pointer : CursorIndicatorController.CursorImage.Hover, GetColor()); cursorIndicatorController.SetPosition(mPInputModule.input.mousePosition); } } private void OnLastActiveControllerChanged(Player player, Controller controller) { if (controller != null) { switch (controller.type) { case ControllerType.Keyboard: currentInputSource = InputSource.MouseAndKeyboard; break; case ControllerType.Mouse: currentInputSource = InputSource.MouseAndKeyboard; break; case ControllerType.Joystick: currentInputSource = InputSource.Gamepad; break; } if (InputBindingDisplayController.onBindingsChanged != null && controller != null) { InputBindingDisplayController.onBindingsChanged(); } } } private void SetCursorIndicatorEnabled(bool cursorIndicatorEnabled) { if (cursorIndicatorController.gameObject.activeSelf != cursorIndicatorEnabled) { cursorIndicatorController.gameObject.SetActive(cursorIndicatorEnabled); if (cursorIndicatorEnabled) { ((MPInput)((MPInputModule)base.currentInputModule).input).CenterCursor(); } } } public Color GetColor() { if (activeCount <= 1) { return Color.white; } return ColorCatalog.GetMultiplayerColor(playerSlot); } public bool GetCursorPosition(out Vector2 position) { if ((bool)base.currentInputModule) { position = base.currentInputModule.input.mousePosition; return true; } position = Vector2.zero; return false; } public Rect GetScreenRect() { CameraRigController cameraRigController = localUser?.cameraRigController; if (!cameraRigController) { return new Rect(0f, 0f, Screen.width, Screen.height); } return cameraRigController.viewport; } private static Vector2 RandomOnCircle() { float value = UnityEngine.Random.value; return new Vector2(Mathf.Cos(value * MathF.PI * 2f), Mathf.Sin(value * MathF.PI * 2f)); } private static Vector2 CalculateCursorPushVector(Vector2 positionA, Vector2 positionB) { Vector2 vector = positionA - positionB; if (vector == Vector2.zero) { vector = RandomOnCircle(); } float sqrMagnitude = vector.sqrMagnitude; if (sqrMagnitude >= 576f) { return Vector2.zero; } float num = Mathf.Sqrt(sqrMagnitude); float num2 = num * (1f / 24f); float num3 = 1f - num2; return vector / num * num3 * 10f * 0.5f; } private static void PushCursorsApart() { if (activeCount <= 1) { return; } int count = instancesList.Count; if (pushInfos.Length < activeCount) { pushInfos = new PushInfo[activeCount]; } int num = 0; for (int i = 0; i < count; i++) { if (instancesList[i].enabled) { instancesList[i].GetCursorPosition(out var position); pushInfos[num++] = new PushInfo { index = i, position = position }; } } for (int j = 0; j < activeCount; j++) { PushInfo pushInfo = pushInfos[j]; for (int k = j + 1; k < activeCount; k++) { PushInfo pushInfo2 = pushInfos[k]; Vector2 vector = CalculateCursorPushVector(pushInfo.position, pushInfo2.position); pushInfo.pushVector += vector; pushInfo2.pushVector -= vector; pushInfos[k] = pushInfo2; } pushInfos[j] = pushInfo; } for (int l = 0; l < activeCount; l++) { PushInfo pushInfo3 = pushInfos[l]; MPEventSystem mPEventSystem = instancesList[pushInfo3.index]; if (mPEventSystem.allowCursorPush && (bool)mPEventSystem.currentInputModule) { ((MPInput)mPEventSystem.currentInputModule.input).internalMousePosition += pushInfo3.pushVector; } } } static MPEventSystem() { instancesList = new List(); readOnlyInstancesList = new ReadOnlyCollection(instancesList); pushInfos = Array.Empty(); RoR2Application.onUpdate += PushCursorsApart; SceneManager.activeSceneChanged += OnActiveSceneChanged; } }