using UnityEngine; using System.Collections.Generic; using System.Linq; public class VillagerManager : MonoBehaviour { [Header("Villager Settings")] public GameObject villagerPrefab; public int initialVillagerCount = 5; public Transform villagerParent; [Header("Spawning")] public Vector3 spawnAreaCenter = Vector3.zero; public float spawnAreaRadius = 5f; private List allVillagers = new List(); private List workplaces = new List(); void SpawnInitialVillagers() { if (villagerPrefab == null) { Debug.LogWarning("No villager prefab assigned, creating basic villagers"); CreateBasicVillagers(); return; } for (int i = 0; i < initialVillagerCount; i++) { Vector3 spawnPos = GetRandomSpawnPosition(); GameObject villagerObj = Instantiate(villagerPrefab, spawnPos, Quaternion.identity, villagerParent); Villager villager = villagerObj.GetComponent(); if (villager != null) { villager.villagerName = $"Villager {i + 1}"; allVillagers.Add(villager); } } } void CreateBasicVillagers() { for (int i = 0; i < initialVillagerCount; i++) { Vector3 spawnPos = GetRandomSpawnPosition(); // Create basic villager GameObject GameObject villagerObj = GameObject.CreatePrimitive(PrimitiveType.Capsule); villagerObj.transform.position = spawnPos + Vector3.up * 0.5f; // Position above ground villagerObj.transform.parent = villagerParent; villagerObj.name = $"Villager_{i + 1}"; villagerObj.tag = "Villager"; // Add Villager component Villager villager = villagerObj.AddComponent(); villager.villagerName = $"Villager {i + 1}"; // Create selection indicator GameObject indicator = GameObject.CreatePrimitive(PrimitiveType.Cylinder); indicator.transform.SetParent(villagerObj.transform); indicator.transform.localPosition = Vector3.down * 0.6f; indicator.transform.localScale = Vector3.one * 1.2f; indicator.GetComponent().material.color = Color.yellow; indicator.SetActive(false); villager.selectionIndicator = indicator; // Remove collider from indicator Destroy(indicator.GetComponent()); allVillagers.Add(villager); } } Vector3 GetRandomSpawnPosition() { // Find TownHall position if it exists GameObject townHall = GameObject.FindWithTag("TownHall"); Vector3 townHallPos = townHall != null ? townHall.transform.position : spawnAreaCenter; Vector3 spawnPos; int attempts = 0; do { // Generate random position around the area Vector2 randomCircle = Random.insideUnitCircle * spawnAreaRadius; spawnPos = spawnAreaCenter + new Vector3(randomCircle.x, 0, randomCircle.y); attempts++; // Ensure minimum distance from TownHall (3 units away) } while (Vector3.Distance(spawnPos, townHallPos) < 3f && attempts < 20); return spawnPos; } void Start() { SpawnInitialVillagers(); FindWorkplaces(); // Ensure villagers are not inside TownHall after spawning StartCoroutine(RepositionVillagersAwayFromTownHall()); } System.Collections.IEnumerator RepositionVillagersAwayFromTownHall() { // Wait a frame to ensure TownHall is created yield return null; GameObject townHall = GameObject.FindWithTag("TownHall"); if (townHall == null) yield break; Vector3 townHallPos = townHall.transform.position; foreach (var villager in allVillagers) { if (villager != null && Vector3.Distance(villager.transform.position, townHallPos) < 4f) { // Move villager to a safe distance from TownHall Vector2 randomDirection = Random.insideUnitCircle.normalized; Vector3 safePos = townHallPos + new Vector3(randomDirection.x, 0, randomDirection.y) * Random.Range(4f, 7f); villager.transform.position = safePos + Vector3.up * 0.5f; // Position above ground Debug.Log($"Repositioned {villager.villagerName} away from TownHall"); } } } void FindWorkplaces() { // Find all workplace objects in the scene workplaces.AddRange(GameObject.FindGameObjectsWithTag("Tree")); workplaces.AddRange(GameObject.FindGameObjectsWithTag("Stone")); workplaces.AddRange(GameObject.FindGameObjectsWithTag("Farm")); // Note: ConstructionSite tag not implemented yet } public GameObject FindWorkplaceForJob(JobType job, Vector3 villagerPosition) { GameObject[] workplaces = null; // Handle Builder job differently since it doesn't use tags if (job == JobType.Builder) { Building[] buildings = FindObjectsByType(FindObjectsSortMode.None); List builderWorkplaces = new List(); foreach (Building building in buildings) { if (building.buildingName == "Builder's Workshop") { builderWorkplaces.Add(building.gameObject); } } workplaces = builderWorkplaces.ToArray(); } else { string targetTag = job switch { JobType.Woodcutter => "Tree", JobType.Stonecutter => "Stone", JobType.Farmer => "Farm", _ => null }; if (targetTag == null) return null; // Find closest workplace of the appropriate type workplaces = GameObject.FindGameObjectsWithTag(targetTag); } GameObject closest = null; float closestDistance = float.MaxValue; foreach (var workplace in workplaces) { // Check if workplace is already occupied if (IsWorkplaceOccupied(workplace)) continue; float distance = Vector3.Distance(villagerPosition, workplace.transform.position); if (distance < closestDistance) { closestDistance = distance; closest = workplace; } } return closest; } bool IsWorkplaceOccupied(GameObject workplace) { // Check if any villager is already working at this workplace return allVillagers.Any(v => v.targetWorkplace == workplace && (v.state == VillagerState.MovingToWork || v.state == VillagerState.Working)); } public void AssignVillagerToJob(Villager villager, JobType job) { if (villager != null) { villager.SetJob(job); Debug.Log($"{villager.villagerName} assigned to job: {job}"); } } public void AssignVillagerToSpecificWorkplace(Villager villager, JobType job, GameObject specificWorkplace) { if (villager != null) { villager.SetJob(job); // If it's a resource job, assign the specific workplace if (job != JobType.None && specificWorkplace != null) { villager.AssignToSpecificWorkplace(specificWorkplace); } Debug.Log($"{villager.villagerName} assigned to {job} at {specificWorkplace?.name}"); } } public List GetVillagersWithJob(JobType job) { return allVillagers.Where(v => v.currentJob == job).ToList(); } public List GetUnemployedVillagers() { return allVillagers.Where(v => v.currentJob == JobType.None).ToList(); } public List GetAllVillagers() { return new List(allVillagers); } public Villager GetVillagerByName(string name) { return allVillagers.FirstOrDefault(v => v.villagerName == name); } public Dictionary GetJobCounts() { var jobCounts = new Dictionary(); foreach (JobType job in System.Enum.GetValues(typeof(JobType))) { jobCounts[job] = allVillagers.Count(v => v.currentJob == job); } return jobCounts; } public void RegisterVillager(Villager villager) { if (!allVillagers.Contains(villager)) { allVillagers.Add(villager); } } public void UnregisterVillager(Villager villager) { allVillagers.Remove(villager); } // Called by UI or selection system for drag-and-drop job assignment public void HandleJobAssignment(Villager villager, Vector3 dropPosition) { // Determine job based on drop position (what's under the mouse) RaycastHit hit; if (Physics.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out hit)) { JobType newJob = DetermineJobFromHit(hit.collider.gameObject); if (newJob != JobType.None) { AssignVillagerToJob(villager, newJob); } } } JobType DetermineJobFromHit(GameObject hitObject) { // Check if it's a building first Building building = hitObject.GetComponent(); if (building != null && building.buildingName == "Builder's Workshop") { return JobType.Builder; } return hitObject.tag switch { "Tree" => JobType.Woodcutter, "Stone" => JobType.Stonecutter, "Farm" => JobType.Farmer, _ => JobType.None }; } void OnDrawGizmosSelected() { // Draw spawn area Gizmos.color = Color.blue; Gizmos.DrawWireSphere(spawnAreaCenter, spawnAreaRadius); } }