using System; using UnityEngine; using Battlehub.RTCommon; using System.Collections.Generic; namespace Datel { [DefaultExecutionOrder(-1)] //fixing controllers flickering by clicking on menu buttons public class MouseControlOverhaul : MVRScript { SuperController sc = SuperController.singleton; bool initialized = false; bool initializationError = false; SuperController.SelectMode previousSelectMode; //Mouse control bool useMouseRDPMode = false; //Remote Desktop Protocol using absolute mouse position, not relative offset like many 3D games bool freeMove; FreeControllerV3 hoveredController; float draggingCameraDistance; Vector3 lastMousePosition; bool wasGUIPressed = false; bool wasAxisPressed = false; Vector3 mouseChangeScaled; float mouseAxisX; float mouseAxisY; Vector3 lastMouseWorldPosition; Vector3 mouseLastPos; //First click before drag bool oldShowHiddenAtoms = false; HashSet interactableControllers = new HashSet(); const float worldRadius = 16f / 1080f; //16 is minimal (about radius of CUA icon rectangle), different resolution fix const float selectedDraggingRadius = 90f; // Hack to hide collider mesh of Empty atoms when collision is disabled GameObject emptySphereRenderer; JSONStorableBool emptyCollisionEnabledJSON; bool NotMouseControl => !sc.MonitorCenterCamera || !sc.IsMonitorRigActive; //MacGruber's PowerHandles FreeControllerV3 modifiedController; //mySelectedController FreeControllerV3.GridMode savedPositionGridMode = FreeControllerV3.GridMode.None; FreeControllerV3.GridMode savedRotationGridMode = FreeControllerV3.GridMode.None; float savedPositionGridAlign; float savedRotationGridAlign; private JSONStorableFloat storablePositionGrid; private JSONStorableFloat storableRotationGrid; static List hotkeys = Enum.GetNames(typeof(KeyCode)).ToList(); KeyCode hotkeyDragRotationXY = KeyCode.LeftShift; KeyCode hotkeyDragRotationZY = KeyCode.LeftControl; KeyCode hotkeyShowPositionHandler = KeyCode.LeftControl; KeyCode hotkeyShowRotationHandler = KeyCode.LeftAlt; KeyCode hotkeyToggleAxisMode = KeyCode.Q; KeyCode hotkeyHoldAxisMode = KeyCode.LeftShift; KeyCode hotkeyDeselect = KeyCode.Escape; KeyCode hotkeyToggleGridMode = KeyCode.Space; // New hotkey for grid mode JSONStorableFloat storableHoverScaleUnselected; JSONStorableFloat storableHoverScaleSelected; JSONStorableBool storableDragEnabled; JSONStorableFloat storableDragActivationPixelDistance; JSONStorableBool storableGlobalAxisMode; private const float BUTTON_HEIGHT = 50f; private const float SLIDER_HEIGHT = 120f; private const float CHOOSER_HEIGHT = 100f; private const float LAYOUT_SPACE_HEIGHT = 15f; private const float SEPARATOR_HEIGHT = 20f; float hoverScaleUnselected => storableHoverScaleUnselected.val; float hoverScaleSelected => storableHoverScaleSelected.val; bool dragEnabled => storableDragEnabled.val; float dragActivationPixelDistance => storableDragActivationPixelDistance.val; bool globalAxisMode { get { return storableGlobalAxisMode.val; } set { storableGlobalAxisMode.val = value; } } public void Log(string txt) { SuperController.LogMessage($"[{DateTime.Now.ToString("h:mm:ss")}] {nameof(MouseControlOverhaul)}: " + txt); } public void LogError(string txt) { SuperController.LogError($"[{DateTime.Now.ToString("h:mm:ss")}] {nameof(MouseControlOverhaul)}: " + txt); } public override void Init() { if (containingAtom.type != "SessionPluginManager") { LogError("Plugin can only be installed as a session plugin."); AddText("ErrorInfo", "Initialization Error", "Plugin can only be installed as a session plugin.", 120f, false); enabledJSON.val = false; initializationError = true; return; } initialized = true; storableHoverScaleUnselected = AddSlider("Hover Scale Unselected", 2f, 0.5f, 50f, false); storableHoverScaleSelected = AddSlider("Hover Scale Selected", 4f, 0.5f, 50f, false); AddText("MouseSelectionInfo", "Mouse Selection", "- Choosing the closest controller to mouse position in screen space." + "\n- Scale 1 is about radius of rectangular controller icon of Empty or CUA Atom." + "\n- Hovered controller's name will be written at left of main menu.", SLIDER_HEIGHT * 2 + LAYOUT_SPACE_HEIGHT, true); AddSpacerBoth(SEPARATOR_HEIGHT); AddHotkeyPopup("Show Position Handler", hotkeyShowPositionHandler, false, OnHotkeyChangedShowPositionHandler); AddHotkeyPopup("Show Rotation Handler", hotkeyShowRotationHandler, false, OnHotkeyChangedShowRotationHandler); AddText("HiddenHandlersInfo", "Hidden Handlers", "- If Show hotkey is not None, handler will be visible when hotkey is holding." + "\n- While Rotation Handler is active, \"Hover Scale Selected\" equals to full rotation handler's circle.", CHOOSER_HEIGHT * 2 + LAYOUT_SPACE_HEIGHT, true); AddSpacerBoth(SEPARATOR_HEIGHT); storableGlobalAxisMode = AddToggle("Global Axis Mode", false, false); AddHotkeyPopup("Toggle Axis Mode", hotkeyToggleAxisMode, false, OnHotkeyChangedToggleAxisMode); AddHotkeyPopup("Hold Axis Mode", hotkeyHoldAxisMode, false, OnHotkeyChangedHoldAxisMode); AddText("GlobalAxisModeInfo", "Global Axis Mode", "- Handlers can be switched to Global Axis Mode to move and rotate controller using Global coordinates." + "\n- Global Axis Mode can be switched by one hit Toggle hotkey or holding Hold hotkey to invert mode.", BUTTON_HEIGHT + CHOOSER_HEIGHT * 2 + LAYOUT_SPACE_HEIGHT * 2, true); AddSpacerBoth(SEPARATOR_HEIGHT); // New UI elements for grid mode storablePositionGrid = AddSlider("Position Grid", 0.1f, 0.01f, 1.0f, false); storablePositionGrid.setCallbackFunction = v => { v = Mathf.Round(v * 100.0f) / 100.0f; storablePositionGrid.valNoCallback = v; if (modifiedController != null) modifiedController.positionGrid = v; }; storableRotationGrid = AddSliderInt("Rotation Grid", 15, 1, 90, false); storableRotationGrid.setCallbackFunction = v => { if (modifiedController != null) modifiedController.rotationGrid = v; }; AddHotkeyPopup("Toggle Grid Mode", hotkeyToggleGridMode, false, OnHotkeyChangedToggleGridMode); AddText("GridModeInfo", "Grid Mode", "- Hold the Toggle Grid Mode hotkey (default Space) to enable grid-based positioning and rotation." + "\n- Grid mode aligns controller movement and rotation to a grid, using local or global coordinates based on Global Axis Mode.", SLIDER_HEIGHT * 2 + CHOOSER_HEIGHT + LAYOUT_SPACE_HEIGHT * 2, true); AddSpacerBoth(SEPARATOR_HEIGHT); storableDragEnabled = AddToggle("Drag Enabled", true, false); storableDragEnabled.setCallbackFunction = b => { if (storableDragEnabled.val) StopDragging(); }; storableDragActivationPixelDistance = AddSliderInt("Activation Pixel Distance", 10f, 1f, 1000f, false); AddHotkeyPopup("Drag RotationXY", hotkeyDragRotationXY, false, OnHotkeyChangedDragRotationXY); AddHotkeyPopup("Drag RotationZY", hotkeyDragRotationZY, false, OnHotkeyChangedDragRotationZY); AddText("DragModeInfo", "Drag Mode", "- Like a default VaM's drag mode, drag the controller to move or rotate it in camera space without selecting it." + "\n- Drag Mode activates when the mouse moves beyond the Activation Pixel Distance from the initial press." + "\n- While in Drag Mode, hold the Drag Rotation hotkey to rotate the controller around the camera's local axes.", BUTTON_HEIGHT + SLIDER_HEIGHT + CHOOSER_HEIGHT * 2 + LAYOUT_SPACE_HEIGHT * 3, true); AddSpacerBoth(SEPARATOR_HEIGHT); AddHotkeyPopup("Deselect", hotkeyDeselect, false, OnHotkeyChangedDeselect); AddText("DeselectInfo", "Deselect or Close menu", "- Deselecting selected controller or closing menu if nothing selected.", CHOOSER_HEIGHT, true, true); OnEnableAction(); } protected void OnEnable() { if (initialized) OnEnableAction(); else if (initializationError) enabledJSON.val = false; } protected void OnEnableAction() { previousSelectMode = sc.currentSelectMode; if (sc.currentSelectMode == SuperController.SelectMode.Targets) ChangeToCustomSelection(false); else if (sc.currentSelectMode == SuperController.SelectMode.FreeMoveMouse) ChangeToCustomSelection(!freeMove); SelectedControllerChanged(sc.GetSelectedController()); } protected void OnDisable() { if (!initialized) return; bool handlesEnabled = sc.mainHUD.gameObject.activeSelf && sc.MonitorCenterCamera; FCPositionHandle positionHandle = sc.selectedControllerPositionHandle; FCRotationHandle rotationHandle = sc.selectedControllerRotationHandle; if (positionHandle) { positionHandle.ForceDeselectAxis(); positionHandle.ForceDrop(); positionHandle.enabled = handlesEnabled; } if (rotationHandle) { rotationHandle.ForceDeselectAxis(); rotationHandle.ForceDrop(); rotationHandle.enabled = handlesEnabled; } RuntimeTools.PivotRotation = RuntimePivotRotation.Local; SelectedControllerChanged(null); ChangeToDefaultSelection(); } protected void Update() { if (!initialized) return; if (Input.GetKeyDown(hotkeyToggleAxisMode)) ToggleAxisMode(); if (sc.currentSelectMode != previousSelectMode) { if (sc.currentSelectMode == SuperController.SelectMode.Targets) ChangeToCustomSelection(false); else if (sc.currentSelectMode == SuperController.SelectMode.FreeMoveMouse) ChangeToCustomSelection(!freeMove); previousSelectMode = sc.currentSelectMode; } ProcessMouseChange(); if (sc.currentSelectMode == SuperController.SelectMode.Custom) { if (Input.GetKeyDown(hotkeyDeselect)) { if (modifiedController == null) sc.activeUI = SuperController.ActiveUI.None; else CustomSelectController(null); } if (sc.GetSelectedController() != modifiedController) SelectedControllerChanged(sc.GetSelectedController()); if (freeMove) ProcessMouseFreeNavigation(); ProcessMouseCustomControl(); ExtraSyncVisibility(); } } protected void LateUpdate() { if (!initialized) return; if (sc.currentSelectMode == SuperController.SelectMode.Custom) UpdateHandles(); } private void ChangeToCustomSelection(bool newFreeMove) { freeMove = newFreeMove; sc.SelectModeCustom(string.Empty); } private void ChangeToDefaultSelection() { sc.SelectModeOff(); StopDragging(); if (hoveredController) { hoveredController.highlighted = false; hoveredController = null; } } private float GetMouseXChange() { return useMouseRDPMode ? mouseChangeScaled.x : mouseAxisX; } private float GetMouseYChange() { return useMouseRDPMode ? mouseChangeScaled.y : mouseAxisY; } private void ProcessMouseChange() { mouseAxisX = Input.GetAxisRaw("Mouse X"); mouseAxisY = Input.GetAxisRaw("Mouse Y"); Vector3 currentMousePosition = Input.mousePosition; Vector3 mouseChange = currentMousePosition - lastMousePosition; mouseChangeScaled = mouseChange * 0.1f; lastMousePosition = currentMousePosition; useMouseRDPMode = !freeMove && mouseAxisX == 0f && mouseAxisY == 0f && (mouseChange.x != 0f || mouseChange.y != 0f); } private bool IsDragging() { return hoveredController && hoveredController.isGrabbing; } private bool SetDragging(Rigidbody rigidBody, FreeControllerV3.SelectLinkState linkState) { if (hoveredController) { if (hoveredController.isGrabbing) hoveredController.RestorePreLinkState(); sc.mouseGrab.position = hoveredController.transform.position; sc.mouseGrab.rotation = hoveredController.transform.rotation; hoveredController.isGrabbing = true; hoveredController.SelectLinkToRigidbody(rigidBody, linkState); return true; } return false; } private bool StopDragging() { if (IsDragging()) { hoveredController.isGrabbing = false; hoveredController.RestorePreLinkState(); hoveredController.highlighted = false; hoveredController = null; return true; } return false; } private void SetHelpHUDTextController(FreeControllerV3 controller) { if (controller) sc.SetHelpHUDText(hoveredController == hoveredController.containingAtom.mainController ? hoveredController.containingAtom.uid : hoveredController.containingAtom.uid + "\n――――――\n" + hoveredController.name); else sc.SetHelpHUDText(string.Empty); } private float DistanceSquared(float x1, float y1, float x2, float y2) { float xx = x1 - x2; float yy = y1 - y2; return xx * xx + yy * yy; } private void DeselectHandlesAxes() { if (sc.selectedControllerPositionHandle) sc.selectedControllerPositionHandle.ForceDeselectAxis(); if (sc.selectedControllerRotationHandle) sc.selectedControllerRotationHandle.ForceDeselectAxis(); } private bool IsAnyAxisSelected() { return (sc.selectedControllerPositionHandle && sc.selectedControllerPositionHandle.HasSelectedAxis) || (sc.selectedControllerRotationHandle && sc.selectedControllerRotationHandle.HasSelectedAxis); } private void UpdateHoverController(Vector3 mousePos, bool GUIhitMouse) { if (!sc.GetSelectedController()) DeselectHandlesAxes(); Vector3 controllerScreenPos; float radius; float minDist = float.PositiveInfinity; FreeControllerV3 minDistController = null; float dist; float k = worldRadius * Screen.height / Mathf.Tan(sc.MonitorCenterCamera.fieldOfView * Mathf.Deg2Rad / 2.0f); if (!Input.GetMouseButton(0)) { if (GUIhitMouse) { DeselectHandlesAxes(); if (hoveredController) { hoveredController.highlighted = false; hoveredController = null; } } else if (IsAnyAxisSelected()) { if (hoveredController) { hoveredController.highlighted = false; hoveredController = null; } } else { foreach (FreeControllerV3 c in sc.GetAllFreeControllers()) { if (!interactableControllers.Contains(c)) continue; controllerScreenPos = sc.MonitorCenterCamera.WorldToScreenPoint(c.transform.position); radius = (c == sc.GetSelectedController()) ? (sc.selectedControllerRotationHandle && sc.selectedControllerRotationHandle.enabled ? selectedDraggingRadius : (hoverScaleSelected * k / controllerScreenPos.z)) : (hoverScaleUnselected * k / controllerScreenPos.z); if (controllerScreenPos.z > 0f) { dist = DistanceSquared(mousePos.x, mousePos.y, controllerScreenPos.x, controllerScreenPos.y); if (dist < radius * radius && minDist > dist) { minDist = dist; minDistController = c; } } } if (hoveredController != minDistController) { if (hoveredController) hoveredController.highlighted = false; if (minDistController) minDistController.highlighted = true; hoveredController = minDistController; } } } SetHelpHUDTextController(hoveredController); } private void ToggleAxisMode() { globalAxisMode = !globalAxisMode; } private void UpdateHandles() { bool handlesEnabled = sc.MonitorCenterCamera && (!UserPreferences.singleton.showTargetsMenuOnly || sc.mainHUD.gameObject.activeSelf); bool usePosition = handlesEnabled && (hotkeyShowPositionHandler == KeyCode.None || Input.GetKey(hotkeyShowPositionHandler)); bool useRotation = handlesEnabled && (hotkeyShowRotationHandler == KeyCode.None || Input.GetKey(hotkeyShowRotationHandler)); bool useGlobal = handlesEnabled && (globalAxisMode ^ Input.GetKey(hotkeyHoldAxisMode)); bool useGrid = handlesEnabled && Input.GetKey(hotkeyToggleGridMode); // New grid mode check FCPositionHandle positionHandle = sc.selectedControllerPositionHandle; FCRotationHandle rotationHandle = sc.selectedControllerRotationHandle; bool isDraggingAny = false; if (positionHandle) { if (!usePosition) { if (positionHandle.enabled) { positionHandle.ForceDeselectAxis(); positionHandle.ForceDrop(); } else if (positionHandle.controller) positionHandle.transform.position = positionHandle.controller.transform.position; } positionHandle.enabled = usePosition; isDraggingAny = isDraggingAny || positionHandle.IsDragging; } if (rotationHandle) { if (!useRotation) { if (rotationHandle.enabled) { rotationHandle.ForceDeselectAxis(); rotationHandle.ForceDrop(); } else if (rotationHandle.controller) rotationHandle.transform.rotation = rotationHandle.controller.transform.rotation; } rotationHandle.enabled = useRotation; isDraggingAny = isDraggingAny || rotationHandle.IsDragging; } if (!isDraggingAny) RuntimeTools.PivotRotation = useGlobal ? RuntimePivotRotation.Global : RuntimePivotRotation.Local; if (modifiedController) { FreeControllerV3.GridMode gridMode = useGrid ? (useGlobal ? FreeControllerV3.GridMode.Global : FreeControllerV3.GridMode.Local) : FreeControllerV3.GridMode.None; if (modifiedController.positionGridMode != gridMode) modifiedController.positionGridMode = gridMode; if (modifiedController.rotationGridMode != gridMode) modifiedController.rotationGridMode = gridMode; } } private void ProcessMouseFreeNavigation() { if (sc.mouseCrosshairPointer) sc.mouseCrosshairPointer.anchoredPosition = Input.mousePosition; if (!sc.navigationRig || !sc.lookCamera || sc.navigationDisabled || sc.worldUIActivated || Input.GetMouseButton(1) || Input.GetMouseButton(2)) return; float mouseXChange = Mathf.Clamp(GetMouseXChange(), -10f, 10f); if (mouseXChange > 0.01f || mouseXChange < -0.01f) { if (sc.MainHUDAnchoredOnMonitor && sc.MonitorCenterCamera) sc.navigationRig.RotateAround(sc.MonitorCenterCamera.transform.position, sc.navigationRig.up, mouseXChange); else if (sc.lookCamera) sc.navigationRig.RotateAround(sc.lookCamera.transform.position, sc.navigationRig.up, mouseXChange); } float mouseYChange = Mathf.Clamp(GetMouseYChange(), -10f, 10f); if ((mouseYChange > 0.01f || mouseYChange < -0.01f) && sc.MonitorCenterCamera) { Vector3 localEulerAngles = sc.MonitorCenterCamera.transform.localEulerAngles; if (localEulerAngles.x > 180f) localEulerAngles.x -= 360f; if (localEulerAngles.x < -180f) localEulerAngles.x += 360f; localEulerAngles.x = Mathf.Clamp(localEulerAngles.x - mouseYChange, -89f, 89f); localEulerAngles.y = 0f; localEulerAngles.z = 0f; sc.MonitorCenterCamera.transform.localEulerAngles = localEulerAngles; } if (!sc.UIDisabled && sc.MainHUDAnchoredOnMonitor && sc.MonitorCenterCamera && sc.MonitorUIAnchor && sc.MonitorUIAttachPoint) { Vector3 position = new Vector3 { x = 5f, y = sc.monitorUIYOffset + 60f, z = sc.MonitorCenterCamera.pixelHeight * 0.08f / sc.monitorUIScale / sc.fixedMonitorUIScale / sc.monitorCameraFOV * sc.worldScale }; sc.MonitorUIAnchor.position = sc.MonitorCenterCamera.ScreenToWorldPoint(position); sc.MoveMainHUD(sc.MonitorUIAttachPoint); HUDAnchor.SetAnchorsToReference(); } } private void ProcessMouseCustomControl() { if (NotMouseControl) return; if (!Application.isFocused) { StopDragging(); if (hoveredController) { hoveredController.highlighted = false; hoveredController = null; } return; } bool isNotHandleDragging = RuntimeTools.ActiveTool == null; bool GUIhitMouse = LookInputModule.singleton.mouseRaycastHit; if (Input.GetMouseButtonUp(0) && !wasGUIPressed && !wasAxisPressed) { if (dragEnabled) { if (!StopDragging() && !GUIhitMouse && isNotHandleDragging) { if (hoveredController) CustomSelectController(hoveredController); else CustomSelectController(null); } } else { if (!GUIhitMouse && isNotHandleDragging) { if (hoveredController && hoveredController.highlighted) CustomSelectController(hoveredController); else CustomSelectController(null); } } return; } Vector3 mousePos = Input.mousePosition; UpdateHoverController(mousePos, GUIhitMouse); if (Input.GetMouseButtonDown(0)) { StopDragging(); wasGUIPressed = GUIhitMouse; wasAxisPressed = IsAnyAxisSelected(); if (!GUIhitMouse && isNotHandleDragging && hoveredController) mouseLastPos = mousePos; } if (Input.GetMouseButton(0) && isNotHandleDragging && hoveredController) { if (IsDragging()) { Vector3 mouseWorldPosition = sc.MonitorCenterCamera.ScreenToWorldPoint(new Vector3(mousePos.x, mousePos.y, draggingCameraDistance)); if (sc.mouseGrab) { float mouseXChange = GetMouseXChange() * 10f; float mouseYChange = GetMouseYChange() * 10f; if (Input.GetKey(hotkeyDragRotationXY)) { if (hoveredController.canGrabRotation) { sc.mouseGrab.Rotate(sc.MonitorCenterCamera.transform.up, 0f - mouseXChange, Space.World); sc.mouseGrab.Rotate(sc.MonitorCenterCamera.transform.right, mouseYChange, Space.World); } } else if (Input.GetKey(hotkeyDragRotationZY)) { if (hoveredController.canGrabRotation) { sc.mouseGrab.Rotate(sc.MonitorCenterCamera.transform.forward, 0f - mouseXChange, Space.World); sc.mouseGrab.Rotate(sc.MonitorCenterCamera.transform.right, mouseYChange, Space.World); } } else if (hoveredController.canGrabPosition) sc.mouseGrab.position += mouseWorldPosition - lastMouseWorldPosition; } lastMouseWorldPosition = mouseWorldPosition; } else { if (dragEnabled) { if (DistanceSquared(mouseLastPos.x, mouseLastPos.y, mousePos.x, mousePos.y) > dragActivationPixelDistance * dragActivationPixelDistance) { if (sc.mouseGrab) { Rigidbody component = sc.mouseGrab.GetComponent(); if (component) { draggingCameraDistance = sc.MonitorCenterCamera.WorldToScreenPoint(hoveredController.transform.position).z; lastMouseWorldPosition = sc.MonitorCenterCamera.ScreenToWorldPoint(new Vector3(mouseLastPos.x, mouseLastPos.y, draggingCameraDistance)); if (hoveredController.canGrabPosition) { if (hoveredController.canGrabRotation) SetDragging(component, FreeControllerV3.SelectLinkState.PositionAndRotation); else SetDragging(component, FreeControllerV3.SelectLinkState.Position); } else if (hoveredController.canGrabRotation) SetDragging(component, FreeControllerV3.SelectLinkState.Rotation); } } } } else { float k = worldRadius * Screen.height / Mathf.Tan(sc.MonitorCenterCamera.fieldOfView * Mathf.Deg2Rad / 2.0f); Vector3 controllerScreenPos = sc.MonitorCenterCamera.WorldToScreenPoint(hoveredController.transform.position); float radius = (hoveredController == sc.GetSelectedController()) ? (sc.selectedControllerRotationHandle && sc.selectedControllerRotationHandle.enabled ? selectedDraggingRadius : (hoverScaleSelected * k / controllerScreenPos.z)) : (hoverScaleUnselected * k / controllerScreenPos.z); bool highlighted = controllerScreenPos.z > 0f && DistanceSquared(controllerScreenPos.x, controllerScreenPos.y, mousePos.x, mousePos.y) <= radius * radius; hoveredController.highlighted = highlighted; SetHelpHUDTextController(highlighted ? hoveredController : null); } } } } private bool IsControllerHidden(FreeControllerV3 controller) { if (!controller.isActiveAndEnabled) return true; else if (UserPreferences.singleton.hideInactiveTargets && controller.currentPositionState == FreeControllerV3.PositionState.Off && controller.currentRotationState == FreeControllerV3.RotationState.Off) return true; else if (!controller.containingAtom) return false; else if (controller.containingAtom.tempHidden) return true; else if (sc.showHiddenAtoms || !controller.containingAtom.hidden) return false; else return true; } private void ExtraSyncVisibility() { if (sc.mouseCrosshair) sc.mouseCrosshair.SetActive(freeMove); if (sc.freeMouseMoveModeIndicator) sc.freeMouseMoveModeIndicator.SetActive(freeMove); if (sc.activeUI != sc.lastActiveUI && emptySphereRenderer) emptySphereRenderer.SetActive(emptyCollisionEnabledJSON.val && sc.activeUI == SuperController.ActiveUI.SelectedOptions); interactableControllers.Clear(); if (sc.gameMode == SuperController.GameMode.Edit) { foreach (FreeControllerV3 controller in sc.GetAllFreeControllers()) if (!IsControllerHidden(controller)) interactableControllers.Add(controller); } else { foreach (FreeControllerV3 controller in sc.GetAllFreeControllers()) if (controller.interactableInPlayMode) interactableControllers.Add(controller); } if (sc.gameMode == SuperController.GameMode.Edit && sc.MainHUDVisible || !UserPreferences.singleton.showTargetsMenuOnly) { Atom selectedAtom = sc.GetSelectedAtom(); if (sc.GetTargetShow()) { foreach (FreeControllerV3 controller in sc.GetAllFreeControllers()) controller.hidden = !interactableControllers.Contains(controller); List allAnimationPatterns = sc.GetAllAnimationPatterns(); if (allAnimationPatterns != null) { bool animationSelected; foreach (AnimationPattern animationPattern in allAnimationPatterns) { animationSelected = false; if (animationPattern.containingAtom.on && (!sc.isolatedAtom || sc.isolatedAtom == animationPattern.containingAtom)) { animationPattern.draw = true; animationSelected = selectedAtom == animationPattern.containingAtom; } foreach (AnimationStep animationStep in animationPattern.steps) { if (!animationStep.containingAtom) continue; if (selectedAtom == animationStep.containingAtom) { animationSelected = true; break; } } if (animationPattern.draw) animationPattern.SetDrawColor(animationSelected ? Color.blue : Color.red); } } ForceProducerV2 forceProducer; foreach (string producerName in sc.forceProducerNames) { forceProducer = sc.ProducerNameToForceProducer(producerName); if (forceProducer) forceProducer.drawLines = selectedAtom == forceProducer.containingAtom; } } else { foreach (FreeControllerV3 controller in sc.GetAllFreeControllers()) controller.hidden = true; List allAnimationPatterns = sc.GetAllAnimationPatterns(); if (allAnimationPatterns != null) foreach (AnimationPattern animationPattern in allAnimationPatterns) animationPattern.draw = false; ForceProducerV2 forceProducer; foreach (string producerName in sc.forceProducerNames) { forceProducer = sc.ProducerNameToForceProducer(producerName); if (forceProducer) forceProducer.drawLines = false; } } } } private void CustomSelectController(FreeControllerV3 controller) { if (controller == modifiedController) return; if (controller) sc.SelectController(controller); else { sc.ClearSelection(); FCPositionHandle positionHandle = sc.selectedControllerPositionHandle; FCRotationHandle rotationHandle = sc.selectedControllerRotationHandle; if (positionHandle) { positionHandle.ForceDeselectAxis(); positionHandle.ForceDrop(); } if (rotationHandle) { rotationHandle.ForceDeselectAxis(); rotationHandle.ForceDrop(); } } SelectedControllerChanged(controller); } private void SelectedControllerChanged(FreeControllerV3 controller) { if (modifiedController) { modifiedController.positionGridMode = savedPositionGridMode; modifiedController.rotationGridMode = savedRotationGridMode; modifiedController.positionGrid = savedPositionGridAlign; modifiedController.rotationGrid = savedRotationGridAlign; modifiedController.selectedScale = 1f; } ClearEmptyShpereRenderer(); modifiedController = controller; if (modifiedController) { savedPositionGridMode = modifiedController.positionGridMode; savedRotationGridMode = modifiedController.rotationGridMode; savedPositionGridAlign = modifiedController.positionGrid; savedRotationGridAlign = modifiedController.rotationGrid; modifiedController.positionGrid = storablePositionGrid.val; modifiedController.rotationGrid = storableRotationGrid.val; modifiedController.selectedScale = 0.5f; TrySetEmptyShpereRenderer(modifiedController.containingAtom); } } private void SyncCollisionEnabled(bool v) { emptySphereRenderer.SetActive(v && sc.activeUI == SuperController.ActiveUI.SelectedOptions); } private void ClearEmptyShpereRenderer() { if (emptySphereRenderer) { emptySphereRenderer.SetActive(false); emptySphereRenderer = null; } if (emptyCollisionEnabledJSON != null) { emptyCollisionEnabledJSON.setCallbackFunction -= SyncCollisionEnabled; emptyCollisionEnabledJSON = null; } } private void TrySetEmptyShpereRenderer(Atom atom) { if (!atom) return; if (atom.type != "Empty") return; JSONStorableBool collisionEnabledJSON = atom.collisionEnabledJSON; if (collisionEnabledJSON != null) { emptySphereRenderer = atom.reParentObject?.Find("object/rescaleObject/SphereRender")?.gameObject; if (emptySphereRenderer) { emptyCollisionEnabledJSON = collisionEnabledJSON; SyncCollisionEnabled(collisionEnabledJSON.val); collisionEnabledJSON.setCallbackFunction += SyncCollisionEnabled; } } } private void OnHotkeyChangedDragRotationXY(string newKey) { hotkeyDragRotationXY = (KeyCode)Enum.Parse(typeof(KeyCode), newKey); } private void OnHotkeyChangedDragRotationZY(string newKey) { hotkeyDragRotationZY = (KeyCode)Enum.Parse(typeof(KeyCode), newKey); } private void OnHotkeyChangedShowPositionHandler(string newKey) { hotkeyShowPositionHandler = (KeyCode)Enum.Parse(typeof(KeyCode), newKey); } private void OnHotkeyChangedShowRotationHandler(string newKey) { hotkeyShowRotationHandler = (KeyCode)Enum.Parse(typeof(KeyCode), newKey); } private void OnHotkeyChangedToggleAxisMode(string newKey) { hotkeyToggleAxisMode = (KeyCode)Enum.Parse(typeof(KeyCode), newKey); } private void OnHotkeyChangedHoldAxisMode(string newKey) { hotkeyHoldAxisMode = (KeyCode)Enum.Parse(typeof(KeyCode), newKey); } private void OnHotkeyChangedDeselect(string newKey) { hotkeyDeselect = (KeyCode)Enum.Parse(typeof(KeyCode), newKey); } private void OnHotkeyChangedToggleGridMode(string newKey) { hotkeyToggleGridMode = (KeyCode)Enum.Parse(typeof(KeyCode), newKey); } private JSONStorableBool AddToggle(string label, bool defaultValue, bool rightSide) { JSONStorableBool storable = new JSONStorableBool(label, defaultValue); storable.storeType = JSONStorableParam.StoreType.Full; CreateToggle(storable, rightSide); RegisterBool(storable); return storable; } private JSONStorableFloat AddSlider(string label, float defaultValue, float minValue, float maxValue, bool rightSide) { JSONStorableFloat storable = new JSONStorableFloat(label, defaultValue, minValue, maxValue, true, true); storable.storeType = JSONStorableParam.StoreType.Full; RegisterFloat(storable); CreateSlider(storable, rightSide); return storable; } private JSONStorableFloat AddSliderInt(string label, float defaultValue, float minValue, float maxValue, bool rightSide) { JSONStorableFloat storable = new JSONStorableFloat(label, defaultValue, minValue, maxValue, true, true); storable.storeType = JSONStorableParam.StoreType.Full; RegisterFloat(storable); UIDynamicSlider slider = CreateSlider(storable, rightSide); slider.slider.wholeNumbers = true; slider.valueFormat = "F0"; return storable; } private JSONStorableStringChooser AddHotkeyPopup(string paramName, KeyCode defaultKeyCode, bool rightSide, JSONStorableStringChooser.SetStringCallback callback) { JSONStorableStringChooser storable = new JSONStorableStringChooser(paramName, hotkeys, defaultKeyCode.ToString(), paramName, callback); RegisterStringChooser(storable); CreateScrollablePopup(storable, rightSide); return storable; } private UIDynamic AddSpacer(float height, bool rightSide) { UIDynamic spacer = CreateSpacer(rightSide); spacer.height = height; return spacer; } private void AddSpacerBoth(float height) { AddSpacer(height, false); AddSpacer(height, true); } private JSONStorableString AddText(string paramName, string title, string text, float height, bool rightSide, bool shrinkHeight = false) { JSONStorableString storable = new JSONStorableString(paramName, " " + title + "\n" + (shrinkHeight ? "" : "\n") + text); UIDynamicTextField textfield = CreateTextField(storable, rightSide); textfield.height = height; return storable; } } }