using UnityEngine; using UnityEngine.InputSystem; using System.Collections.Generic; public class SelectionManager : MonoBehaviour { [Header("Selection Settings")] public LayerMask selectableLayerMask = -1; public Material ghostMaterial; private Villager selectedVillager; private GameObject selectedObject; private Camera playerCamera; // Drag and Drop private bool isDragging = false; private GameObject dragGhost; private Vector3 dragStartPosition; // Box Selection private bool isBoxSelecting = false; private Vector2 boxSelectionStart; public Villager SelectedVillager => selectedVillager; public GameObject SelectedObject => selectedObject; // Event for selection changes public delegate void SelectionChangedHandler(); public static event SelectionChangedHandler OnSelectionChanged; void Start() { playerCamera = Camera.main; if (playerCamera == null) playerCamera = FindObjectOfType(); } void Update() { HandleInput(); HandleDragAndDrop(); } void HandleInput() { // Left click - selection or box selection start var mouse = Mouse.current; if (mouse.leftButton.wasPressedThisFrame) { // Start potential box selection boxSelectionStart = mouse.position.ReadValue(); isBoxSelecting = false; // Not yet, wait for drag } // Check if dragging for box selection (only when no villager is selected) if (mouse.leftButton.isPressed && !isDragging && selectedVillager == null) { Vector2 currentMousePos = mouse.position.ReadValue(); float dragDistance = Vector2.Distance(boxSelectionStart, currentMousePos); // Start box selection if dragged more than 10 pixels if (dragDistance > 10f && !isBoxSelecting) { isBoxSelecting = true; Debug.Log("Started box selection"); } } // Complete box selection on release if (mouse.leftButton.wasReleasedThisFrame) { if (isBoxSelecting) { CompleteBoxSelection(); isBoxSelecting = false; } else if (!isDragging) { // Single click selection (only if not dragging) HandleSelection(); } } // Right click - cancel/deselect if (mouse.rightButton.wasPressedThisFrame) { CancelCurrentAction(); } } void HandleSelection() { if (Mouse.current == null || playerCamera == null) return; Ray ray = playerCamera.ScreenPointToRay(Mouse.current.position.ReadValue()); RaycastHit hit; if (Physics.Raycast(ray, out hit, Mathf.Infinity, selectableLayerMask)) { GameObject hitObject = hit.collider.gameObject; // Check if we hit a villager Villager villager = hitObject.GetComponent(); if (villager != null) { SelectVillager(villager); return; } // Check for other selectable objects ISelectable selectable = hitObject.GetComponent(); if (selectable != null) { SelectObject(hitObject); return; } // If we hit something else, check parent objects Transform parent = hitObject.transform.parent; while (parent != null) { villager = parent.GetComponent(); if (villager != null) { SelectVillager(villager); return; } selectable = parent.GetComponent(); if (selectable != null) { SelectObject(parent.gameObject); return; } parent = parent.parent; } } // If we didn't hit anything selectable, deselect DeselectAll(); } void HandleDragAndDrop() { if (Mouse.current == null) return; var mouse = Mouse.current; // Don't start dragging if box selecting if (isBoxSelecting) return; // Start dragging if (selectedVillager != null && mouse.leftButton.wasPressedThisFrame && !isDragging) { StartDrag(); } // Update drag if (isDragging) { UpdateDrag(); } // End dragging if (isDragging && mouse.leftButton.wasReleasedThisFrame) { EndDrag(); } } void StartDrag() { isDragging = true; dragStartPosition = selectedVillager.transform.position; // Create ghost object CreateDragGhost(); } void UpdateDrag() { if (dragGhost != null) { // Move ghost to mouse position Ray ray = playerCamera.ScreenPointToRay(Mouse.current.position.ReadValue()); if (Physics.Raycast(ray, out RaycastHit hit)) { dragGhost.transform.position = hit.point + Vector3.up * 0.1f; } } } void EndDrag() { isDragging = false; if (dragGhost != null) { Vector3 dropPosition = dragGhost.transform.position; // Check what we're dropping on Ray ray = playerCamera.ScreenPointToRay(Mouse.current.position.ReadValue()); if (Physics.Raycast(ray, out RaycastHit hit)) { HandleDrop(hit.collider.gameObject, dropPosition); } DestroyDragGhost(); } } void CreateDragGhost() { if (selectedVillager != null) { dragGhost = Instantiate(selectedVillager.gameObject); // Make it a ghost (semi-transparent, no collisions) Collider ghostCollider = dragGhost.GetComponent(); if (ghostCollider != null) ghostCollider.enabled = false; // Remove any scripts that might interfere Villager ghostVillager = dragGhost.GetComponent(); if (ghostVillager != null) Destroy(ghostVillager); // Apply ghost material Renderer ghostRenderer = dragGhost.GetComponent(); if (ghostRenderer != null && ghostMaterial != null) { ghostRenderer.material = ghostMaterial; } else if (ghostRenderer != null) { Color ghostColor = ghostRenderer.material.color; ghostColor.a = 0.5f; ghostRenderer.material.color = ghostColor; } } } void DestroyDragGhost() { if (dragGhost != null) { Destroy(dragGhost); dragGhost = null; } } void HandleDrop(GameObject dropTarget, Vector3 dropPosition) { if (selectedVillager == null) return; VillagerManager villagerManager = GameManager.Instance.villagerManager; // Determine what to do based on drop target string targetTag = dropTarget.tag; JobType newJob = targetTag switch { "Tree" => JobType.Woodcutter, "Stone" => JobType.Stonecutter, "Farm" => JobType.Farmer, "TownHall" => JobType.None, // Unemployed - return to town hall _ => selectedVillager.currentJob // Keep current job if dropped on something else }; // Special check for Builder job - requires workshop if (newJob == JobType.Builder) { GameObject[] workshops = GameObject.FindGameObjectsWithTag("BuildingWorkshop"); if (workshops == null || workshops.Length == 0) { Debug.LogWarning("⚠️ Cannot assign Builder job - No Builder's Workshop found! Build one first."); return; } } if (newJob != selectedVillager.currentJob || (newJob != JobType.None && selectedVillager.targetWorkplace != dropTarget)) { // Assign job and specific workplace villagerManager.AssignVillagerToSpecificWorkplace(selectedVillager, newJob, dropTarget); Debug.Log($"Assigned {selectedVillager.villagerName} to {newJob} at specific {dropTarget.name}"); } } public void SelectVillager(Villager villager) { // Deselect previous DeselectAll(); selectedVillager = villager; selectedObject = villager.gameObject; villager.SetSelected(true); Debug.Log($"SelectionManager: Selected villager: {villager.villagerName}"); Debug.Log($"SelectionManager: OnVillagerSelected event triggered"); // Notify UI of selection change OnSelectionChanged?.Invoke(); } public void SelectObject(GameObject obj) { // Deselect previous DeselectAll(); selectedObject = obj; ISelectable selectable = obj.GetComponent(); selectable?.OnSelected(); Debug.Log($"Selected object: {obj.name}"); // Notify UI of selection change OnSelectionChanged?.Invoke(); } public void DeselectAll() { if (selectedVillager != null) { selectedVillager.SetSelected(false); selectedVillager = null; } if (selectedObject != null) { ISelectable selectable = selectedObject.GetComponent(); selectable?.OnDeselected(); selectedObject = null; } // Cancel any ongoing drag if (isDragging) { isDragging = false; DestroyDragGhost(); } Debug.Log("SelectionManager: Deselected all"); // Notify UI of selection change OnSelectionChanged?.Invoke(); } void OnGUI() { if (isBoxSelecting && Mouse.current != null) { Vector2 mouseStart = boxSelectionStart; Vector2 mouseEnd = Mouse.current.position.ReadValue(); // Convert to GUI coordinates (Y is flipped) mouseStart.y = Screen.height - mouseStart.y; mouseEnd.y = Screen.height - mouseEnd.y; Rect selectionRect = new Rect( Mathf.Min(mouseStart.x, mouseEnd.x), Mathf.Min(mouseStart.y, mouseEnd.y), Mathf.Abs(mouseStart.x - mouseEnd.x), Mathf.Abs(mouseStart.y - mouseEnd.y) ); // Draw selection box GUI.color = new Color(0.8f, 0.8f, 0.95f, 0.25f); GUI.DrawTexture(selectionRect, Texture2D.whiteTexture); GUI.color = new Color(0.8f, 0.8f, 0.95f, 1f); GUI.Box(selectionRect, ""); } } void CompleteBoxSelection() { Vector2 mouseStart = boxSelectionStart; Vector2 mouseEnd = Mouse.current.position.ReadValue(); // Create screen space rectangle Rect selectionRect = new Rect( Mathf.Min(mouseStart.x, mouseEnd.x), Mathf.Min(mouseStart.y, mouseEnd.y), Mathf.Abs(mouseStart.x - mouseEnd.x), Mathf.Abs(mouseStart.y - mouseEnd.y) ); // Find all villagers Villager[] allVillagers = FindObjectsOfType(); Villager firstVillager = null; foreach (Villager villager in allVillagers) { Vector3 screenPos = playerCamera.WorldToScreenPoint(villager.transform.position); // Check if villager is in selection box and in front of camera if (screenPos.z > 0 && selectionRect.Contains(new Vector2(screenPos.x, screenPos.y))) { if (firstVillager == null) { firstVillager = villager; } } } // Select the first villager found if (firstVillager != null) { SelectVillager(firstVillager); Debug.Log($"Box selection completed: selected {firstVillager.villagerName}"); } else { Debug.Log("Box selection completed: no villagers found"); } } void CancelCurrentAction() { if (isDragging) { isDragging = false; DestroyDragGhost(); } else if (isBoxSelecting) { isBoxSelecting = false; } else { DeselectAll(); } Debug.Log("Cancelled current action"); } } // Interface for selectable objects public interface ISelectable { void OnSelected(); void OnDeselected(); }