215 lines
7.3 KiB
C#
215 lines
7.3 KiB
C#
using UnityEngine;
|
|
using UnityEngine.AI;
|
|
using System.Collections.Generic;
|
|
using UnityEngine.UI;
|
|
|
|
|
|
public class NPC : MonoBehaviour
|
|
{
|
|
public enum Job
|
|
{
|
|
Builder, Farmer, Soldier, Forester, Merchant, Miner
|
|
}
|
|
public Job job;
|
|
private Resource.ResourceType resourceToWorkWith, carryingResource;
|
|
public Dictionary<Resource.ResourceType, int> inventory = new Dictionary<Resource.ResourceType, int>();
|
|
[SerializeField] int gatherAmount, inventoryCapacity;
|
|
[SerializeField] NavMeshAgent navMeshAgent;
|
|
[SerializeField] Animator animationController;
|
|
[SerializeField] float movementSpeed, interactionRadius;
|
|
[SerializeField] Image jobImg;
|
|
[SerializeField] Sprite miner, forester;
|
|
|
|
// Cache the target so the NPC remembers what it is walking toward
|
|
private Resource currentTargetResource;
|
|
private IEnumerable<Building> buildings;
|
|
|
|
void Start()
|
|
{
|
|
navMeshAgent.speed = movementSpeed;
|
|
SetJob(job);
|
|
}
|
|
|
|
void Update()
|
|
{
|
|
// 1. If we don't have a valid target, find the nearest one
|
|
if (currentTargetResource == null || currentTargetResource.resourceState != Resource.ResourceState.Full)
|
|
{
|
|
FindResources();
|
|
}
|
|
// 2. If we do have a target, check if we are close enough to gather it
|
|
else
|
|
{
|
|
float distanceToTarget = Vector3.Distance(transform.position, currentTargetResource.transform.position);
|
|
|
|
if (distanceToTarget <= interactionRadius)
|
|
{
|
|
GatherResource();
|
|
}
|
|
else
|
|
{
|
|
// Ensure movement animation is playing while walking
|
|
animationController.SetBool("MovementInputHeld", true);
|
|
animationController.SetFloat("MoveSpeed", movementSpeed);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void MoveToLocation(Vector3 destination)
|
|
{
|
|
navMeshAgent.SetDestination(destination);
|
|
animationController.SetBool("MovementInputHeld", true);
|
|
animationController.SetFloat("MoveSpeed", movementSpeed);
|
|
}
|
|
|
|
private void FindResources()
|
|
{
|
|
// FindObjectsSortMode.None is required in newer Unity versions for performance
|
|
Resource[] resources = Object.FindObjectsByType<Resource>(FindObjectsSortMode.None);
|
|
|
|
Resource closestResource = null;
|
|
float closestDistance = Mathf.Infinity;
|
|
|
|
// Loop through to find the *closest* valid resource, not just the first one
|
|
foreach (Resource res in resources)
|
|
{
|
|
if (res.resourceType == resourceToWorkWith && res.resourceState == Resource.ResourceState.Full)
|
|
{
|
|
float distance = Vector3.Distance(transform.position, res.transform.position);
|
|
if (distance < closestDistance)
|
|
{
|
|
closestDistance = distance;
|
|
closestResource = res;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (closestResource != null)
|
|
{
|
|
currentTargetResource = closestResource;
|
|
MoveToLocation(currentTargetResource.transform.position);
|
|
}
|
|
else
|
|
{
|
|
// If no resources are found, stop moving and stand idle
|
|
navMeshAgent.ResetPath();
|
|
animationController.SetBool("MovementInputHeld", false);
|
|
animationController.SetFloat("MoveSpeed", 0f);
|
|
}
|
|
}
|
|
|
|
private void GatherResource()
|
|
{
|
|
// 1. Stop the agent
|
|
navMeshAgent.ResetPath();
|
|
animationController.SetBool("MovementInputHeld", false);
|
|
animationController.SetFloat("MoveSpeed", 0f);
|
|
transform.LookAt(currentTargetResource.transform.position);
|
|
|
|
// 2. Start the harvesting animation loop
|
|
animationController.SetBool("IsHarvesting", true);
|
|
|
|
// 3. Handle resource type switching if necessary
|
|
if (carryingResource != resourceToWorkWith)
|
|
{
|
|
DespositResources();
|
|
carryingResource = resourceToWorkWith;
|
|
}
|
|
}
|
|
|
|
// 4. This is called automatically by Unity when the tool hits the tree/rock
|
|
private void Harvest()
|
|
{
|
|
// Safety check: Make sure we actually have a target to harvest
|
|
if (currentTargetResource != null)
|
|
{
|
|
// Do the actual resource extraction at the exact moment of the visual hit
|
|
currentTargetResource.RemoveResource(gatherAmount);
|
|
AddToInventory(resourceToWorkWith, gatherAmount);
|
|
|
|
// 5. Check if the resource node is now empty
|
|
if (currentTargetResource.GetResourceState() == false)
|
|
{
|
|
// Clear the target and stop the animation because the node is depleted
|
|
currentTargetResource = null;
|
|
animationController.SetBool("IsHarvesting", false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If there's no resource left, stop animating
|
|
animationController.SetBool("IsHarvesting", false);
|
|
}
|
|
}
|
|
|
|
private void SetJob(Job job)
|
|
{
|
|
switch (job)
|
|
{
|
|
case Job.Builder:
|
|
//resourceToWorkWith = Resource.ResourceType.Wood;
|
|
break;
|
|
case Job.Soldier:
|
|
// Uncomment or assign when ready
|
|
// resourceToWorkWith = Resource.ResourceType.Wood;
|
|
break;
|
|
case Job.Farmer:
|
|
resourceToWorkWith = Resource.ResourceType.Food;
|
|
break;
|
|
case Job.Forester:
|
|
resourceToWorkWith = Resource.ResourceType.Wood;
|
|
jobImg.sprite = forester;
|
|
break;
|
|
case Job.Merchant:
|
|
resourceToWorkWith = Resource.ResourceType.Money;
|
|
break;
|
|
case Job.Miner:
|
|
resourceToWorkWith = Resource.ResourceType.Stone;
|
|
jobImg.sprite = miner;
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void AddToInventory(Resource.ResourceType resourceToAdd, int amountToAdd)
|
|
{
|
|
// Note: This capacity check assumes you mean "number of unique slots"
|
|
// If you mean total quantity, you'll need to sum the dictionary values first.
|
|
if (!inventory.ContainsKey(resourceToAdd) && inventory.Count >= inventoryCapacity)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (inventory.ContainsKey(resourceToAdd))
|
|
{
|
|
inventory[resourceToAdd] += amountToAdd;
|
|
}
|
|
else
|
|
{
|
|
inventory.Add(resourceToAdd, amountToAdd);
|
|
}
|
|
|
|
// Using string interpolation for slightly cleaner formatting
|
|
Debug.Log($"{amountToAdd} of {resourceToAdd} added to inventory");
|
|
}
|
|
private void DespositResources()
|
|
{
|
|
// Properly assign the found buildings to an array so we can loop through them
|
|
Building[] activeBuildings = Object.FindObjectsByType<Building>(FindObjectsSortMode.None);
|
|
|
|
foreach (Building building in activeBuildings)
|
|
{
|
|
if (building.GetBuildingState() == Building.BuildingState.Active)
|
|
{
|
|
// NOTE: Currently this only interacts with Farms.
|
|
if (building.GetBuildingType() == Building.BuildingType.Farm)
|
|
{
|
|
building.Interact(this);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
public Dictionary<Resource.ResourceType, int> GetInventory()
|
|
{
|
|
return inventory;
|
|
}
|
|
} |