SelectionManager.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. using UnityEngine;
  2. using UnityEngine.InputSystem;
  3. using System.Collections.Generic;
  4. public class SelectionManager : MonoBehaviour
  5. {
  6. [Header("Selection Settings")]
  7. public LayerMask selectableLayerMask = -1;
  8. public Material ghostMaterial;
  9. private Villager selectedVillager;
  10. private GameObject selectedObject;
  11. private Camera playerCamera;
  12. // Drag and Drop
  13. private bool isDragging = false;
  14. private GameObject dragGhost;
  15. private Vector3 dragStartPosition;
  16. // Box Selection
  17. private bool isBoxSelecting = false;
  18. private Vector2 boxSelectionStart;
  19. public Villager SelectedVillager => selectedVillager;
  20. public GameObject SelectedObject => selectedObject;
  21. // Event for selection changes
  22. public delegate void SelectionChangedHandler();
  23. public static event SelectionChangedHandler OnSelectionChanged;
  24. void Start()
  25. {
  26. playerCamera = Camera.main;
  27. if (playerCamera == null)
  28. playerCamera = FindFirstObjectByType<Camera>();
  29. }
  30. void Update()
  31. {
  32. HandleInput();
  33. HandleDragAndDrop();
  34. }
  35. void HandleInput()
  36. {
  37. // Left click - selection or box selection start
  38. var mouse = Mouse.current;
  39. if (mouse.leftButton.wasPressedThisFrame)
  40. {
  41. // Start potential box selection
  42. boxSelectionStart = mouse.position.ReadValue();
  43. isBoxSelecting = false; // Not yet, wait for drag
  44. }
  45. // Check if dragging for box selection (only when no villager is selected)
  46. if (mouse.leftButton.isPressed && !isDragging && selectedVillager == null)
  47. {
  48. Vector2 currentMousePos = mouse.position.ReadValue();
  49. float dragDistance = Vector2.Distance(boxSelectionStart, currentMousePos);
  50. // Start box selection if dragged more than 10 pixels
  51. if (dragDistance > 10f && !isBoxSelecting)
  52. {
  53. isBoxSelecting = true;
  54. Debug.Log("Started box selection");
  55. }
  56. } // Complete box selection on release
  57. if (mouse.leftButton.wasReleasedThisFrame)
  58. {
  59. if (isBoxSelecting)
  60. {
  61. CompleteBoxSelection();
  62. isBoxSelecting = false;
  63. }
  64. else if (!isDragging)
  65. {
  66. // Single click selection (only if not dragging)
  67. HandleSelection();
  68. }
  69. }
  70. // Right click - cancel/deselect
  71. if (mouse.rightButton.wasPressedThisFrame)
  72. {
  73. CancelCurrentAction();
  74. }
  75. }
  76. void HandleSelection()
  77. {
  78. if (Mouse.current == null || playerCamera == null) return;
  79. Ray ray = playerCamera.ScreenPointToRay(Mouse.current.position.ReadValue());
  80. RaycastHit hit;
  81. if (Physics.Raycast(ray, out hit, Mathf.Infinity, selectableLayerMask))
  82. {
  83. GameObject hitObject = hit.collider.gameObject;
  84. // Check if we hit a villager
  85. Villager villager = hitObject.GetComponent<Villager>();
  86. if (villager != null)
  87. {
  88. SelectVillager(villager);
  89. return;
  90. }
  91. // Check for other selectable objects
  92. ISelectable selectable = hitObject.GetComponent<ISelectable>();
  93. if (selectable != null)
  94. {
  95. SelectObject(hitObject);
  96. return;
  97. }
  98. // If we hit something else, check parent objects
  99. Transform parent = hitObject.transform.parent;
  100. while (parent != null)
  101. {
  102. villager = parent.GetComponent<Villager>();
  103. if (villager != null)
  104. {
  105. SelectVillager(villager);
  106. return;
  107. }
  108. selectable = parent.GetComponent<ISelectable>();
  109. if (selectable != null)
  110. {
  111. SelectObject(parent.gameObject);
  112. return;
  113. }
  114. parent = parent.parent;
  115. }
  116. }
  117. // If we didn't hit anything selectable, deselect
  118. DeselectAll();
  119. }
  120. void HandleDragAndDrop()
  121. {
  122. if (Mouse.current == null) return;
  123. var mouse = Mouse.current;
  124. // Don't start dragging if box selecting
  125. if (isBoxSelecting) return;
  126. // Start dragging
  127. if (selectedVillager != null && mouse.leftButton.wasPressedThisFrame && !isDragging)
  128. {
  129. StartDrag();
  130. }
  131. // Update drag
  132. if (isDragging)
  133. {
  134. UpdateDrag();
  135. }
  136. // End dragging
  137. if (isDragging && mouse.leftButton.wasReleasedThisFrame)
  138. {
  139. EndDrag();
  140. }
  141. }
  142. void StartDrag()
  143. {
  144. isDragging = true;
  145. dragStartPosition = selectedVillager.transform.position;
  146. // Create ghost object
  147. CreateDragGhost();
  148. }
  149. void UpdateDrag()
  150. {
  151. if (dragGhost != null)
  152. {
  153. // Move ghost to mouse position
  154. Ray ray = playerCamera.ScreenPointToRay(Mouse.current.position.ReadValue());
  155. if (Physics.Raycast(ray, out RaycastHit hit))
  156. {
  157. dragGhost.transform.position = hit.point + Vector3.up * 0.1f;
  158. }
  159. }
  160. }
  161. void EndDrag()
  162. {
  163. isDragging = false;
  164. if (dragGhost != null)
  165. {
  166. Vector3 dropPosition = dragGhost.transform.position;
  167. // Check what we're dropping on
  168. Ray ray = playerCamera.ScreenPointToRay(Mouse.current.position.ReadValue());
  169. if (Physics.Raycast(ray, out RaycastHit hit))
  170. {
  171. HandleDrop(hit.collider.gameObject, dropPosition);
  172. }
  173. DestroyDragGhost();
  174. }
  175. }
  176. void CreateDragGhost()
  177. {
  178. if (selectedVillager != null)
  179. {
  180. dragGhost = Instantiate(selectedVillager.gameObject);
  181. // Make it a ghost (semi-transparent, no collisions)
  182. Collider ghostCollider = dragGhost.GetComponent<Collider>();
  183. if (ghostCollider != null)
  184. ghostCollider.enabled = false;
  185. // Remove any scripts that might interfere
  186. Villager ghostVillager = dragGhost.GetComponent<Villager>();
  187. if (ghostVillager != null)
  188. Destroy(ghostVillager);
  189. // Apply ghost material
  190. Renderer ghostRenderer = dragGhost.GetComponent<Renderer>();
  191. if (ghostRenderer != null && ghostMaterial != null)
  192. {
  193. ghostRenderer.material = ghostMaterial;
  194. }
  195. else if (ghostRenderer != null)
  196. {
  197. Color ghostColor = ghostRenderer.material.color;
  198. ghostColor.a = 0.5f;
  199. ghostRenderer.material.color = ghostColor;
  200. }
  201. }
  202. }
  203. void DestroyDragGhost()
  204. {
  205. if (dragGhost != null)
  206. {
  207. Destroy(dragGhost);
  208. dragGhost = null;
  209. }
  210. }
  211. void HandleDrop(GameObject dropTarget, Vector3 dropPosition)
  212. {
  213. if (selectedVillager == null) return;
  214. VillagerManager villagerManager = GameManager.Instance.villagerManager;
  215. // Determine what to do based on drop target
  216. string targetTag = dropTarget.tag;
  217. JobType newJob = targetTag switch
  218. {
  219. "Tree" => JobType.Woodcutter,
  220. "Stone" => JobType.Stonecutter,
  221. "Farm" => JobType.Farmer,
  222. "TownHall" => JobType.None, // Unemployed - return to town hall
  223. _ => selectedVillager.currentJob // Keep current job if dropped on something else
  224. };
  225. // Special check for Builder job - requires workshop
  226. if (newJob == JobType.Builder)
  227. {
  228. GameObject[] workshops = GameObject.FindGameObjectsWithTag("BuildingWorkshop");
  229. if (workshops == null || workshops.Length == 0)
  230. {
  231. Debug.LogWarning("⚠️ Cannot assign Builder job - No Builder's Workshop found! Build one first.");
  232. return;
  233. }
  234. }
  235. if (newJob != selectedVillager.currentJob || (newJob != JobType.None && selectedVillager.targetWorkplace != dropTarget))
  236. {
  237. // Assign job and specific workplace
  238. villagerManager.AssignVillagerToSpecificWorkplace(selectedVillager, newJob, dropTarget);
  239. Debug.Log($"Assigned {selectedVillager.villagerName} to {newJob} at specific {dropTarget.name}");
  240. // Deselect to prevent accidental clicks
  241. DeselectAll();
  242. }
  243. }
  244. public void SelectVillager(Villager villager)
  245. {
  246. // Deselect previous
  247. DeselectAll();
  248. selectedVillager = villager;
  249. selectedObject = villager.gameObject;
  250. villager.SetSelected(true);
  251. Debug.Log($"SelectionManager: Selected villager: {villager.villagerName}");
  252. Debug.Log($"SelectionManager: OnVillagerSelected event triggered");
  253. // Notify UI of selection change
  254. OnSelectionChanged?.Invoke();
  255. }
  256. public void SelectObject(GameObject obj)
  257. {
  258. // Deselect previous
  259. DeselectAll();
  260. selectedObject = obj;
  261. ISelectable selectable = obj.GetComponent<ISelectable>();
  262. selectable?.OnSelected();
  263. Debug.Log($"Selected object: {obj.name}");
  264. // Notify UI of selection change
  265. OnSelectionChanged?.Invoke();
  266. }
  267. public void DeselectAll()
  268. {
  269. if (selectedVillager != null)
  270. {
  271. selectedVillager.SetSelected(false);
  272. selectedVillager = null;
  273. }
  274. if (selectedObject != null)
  275. {
  276. ISelectable selectable = selectedObject.GetComponent<ISelectable>();
  277. selectable?.OnDeselected();
  278. selectedObject = null;
  279. }
  280. // Cancel any ongoing drag
  281. if (isDragging)
  282. {
  283. isDragging = false;
  284. DestroyDragGhost();
  285. }
  286. Debug.Log("SelectionManager: Deselected all");
  287. // Notify UI of selection change
  288. OnSelectionChanged?.Invoke();
  289. }
  290. void OnGUI()
  291. {
  292. if (isBoxSelecting && Mouse.current != null)
  293. {
  294. Vector2 mouseStart = boxSelectionStart;
  295. Vector2 mouseEnd = Mouse.current.position.ReadValue();
  296. // Convert to GUI coordinates (Y is flipped)
  297. mouseStart.y = Screen.height - mouseStart.y;
  298. mouseEnd.y = Screen.height - mouseEnd.y;
  299. Rect selectionRect = new Rect(
  300. Mathf.Min(mouseStart.x, mouseEnd.x),
  301. Mathf.Min(mouseStart.y, mouseEnd.y),
  302. Mathf.Abs(mouseStart.x - mouseEnd.x),
  303. Mathf.Abs(mouseStart.y - mouseEnd.y)
  304. );
  305. // Draw selection box
  306. GUI.color = new Color(0.8f, 0.8f, 0.95f, 0.25f);
  307. GUI.DrawTexture(selectionRect, Texture2D.whiteTexture);
  308. GUI.color = new Color(0.8f, 0.8f, 0.95f, 1f);
  309. GUI.Box(selectionRect, "");
  310. }
  311. }
  312. void CompleteBoxSelection()
  313. {
  314. Vector2 mouseStart = boxSelectionStart;
  315. Vector2 mouseEnd = Mouse.current.position.ReadValue();
  316. // Create screen space rectangle
  317. Rect selectionRect = new Rect(
  318. Mathf.Min(mouseStart.x, mouseEnd.x),
  319. Mathf.Min(mouseStart.y, mouseEnd.y),
  320. Mathf.Abs(mouseStart.x - mouseEnd.x),
  321. Mathf.Abs(mouseStart.y - mouseEnd.y)
  322. );
  323. // Find all villagers
  324. Villager[] allVillagers = FindObjectsByType<Villager>(FindObjectsSortMode.None);
  325. Villager firstVillager = null;
  326. foreach (Villager villager in allVillagers)
  327. {
  328. Vector3 screenPos = playerCamera.WorldToScreenPoint(villager.transform.position);
  329. // Check if villager is in selection box and in front of camera
  330. if (screenPos.z > 0 && selectionRect.Contains(new Vector2(screenPos.x, screenPos.y)))
  331. {
  332. if (firstVillager == null)
  333. {
  334. firstVillager = villager;
  335. }
  336. }
  337. }
  338. // Select the first villager found
  339. if (firstVillager != null)
  340. {
  341. SelectVillager(firstVillager);
  342. Debug.Log($"Box selection completed: selected {firstVillager.villagerName}");
  343. }
  344. else
  345. {
  346. Debug.Log("Box selection completed: no villagers found");
  347. }
  348. }
  349. void CancelCurrentAction()
  350. {
  351. if (isDragging)
  352. {
  353. isDragging = false;
  354. DestroyDragGhost();
  355. }
  356. else if (isBoxSelecting)
  357. {
  358. isBoxSelecting = false;
  359. }
  360. else
  361. {
  362. DeselectAll();
  363. }
  364. Debug.Log("Cancelled current action");
  365. }
  366. }
  367. // Interface for selectable objects
  368. public interface ISelectable
  369. {
  370. void OnSelected();
  371. void OnDeselected();
  372. }