using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; /// /// Manages selection state for placed interactable objects. /// Listens to RayInteractor.OnSelectEvent (which only fires when no ghost is active). /// Supports single and multi-select (Ctrl/Shift). /// public class SelectionManager : MonoBehaviour { private static SelectionManager instance; public static SelectionManager getInstance() => instance; private RayInteractor rayInteractor; private BaseInputProvider inputProvider; private readonly List currentSelections = new List(); private bool multiSelectEnabled = false; public UnityEvent SelectionEvent; // ------------------------------------------------------------------------- // Unity lifecycle // ------------------------------------------------------------------------- private void Awake() { if (instance == null) instance = this; else Destroy(gameObject); } private void Start() { rayInteractor = RayInteractor.getInstance(); inputProvider = InputProviderManager.getInstance().getCurrentInputProvider(); inputProvider.OnEnableMultiSelect.AddListener(() => multiSelectEnabled = true); inputProvider.OnDisableMultiSelect.AddListener(() => multiSelectEnabled = false); // RayInteractor only fires this when no ghost is active, so we can safely handle selection here rayInteractor.OnSelectEvent.AddListener(HandleSelect); } // ------------------------------------------------------------------------- // Selection logic // ------------------------------------------------------------------------- private void HandleSelect(IInteractable interactable) { if (InputModeManager.Is(InputMode.UI)) return; if (interactable == null) { SelectionEvent?.Invoke(interactable); // Clicked empty space – clear selection unless multi-select is held if (!multiSelectEnabled) ClearSelection(); return; } if (multiSelectEnabled) { // Toggle the clicked interactable if (currentSelections.Contains(interactable)) { currentSelections.Remove(interactable); interactable.highLightOff(); } else { currentSelections.Add(interactable); interactable.highLightOn(); } } else { // Replace selection with just this interactable bool Exists = currentSelections.Contains(interactable); ClearSelection(); if (!Exists) { currentSelections.Add(interactable); interactable.highLightOn(); } } SelectionEvent?.Invoke(interactable); } // ------------------------------------------------------------------------- // Public API // ------------------------------------------------------------------------- public List GetCurrentSelections() => currentSelections; public void ClearSelection() { foreach (IInteractable selection in currentSelections) selection.highLightOff(); currentSelections.Clear(); SelectionEvent?.Invoke(null); } public bool HasOneSelection() { return !(currentSelections.Count > 1 || currentSelections.Count == 0); } }