VillagerManager.cs 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  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 + Vector3.up * 0.5f; // Position above ground
  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 + Vector3.up * 0.5f; // Position above ground
  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. GameObject[] workplaces = null;
  116. // Handle Builder job differently since it doesn't use tags
  117. if (job == JobType.Builder)
  118. {
  119. Building[] buildings = FindObjectsOfType<Building>();
  120. List<GameObject> builderWorkplaces = new List<GameObject>();
  121. foreach (Building building in buildings)
  122. {
  123. if (building.buildingName == "Builder's Workshop")
  124. {
  125. builderWorkplaces.Add(building.gameObject);
  126. }
  127. }
  128. workplaces = builderWorkplaces.ToArray();
  129. }
  130. else
  131. {
  132. string targetTag = job switch
  133. {
  134. JobType.Woodcutter => "Tree",
  135. JobType.Stonecutter => "Stone",
  136. JobType.Farmer => "Farm",
  137. _ => null
  138. };
  139. if (targetTag == null) return null;
  140. // Find closest workplace of the appropriate type
  141. workplaces = GameObject.FindGameObjectsWithTag(targetTag);
  142. }
  143. GameObject closest = null;
  144. float closestDistance = float.MaxValue;
  145. foreach (var workplace in workplaces)
  146. {
  147. // Check if workplace is already occupied
  148. if (IsWorkplaceOccupied(workplace)) continue;
  149. float distance = Vector3.Distance(villagerPosition, workplace.transform.position);
  150. if (distance < closestDistance)
  151. {
  152. closestDistance = distance;
  153. closest = workplace;
  154. }
  155. }
  156. return closest;
  157. }
  158. bool IsWorkplaceOccupied(GameObject workplace)
  159. {
  160. // Check if any villager is already working at this workplace
  161. return allVillagers.Any(v => v.targetWorkplace == workplace &&
  162. (v.state == VillagerState.MovingToWork || v.state == VillagerState.Working));
  163. }
  164. public void AssignVillagerToJob(Villager villager, JobType job)
  165. {
  166. if (villager != null)
  167. {
  168. villager.SetJob(job);
  169. Debug.Log($"{villager.villagerName} assigned to job: {job}");
  170. }
  171. }
  172. public void AssignVillagerToSpecificWorkplace(Villager villager, JobType job, GameObject specificWorkplace)
  173. {
  174. if (villager != null)
  175. {
  176. villager.SetJob(job);
  177. // If it's a resource job, assign the specific workplace
  178. if (job != JobType.None && specificWorkplace != null)
  179. {
  180. villager.AssignToSpecificWorkplace(specificWorkplace);
  181. }
  182. Debug.Log($"{villager.villagerName} assigned to {job} at {specificWorkplace?.name}");
  183. }
  184. }
  185. public List<Villager> GetVillagersWithJob(JobType job)
  186. {
  187. return allVillagers.Where(v => v.currentJob == job).ToList();
  188. }
  189. public List<Villager> GetUnemployedVillagers()
  190. {
  191. return allVillagers.Where(v => v.currentJob == JobType.None).ToList();
  192. }
  193. public List<Villager> GetAllVillagers()
  194. {
  195. return new List<Villager>(allVillagers);
  196. }
  197. public Villager GetVillagerByName(string name)
  198. {
  199. return allVillagers.FirstOrDefault(v => v.villagerName == name);
  200. }
  201. public Dictionary<JobType, int> GetJobCounts()
  202. {
  203. var jobCounts = new Dictionary<JobType, int>();
  204. foreach (JobType job in System.Enum.GetValues(typeof(JobType)))
  205. {
  206. jobCounts[job] = allVillagers.Count(v => v.currentJob == job);
  207. }
  208. return jobCounts;
  209. }
  210. public void RegisterVillager(Villager villager)
  211. {
  212. if (!allVillagers.Contains(villager))
  213. {
  214. allVillagers.Add(villager);
  215. }
  216. }
  217. public void UnregisterVillager(Villager villager)
  218. {
  219. allVillagers.Remove(villager);
  220. }
  221. // Called by UI or selection system for drag-and-drop job assignment
  222. public void HandleJobAssignment(Villager villager, Vector3 dropPosition)
  223. {
  224. // Determine job based on drop position (what's under the mouse)
  225. RaycastHit hit;
  226. if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit))
  227. {
  228. JobType newJob = DetermineJobFromHit(hit.collider.gameObject);
  229. if (newJob != JobType.None)
  230. {
  231. AssignVillagerToJob(villager, newJob);
  232. }
  233. }
  234. }
  235. JobType DetermineJobFromHit(GameObject hitObject)
  236. {
  237. // Check if it's a building first
  238. Building building = hitObject.GetComponent<Building>();
  239. if (building != null && building.buildingName == "Builder's Workshop")
  240. {
  241. return JobType.Builder;
  242. }
  243. return hitObject.tag switch
  244. {
  245. "Tree" => JobType.Woodcutter,
  246. "Stone" => JobType.Stonecutter,
  247. "Farm" => JobType.Farmer,
  248. _ => JobType.None
  249. };
  250. }
  251. void OnDrawGizmosSelected()
  252. {
  253. // Draw spawn area
  254. Gizmos.color = Color.blue;
  255. Gizmos.DrawWireSphere(spawnAreaCenter, spawnAreaRadius);
  256. }
  257. }