using UnityEngine; using UnityEngine.InputSystem; using System.Collections.Generic; using System; [System.Serializable] public enum JobType { None, // Unemployed - transports goods Woodcutter, // Cuts trees Stonecutter, // Mines stone Farmer, // Grows food Builder // Constructs buildings } [System.Serializable] public enum VillagerState { Idle, MovingToWork, Working, MovingToDeliver, Delivering, Transporting } [System.Serializable] public class Experience { public float woodcutting = 1f; public float stonecutting = 1f; public float farming = 1f; public float building = 1f; public float GetExperienceForJob(JobType job) { return job switch { JobType.Woodcutter => woodcutting, JobType.Stonecutter => stonecutting, JobType.Farmer => farming, JobType.Builder => building, _ => 1f }; } public void AddExperience(JobType job, float amount) { switch (job) { case JobType.Woodcutter: woodcutting += amount; break; case JobType.Stonecutter: stonecutting += amount; break; case JobType.Farmer: farming += amount; break; case JobType.Builder: building += amount; break; } } } public class Villager : MonoBehaviour { [Header("Villager Info")] public string villagerName; public JobType currentJob = JobType.None; public VillagerState state = VillagerState.Idle; [Header("Experience")] public Experience experience = new Experience(); [Header("Work Settings")] public float workSpeed = 1f; public float moveSpeed = 3f; public float workRange = 2f; [Header("Current Task")] public GameObject targetWorkplace; public GameObject targetDelivery; public ResourceType carryingResource; public int carryingAmount = 0; public int maxCarryCapacity = 10; [Header("Visual")] public Renderer villagerRenderer; public GameObject selectionIndicator; private Vector3 targetPosition; private bool isSelected = false; private float workTimer = 0f; private VillagerManager villagerManager; public static event Action OnVillagerSelected; public static event Action OnJobChanged; void Start() { villagerManager = FindObjectOfType(); if (villagerRenderer == null) villagerRenderer = GetComponent(); if (selectionIndicator != null) selectionIndicator.SetActive(false); // Random starting experience experience.woodcutting = UnityEngine.Random.Range(0.8f, 1.5f); experience.stonecutting = UnityEngine.Random.Range(0.8f, 1.5f); experience.farming = UnityEngine.Random.Range(0.8f, 1.5f); experience.building = UnityEngine.Random.Range(0.8f, 1.5f); } void Update() { UpdateBehavior(); } void UpdateBehavior() { switch (state) { case VillagerState.Idle: HandleIdleState(); break; case VillagerState.MovingToWork: HandleMovingToWork(); break; case VillagerState.Working: HandleWorking(); break; case VillagerState.MovingToDeliver: HandleMovingToDeliver(); break; case VillagerState.Delivering: HandleDelivering(); break; case VillagerState.Transporting: HandleTransporting(); break; } } void HandleIdleState() { if (currentJob == JobType.None) { // Unemployed villagers stay idle until assigned a job // They can be selected and assigned jobs via drag-and-drop return; } else { // Look for work appropriate to job FindWork(); } } void HandleMovingToWork() { if (MoveToTarget(targetPosition)) { state = VillagerState.Working; workTimer = 0f; } } void HandleWorking() { if (targetWorkplace == null) { state = VillagerState.Idle; return; } workTimer += Time.deltaTime * GetWorkEfficiency(); float workDuration = GetWorkDuration(); if (workTimer >= workDuration) { CompleteWork(); } } void HandleMovingToDeliver() { if (MoveToTarget(targetPosition)) { state = VillagerState.Delivering; } } void HandleDelivering() { DeliverResources(); state = VillagerState.Idle; } void HandleTransporting() { // Similar to delivering but for transport tasks if (MoveToTarget(targetPosition)) { DeliverResources(); state = VillagerState.Idle; } } bool MoveToTarget(Vector3 target) { Vector3 direction = (target - transform.position).normalized; transform.position += direction * moveSpeed * Time.deltaTime; return Vector3.Distance(transform.position, target) < 0.5f; } void FindWork() { // If villager already has a specific workplace assigned, use it if (targetWorkplace != null) { targetPosition = targetWorkplace.transform.position; state = VillagerState.MovingToWork; return; } // Otherwise, find appropriate workplace based on job GameObject workplace = villagerManager.FindWorkplaceForJob(currentJob, transform.position); if (workplace != null) { targetWorkplace = workplace; targetPosition = workplace.transform.position; state = VillagerState.MovingToWork; } } void FindTransportTask() { // Look for resources that need to be moved // This is simplified - in a real game you'd have a more complex task system var townHall = GameObject.FindWithTag("TownHall"); if (townHall != null && carryingAmount == 0) { // Simple transport: move to a random resource and bring it to town hall state = VillagerState.Transporting; } } void CompleteWork() { ResourceType producedResource = GetProducedResource(); int amount = GetProducedAmount(); carryingResource = producedResource; carryingAmount = amount; // Add experience experience.AddExperience(currentJob, 0.1f); // Find delivery target (usually TownHall) GameObject townHall = GameObject.FindWithTag("TownHall"); if (townHall != null) { targetDelivery = townHall; targetPosition = townHall.transform.position; state = VillagerState.MovingToDeliver; } else { // Deliver immediately if no town hall found DeliverResources(); state = VillagerState.Idle; } } void DeliverResources() { if (carryingAmount > 0) { Debug.Log($"{villagerName} delivering {carryingAmount} {carryingResource} to storage"); GameManager.Instance.resourceManager.AddResource(carryingResource, carryingAmount); carryingAmount = 0; } } ResourceType GetProducedResource() { return currentJob switch { JobType.Woodcutter => ResourceType.Wood, JobType.Stonecutter => ResourceType.Stone, JobType.Farmer => ResourceType.Food, _ => ResourceType.Wood }; } int GetProducedAmount() { float efficiency = GetWorkEfficiency(); return Mathf.RoundToInt(UnityEngine.Random.Range(1, 4) * efficiency); } float GetWorkEfficiency() { return experience.GetExperienceForJob(currentJob) * workSpeed; } float GetWorkDuration() { return 3f / GetWorkEfficiency(); // Base 3 seconds, modified by efficiency } public void SetJob(JobType newJob) { currentJob = newJob; state = VillagerState.Idle; // Clear previous workplace assignment when changing jobs if (currentJob == JobType.None) { targetWorkplace = null; } OnJobChanged?.Invoke(this, newJob); // Update visual appearance based on job UpdateAppearance(); } public void AssignToSpecificWorkplace(GameObject workplace) { targetWorkplace = workplace; if (workplace != null) { targetPosition = workplace.transform.position; state = VillagerState.MovingToWork; Debug.Log($"{villagerName} assigned to specific workplace: {workplace.name}"); } } void UpdateAppearance() { if (villagerRenderer != null) { Color jobColor = currentJob switch { JobType.Woodcutter => Color.green, JobType.Stonecutter => Color.gray, JobType.Farmer => Color.yellow, JobType.Builder => Color.blue, _ => Color.white }; villagerRenderer.material.color = jobColor; } } public void SetSelected(bool selected) { isSelected = selected; if (selectionIndicator != null) selectionIndicator.SetActive(selected); Debug.Log($"Villager {villagerName} SetSelected: {selected}"); if (selected) { Debug.Log($"Villager {villagerName} triggering OnVillagerSelected event"); OnVillagerSelected?.Invoke(this); } } void OnMouseDown() { // Use Physics raycast instead of OnMouseDown for better reliability if (Mouse.current != null && Mouse.current.leftButton.wasPressedThisFrame) { if (GameManager.Instance?.selectionManager != null) { GameManager.Instance.selectionManager.SelectVillager(this); } } } public string GetStatusText() { string status = $"{villagerName}\nJob: {currentJob}\nState: {state}"; if (carryingAmount > 0) status += $"\nCarrying: {carryingAmount} {carryingResource}"; if (currentJob == JobType.None) status += "\n[Click and drag to assign job]"; return status; } }