Working on UI and job assignment, harvesting and depositing to TownHall working
This commit is contained in:
@@ -3,13 +3,21 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
[System.Serializable]
|
||||
public class StorageConfig
|
||||
{
|
||||
public Resource.ResourceType resourceType;
|
||||
public int capacity;
|
||||
}
|
||||
|
||||
public class Building : MonoBehaviour
|
||||
{
|
||||
public enum BuildingType
|
||||
{
|
||||
House,
|
||||
Farm,
|
||||
Barracks
|
||||
Barracks,
|
||||
TownHall
|
||||
}
|
||||
public enum BuildingState
|
||||
{
|
||||
@@ -19,16 +27,37 @@ public class Building : MonoBehaviour
|
||||
Destroyed
|
||||
}
|
||||
[SerializeField] float constructionTime;
|
||||
[SerializeField] private Resource.ResourceType[] resourceTypes;
|
||||
[SerializeField] private Resource.ResourceType[] constructionResources;
|
||||
[SerializeField] private int[] constructionCost;
|
||||
private BuildingState buildingState;
|
||||
[SerializeField] private BuildingType buildingType;
|
||||
[SerializeField] private GameObject unbuiltModel, constructingModel, activeModel, destroyedModel;
|
||||
[SerializeField] private StorageConfig[] storageConfig = new StorageConfig[0];
|
||||
public Dictionary<Resource.ResourceType, int> resourcesToStore = new Dictionary<Resource.ResourceType, int>();
|
||||
// Start is called once before the first execution of Update after the MonoBehaviour is created
|
||||
void Start()
|
||||
{
|
||||
buildingState = BuildingState.Unbuilt;
|
||||
if (buildingType == BuildingType.TownHall)
|
||||
{
|
||||
buildingState = BuildingState.Active;
|
||||
}
|
||||
else
|
||||
{
|
||||
buildingState = BuildingState.Unbuilt;
|
||||
}
|
||||
|
||||
SetBuildingModel(buildingState);
|
||||
InitializeStorage();
|
||||
}
|
||||
|
||||
private void InitializeStorage()
|
||||
{
|
||||
resourcesToStore.Clear();
|
||||
foreach (StorageConfig config in storageConfig)
|
||||
{
|
||||
resourcesToStore[config.resourceType] = config.capacity;
|
||||
Debug.Log($"Storage capacity for {config.resourceType} set to {config.capacity}");
|
||||
}
|
||||
}
|
||||
|
||||
// Update is called once per frame
|
||||
@@ -80,14 +109,14 @@ public class Building : MonoBehaviour
|
||||
if (buildingState != BuildingState.Unbuilt)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < resourceTypes.Length && i < constructionCost.Length; i++)
|
||||
for (int i = 0; i < constructionResources.Length && i < constructionCost.Length; i++)
|
||||
{
|
||||
if (GameManager.Instance.GetResource(resourceTypes[i]) < constructionCost[i])
|
||||
if (GameManager.Instance.GetResource(constructionResources[i]) < constructionCost[i])
|
||||
{
|
||||
Debug.Log("Not enough resources");
|
||||
return;
|
||||
}
|
||||
GameManager.Instance.AddResource(resourceTypes[i], -constructionCost[i]);
|
||||
GameManager.Instance.AddResource(constructionResources[i], -constructionCost[i]);
|
||||
}
|
||||
Debug.Log("Building Started");
|
||||
buildingState = BuildingState.Constructing;
|
||||
@@ -135,12 +164,32 @@ public class Building : MonoBehaviour
|
||||
case BuildingState.Active:
|
||||
//DestroyBuilding();
|
||||
var npcInventory = npcInteracting.GetInventory();
|
||||
foreach (KeyValuePair<Resource.ResourceType, int> entry in npcInventory)
|
||||
var entries = new List<KeyValuePair<Resource.ResourceType, int>>(npcInventory);
|
||||
foreach (KeyValuePair<Resource.ResourceType, int> entry in entries)
|
||||
{
|
||||
GameManager.Instance.AddResource(entry.Key, entry.Value);
|
||||
UIController.Instance.UpdateStats();
|
||||
npcInteracting.inventory.Remove(entry.Key);
|
||||
}
|
||||
break;
|
||||
case BuildingState.Destroyed:
|
||||
CancelBuilding();
|
||||
break;
|
||||
}
|
||||
}
|
||||
public void SetUpStore(StorageConfig[] configs)
|
||||
{
|
||||
if (configs == null)
|
||||
{
|
||||
Debug.LogWarning("SetUpStore received null config array.");
|
||||
return;
|
||||
}
|
||||
|
||||
resourcesToStore.Clear();
|
||||
foreach (StorageConfig config in configs)
|
||||
{
|
||||
resourcesToStore[config.resourceType] = config.capacity;
|
||||
Debug.Log($"Storage capacity for {config.resourceType} set to {config.capacity}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
22
Assets/Scripts/JobButton.cs
Normal file
22
Assets/Scripts/JobButton.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
|
||||
// IPointerClickHandler is the magic interface that listens to ALL mouse clicks
|
||||
public class JobButton : MonoBehaviour, IPointerClickHandler
|
||||
{
|
||||
[Tooltip("Select which job this specific button manages.")]
|
||||
public NPC.Job jobType;
|
||||
|
||||
public void OnPointerClick(PointerEventData eventData)
|
||||
{
|
||||
// Intercept the click and pass the command to the UIController
|
||||
if (eventData.button == PointerEventData.InputButton.Left)
|
||||
{
|
||||
UIController.Instance.AddJob(jobType);
|
||||
}
|
||||
else if (eventData.button == PointerEventData.InputButton.Right)
|
||||
{
|
||||
UIController.Instance.RemoveJob(jobType);
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/JobButton.cs.meta
Normal file
2
Assets/Scripts/JobButton.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6cf51a3decb7ccf4d8f348108aafb23e
|
||||
@@ -3,26 +3,27 @@ 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;
|
||||
public enum Job { Builder, Farmer, Soldier, Forester, Merchant, Miner, Citizen }
|
||||
public Job job = Job.Citizen;
|
||||
|
||||
// Added a simple state tracker to cleanly separate gathering and traveling
|
||||
public enum NPCState { Gathering, Delivering, Idle }
|
||||
public NPCState currentState = NPCState.Idle;
|
||||
|
||||
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;
|
||||
[SerializeField] Sprite miner, forester, citizen, farmer, merchant, soldier;
|
||||
|
||||
// Cache the target so the NPC remembers what it is walking toward
|
||||
private Resource currentTargetResource;
|
||||
private IEnumerable<Building> buildings;
|
||||
private Building currentTargetBuilding; // Cache the TownHall so we don't search for it every frame
|
||||
|
||||
void Start()
|
||||
{
|
||||
@@ -32,12 +33,37 @@ public class NPC : MonoBehaviour
|
||||
|
||||
void Update()
|
||||
{
|
||||
// 1. If we don't have a valid target, find the nearest one
|
||||
// 1. Evaluate if we need to switch from Gathering to Delivering
|
||||
if (currentState == NPCState.Gathering && !CanAddToInventory(gatherAmount))
|
||||
{
|
||||
animationController.SetBool("IsHarvesting", false);
|
||||
currentTargetResource = null;
|
||||
currentState = NPCState.Delivering;
|
||||
}
|
||||
|
||||
// 2. Execute the current state's logic
|
||||
switch (currentState)
|
||||
{
|
||||
case NPCState.Gathering:
|
||||
HandleGatheringPhase();
|
||||
break;
|
||||
case NPCState.Delivering:
|
||||
HandleDeliveryPhase();
|
||||
break;
|
||||
case NPCState.Idle:
|
||||
navMeshAgent.ResetPath();
|
||||
animationController.SetBool("MovementInputHeld", false);
|
||||
animationController.SetFloat("MoveSpeed", 0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleGatheringPhase()
|
||||
{
|
||||
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);
|
||||
@@ -48,13 +74,47 @@ public class NPC : MonoBehaviour
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ensure movement animation is playing while walking
|
||||
animationController.SetBool("MovementInputHeld", true);
|
||||
animationController.SetFloat("MoveSpeed", movementSpeed);
|
||||
MoveToLocation(currentTargetResource.transform.position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleDeliveryPhase()
|
||||
{
|
||||
// 1. Find a TownHall if we haven't cached one yet
|
||||
if (currentTargetBuilding == null)
|
||||
{
|
||||
Building[] activeBuildings = Object.FindObjectsByType<Building>(FindObjectsSortMode.None);
|
||||
foreach (Building building in activeBuildings)
|
||||
{
|
||||
if (building.GetBuildingState() == Building.BuildingState.Active && building.GetBuildingType() == Building.BuildingType.TownHall)
|
||||
{
|
||||
currentTargetBuilding = building;
|
||||
break; // Exit the loop once we find one
|
||||
}
|
||||
}
|
||||
|
||||
// If there's no TownHall active on the map, stand idle
|
||||
if (currentTargetBuilding == null)
|
||||
{
|
||||
currentState = NPCState.Idle;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Move towards the TownHall
|
||||
float distanceToTarget = Vector3.Distance(transform.position, currentTargetBuilding.transform.position);
|
||||
|
||||
if (distanceToTarget <= interactionRadius)
|
||||
{
|
||||
DepositResources();
|
||||
}
|
||||
else
|
||||
{
|
||||
MoveToLocation(currentTargetBuilding.transform.position);
|
||||
}
|
||||
}
|
||||
|
||||
private void MoveToLocation(Vector3 destination)
|
||||
{
|
||||
navMeshAgent.SetDestination(destination);
|
||||
@@ -64,13 +124,10 @@ public class NPC : MonoBehaviour
|
||||
|
||||
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)
|
||||
@@ -91,67 +148,69 @@ public class NPC : MonoBehaviour
|
||||
}
|
||||
else
|
||||
{
|
||||
// If no resources are found, stop moving and stand idle
|
||||
navMeshAgent.ResetPath();
|
||||
animationController.SetBool("MovementInputHeld", false);
|
||||
animationController.SetFloat("MoveSpeed", 0f);
|
||||
// If the map is empty, check our pockets.
|
||||
// If we have items, go deliver them. If empty, stand idle.
|
||||
if (GetTotalInventoryAmount() > 0)
|
||||
{
|
||||
currentState = NPCState.Delivering;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentState = NPCState.Idle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// Safety check: if full, stop hitting and trigger delivery on the next frame
|
||||
if (!CanAddToInventory(gatherAmount))
|
||||
{
|
||||
animationController.SetBool("IsHarvesting", false);
|
||||
currentTargetResource = null;
|
||||
currentState = NPCState.Delivering;
|
||||
return;
|
||||
}
|
||||
|
||||
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)
|
||||
public 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;
|
||||
@@ -167,47 +226,73 @@ public class NPC : MonoBehaviour
|
||||
resourceToWorkWith = Resource.ResourceType.Stone;
|
||||
jobImg.sprite = miner;
|
||||
break;
|
||||
case Job.Citizen:
|
||||
resourceToWorkWith = Resource.ResourceType.Citizens;
|
||||
jobImg.sprite = citizen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void AddToInventory(Resource.ResourceType resourceToAdd, int amountToAdd)
|
||||
// Don't instantly teleport resources. Put the NPC into Delivery state.
|
||||
if (inventory.Count > 0 && carryingResource != resourceToWorkWith)
|
||||
{
|
||||
currentState = NPCState.Delivering;
|
||||
}
|
||||
|
||||
carryingResource = resourceToWorkWith;
|
||||
this.job = job;
|
||||
}
|
||||
public Job GetJob()
|
||||
{
|
||||
// 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");
|
||||
return job;
|
||||
}
|
||||
private void DespositResources()
|
||||
|
||||
private bool AddToInventory(Resource.ResourceType resourceToAdd, int amountToAdd)
|
||||
{
|
||||
// Properly assign the found buildings to an array so we can loop through them
|
||||
Building[] activeBuildings = Object.FindObjectsByType<Building>(FindObjectsSortMode.None);
|
||||
if (amountToAdd <= 0) return true;
|
||||
|
||||
foreach (Building building in activeBuildings)
|
||||
if (!inventory.ContainsKey(resourceToAdd))
|
||||
{
|
||||
if (building.GetBuildingState() == Building.BuildingState.Active)
|
||||
{
|
||||
// NOTE: Currently this only interacts with Farms.
|
||||
if (building.GetBuildingType() == Building.BuildingType.Farm)
|
||||
{
|
||||
building.Interact(this);
|
||||
}
|
||||
}
|
||||
inventory.Add(resourceToAdd, 0);
|
||||
}
|
||||
|
||||
inventory[resourceToAdd] += amountToAdd;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool CanAddToInventory(int amountToAdd)
|
||||
{
|
||||
return GetTotalInventoryAmount() + amountToAdd <= inventoryCapacity;
|
||||
}
|
||||
|
||||
// Efficiency: Created a helper method so we aren't writing out the dictionary loop constantly
|
||||
private int GetTotalInventoryAmount()
|
||||
{
|
||||
int currentTotal = 0;
|
||||
foreach (int amount in inventory.Values)
|
||||
{
|
||||
currentTotal += amount;
|
||||
}
|
||||
return currentTotal;
|
||||
}
|
||||
|
||||
private void DepositResources()
|
||||
{
|
||||
navMeshAgent.ResetPath();
|
||||
animationController.SetBool("MovementInputHeld", false);
|
||||
animationController.SetFloat("MoveSpeed", 0f);
|
||||
|
||||
if (currentTargetBuilding != null)
|
||||
{
|
||||
currentTargetBuilding.Interact(this);
|
||||
}
|
||||
|
||||
// CRITICAL: You must clear the local inventory so CanAddToInventory() becomes true again.
|
||||
inventory.Clear();
|
||||
|
||||
currentTargetBuilding = null;
|
||||
currentState = NPCState.Gathering;
|
||||
}
|
||||
|
||||
public Dictionary<Resource.ResourceType, int> GetInventory()
|
||||
{
|
||||
return inventory;
|
||||
|
||||
58
Assets/Scripts/UIController.cs
Normal file
58
Assets/Scripts/UIController.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using UnityEngine;
|
||||
using TMPro;
|
||||
|
||||
public class UIController : MonoBehaviour
|
||||
{
|
||||
public static UIController Instance;
|
||||
|
||||
[SerializeField] TextMeshProUGUI wood, stone, population;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
UpdateStats();
|
||||
}
|
||||
|
||||
public void UpdateStats()
|
||||
{
|
||||
wood.text = GameManager.Instance.GetResource(Resource.ResourceType.Wood).ToString();
|
||||
stone.text = GameManager.Instance.GetResource(Resource.ResourceType.Stone).ToString();
|
||||
population.text = GameManager.Instance.GetResource(Resource.ResourceType.Citizens).ToString();
|
||||
}
|
||||
|
||||
// Called by left-clicking a JobButton
|
||||
public void AddJob(NPC.Job jobToAssign)
|
||||
{
|
||||
NPC[] npcs = FindObjectsByType<NPC>(FindObjectsSortMode.None);
|
||||
foreach(NPC npc in npcs)
|
||||
{
|
||||
// Find a Citizen and give them the new job
|
||||
if(npc.job == NPC.Job.Citizen)
|
||||
{
|
||||
npc.SetJob(jobToAssign);
|
||||
Debug.Log($"Assigned a Citizen to {jobToAssign}");
|
||||
break; // Stop after assigning one
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Called by right-clicking a JobButton
|
||||
public void RemoveJob(NPC.Job jobToRemove)
|
||||
{
|
||||
NPC[] npcs = FindObjectsByType<NPC>(FindObjectsSortMode.None);
|
||||
foreach(NPC npc in npcs)
|
||||
{
|
||||
// Find an NPC currently doing this job and revert them to a Citizen
|
||||
if(npc.job == jobToRemove)
|
||||
{
|
||||
npc.SetJob(NPC.Job.Citizen);
|
||||
Debug.Log($"Removed a {jobToRemove} and made them a Citizen");
|
||||
break; // Stop after removing one
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/UIController.cs.meta
Normal file
2
Assets/Scripts/UIController.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 97da0d81e511b1d4abf682e2af1d7ddb
|
||||
Reference in New Issue
Block a user