Villager.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. using UnityEngine;
  2. using UnityEngine.InputSystem;
  3. using System.Collections.Generic;
  4. using System;
  5. [System.Serializable]
  6. public enum JobType
  7. {
  8. None, // Unemployed - transports goods
  9. Woodcutter, // Cuts trees
  10. Stonecutter, // Mines stone
  11. Farmer, // Grows food
  12. Builder // Constructs buildings
  13. }
  14. [System.Serializable]
  15. public enum VillagerState
  16. {
  17. Idle,
  18. MovingToWork,
  19. Working,
  20. MovingToDeliver,
  21. Delivering,
  22. Transporting
  23. }
  24. [System.Serializable]
  25. public class Experience
  26. {
  27. public float woodcutting = 1f;
  28. public float stonecutting = 1f;
  29. public float farming = 1f;
  30. public float building = 1f;
  31. public float GetExperienceForJob(JobType job)
  32. {
  33. return job switch
  34. {
  35. JobType.Woodcutter => woodcutting,
  36. JobType.Stonecutter => stonecutting,
  37. JobType.Farmer => farming,
  38. JobType.Builder => building,
  39. _ => 1f
  40. };
  41. }
  42. public void AddExperience(JobType job, float amount)
  43. {
  44. switch (job)
  45. {
  46. case JobType.Woodcutter:
  47. woodcutting += amount;
  48. break;
  49. case JobType.Stonecutter:
  50. stonecutting += amount;
  51. break;
  52. case JobType.Farmer:
  53. farming += amount;
  54. break;
  55. case JobType.Builder:
  56. building += amount;
  57. break;
  58. }
  59. }
  60. }
  61. public class Villager : MonoBehaviour
  62. {
  63. [Header("Villager Info")]
  64. public string villagerName;
  65. public JobType currentJob = JobType.None;
  66. public VillagerState state = VillagerState.Idle;
  67. [Header("Experience")]
  68. public Experience experience = new Experience();
  69. [Header("Work Settings")]
  70. public float workSpeed = 1f;
  71. public float moveSpeed = 3f;
  72. public float workRange = 2f;
  73. [Header("Current Task")]
  74. public GameObject targetWorkplace;
  75. public GameObject targetDelivery;
  76. public ResourceType carryingResource;
  77. public int carryingAmount = 0;
  78. public int maxCarryCapacity = 10;
  79. [Header("Visual")]
  80. public Renderer villagerRenderer;
  81. public GameObject selectionIndicator;
  82. private Vector3 targetPosition;
  83. private bool isSelected = false;
  84. private float workTimer = 0f;
  85. private VillagerManager villagerManager;
  86. public static event Action<Villager> OnVillagerSelected;
  87. public static event Action<Villager, JobType> OnJobChanged;
  88. void Start()
  89. {
  90. villagerManager = FindObjectOfType<VillagerManager>();
  91. if (villagerRenderer == null)
  92. villagerRenderer = GetComponent<Renderer>();
  93. if (selectionIndicator != null)
  94. selectionIndicator.SetActive(false);
  95. // Random starting experience
  96. experience.woodcutting = UnityEngine.Random.Range(0.8f, 1.5f);
  97. experience.stonecutting = UnityEngine.Random.Range(0.8f, 1.5f);
  98. experience.farming = UnityEngine.Random.Range(0.8f, 1.5f);
  99. experience.building = UnityEngine.Random.Range(0.8f, 1.5f);
  100. }
  101. void Update()
  102. {
  103. UpdateBehavior();
  104. }
  105. void UpdateBehavior()
  106. {
  107. switch (state)
  108. {
  109. case VillagerState.Idle:
  110. HandleIdleState();
  111. break;
  112. case VillagerState.MovingToWork:
  113. HandleMovingToWork();
  114. break;
  115. case VillagerState.Working:
  116. HandleWorking();
  117. break;
  118. case VillagerState.MovingToDeliver:
  119. HandleMovingToDeliver();
  120. break;
  121. case VillagerState.Delivering:
  122. HandleDelivering();
  123. break;
  124. case VillagerState.Transporting:
  125. HandleTransporting();
  126. break;
  127. }
  128. }
  129. void HandleIdleState()
  130. {
  131. if (currentJob == JobType.None)
  132. {
  133. // Unemployed villagers stay idle until assigned a job
  134. // They can be selected and assigned jobs via drag-and-drop
  135. return;
  136. }
  137. else
  138. {
  139. // Look for work appropriate to job
  140. FindWork();
  141. }
  142. }
  143. void HandleMovingToWork()
  144. {
  145. if (MoveToTarget(targetPosition))
  146. {
  147. state = VillagerState.Working;
  148. workTimer = 0f;
  149. }
  150. }
  151. void HandleWorking()
  152. {
  153. if (targetWorkplace == null)
  154. {
  155. state = VillagerState.Idle;
  156. return;
  157. }
  158. workTimer += Time.deltaTime * GetWorkEfficiency();
  159. float workDuration = GetWorkDuration();
  160. if (workTimer >= workDuration)
  161. {
  162. CompleteWork();
  163. }
  164. }
  165. void HandleMovingToDeliver()
  166. {
  167. if (MoveToTarget(targetPosition))
  168. {
  169. state = VillagerState.Delivering;
  170. }
  171. }
  172. void HandleDelivering()
  173. {
  174. DeliverResources();
  175. state = VillagerState.Idle;
  176. }
  177. void HandleTransporting()
  178. {
  179. // Similar to delivering but for transport tasks
  180. if (MoveToTarget(targetPosition))
  181. {
  182. DeliverResources();
  183. state = VillagerState.Idle;
  184. }
  185. }
  186. bool MoveToTarget(Vector3 target)
  187. {
  188. Vector3 direction = (target - transform.position).normalized;
  189. transform.position += direction * moveSpeed * Time.deltaTime;
  190. return Vector3.Distance(transform.position, target) < 0.5f;
  191. }
  192. void FindWork()
  193. {
  194. // If villager already has a specific workplace assigned, use it
  195. if (targetWorkplace != null)
  196. {
  197. targetPosition = targetWorkplace.transform.position;
  198. state = VillagerState.MovingToWork;
  199. return;
  200. }
  201. // Otherwise, find appropriate workplace based on job
  202. GameObject workplace = villagerManager.FindWorkplaceForJob(currentJob, transform.position);
  203. if (workplace != null)
  204. {
  205. targetWorkplace = workplace;
  206. targetPosition = workplace.transform.position;
  207. state = VillagerState.MovingToWork;
  208. }
  209. }
  210. void FindTransportTask()
  211. {
  212. // Look for resources that need to be moved
  213. // This is simplified - in a real game you'd have a more complex task system
  214. var townHall = GameObject.FindWithTag("TownHall");
  215. if (townHall != null && carryingAmount == 0)
  216. {
  217. // Simple transport: move to a random resource and bring it to town hall
  218. state = VillagerState.Transporting;
  219. }
  220. }
  221. void CompleteWork()
  222. {
  223. ResourceType producedResource = GetProducedResource();
  224. int amount = GetProducedAmount();
  225. carryingResource = producedResource;
  226. carryingAmount = amount;
  227. // Add experience
  228. experience.AddExperience(currentJob, 0.1f);
  229. // Find delivery target (usually TownHall)
  230. GameObject townHall = GameObject.FindWithTag("TownHall");
  231. if (townHall != null)
  232. {
  233. targetDelivery = townHall;
  234. targetPosition = townHall.transform.position;
  235. state = VillagerState.MovingToDeliver;
  236. }
  237. else
  238. {
  239. // Deliver immediately if no town hall found
  240. DeliverResources();
  241. state = VillagerState.Idle;
  242. }
  243. }
  244. void DeliverResources()
  245. {
  246. if (carryingAmount > 0)
  247. {
  248. Debug.Log($"{villagerName} delivering {carryingAmount} {carryingResource} to storage");
  249. GameManager.Instance.resourceManager.AddResource(carryingResource, carryingAmount);
  250. carryingAmount = 0;
  251. }
  252. }
  253. ResourceType GetProducedResource()
  254. {
  255. return currentJob switch
  256. {
  257. JobType.Woodcutter => ResourceType.Wood,
  258. JobType.Stonecutter => ResourceType.Stone,
  259. JobType.Farmer => ResourceType.Food,
  260. _ => ResourceType.Wood
  261. };
  262. }
  263. int GetProducedAmount()
  264. {
  265. float efficiency = GetWorkEfficiency();
  266. return Mathf.RoundToInt(UnityEngine.Random.Range(1, 4) * efficiency);
  267. }
  268. float GetWorkEfficiency()
  269. {
  270. return experience.GetExperienceForJob(currentJob) * workSpeed;
  271. }
  272. float GetWorkDuration()
  273. {
  274. return 3f / GetWorkEfficiency(); // Base 3 seconds, modified by efficiency
  275. }
  276. public void SetJob(JobType newJob)
  277. {
  278. currentJob = newJob;
  279. state = VillagerState.Idle;
  280. // Clear previous workplace assignment when changing jobs
  281. if (currentJob == JobType.None)
  282. {
  283. targetWorkplace = null;
  284. }
  285. OnJobChanged?.Invoke(this, newJob);
  286. // Update visual appearance based on job
  287. UpdateAppearance();
  288. }
  289. public void AssignToSpecificWorkplace(GameObject workplace)
  290. {
  291. targetWorkplace = workplace;
  292. if (workplace != null)
  293. {
  294. targetPosition = workplace.transform.position;
  295. state = VillagerState.MovingToWork;
  296. Debug.Log($"{villagerName} assigned to specific workplace: {workplace.name}");
  297. }
  298. }
  299. void UpdateAppearance()
  300. {
  301. if (villagerRenderer != null)
  302. {
  303. Color jobColor = currentJob switch
  304. {
  305. JobType.Woodcutter => Color.green,
  306. JobType.Stonecutter => Color.gray,
  307. JobType.Farmer => Color.yellow,
  308. JobType.Builder => Color.blue,
  309. _ => Color.white
  310. };
  311. villagerRenderer.material.color = jobColor;
  312. }
  313. }
  314. public void SetSelected(bool selected)
  315. {
  316. isSelected = selected;
  317. if (selectionIndicator != null)
  318. selectionIndicator.SetActive(selected);
  319. Debug.Log($"Villager {villagerName} SetSelected: {selected}");
  320. if (selected)
  321. {
  322. Debug.Log($"Villager {villagerName} triggering OnVillagerSelected event");
  323. OnVillagerSelected?.Invoke(this);
  324. }
  325. }
  326. void OnMouseDown()
  327. {
  328. // Use Physics raycast instead of OnMouseDown for better reliability
  329. if (Mouse.current != null && Mouse.current.leftButton.wasPressedThisFrame)
  330. {
  331. if (GameManager.Instance?.selectionManager != null)
  332. {
  333. GameManager.Instance.selectionManager.SelectVillager(this);
  334. }
  335. }
  336. }
  337. public string GetStatusText()
  338. {
  339. string status = $"{villagerName}\nJob: {currentJob}\nState: {state}";
  340. if (carryingAmount > 0)
  341. status += $"\nCarrying: {carryingAmount} {carryingResource}";
  342. if (currentJob == JobType.None)
  343. status += "\n[Click and drag to assign job]";
  344. return status;
  345. }
  346. }