| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548 |
- 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,
- BuildingConstruction, // Working on a building construction site
- BuildingField // Farmer building a field
- }
- [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("Construction")]
- public ConstructionSite assignedConstructionSite;
- public Field assignedField;
- public GameObject homeBuilding; // For farmers living in farmhouses
- [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<Villager> OnVillagerSelected;
- public static event Action<Villager, JobType> OnJobChanged;
- void Start()
- {
- villagerManager = FindFirstObjectByType<VillagerManager>();
- if (villagerRenderer == null)
- villagerRenderer = GetComponent<Renderer>();
- 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;
- case VillagerState.BuildingConstruction:
- HandleConstruction();
- break;
- case VillagerState.BuildingField:
- HandleFieldBuilding();
- 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()
- {
- if (carryingAmount > 0)
- {
- DeliverResources();
- }
- // Always transition back to idle after delivery attempt
- state = VillagerState.Idle;
- targetPosition = transform.position; // Clear target
- }
- 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()
- {
- // Builders should look for construction sites, not resource nodes
- if (currentJob == JobType.Builder)
- {
- FindAvailableConstructionSite();
- return;
- }
- // 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;
- carryingResource = ResourceType.None;
- }
- }
- ResourceType GetProducedResource()
- {
- return currentJob switch
- {
- JobType.Woodcutter => ResourceType.Wood,
- JobType.Stonecutter => ResourceType.Stone,
- JobType.Farmer => ResourceType.Food,
- JobType.Builder => ResourceType.None, // Builders don't produce resources
- _ => ResourceType.None
- };
- }
- int GetProducedAmount()
- {
- float efficiency = GetWorkEfficiency();
- int baseAmount = UnityEngine.Random.Range(1, 4);
- int producedAmount = Mathf.RoundToInt(baseAmount * efficiency);
- // Cap maximum production per work cycle to prevent resource explosion
- return Mathf.Clamp(producedAmount, 1, 10);
- }
- float GetWorkEfficiency()
- {
- float baseEfficiency = experience.GetExperienceForJob(currentJob) * workSpeed;
- // Cap efficiency to prevent resource explosion (max 3x efficiency)
- return Mathf.Clamp(baseEfficiency, 0.5f, 3f);
- }
- 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]";
- if (assignedConstructionSite != null)
- status += $"\nBuilding: {assignedConstructionSite.buildingName}";
- if (assignedField != null)
- status += $"\nField: {assignedField.fieldSize}";
- return status;
- }
- // Construction methods
- public void AssignToConstruction(ConstructionSite site)
- {
- assignedConstructionSite = site;
- site.AssignBuilder(this);
- state = VillagerState.BuildingConstruction;
- targetPosition = site.transform.position;
- Debug.Log($"{villagerName} assigned to build {site.buildingName}");
- }
- public void AssignToFieldConstruction(Field field)
- {
- assignedField = field;
- state = VillagerState.BuildingField;
- targetPosition = field.transform.position;
- Debug.Log($"{villagerName} assigned to build {field.fieldSize} field");
- }
- void FindAvailableConstructionSite()
- {
- if (assignedConstructionSite != null) return;
- // Find nearest construction site that needs a builder
- ConstructionSite[] sites = FindObjectsOfType<ConstructionSite>();
- ConstructionSite nearestSite = null;
- float nearestDistance = float.MaxValue;
- foreach (var site in sites)
- {
- if (site.assignedBuilder == null && !site.isComplete)
- {
- float distance = Vector3.Distance(transform.position, site.transform.position);
- if (distance < nearestDistance)
- {
- nearestDistance = distance;
- nearestSite = site;
- }
- }
- }
- if (nearestSite != null)
- {
- AssignToConstruction(nearestSite);
- Debug.Log($"{villagerName} found construction site: {nearestSite.buildingName}");
- }
- }
- public void SetHomeBuilding(GameObject building)
- {
- homeBuilding = building;
- Debug.Log($"{villagerName} home set to {(building != null ? building.name : "none")}");
- }
- void HandleConstruction()
- {
- if (assignedConstructionSite == null)
- {
- state = VillagerState.Idle;
- return;
- }
- // Move to construction site
- float distance = Vector3.Distance(transform.position, assignedConstructionSite.transform.position);
- if (distance > workRange)
- {
- transform.position = Vector3.MoveTowards(transform.position, assignedConstructionSite.transform.position, moveSpeed * Time.deltaTime);
- }
- else
- {
- // Work on construction
- assignedConstructionSite.AdvanceConstruction(Time.deltaTime * GetWorkEfficiency());
- // Add building experience
- experience.AddExperience(JobType.Builder, 0.1f * Time.deltaTime);
- if (assignedConstructionSite.isComplete)
- {
- Debug.Log($"{villagerName} completed construction of {assignedConstructionSite.buildingName}");
- assignedConstructionSite = null;
- state = VillagerState.Idle;
- }
- }
- }
- void HandleFieldBuilding()
- {
- if (assignedField == null)
- {
- state = VillagerState.Idle;
- return;
- }
- // Move to field location
- float distance = Vector3.Distance(transform.position, assignedField.transform.position);
- if (distance > workRange)
- {
- transform.position = Vector3.MoveTowards(transform.position, assignedField.transform.position, moveSpeed * Time.deltaTime);
- }
- else
- {
- // Work on field construction
- assignedField.AdvanceConstruction(Time.deltaTime * GetWorkEfficiency());
- // Add farming experience
- experience.AddExperience(JobType.Farmer, 0.1f * Time.deltaTime);
- if (!assignedField.isUnderConstruction)
- {
- Debug.Log($"{villagerName} completed field construction");
- assignedField = null;
- state = VillagerState.Idle;
- }
- }
- }
- }
|