VillagerManager.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. using UnityEngine;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. public class VillagerManager : MonoBehaviour
  5. {
  6. [Header("Villager Settings")]
  7. public GameObject villagerPrefab;
  8. public int initialVillagerCount = 5;
  9. public Transform villagerParent;
  10. [Header("Spawning")]
  11. public Vector3 spawnAreaCenter = Vector3.zero;
  12. public float spawnAreaRadius = 5f;
  13. private List<Villager> allVillagers = new List<Villager>();
  14. private List<GameObject> workplaces = new List<GameObject>();
  15. void SpawnInitialVillagers()
  16. {
  17. if (villagerPrefab == null)
  18. {
  19. Debug.LogWarning("No villager prefab assigned, creating basic villagers");
  20. CreateBasicVillagers();
  21. return;
  22. }
  23. for (int i = 0; i < initialVillagerCount; i++)
  24. {
  25. Vector3 spawnPos = GetRandomSpawnPosition();
  26. GameObject villagerObj = Instantiate(villagerPrefab, spawnPos, Quaternion.identity, villagerParent);
  27. Villager villager = villagerObj.GetComponent<Villager>();
  28. if (villager != null)
  29. {
  30. villager.villagerName = $"Villager {i + 1}";
  31. allVillagers.Add(villager);
  32. }
  33. }
  34. }
  35. void CreateBasicVillagers()
  36. {
  37. for (int i = 0; i < initialVillagerCount; i++)
  38. {
  39. Vector3 spawnPos = GetRandomSpawnPosition();
  40. // Create basic villager GameObject
  41. GameObject villagerObj = GameObject.CreatePrimitive(PrimitiveType.Capsule);
  42. villagerObj.transform.position = spawnPos;
  43. villagerObj.transform.parent = villagerParent;
  44. villagerObj.name = $"Villager_{i + 1}";
  45. villagerObj.tag = "Villager";
  46. // Add Villager component
  47. Villager villager = villagerObj.AddComponent<Villager>();
  48. villager.villagerName = $"Villager {i + 1}";
  49. // Create selection indicator
  50. GameObject indicator = GameObject.CreatePrimitive(PrimitiveType.Cylinder);
  51. indicator.transform.SetParent(villagerObj.transform);
  52. indicator.transform.localPosition = Vector3.down * 0.6f;
  53. indicator.transform.localScale = Vector3.one * 1.2f;
  54. indicator.GetComponent<Renderer>().material.color = Color.yellow;
  55. indicator.SetActive(false);
  56. villager.selectionIndicator = indicator;
  57. // Remove collider from indicator
  58. Destroy(indicator.GetComponent<Collider>());
  59. allVillagers.Add(villager);
  60. }
  61. }
  62. Vector3 GetRandomSpawnPosition()
  63. {
  64. // Find TownHall position if it exists
  65. GameObject townHall = GameObject.FindWithTag("TownHall");
  66. Vector3 townHallPos = townHall != null ? townHall.transform.position : spawnAreaCenter;
  67. Vector3 spawnPos;
  68. int attempts = 0;
  69. do
  70. {
  71. // Generate random position around the area
  72. Vector2 randomCircle = Random.insideUnitCircle * spawnAreaRadius;
  73. spawnPos = spawnAreaCenter + new Vector3(randomCircle.x, 0, randomCircle.y);
  74. attempts++;
  75. // Ensure minimum distance from TownHall (3 units away)
  76. } while (Vector3.Distance(spawnPos, townHallPos) < 3f && attempts < 20);
  77. return spawnPos;
  78. }
  79. void Start()
  80. {
  81. SpawnInitialVillagers();
  82. FindWorkplaces();
  83. // Ensure villagers are not inside TownHall after spawning
  84. StartCoroutine(RepositionVillagersAwayFromTownHall());
  85. }
  86. System.Collections.IEnumerator RepositionVillagersAwayFromTownHall()
  87. {
  88. // Wait a frame to ensure TownHall is created
  89. yield return null;
  90. GameObject townHall = GameObject.FindWithTag("TownHall");
  91. if (townHall == null) yield break;
  92. Vector3 townHallPos = townHall.transform.position;
  93. foreach (var villager in allVillagers)
  94. {
  95. if (villager != null && Vector3.Distance(villager.transform.position, townHallPos) < 4f)
  96. {
  97. // Move villager to a safe distance from TownHall
  98. Vector2 randomDirection = Random.insideUnitCircle.normalized;
  99. Vector3 safePos = townHallPos + new Vector3(randomDirection.x, 0, randomDirection.y) * Random.Range(4f, 7f);
  100. villager.transform.position = safePos;
  101. Debug.Log($"Repositioned {villager.villagerName} away from TownHall");
  102. }
  103. }
  104. }
  105. void FindWorkplaces()
  106. {
  107. // Find all workplace objects in the scene
  108. workplaces.AddRange(GameObject.FindGameObjectsWithTag("Tree"));
  109. workplaces.AddRange(GameObject.FindGameObjectsWithTag("Stone"));
  110. workplaces.AddRange(GameObject.FindGameObjectsWithTag("Farm"));
  111. // Note: ConstructionSite tag not implemented yet
  112. }
  113. public GameObject FindWorkplaceForJob(JobType job, Vector3 villagerPosition)
  114. {
  115. string targetTag = job switch
  116. {
  117. JobType.Woodcutter => "Tree",
  118. JobType.Stonecutter => "Stone",
  119. JobType.Farmer => "Farm",
  120. JobType.Builder => "ConstructionSite",
  121. _ => null
  122. };
  123. if (targetTag == null) return null;
  124. // Find closest workplace of the appropriate type
  125. GameObject[] workplaces = GameObject.FindGameObjectsWithTag(targetTag);
  126. GameObject closest = null;
  127. float closestDistance = float.MaxValue;
  128. foreach (var workplace in workplaces)
  129. {
  130. // Check if workplace is already occupied
  131. if (IsWorkplaceOccupied(workplace)) continue;
  132. float distance = Vector3.Distance(villagerPosition, workplace.transform.position);
  133. if (distance < closestDistance)
  134. {
  135. closestDistance = distance;
  136. closest = workplace;
  137. }
  138. }
  139. return closest;
  140. }
  141. bool IsWorkplaceOccupied(GameObject workplace)
  142. {
  143. // Check if any villager is already working at this workplace
  144. return allVillagers.Any(v => v.targetWorkplace == workplace &&
  145. (v.state == VillagerState.MovingToWork || v.state == VillagerState.Working));
  146. }
  147. public void AssignVillagerToJob(Villager villager, JobType job)
  148. {
  149. if (villager != null)
  150. {
  151. villager.SetJob(job);
  152. Debug.Log($"{villager.villagerName} assigned to job: {job}");
  153. }
  154. }
  155. public void AssignVillagerToSpecificWorkplace(Villager villager, JobType job, GameObject specificWorkplace)
  156. {
  157. if (villager != null)
  158. {
  159. villager.SetJob(job);
  160. // If it's a resource job, assign the specific workplace
  161. if (job != JobType.None && specificWorkplace != null)
  162. {
  163. villager.AssignToSpecificWorkplace(specificWorkplace);
  164. }
  165. Debug.Log($"{villager.villagerName} assigned to {job} at {specificWorkplace?.name}");
  166. }
  167. }
  168. public List<Villager> GetVillagersWithJob(JobType job)
  169. {
  170. return allVillagers.Where(v => v.currentJob == job).ToList();
  171. }
  172. public List<Villager> GetUnemployedVillagers()
  173. {
  174. return allVillagers.Where(v => v.currentJob == JobType.None).ToList();
  175. }
  176. public List<Villager> GetAllVillagers()
  177. {
  178. return new List<Villager>(allVillagers);
  179. }
  180. public Villager GetVillagerByName(string name)
  181. {
  182. return allVillagers.FirstOrDefault(v => v.villagerName == name);
  183. }
  184. public Dictionary<JobType, int> GetJobCounts()
  185. {
  186. var jobCounts = new Dictionary<JobType, int>();
  187. foreach (JobType job in System.Enum.GetValues(typeof(JobType)))
  188. {
  189. jobCounts[job] = allVillagers.Count(v => v.currentJob == job);
  190. }
  191. return jobCounts;
  192. }
  193. public void RegisterVillager(Villager villager)
  194. {
  195. if (!allVillagers.Contains(villager))
  196. {
  197. allVillagers.Add(villager);
  198. }
  199. }
  200. public void UnregisterVillager(Villager villager)
  201. {
  202. allVillagers.Remove(villager);
  203. }
  204. // Called by UI or selection system for drag-and-drop job assignment
  205. public void HandleJobAssignment(Villager villager, Vector3 dropPosition)
  206. {
  207. // Determine job based on drop position (what's under the mouse)
  208. RaycastHit hit;
  209. if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit))
  210. {
  211. JobType newJob = DetermineJobFromHit(hit.collider.gameObject);
  212. if (newJob != JobType.None)
  213. {
  214. AssignVillagerToJob(villager, newJob);
  215. }
  216. }
  217. }
  218. JobType DetermineJobFromHit(GameObject hitObject)
  219. {
  220. return hitObject.tag switch
  221. {
  222. "Tree" => JobType.Woodcutter,
  223. "Stone" => JobType.Stonecutter,
  224. "Farm" => JobType.Farmer,
  225. "ConstructionSite" => JobType.Builder,
  226. _ => JobType.None
  227. };
  228. }
  229. void OnDrawGizmosSelected()
  230. {
  231. // Draw spawn area
  232. Gizmos.color = Color.blue;
  233. Gizmos.DrawWireSphere(spawnAreaCenter, spawnAreaRadius);
  234. }
  235. }