Initial commit

This commit is contained in:
2026-06-11 17:30:53 +01:00
commit 479b0577a8
25870 changed files with 7314001 additions and 0 deletions

215
Assets/Scripts/NPC.cs Normal file
View File

@@ -0,0 +1,215 @@
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;
}
}