Initial commit

This commit is contained in:
2026-09-02 21:14:02 +01:00
parent 10d6c0fa34
commit 41d8d1804d
17 changed files with 9427 additions and 85 deletions

View File

@@ -74,6 +74,15 @@ public class Building : MonoBehaviour
{
return buildingState;
}
public Resource.ResourceType[] GetResourceRequirement()
{
return constructionResources;
}
public int[] GetConstructionCost()
{
return constructionCost;
}
private void SetBuildingModel(BuildingState state)
{
switch (state)
@@ -105,24 +114,27 @@ public class Building : MonoBehaviour
break;
}
}
public void StartBuilding()
{
if (buildingState != BuildingState.Unbuilt)
return;
public void StartBuilding()
{
if (buildingState != BuildingState.Unbuilt)
return;
for (int i = 0; i < constructionResources.Length && i < constructionCost.Length; i++)
Resource.ResourceType[] resources = GetResourceRequirement();
int[] costs = GetConstructionCost();
for (int i = 0; i < resources.Length && i < costs.Length; i++)
{
if (GameManager.Instance.GetResource(resources[i]) < costs[i])
{
if (GameManager.Instance.GetResource(constructionResources[i]) < constructionCost[i])
{
Debug.Log("Not enough resources");
return;
}
GameManager.Instance.AddResource(constructionResources[i], -constructionCost[i]);
Debug.Log("Not enough resources");
return;
}
Debug.Log("Building Started");
buildingState = BuildingState.Constructing;
SetBuildingModel(buildingState);
GameManager.Instance.AddResource(resources[i], -costs[i]);
}
Debug.Log("Building Started");
buildingState = BuildingState.Constructing;
SetBuildingModel(buildingState);
}
public void FinishBuilding()
{
if (buildingState != BuildingState.Constructing)

View File

@@ -42,8 +42,17 @@ public class GameManager : MonoBehaviour
population += amountToAdd;
break;
}
// If the dictionary doesn't contain this resource yet, initialize it at 0
if (!statistics.ContainsKey(resourceToAdd))
{
statistics.Add(resourceToAdd, 0);
}
statistics[resourceToAdd] += amountToAdd;
UIController.Instance.UpdateStats();
// Safety check to prevent a crash on Awake
if (UIController.Instance != null)
{
UIController.Instance.UpdateStats();
}
}
public float GetResource(Resource.ResourceType resourceToGet)

View File

@@ -1,4 +1,5 @@
using UnityEngine;
using System.Collections.Generic;
public class JobManager : MonoBehaviour
{
@@ -28,13 +29,57 @@ public class JobManager : MonoBehaviour
private Building GetUnfinishedBuildings()
{
Building[] buildings = GameManager.Instance.CountBuildings();
List<Building> candidates = new List<Building>();
foreach (Building building in buildings)
{
if(building.GetBuildingState() == Building.BuildingState.Unbuilt)
var state = building.GetBuildingState();
if (state == Building.BuildingState.Unbuilt)
{
return building;
if (CanBuildBuilding(building))
{
candidates.Add(building);
}
}
else if (state == Building.BuildingState.Constructing)
{
// Allow additional builders to join already-constructing buildings
candidates.Add(building);
}
}
return null;
if (candidates.Count == 0)
return null;
int idx = Random.Range(0, candidates.Count);
return candidates[idx];
}
private bool CanBuildBuilding(Building building)
{
Resource.ResourceType[] requiredResources = building.GetResourceRequirement();
int[] requiredCosts = building.GetConstructionCost();
if (requiredResources == null || requiredCosts == null)
{
Debug.LogWarning("Building missing resource or cost arrays");
return false;
}
if (requiredResources.Length != requiredCosts.Length)
{
Debug.LogWarning("Building resource and cost arrays don't match in length");
return false;
}
for (int i = 0; i < requiredResources.Length; i++)
{
if (GameManager.Instance.GetResource(requiredResources[i]) < requiredCosts[i])
{
return false;
}
}
return true;
}
}

View File

@@ -14,6 +14,7 @@ public class NPC : MonoBehaviour
private Resource.ResourceType resourceToWorkWith, carryingResource;
public Dictionary<Resource.ResourceType, int> inventory = new Dictionary<Resource.ResourceType, int>();
public Dictionary<Skills.SkillType, Skills> skills = new Dictionary<Skills.SkillType, Skills>();
[SerializeField] int gatherAmount, inventoryCapacity;
[SerializeField] NavMeshAgent navMeshAgent;
@@ -74,7 +75,8 @@ public class NPC : MonoBehaviour
}
else
{
float distanceToTarget = Vector3.Distance(transform.position, currentTargetResource.transform.position);
Vector3 targetPosition = GetResourceTargetPosition();
float distanceToTarget = Vector3.Distance(transform.position, targetPosition);
if (distanceToTarget <= interactionRadius)
{
@@ -82,11 +84,21 @@ public class NPC : MonoBehaviour
}
else
{
MoveToLocation(currentTargetResource.transform.position);
MoveToLocation(targetPosition);
}
}
}
private Vector3 GetResourceTargetPosition()
{
if (currentTargetResource != null && job == Job.Miner && currentTargetResource.mineLocation != null)
{
return currentTargetResource.mineLocation.position;
}
return currentTargetResource != null ? currentTargetResource.transform.position : transform.position;
}
private void HandleDeliveryPhase()
{
// 1. Find a TownHall if we haven't cached one yet
@@ -191,7 +203,7 @@ public class NPC : MonoBehaviour
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)
{
@@ -204,7 +216,10 @@ public class NPC : MonoBehaviour
if (closestResource != null)
{
currentTargetResource = closestResource;
MoveToLocation(currentTargetResource.transform.position);
if(this.job == Job.Miner)
MoveToLocation(currentTargetResource.mineLocation.transform.position);
else
MoveToLocation(currentTargetResource.transform.position);
}
else
{
@@ -250,7 +265,40 @@ public class NPC : MonoBehaviour
}
currentTargetResource.RemoveResource(gatherAmount);
AddToInventory(resourceToWorkWith, gatherAmount);
Skills.SkillType currentSkillType = resourceToWorkWith switch
{
Resource.ResourceType.Wood => Skills.SkillType.Woodcutting,
Resource.ResourceType.Stone => Skills.SkillType.Stonecutting,
Resource.ResourceType.Metal => Skills.SkillType.Mining,
Resource.ResourceType.Food => Skills.SkillType.Farming,
_ => Skills.SkillType.Woodcutting
};
int currentSkillLevel = 1;
if (skills.TryGetValue(currentSkillType, out Skills currentSkill))
{
currentSkillLevel = currentSkill.GetLevel(currentSkill.skillType);
}
int bonusHarvest = Mathf.Max(0, currentSkillLevel - 1);
AddToInventory(resourceToWorkWith, gatherAmount + bonusHarvest);
switch (resourceToWorkWith)
{
case Resource.ResourceType.Wood:
GainExperience(Skills.SkillType.Woodcutting, 10);
break;
case Resource.ResourceType.Stone:
GainExperience(Skills.SkillType.Stonecutting, 10);
break;
case Resource.ResourceType.Metal:
GainExperience(Skills.SkillType.Mining, 10);
break;
case Resource.ResourceType.Food:
GainExperience(Skills.SkillType.Farming, 10);
break;
}
if (currentTargetResource.GetResourceState() == false)
{
@@ -350,6 +398,18 @@ public class NPC : MonoBehaviour
{
return GetTotalInventoryAmount() + amountToAdd <= inventoryCapacity;
}
private void GainExperience(Skills.SkillType type, int amount)
{
// 1. Check if the dictionary does NOT contain the skill yet
if (!skills.ContainsKey(type))
{
// The NPC learns the skill for the first time, using our new Constructor
skills.Add(type, new Skills(type));
}
// 2. Add the XP to that specific skill in the dictionary
skills[type].AddExperience(amount);
}
// Efficiency: Created a helper method so we aren't writing out the dictionary loop constantly
private int GetTotalInventoryAmount()

View File

@@ -22,6 +22,7 @@ public class Resource : MonoBehaviour
[SerializeField] int maxResourceQuantity;
private int curResourceQuantity;
[SerializeField] private GameObject fullModel, depletedModel;
public Transform mineLocation;
// Start is called once before the first execution of Update after the MonoBehaviour is created
void Start()
{

53
Assets/Scripts/Skills.cs Normal file
View File

@@ -0,0 +1,53 @@
using UnityEngine;
// System.Serializable makes these show up in the Unity Inspector so you can debug NPC levels!
[System.Serializable]
public class Skills
{
public enum SkillType
{
Woodcutting,
Stonecutting,
Mining,
Building,
Farming // Added to match your Farmer job
}
public SkillType skillType;
public int skillExp;
public int skillLevel;
// 1. The Constructor: This runs automatically when the NPC learns the skill for the first time
public Skills(SkillType type)
{
skillType = type;
skillLevel = 1;
skillExp = 0;
}
// 2. The Math: Handles gaining XP and checking for level ups
public void AddExperience(int amount)
{
skillExp += amount;
// Example leveling formula: Level 1 requires 100xp, Level 2 requires 200xp, etc.
int xpRequiredForNextLevel = skillLevel * 100;
if (skillExp >= xpRequiredForNextLevel)
{
// Subtract the requirement but keep the rollover XP
skillExp -= xpRequiredForNextLevel;
skillLevel++;
Debug.Log($"Leveled up! {skillType} is now Level {skillLevel}");
}
}
public int GetLevel(SkillType type)
{
if (type == skillType)
return skillLevel;
Debug.LogWarning($"Requested level for {type} but this skill is {skillType}");
return 0;
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 073eee246aee481449d8f5619dc58baa

View File

@@ -0,0 +1,13 @@
using UnityEngine;
public class SpinningWindmill : MonoBehaviour
{
[SerializeField] private float rotationSpeed;
[SerializeField] GameObject windmillBlades;
void Update()
{
//transform.Rotate(0f, 0f, rotationSpeed * Time.deltaTime);
windmillBlades.transform.Rotate(0f, 0f, rotationSpeed * Time.deltaTime);
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 9f8348a8c83d0884f864ffaf4554faa1