BuildingSystem.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3. using UnityEngine.InputSystem;
  4. public enum BuildingCategory
  5. {
  6. Living,
  7. Working,
  8. Decorative,
  9. Infrastructure
  10. }
  11. [System.Serializable]
  12. public class BuildingTemplate
  13. {
  14. public string name;
  15. public BuildingCategory category;
  16. public GameObject prefab;
  17. public ResourceCost[] costs;
  18. public string description;
  19. public Vector2Int size = new Vector2Int(1, 1); // Grid size
  20. public bool requiresConstruction = false; // Whether building needs construction time
  21. public float constructionTime = 5f; // Time in seconds to build
  22. }
  23. [System.Serializable]
  24. public class ResourceCost
  25. {
  26. public ResourceType resourceType;
  27. public int amount;
  28. }
  29. public class BuildingSystem : MonoBehaviour
  30. {
  31. [Header("Building Templates")]
  32. public List<BuildingTemplate> buildingTemplates = new List<BuildingTemplate>();
  33. [Header("Building Settings")]
  34. public LayerMask buildableLayer = 1; // Ground layer
  35. public Material previewMaterial;
  36. private BuildingTemplate selectedBuildingTemplate;
  37. private GameObject previewObject;
  38. private Camera playerCamera;
  39. private bool isBuildModeActive = false;
  40. // Events
  41. public static System.Action<BuildingTemplate> OnBuildingSelected;
  42. public static System.Action OnBuildModeEntered;
  43. public static System.Action OnBuildModeExited;
  44. public static System.Action<GameObject> OnBuildingPlaced;
  45. void Start()
  46. {
  47. playerCamera = Camera.main;
  48. InitializeBuildingTemplates();
  49. }
  50. void InitializeBuildingTemplates()
  51. {
  52. // Clear existing templates
  53. buildingTemplates.Clear();
  54. // Add House template
  55. buildingTemplates.Add(new BuildingTemplate
  56. {
  57. name = "House",
  58. category = BuildingCategory.Living,
  59. costs = new ResourceCost[]
  60. {
  61. new ResourceCost { resourceType = ResourceType.Wood, amount = 20 },
  62. new ResourceCost { resourceType = ResourceType.Stone, amount = 10 }
  63. },
  64. description = "Provides housing for villagers. Increases happiness and allows population growth.",
  65. size = new Vector2Int(2, 2)
  66. });
  67. // Add Builder's Workshop template
  68. buildingTemplates.Add(new BuildingTemplate
  69. {
  70. name = "Builder's Workshop",
  71. category = BuildingCategory.Working,
  72. costs = new ResourceCost[]
  73. {
  74. new ResourceCost { resourceType = ResourceType.Wood, amount = 30 },
  75. new ResourceCost { resourceType = ResourceType.Stone, amount = 15 }
  76. },
  77. description = "Allows villagers to work as builders. Required for construction projects.",
  78. size = new Vector2Int(2, 1)
  79. });
  80. // Add Farmhouse template
  81. buildingTemplates.Add(new BuildingTemplate
  82. {
  83. name = "Farmhouse",
  84. category = BuildingCategory.Working,
  85. costs = new ResourceCost[]
  86. {
  87. new ResourceCost { resourceType = ResourceType.Wood, amount = 40 },
  88. new ResourceCost { resourceType = ResourceType.Stone, amount = 20 }
  89. },
  90. description = "Houses farmers and allows construction of fields. Produces food from nearby fields.",
  91. size = new Vector2Int(2, 2),
  92. requiresConstruction = true, // Enable construction system
  93. constructionTime = 10f // Takes 10 seconds to build
  94. });
  95. Debug.Log($"Initialized {buildingTemplates.Count} building templates");
  96. }
  97. void Update()
  98. {
  99. if (isBuildModeActive)
  100. {
  101. HandleBuildMode();
  102. }
  103. }
  104. public void EnterBuildMode(BuildingTemplate template)
  105. {
  106. selectedBuildingTemplate = template;
  107. isBuildModeActive = true;
  108. CreatePreviewObject();
  109. OnBuildModeEntered?.Invoke();
  110. Debug.Log($"Entered build mode for: {template.name}");
  111. }
  112. public void ExitBuildMode()
  113. {
  114. isBuildModeActive = false;
  115. selectedBuildingTemplate = null;
  116. if (previewObject != null)
  117. {
  118. DestroyImmediate(previewObject);
  119. }
  120. OnBuildModeExited?.Invoke();
  121. Debug.Log("Exited build mode");
  122. }
  123. public List<BuildingTemplate> GetBuildingTemplates()
  124. {
  125. return buildingTemplates;
  126. }
  127. void CreatePreviewObject()
  128. {
  129. if (selectedBuildingTemplate?.prefab != null)
  130. {
  131. previewObject = Instantiate(selectedBuildingTemplate.prefab);
  132. // Make it a preview (transparent, non-interactive)
  133. MakePreviewObject(previewObject);
  134. }
  135. else
  136. {
  137. // Create a simple cube preview if no prefab
  138. previewObject = GameObject.CreatePrimitive(PrimitiveType.Cube);
  139. previewObject.name = $"{selectedBuildingTemplate.name}_Preview";
  140. // Scale based on building size
  141. previewObject.transform.localScale = new Vector3(
  142. selectedBuildingTemplate.size.x,
  143. 1f,
  144. selectedBuildingTemplate.size.y
  145. );
  146. MakePreviewObject(previewObject);
  147. }
  148. }
  149. void MakePreviewObject(GameObject obj)
  150. {
  151. // Make transparent
  152. Renderer[] renderers = obj.GetComponentsInChildren<Renderer>();
  153. foreach (var renderer in renderers)
  154. {
  155. if (previewMaterial != null)
  156. {
  157. renderer.material = previewMaterial;
  158. }
  159. else
  160. {
  161. // Create basic transparent material
  162. Material transparentMat = new Material(Shader.Find("Standard"));
  163. transparentMat.SetFloat("_Mode", 3); // Transparent
  164. transparentMat.color = new Color(0, 1, 0, 0.5f);
  165. renderer.material = transparentMat;
  166. }
  167. }
  168. // Remove colliders to prevent interference
  169. Collider[] colliders = obj.GetComponentsInChildren<Collider>();
  170. foreach (var collider in colliders)
  171. {
  172. collider.enabled = false;
  173. }
  174. }
  175. void HandleBuildMode()
  176. {
  177. if (previewObject == null) return;
  178. // Update preview position based on mouse
  179. if (Mouse.current == null) return;
  180. Vector2 mousePosition = Mouse.current.position.ReadValue();
  181. Ray ray = playerCamera.ScreenPointToRay(mousePosition);
  182. if (Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity, buildableLayer))
  183. {
  184. Vector3 buildPosition = hit.point;
  185. // Snap to grid (optional)
  186. buildPosition.x = Mathf.Round(buildPosition.x);
  187. buildPosition.z = Mathf.Round(buildPosition.z);
  188. buildPosition.y = hit.point.y;
  189. previewObject.transform.position = buildPosition;
  190. // Check if position is valid
  191. bool canBuild = CanBuildAtPosition(buildPosition);
  192. // Change preview color based on validity
  193. ChangePreviewColor(canBuild);
  194. // Handle click to place building
  195. if (Mouse.current.leftButton.wasPressedThisFrame && canBuild)
  196. {
  197. PlaceBuilding(buildPosition);
  198. }
  199. }
  200. // Cancel build mode
  201. if (Keyboard.current.escapeKey.wasPressedThisFrame || Mouse.current.rightButton.wasPressedThisFrame)
  202. {
  203. ExitBuildMode();
  204. }
  205. }
  206. bool CanBuildAtPosition(Vector3 position)
  207. {
  208. // Check if we have enough resources
  209. if (!HasEnoughResources(selectedBuildingTemplate))
  210. {
  211. return false;
  212. }
  213. // Check for overlapping objects using a box that matches building size
  214. Vector3 boxSize = new Vector3(selectedBuildingTemplate.size.x, 2f, selectedBuildingTemplate.size.y);
  215. Collider[] overlapping = Physics.OverlapBox(position, boxSize * 0.5f, Quaternion.identity);
  216. foreach (var col in overlapping)
  217. {
  218. // Skip the preview object itself
  219. if (col.gameObject == previewObject)
  220. continue;
  221. // Skip ground/terrain objects
  222. string objName = col.gameObject.name.ToLower();
  223. if (objName.Contains("ground") || objName.Contains("terrain") || objName.Contains("plane"))
  224. continue;
  225. // Check for buildings
  226. if (col.GetComponent<Building>() != null)
  227. {
  228. Debug.Log($"Building blocked by existing building: {col.gameObject.name}");
  229. return false;
  230. }
  231. // Check for resource objects and other blocking objects by tag
  232. string tag = col.gameObject.tag;
  233. if (tag == "Tree" || tag == "Stone" || tag == "Farm" || tag == "TownHall" || tag == "Villager")
  234. {
  235. Debug.Log($"Building blocked by {tag}: {col.gameObject.name}");
  236. return false;
  237. }
  238. // Check for any other objects with colliders (except ground layer)
  239. if (col.gameObject.layer != 0) // Layer 0 is Default, usually ground
  240. {
  241. Debug.Log($"Building blocked by object: {col.gameObject.name} (layer: {col.gameObject.layer})");
  242. return false;
  243. }
  244. }
  245. return true;
  246. }
  247. bool HasEnoughResources(BuildingTemplate template)
  248. {
  249. if (GameManager.Instance?.resourceManager == null) return false;
  250. foreach (var cost in template.costs)
  251. {
  252. if (!GameManager.Instance.resourceManager.HasResource(cost.resourceType, cost.amount))
  253. {
  254. return false;
  255. }
  256. }
  257. return true;
  258. }
  259. void ChangePreviewColor(bool canBuild)
  260. {
  261. Color previewColor = canBuild ? Color.green : Color.red;
  262. previewColor.a = 0.5f;
  263. Renderer[] renderers = previewObject.GetComponentsInChildren<Renderer>();
  264. foreach (var renderer in renderers)
  265. {
  266. renderer.material.color = previewColor;
  267. }
  268. }
  269. void PlaceBuilding(Vector3 position)
  270. {
  271. // Consume resources
  272. foreach (var cost in selectedBuildingTemplate.costs)
  273. {
  274. GameManager.Instance.resourceManager.RemoveResource(cost.resourceType, cost.amount);
  275. }
  276. GameObject newBuilding;
  277. // Check if this building requires construction
  278. if (selectedBuildingTemplate.requiresConstruction)
  279. {
  280. // Create construction site
  281. newBuilding = CreateConstructionSite(selectedBuildingTemplate, position);
  282. Debug.Log($"Started construction of {selectedBuildingTemplate.name} at {position}");
  283. }
  284. else
  285. {
  286. // Create the building immediately (for simple buildings like houses)
  287. if (selectedBuildingTemplate.prefab != null)
  288. {
  289. newBuilding = Instantiate(selectedBuildingTemplate.prefab, position, Quaternion.identity);
  290. }
  291. else
  292. {
  293. newBuilding = CreateBasicBuilding(selectedBuildingTemplate, position);
  294. }
  295. // Add building component if it doesn't exist
  296. Building buildingComponent = newBuilding.GetComponent<Building>();
  297. if (buildingComponent == null)
  298. {
  299. buildingComponent = newBuilding.AddComponent<Building>();
  300. }
  301. buildingComponent.buildingName = selectedBuildingTemplate.name;
  302. buildingComponent.category = selectedBuildingTemplate.category;
  303. // Add specific building components
  304. if (selectedBuildingTemplate.name == "House")
  305. {
  306. if (newBuilding.GetComponent<House>() == null)
  307. newBuilding.AddComponent<House>();
  308. }
  309. else if (selectedBuildingTemplate.name == "Farm")
  310. {
  311. if (newBuilding.GetComponent<Farm>() == null)
  312. newBuilding.AddComponent<Farm>();
  313. }
  314. else if (selectedBuildingTemplate.name == "Farmhouse")
  315. {
  316. if (newBuilding.GetComponent<SimpleFarmhouse>() == null)
  317. newBuilding.AddComponent<SimpleFarmhouse>();
  318. }
  319. Debug.Log($"Instantly placed {selectedBuildingTemplate.name} at {position}");
  320. }
  321. OnBuildingPlaced?.Invoke(newBuilding);
  322. // Exit build mode after placing
  323. ExitBuildMode();
  324. }
  325. GameObject CreateConstructionSite(BuildingTemplate template, Vector3 position)
  326. {
  327. // Create a temporary construction site object
  328. GameObject constructionSite = GameObject.CreatePrimitive(PrimitiveType.Cube);
  329. constructionSite.name = $"{template.name}_ConstructionSite";
  330. constructionSite.transform.position = position;
  331. constructionSite.transform.localScale = new Vector3(template.size.x, 0.5f, template.size.y);
  332. // Add construction site component
  333. ConstructionSite constructionComponent = constructionSite.AddComponent<ConstructionSite>();
  334. constructionComponent.buildingName = template.name;
  335. constructionComponent.buildingTemplate = template;
  336. constructionComponent.constructionTime = template.constructionTime;
  337. // Make it look like a construction site
  338. Renderer renderer = constructionSite.GetComponent<Renderer>();
  339. renderer.material.color = new Color(0.7f, 0.7f, 0.7f, 0.5f); // Semi-transparent gray
  340. return constructionSite;
  341. }
  342. GameObject CreateBasicBuilding(BuildingTemplate template, Vector3 position)
  343. {
  344. GameObject building = new GameObject(template.name);
  345. building.transform.position = position;
  346. // Create visual representation
  347. GameObject visual = GameObject.CreatePrimitive(PrimitiveType.Cube);
  348. visual.transform.SetParent(building.transform);
  349. visual.transform.localPosition = Vector3.zero;
  350. visual.transform.localScale = new Vector3(template.size.x, 1f, template.size.y);
  351. // Color based on category
  352. Renderer renderer = visual.GetComponent<Renderer>();
  353. renderer.material.color = GetCategoryColor(template.category);
  354. // Add collider for selection
  355. if (building.GetComponent<Collider>() == null)
  356. {
  357. BoxCollider collider = building.AddComponent<BoxCollider>();
  358. collider.size = new Vector3(template.size.x, 2f, template.size.y);
  359. collider.center = new Vector3(0, 1f, 0);
  360. }
  361. return building;
  362. }
  363. Color GetCategoryColor(BuildingCategory category)
  364. {
  365. return category switch
  366. {
  367. BuildingCategory.Living => new Color(0.8f, 0.6f, 0.4f), // Brown
  368. BuildingCategory.Working => new Color(0.6f, 0.6f, 0.8f), // Blue
  369. BuildingCategory.Decorative => new Color(0.8f, 0.4f, 0.8f), // Purple
  370. BuildingCategory.Infrastructure => new Color(0.6f, 0.8f, 0.6f), // Green
  371. _ => Color.gray
  372. };
  373. }
  374. public List<BuildingTemplate> GetBuildingsByCategory(BuildingCategory category)
  375. {
  376. List<BuildingTemplate> buildings = new List<BuildingTemplate>();
  377. foreach (var template in buildingTemplates)
  378. {
  379. if (template.category == category)
  380. {
  381. buildings.Add(template);
  382. }
  383. }
  384. return buildings;
  385. }
  386. public string GetResourceCostString(BuildingTemplate template)
  387. {
  388. if (template.costs == null || template.costs.Length == 0)
  389. return "Free";
  390. List<string> costStrings = new List<string>();
  391. foreach (var cost in template.costs)
  392. {
  393. costStrings.Add($"{cost.amount} {cost.resourceType}");
  394. }
  395. return string.Join(", ", costStrings);
  396. }
  397. // Debug visualization
  398. void OnDrawGizmos()
  399. {
  400. if (isBuildModeActive && selectedBuildingTemplate != null && previewObject != null)
  401. {
  402. // Draw building bounds
  403. Gizmos.color = Color.yellow;
  404. Vector3 boxSize = new Vector3(selectedBuildingTemplate.size.x, 2f, selectedBuildingTemplate.size.y);
  405. Gizmos.DrawWireCube(previewObject.transform.position, boxSize);
  406. }
  407. }
  408. }