SelectionManager.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  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 = FindObjectOfType<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. }
  241. }
  242. public void SelectVillager(Villager villager)
  243. {
  244. // Deselect previous
  245. DeselectAll();
  246. selectedVillager = villager;
  247. selectedObject = villager.gameObject;
  248. villager.SetSelected(true);
  249. Debug.Log($"SelectionManager: Selected villager: {villager.villagerName}");
  250. Debug.Log($"SelectionManager: OnVillagerSelected event triggered");
  251. // Notify UI of selection change
  252. OnSelectionChanged?.Invoke();
  253. }
  254. public void SelectObject(GameObject obj)
  255. {
  256. // Deselect previous
  257. DeselectAll();
  258. selectedObject = obj;
  259. ISelectable selectable = obj.GetComponent<ISelectable>();
  260. selectable?.OnSelected();
  261. Debug.Log($"Selected object: {obj.name}");
  262. // Notify UI of selection change
  263. OnSelectionChanged?.Invoke();
  264. }
  265. public void DeselectAll()
  266. {
  267. if (selectedVillager != null)
  268. {
  269. selectedVillager.SetSelected(false);
  270. selectedVillager = null;
  271. }
  272. if (selectedObject != null)
  273. {
  274. ISelectable selectable = selectedObject.GetComponent<ISelectable>();
  275. selectable?.OnDeselected();
  276. selectedObject = null;
  277. }
  278. // Cancel any ongoing drag
  279. if (isDragging)
  280. {
  281. isDragging = false;
  282. DestroyDragGhost();
  283. }
  284. Debug.Log("SelectionManager: Deselected all");
  285. // Notify UI of selection change
  286. OnSelectionChanged?.Invoke();
  287. }
  288. void OnGUI()
  289. {
  290. if (isBoxSelecting && Mouse.current != null)
  291. {
  292. Vector2 mouseStart = boxSelectionStart;
  293. Vector2 mouseEnd = Mouse.current.position.ReadValue();
  294. // Convert to GUI coordinates (Y is flipped)
  295. mouseStart.y = Screen.height - mouseStart.y;
  296. mouseEnd.y = Screen.height - mouseEnd.y;
  297. Rect selectionRect = new Rect(
  298. Mathf.Min(mouseStart.x, mouseEnd.x),
  299. Mathf.Min(mouseStart.y, mouseEnd.y),
  300. Mathf.Abs(mouseStart.x - mouseEnd.x),
  301. Mathf.Abs(mouseStart.y - mouseEnd.y)
  302. );
  303. // Draw selection box
  304. GUI.color = new Color(0.8f, 0.8f, 0.95f, 0.25f);
  305. GUI.DrawTexture(selectionRect, Texture2D.whiteTexture);
  306. GUI.color = new Color(0.8f, 0.8f, 0.95f, 1f);
  307. GUI.Box(selectionRect, "");
  308. }
  309. }
  310. void CompleteBoxSelection()
  311. {
  312. Vector2 mouseStart = boxSelectionStart;
  313. Vector2 mouseEnd = Mouse.current.position.ReadValue();
  314. // Create screen space rectangle
  315. Rect selectionRect = new Rect(
  316. Mathf.Min(mouseStart.x, mouseEnd.x),
  317. Mathf.Min(mouseStart.y, mouseEnd.y),
  318. Mathf.Abs(mouseStart.x - mouseEnd.x),
  319. Mathf.Abs(mouseStart.y - mouseEnd.y)
  320. );
  321. // Find all villagers
  322. Villager[] allVillagers = FindObjectsOfType<Villager>();
  323. Villager firstVillager = null;
  324. foreach (Villager villager in allVillagers)
  325. {
  326. Vector3 screenPos = playerCamera.WorldToScreenPoint(villager.transform.position);
  327. // Check if villager is in selection box and in front of camera
  328. if (screenPos.z > 0 && selectionRect.Contains(new Vector2(screenPos.x, screenPos.y)))
  329. {
  330. if (firstVillager == null)
  331. {
  332. firstVillager = villager;
  333. }
  334. }
  335. }
  336. // Select the first villager found
  337. if (firstVillager != null)
  338. {
  339. SelectVillager(firstVillager);
  340. Debug.Log($"Box selection completed: selected {firstVillager.villagerName}");
  341. }
  342. else
  343. {
  344. Debug.Log("Box selection completed: no villagers found");
  345. }
  346. }
  347. void CancelCurrentAction()
  348. {
  349. if (isDragging)
  350. {
  351. isDragging = false;
  352. DestroyDragGhost();
  353. }
  354. else if (isBoxSelecting)
  355. {
  356. isBoxSelecting = false;
  357. }
  358. else
  359. {
  360. DeselectAll();
  361. }
  362. Debug.Log("Cancelled current action");
  363. }
  364. }
  365. // Interface for selectable objects
  366. public interface ISelectable
  367. {
  368. void OnSelected();
  369. void OnDeselected();
  370. }