using System.Collections.Generic; using UnityEngine; using UnityEngine.InputSystem; public enum BuildingCategory { Living, Working, Decorative, Infrastructure } [System.Serializable] public class BuildingTemplate { public string name; public BuildingCategory category; public GameObject prefab; public ResourceCost[] costs; public string description; public Vector2Int size = new Vector2Int(1, 1); // Grid size public bool requiresConstruction = false; // Whether building needs construction time public float constructionTime = 5f; // Time in seconds to build } [System.Serializable] public class ResourceCost { public ResourceType resourceType; public int amount; } public class BuildingSystem : MonoBehaviour { [Header("Building Templates")] public List buildingTemplates = new List(); [Header("Building Settings")] public LayerMask buildableLayer = 1; // Ground layer public Material previewMaterial; private BuildingTemplate selectedBuildingTemplate; private GameObject previewObject; private Camera playerCamera; private bool isBuildModeActive = false; // Events public static System.Action OnBuildingSelected; public static System.Action OnBuildModeEntered; public static System.Action OnBuildModeExited; public static System.Action OnBuildingPlaced; void Start() { playerCamera = Camera.main; InitializeBuildingTemplates(); } void InitializeBuildingTemplates() { // Clear existing templates buildingTemplates.Clear(); // Add House template buildingTemplates.Add(new BuildingTemplate { name = "House", category = BuildingCategory.Living, costs = new ResourceCost[] { new ResourceCost { resourceType = ResourceType.Wood, amount = 20 }, new ResourceCost { resourceType = ResourceType.Stone, amount = 10 } }, description = "Provides housing for villagers. Increases happiness and allows population growth.", size = new Vector2Int(2, 2) }); // Add Builder's Workshop template buildingTemplates.Add(new BuildingTemplate { name = "Builder's Workshop", category = BuildingCategory.Working, costs = new ResourceCost[] { new ResourceCost { resourceType = ResourceType.Wood, amount = 30 }, new ResourceCost { resourceType = ResourceType.Stone, amount = 15 } }, description = "Allows villagers to work as builders. Required for construction projects.", size = new Vector2Int(2, 1) }); // Add Farmhouse template buildingTemplates.Add(new BuildingTemplate { name = "Farmhouse", category = BuildingCategory.Working, costs = new ResourceCost[] { new ResourceCost { resourceType = ResourceType.Wood, amount = 40 }, new ResourceCost { resourceType = ResourceType.Stone, amount = 20 } }, description = "Houses farmers and allows construction of fields. Produces food from nearby fields.", size = new Vector2Int(2, 2), requiresConstruction = true, // Enable construction system constructionTime = 10f // Takes 10 seconds to build }); Debug.Log($"Initialized {buildingTemplates.Count} building templates"); } void Update() { if (isBuildModeActive) { HandleBuildMode(); } } public void EnterBuildMode(BuildingTemplate template) { selectedBuildingTemplate = template; isBuildModeActive = true; CreatePreviewObject(); OnBuildModeEntered?.Invoke(); Debug.Log($"Entered build mode for: {template.name}"); } public void ExitBuildMode() { isBuildModeActive = false; selectedBuildingTemplate = null; if (previewObject != null) { DestroyImmediate(previewObject); } OnBuildModeExited?.Invoke(); Debug.Log("Exited build mode"); } public List GetBuildingTemplates() { return buildingTemplates; } void CreatePreviewObject() { if (selectedBuildingTemplate?.prefab != null) { previewObject = Instantiate(selectedBuildingTemplate.prefab); // Make it a preview (transparent, non-interactive) MakePreviewObject(previewObject); } else { // Create a simple cube preview if no prefab previewObject = GameObject.CreatePrimitive(PrimitiveType.Cube); previewObject.name = $"{selectedBuildingTemplate.name}_Preview"; // Scale based on building size previewObject.transform.localScale = new Vector3( selectedBuildingTemplate.size.x, 1f, selectedBuildingTemplate.size.y ); MakePreviewObject(previewObject); } } void MakePreviewObject(GameObject obj) { // Make transparent Renderer[] renderers = obj.GetComponentsInChildren(); foreach (var renderer in renderers) { if (previewMaterial != null) { renderer.material = previewMaterial; } else { // Create basic transparent material Material transparentMat = new Material(Shader.Find("Standard")); transparentMat.SetFloat("_Mode", 3); // Transparent transparentMat.color = new Color(0, 1, 0, 0.5f); renderer.material = transparentMat; } } // Remove colliders to prevent interference Collider[] colliders = obj.GetComponentsInChildren(); foreach (var collider in colliders) { collider.enabled = false; } } void HandleBuildMode() { if (previewObject == null) return; // Update preview position based on mouse if (Mouse.current == null) return; Vector2 mousePosition = Mouse.current.position.ReadValue(); Ray ray = playerCamera.ScreenPointToRay(mousePosition); if (Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity, buildableLayer)) { Vector3 buildPosition = hit.point; // Snap to grid (optional) buildPosition.x = Mathf.Round(buildPosition.x); buildPosition.z = Mathf.Round(buildPosition.z); buildPosition.y = hit.point.y; previewObject.transform.position = buildPosition; // Check if position is valid bool canBuild = CanBuildAtPosition(buildPosition); // Change preview color based on validity ChangePreviewColor(canBuild); // Handle click to place building if (Mouse.current.leftButton.wasPressedThisFrame && canBuild) { PlaceBuilding(buildPosition); } } // Cancel build mode if (Keyboard.current.escapeKey.wasPressedThisFrame || Mouse.current.rightButton.wasPressedThisFrame) { ExitBuildMode(); } } bool CanBuildAtPosition(Vector3 position) { // Check if we have enough resources if (!HasEnoughResources(selectedBuildingTemplate)) { return false; } // Check for overlapping objects using a box that matches building size Vector3 boxSize = new Vector3(selectedBuildingTemplate.size.x, 2f, selectedBuildingTemplate.size.y); Collider[] overlapping = Physics.OverlapBox(position, boxSize * 0.5f, Quaternion.identity); foreach (var col in overlapping) { // Skip the preview object itself if (col.gameObject == previewObject) continue; // Skip ground/terrain objects string objName = col.gameObject.name.ToLower(); if (objName.Contains("ground") || objName.Contains("terrain") || objName.Contains("plane")) continue; // Check for buildings if (col.GetComponent() != null) { Debug.Log($"Building blocked by existing building: {col.gameObject.name}"); return false; } // Check for resource objects and other blocking objects by tag string tag = col.gameObject.tag; if (tag == "Tree" || tag == "Stone" || tag == "Farm" || tag == "TownHall" || tag == "Villager") { Debug.Log($"Building blocked by {tag}: {col.gameObject.name}"); return false; } // Check for any other objects with colliders (except ground layer) if (col.gameObject.layer != 0) // Layer 0 is Default, usually ground { Debug.Log($"Building blocked by object: {col.gameObject.name} (layer: {col.gameObject.layer})"); return false; } } return true; } bool HasEnoughResources(BuildingTemplate template) { if (GameManager.Instance?.resourceManager == null) return false; foreach (var cost in template.costs) { if (!GameManager.Instance.resourceManager.HasResource(cost.resourceType, cost.amount)) { return false; } } return true; } void ChangePreviewColor(bool canBuild) { Color previewColor = canBuild ? Color.green : Color.red; previewColor.a = 0.5f; Renderer[] renderers = previewObject.GetComponentsInChildren(); foreach (var renderer in renderers) { renderer.material.color = previewColor; } } void PlaceBuilding(Vector3 position) { // Consume resources foreach (var cost in selectedBuildingTemplate.costs) { GameManager.Instance.resourceManager.RemoveResource(cost.resourceType, cost.amount); } GameObject newBuilding; // Check if this building requires construction if (selectedBuildingTemplate.requiresConstruction) { // Create construction site newBuilding = CreateConstructionSite(selectedBuildingTemplate, position); Debug.Log($"Started construction of {selectedBuildingTemplate.name} at {position}"); } else { // Create the building immediately (for simple buildings like houses) if (selectedBuildingTemplate.prefab != null) { newBuilding = Instantiate(selectedBuildingTemplate.prefab, position, Quaternion.identity); } else { newBuilding = CreateBasicBuilding(selectedBuildingTemplate, position); } // Add building component if it doesn't exist Building buildingComponent = newBuilding.GetComponent(); if (buildingComponent == null) { buildingComponent = newBuilding.AddComponent(); } buildingComponent.buildingName = selectedBuildingTemplate.name; buildingComponent.category = selectedBuildingTemplate.category; // Add specific building components if (selectedBuildingTemplate.name == "House") { if (newBuilding.GetComponent() == null) newBuilding.AddComponent(); } else if (selectedBuildingTemplate.name == "Farm") { if (newBuilding.GetComponent() == null) newBuilding.AddComponent(); } else if (selectedBuildingTemplate.name == "Farmhouse") { if (newBuilding.GetComponent() == null) newBuilding.AddComponent(); } Debug.Log($"Instantly placed {selectedBuildingTemplate.name} at {position}"); } OnBuildingPlaced?.Invoke(newBuilding); // Exit build mode after placing ExitBuildMode(); } GameObject CreateConstructionSite(BuildingTemplate template, Vector3 position) { // Create a temporary construction site object GameObject constructionSite = GameObject.CreatePrimitive(PrimitiveType.Cube); constructionSite.name = $"{template.name}_ConstructionSite"; constructionSite.transform.position = position; constructionSite.transform.localScale = new Vector3(template.size.x, 0.5f, template.size.y); // Add construction site component ConstructionSite constructionComponent = constructionSite.AddComponent(); constructionComponent.buildingName = template.name; constructionComponent.buildingTemplate = template; constructionComponent.constructionTime = template.constructionTime; // Make it look like a construction site Renderer renderer = constructionSite.GetComponent(); renderer.material.color = new Color(0.7f, 0.7f, 0.7f, 0.5f); // Semi-transparent gray return constructionSite; } GameObject CreateBasicBuilding(BuildingTemplate template, Vector3 position) { GameObject building = new GameObject(template.name); building.transform.position = position; // Create visual representation GameObject visual = GameObject.CreatePrimitive(PrimitiveType.Cube); visual.transform.SetParent(building.transform); visual.transform.localPosition = Vector3.zero; visual.transform.localScale = new Vector3(template.size.x, 1f, template.size.y); // Color based on category Renderer renderer = visual.GetComponent(); renderer.material.color = GetCategoryColor(template.category); // Add collider for selection if (building.GetComponent() == null) { BoxCollider collider = building.AddComponent(); collider.size = new Vector3(template.size.x, 2f, template.size.y); collider.center = new Vector3(0, 1f, 0); } return building; } Color GetCategoryColor(BuildingCategory category) { return category switch { BuildingCategory.Living => new Color(0.8f, 0.6f, 0.4f), // Brown BuildingCategory.Working => new Color(0.6f, 0.6f, 0.8f), // Blue BuildingCategory.Decorative => new Color(0.8f, 0.4f, 0.8f), // Purple BuildingCategory.Infrastructure => new Color(0.6f, 0.8f, 0.6f), // Green _ => Color.gray }; } public List GetBuildingsByCategory(BuildingCategory category) { List buildings = new List(); foreach (var template in buildingTemplates) { if (template.category == category) { buildings.Add(template); } } return buildings; } public string GetResourceCostString(BuildingTemplate template) { if (template.costs == null || template.costs.Length == 0) return "Free"; List costStrings = new List(); foreach (var cost in template.costs) { costStrings.Add($"{cost.amount} {cost.resourceType}"); } return string.Join(", ", costStrings); } // Debug visualization void OnDrawGizmos() { if (isBuildModeActive && selectedBuildingTemplate != null && previewObject != null) { // Draw building bounds Gizmos.color = Color.yellow; Vector3 boxSize = new Vector3(selectedBuildingTemplate.size.x, 2f, selectedBuildingTemplate.size.y); Gizmos.DrawWireCube(previewObject.transform.position, boxSize); } } }