Untitled
unknown
plain_text
a year ago
27 kB
9
Indexable
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
public enum Faction
{
Civilian,
Gang,
Mafia,
Police
}
public enum SubFaction
{
None,
MafiaBlack,
MafiaBlue,
MafiaBrown,
MafiaGreen,
MafiaOrange,
MafiaPurple,
MafiaRed,
MafiaWhite,
GangBlack,
GangBlue,
GangGreen,
GangOrange,
GangPurple,
GangRed,
GangYelow
}
public enum Occupation
{
Unemployed,
PoliceMan,
Doctor,
FireFighter,
Mechanic,
Model,
TopModel,
Cashier,
Taxi,
Musician,
Programmer,
}
public enum Interests
{
Sports,
Park,
Games
}
public enum Gender
{
Male,
Female
}
public enum SkinColor
{
White,
Black
}
public enum HairColor
{
Black,
Blond,
Brun,
Ginger,
Yellow
}
public enum NPCTask
{
Sleeping,
Working,
Eating,
RunningErrands,
FreeTime
}
public enum LocationType
{
Home,
Work
}
public enum LocationCategory
{
None,
Supermarket,
Park,
Restaurant,
Home,
Police,
Hospital,
FireFighter,
Mechanic,
Models,
Musician,
Programmer
}
public class NpcData : MonoBehaviour
{
[Header("NPC Data")]
public string npcName;
public int id;
public Gender gender;
public SkinColor skinColor;
public HairColor hairColor;
public Faction faction;
public SubFaction subFaction;
public Occupation Occupation;
public Interests interests;
public NPCTask currentTask;
public int homeID; // Index into NPCSpawner.homes
public int workID; // Index into appropriate array (e.g. supermarkets, parks, etc.)
public LocationCategory workLocationCategory;
public int numberOfTimesLate;
public bool nightShift;
public CarController ownedCar;
public int salary;
[Range(-10, 10)] public int playerRelation;
public int[] friendListid;
public int[] enemyListId;
[Header("NPC Stats")]
[Min(1)] public int strength = 1;
[Min(1)] public int charisma = 1;
[Min(1)] public int perception = 1;
[Min(1)] public int dexterity = 1;
[Min(1)] public int manipulation = 1;
[Min(1)] public int intelligence = 1;
[Min(1)] public int stamina = 1;
[Min(1)] public int appearance = 1;
[Min(1)] public int wits = 1;
[Header("NPC Skills")]
public int brawl;
public int awareness;
public int etiquette;
public int melee;
public int investigation;
public int stealth;
public int firearms;
public int medicine;
public int larceny;
public int athletics;
public int technology;
public int presence;
public int drive;
public int occult;
public int survival;
private EnemyAI enemyAI;
private Animator animator;
private RuntimeAnimatorController defaultController;
private NPCSpawner npcSpawner;
private CarSavingSystem carSavingSystem;
private void Awake()
{
enemyAI = GetComponent<EnemyAI>();
npcSpawner = FindObjectOfType<NPCSpawner>();
carSavingSystem = FindObjectOfType<CarSavingSystem>();
animator = enemyAI.anim;
defaultController = animator.runtimeAnimatorController;
InitializeSkillsAndJobs();
}
private void Update()
{
UpdateTaskBasedOnTime();
}
public void InitializeSkillsAndJobs()
{
//unlike skills stats start default by 1
strength = 1;
charisma = 1;
perception = 1;
dexterity = 1;
manipulation = 1;
intelligence = 1;
stamina = 1;
appearance = 1;
wits = 1;
int xpToDistrubute = npcSpawner.xpForThisLevel;
DistributeXP(xpToDistrubute);
AssignName();
AssignInterest();
AssignJob();
AssignJobOutfit();
UpdateMoneyBasedOnSalary();
AssignLocations();
AssignCar();
}
public void AssignCar()
{
if(true /*salary > 2 && drive > 0*/)
{
if (enemyAI.ownedCar)
{
//Resets previous Assigned cars on spawn
enemyAI.ownedCar.npcOwner = null;
enemyAI.ownedCar = null;
}
}
}
private void WearCivilianOutfit()
{
animator.runtimeAnimatorController = defaultController;
}
private void AssignLocations()
{
// 1. Select a random home
Transform randomHome = npcSpawner.homes[UnityEngine.Random.Range(0, npcSpawner.homes.Length)];
homeID = Array.IndexOf(npcSpawner.homes, randomHome);
// 2. Determine work location
Transform workLoc = randomHome; // default fallback
workID = -1;
workLocationCategory = LocationCategory.None;
if (faction != Faction.Gang && faction != Faction.Mafia && Occupation != Occupation.Unemployed)
{
switch (Occupation)
{
case Occupation.PoliceMan:
workLoc = npcSpawner.policeStation;
workLocationCategory = LocationCategory.Police;
workID = 0;
break;
case Occupation.Doctor:
workLoc = npcSpawner.hospital;
workLocationCategory = LocationCategory.Hospital;
workID = 0;
break;
case Occupation.FireFighter:
workLoc = npcSpawner.fireFighterStation;
workLocationCategory = LocationCategory.FireFighter;
workID = 0;
break;
case Occupation.Mechanic:
workLoc = npcSpawner.mechanicStation;
workLocationCategory = LocationCategory.Mechanic;
workID = 0;
break;
case Occupation.Model:
case Occupation.TopModel:
workLoc = npcSpawner.modelsStudio;
workLocationCategory = LocationCategory.Models;
workID = 0;
break;
case Occupation.Musician:
workLoc = npcSpawner.musicianStudio;
workLocationCategory = LocationCategory.Musician;
workID = 0;
break;
case Occupation.Programmer:
workLoc = npcSpawner.programmerCompany;
workLocationCategory = LocationCategory.Programmer;
workID = 0;
break;
case Occupation.Cashier:
Transform randomSuperMarket = npcSpawner.superMarkets[UnityEngine.Random.Range(0, npcSpawner.superMarkets.Length)];
workLoc = randomSuperMarket;
workLocationCategory = LocationCategory.Supermarket;
workID = Array.IndexOf(npcSpawner.superMarkets, randomSuperMarket);
break;
case Occupation.Taxi:
Transform randomPark = npcSpawner.parks[UnityEngine.Random.Range(0, npcSpawner.parks.Length)];
workLoc = randomPark;
workLocationCategory = LocationCategory.Park;
workID = Array.IndexOf(npcSpawner.parks, randomPark);
break;
}
}
// 3. Merge restaurant + park + home into eat locations
List<Transform> eatLocsList = new List<Transform>();
eatLocsList.AddRange(npcSpawner.restaurants);
eatLocsList.AddRange(npcSpawner.parks);
eatLocsList.Add(randomHome);
Transform[] eatLocs = eatLocsList.ToArray();
// 4. Assign locations to EnemyAI
enemyAI.SetLocations(randomHome, npcSpawner.parks, workLoc, eatLocs, npcSpawner.superMarkets);
}
public void UpdateMoneyBasedOnSalary()
{
enemyAI.UpdateMoney(salary * 50);
}
public void UpdateTaskBasedOnTime()
{
DateTime now = TimeDisplay.instance.gameTime;
TimeSpan current = new TimeSpan(now.Hour, now.Minute, 0);
if (nightShift)
{
// === Night Shift Schedule ===
int sleepHours = Mathf.Clamp(10 - stamina, 5, 9);
TimeSpan sleepStart = new TimeSpan(8, 0, 0);
TimeSpan sleepEnd = sleepStart.Add(TimeSpan.FromHours(sleepHours));
TimeSpan errandStart = sleepEnd;
TimeSpan errandEnd = errandStart.Add(TimeSpan.FromMinutes(30));
TimeSpan freeTimeStart = errandEnd;
TimeSpan dinnerStart = new TimeSpan(22, 0, 0);
TimeSpan dinnerEnd = new TimeSpan(23, 0, 0);
TimeSpan workStart1 = new TimeSpan(23, 0, 0); // After dinner
TimeSpan workEnd1 = new TimeSpan(3, 0, 0); // Before lunch
TimeSpan lunchStart = new TimeSpan(3, 0, 0);
TimeSpan lunchEnd = new TimeSpan(4, 0, 0);
TimeSpan workStart2 = new TimeSpan(4, 0, 0);
TimeSpan workEnd2 = new TimeSpan(8, 0, 0);
// Assign task based on current time
if (current >= sleepStart && current < sleepEnd)
{
currentTask = NPCTask.Sleeping;
}
else if (current >= errandStart && current < errandEnd)
{
currentTask = NPCTask.RunningErrands;
}
else if (current >= freeTimeStart && current < dinnerStart)
{
currentTask = NPCTask.FreeTime;
}
else if (current >= dinnerStart && current < dinnerEnd)
{
currentTask = NPCTask.Eating;
}
else if (current >= workStart1 || current < workEnd1)
{
currentTask = NPCTask.Working;
}
else if (current >= lunchStart && current < lunchEnd)
{
currentTask = NPCTask.Eating;
}
else if (current >= workStart2 && current < workEnd2)
{
currentTask = NPCTask.Working;
}
else
{
currentTask = NPCTask.FreeTime; // fallback
}
}
else
{
// === Day Shift ===
int sleepHours = Mathf.Clamp(10 - stamina, 5, 9);
int sleepStartHour = 22;
int sleepEndHour = (sleepStartHour + sleepHours) % 24;
TimeSpan lunchStart = new TimeSpan(12, 0, 0);
TimeSpan lunchEnd = new TimeSpan(13, 0, 0);
TimeSpan errandStart = new TimeSpan(13, 0, 0);
TimeSpan errandEnd = new TimeSpan(13, 30, 0);
TimeSpan dinnerStart = new TimeSpan(18, 0, 0);
TimeSpan dinnerEnd = new TimeSpan(19, 0, 0);
bool isSleeping = sleepStartHour > sleepEndHour
? (now.Hour >= sleepStartHour || now.Hour < sleepEndHour)
: (now.Hour >= sleepStartHour && now.Hour < sleepEndHour);
if (isSleeping)
{
currentTask = NPCTask.Sleeping;
}
else if (current >= lunchStart && current < lunchEnd)
{
currentTask = NPCTask.Eating;
}
else if (current >= errandStart && current < errandEnd)
{
currentTask = NPCTask.RunningErrands;
}
else if (current >= dinnerStart && current < dinnerEnd)
{
currentTask = NPCTask.Eating;
}
else
{
if (Occupation == Occupation.Unemployed)
{
if (now.Hour < 12 || now.Hour >= 19)
currentTask = NPCTask.FreeTime;
else
currentTask = NPCTask.FreeTime;
}
else
{
if (now.Hour < 12 || (now.Hour >= 13 && now.Hour < 18))
currentTask = NPCTask.Working;
else if (now.Hour >= 19)
currentTask = NPCTask.FreeTime;
}
}
}
}
public void AssignJobOutfit()
{
// === POLICE ===
if (Occupation == Occupation.PoliceMan)
{
if (gender == Gender.Female)
animator.runtimeAnimatorController = npcSpawner.policeFemaleWhite;
else if (skinColor == SkinColor.White)
animator.runtimeAnimatorController = npcSpawner.policeMaleWhite;
else
animator.runtimeAnimatorController = npcSpawner.policeMaleBlack;
return;
}
// === DOCTOR ===
if (Occupation == Occupation.Doctor)
{
if (gender == Gender.Female)
{
animator.runtimeAnimatorController = npcSpawner.doctorFemaleWhite;
}
else if (skinColor == SkinColor.White)
{
if(hairColor == HairColor.Black)
animator.runtimeAnimatorController = npcSpawner.doctorMaleWhiteBlackHair;
else if(hairColor == HairColor.Blond)
animator.runtimeAnimatorController = npcSpawner.doctorMaleWhiteBlondHair;
else if(hairColor == HairColor.Brun)
animator.runtimeAnimatorController = npcSpawner.doctorMaleWhiteBrunHair;
else if(hairColor == HairColor.Ginger)
animator.runtimeAnimatorController = npcSpawner.doctorMaleWhiteGingerHair;
}
else
{
animator.runtimeAnimatorController = npcSpawner.doctorMaleBlack;
}
return;
}
// === FIREFIGHTER ===
if (Occupation == Occupation.FireFighter)
{
if (gender == Gender.Female)
animator.runtimeAnimatorController = npcSpawner.fireFighterFemaleWhite;
else if (skinColor == SkinColor.White)
animator.runtimeAnimatorController = npcSpawner.fireFighterMaleWhite;
else
animator.runtimeAnimatorController = npcSpawner.fireFighterMaleBlack;
return;
}
// === MECHANIC ===
if (Occupation == Occupation.Mechanic)
{
if (gender == Gender.Female)
animator.runtimeAnimatorController = npcSpawner.mechanicFemaleWhite;
else if (skinColor == SkinColor.White)
animator.runtimeAnimatorController = npcSpawner.mechanicMaleWhite;
else
animator.runtimeAnimatorController = npcSpawner.mechanicMaleBlack;
return;
}
// === GANG MEMBERS ===
if (faction == Faction.Gang)
{
RuntimeAnimatorController controller = null;
switch (subFaction)
{
case SubFaction.GangBlack: controller = GetThugController(skinColor, npcSpawner.afroStreetThugBlack, npcSpawner.whiteStreetThugBlack); break;
case SubFaction.GangBlue: controller = GetThugController(skinColor, npcSpawner.afroStreetThugBlue, npcSpawner.whiteStreetThugBlue); break;
case SubFaction.GangGreen: controller = GetThugController(skinColor, npcSpawner.afroStreetThugGreen, npcSpawner.whiteStreetThugGreen); break;
case SubFaction.GangOrange: controller = GetThugController(skinColor, npcSpawner.afroStreetThugOrange, npcSpawner.whiteStreetThugOrange); break;
case SubFaction.GangPurple: controller = GetThugController(skinColor, npcSpawner.afroStreetThugPurple, npcSpawner.whiteStreetThugPurple); break;
case SubFaction.GangRed: controller = GetThugController(skinColor, npcSpawner.afroStreetThugRed, npcSpawner.whiteStreetThugRed); break;
case SubFaction.GangYelow: controller = GetThugController(skinColor, npcSpawner.afroStreetThugYellow, npcSpawner.whiteStreetThugYellow); break;
}
animator.runtimeAnimatorController = controller;
return;
}
// === MAFIA MEMBERS ===
if (faction == Faction.Mafia)
{
switch (subFaction)
{
case SubFaction.MafiaBlack: animator.runtimeAnimatorController = npcSpawner.mafiaBlack; break;
case SubFaction.MafiaBlue: animator.runtimeAnimatorController = npcSpawner.mafiaBlue; break;
case SubFaction.MafiaBrown: animator.runtimeAnimatorController = npcSpawner.mafiaBrown; break;
case SubFaction.MafiaGreen: animator.runtimeAnimatorController = npcSpawner.mafiaGreen; break;
case SubFaction.MafiaOrange: animator.runtimeAnimatorController = npcSpawner.mafiaOrange; break;
case SubFaction.MafiaPurple: animator.runtimeAnimatorController = npcSpawner.mafiaPurple; break;
case SubFaction.MafiaRed: animator.runtimeAnimatorController = npcSpawner.mafiaRed; break;
case SubFaction.MafiaWhite: animator.runtimeAnimatorController = npcSpawner.mafiaWhite; break;
}
return;
}
// === CIVILIAN DEFAULT ===
WearCivilianOutfit(); // <- This ensures non-uniform roles get default appearance
RuntimeAnimatorController GetThugController(SkinColor skin, RuntimeAnimatorController blackController, RuntimeAnimatorController whiteController)
{
return skin == SkinColor.Black ? blackController : whiteController;
}
}
private void AssignName()
{
System.Random rng = new System.Random();
if (gender == Gender.Male)
{
npcName = maleNames[rng.Next(maleNames.Count)];
}
else
{
npcName = femaleNames[rng.Next(femaleNames.Count)];
}
}
private void AssignInterest()
{
Array values = Enum.GetValues(typeof(Interests));
interests = (Interests)values.GetValue(UnityEngine.Random.Range(0, values.Length));
}
private void AssignJob()
{
salary = 0;
Occupation = Occupation.Unemployed;
faction = Faction.Civilian;
subFaction = SubFaction.None;
// Hardest jobs first
if (medicine >= 3 && intelligence >= 3)
{
Occupation = Occupation.Doctor;
salary = 3;
nightShift = UnityEngine.Random.value < 0.5f;
Debug.Log($"{npcName} assigned job: Doctor");
return;
}
else if (firearms >= 2 && athletics >= 2)
{
faction = Faction.Police;
Occupation = Occupation.PoliceMan;
salary = 2;
nightShift = UnityEngine.Random.value < 0.5f;
Debug.Log($"{npcName} assigned job: PoliceMan");
return;
}
else if (strength >= 3 && athletics >= 3)
{
Occupation = Occupation.FireFighter;
salary = 3;
nightShift = UnityEngine.Random.value < 0.5f;
Debug.Log($"{npcName} assigned job: FireFighter");
return;
}
else if (technology >= 3 && NPCSpawner.numberOfProgrammers < 10)
{
Occupation = Occupation.Programmer;
salary = 2;
NPCSpawner.numberOfProgrammers++;
Debug.Log($"{npcName} assigned job: Programmer");
return;
}
else if (drive >= 1 && technology >= 2 && NPCSpawner.numberOfMechanics < 10)
{
Occupation = Occupation.Mechanic;
salary = 2;
NPCSpawner.numberOfMechanics++;
Debug.Log($"{npcName} assigned job: Mechanic");
return;
}
else if (drive >= 3)
{
Occupation = Occupation.Taxi;
salary = 1 + drive;
Debug.Log($"{npcName} assigned job: Taxi Driver");
return;
}
else if (dexterity >= 3 && NPCSpawner.numberOfMusicians < 10)
{
Occupation = Occupation.Musician;
salary = 1 + dexterity;
NPCSpawner.numberOfMusicians++;
Debug.Log($"{npcName} assigned job: Musician");
return;
}
else if (appearance >= 5 && NPCSpawner.numberOfSuperModels < 5)
{
Occupation = Occupation.TopModel;
salary = 4;
NPCSpawner.numberOfSuperModels++;
Debug.Log($"{npcName} assigned job: Top Model");
return;
}
else if (appearance >= 3 && NPCSpawner.numberOfModels < 5)
{
Occupation = Occupation.Model;
salary = 3;
NPCSpawner.numberOfModels++;
Debug.Log($"{npcName} assigned job: Model");
return;
}
else if (charisma >= 2 && NPCSpawner.numberOfCashiers < 10)
{
Occupation = Occupation.Cashier;
NPCSpawner.numberOfCashiers++;
salary = 1;
Debug.Log($"{npcName} assigned job: Cashier");
return;
}
if (firearms >= 1 || melee >= 1)
{
// 50% chance to become a criminal
if (UnityEngine.Random.value < 0.5f)
{
// 50% chance between Gang and Mafia
bool isMafia = UnityEngine.Random.value < 0.5f;
nightShift = UnityEngine.Random.value < 0.5f;
faction = isMafia ? Faction.Mafia : Faction.Gang;
subFaction = isMafia ? SubFaction.MafiaBlack : SubFaction.GangBlack;
Debug.Log($"{npcName} had combat skills and became a {faction} member.");
return;
}
}
Debug.Log($"{npcName} is Unemployed.");
}
private void DistributeXP(int xpToDistribute)
{
System.Random rng = new System.Random();
List<(string name, Func<int> get, Action<int> set, bool isStat)> allAttributes = new()
{
// Stats (true)
("Strength", () => strength, v => strength = v, true),
("Charisma", () => charisma, v => charisma = v, true),
("Perception", () => perception, v => perception = v, true),
("Dexterity", () => dexterity, v => dexterity = v, true),
("Manipulation", () => manipulation, v => manipulation = v, true),
("Intelligence", () => intelligence, v => intelligence = v, true),
("Stamina", () => stamina, v => stamina = v, true),
("Appearance", () => appearance, v => appearance = v, true),
("Wits", () => wits, v => wits = v, true),
// Skills (false)
("Brawl", () => brawl, v => brawl = v, false),
("Awareness", () => awareness, v => awareness = v, false),
("Etiquette", () => etiquette, v => etiquette = v, false),
("Melee", () => melee, v => melee = v, false),
("Investigation", () => investigation, v => investigation = v, false),
("Stealth", () => stealth, v => stealth = v, false),
("Firearms", () => firearms, v => firearms = v, false),
("Medicine", () => medicine, v => medicine = v, false),
("Larceny", () => larceny, v => larceny = v, false),
("Athletics", () => athletics, v => athletics = v, false),
("Technology", () => technology, v => technology = v, false),
("Presence", () => presence, v => presence = v, false),
("Drive", () => drive, v => drive = v, false),
("Occult", () => occult, v => occult = v, false),
("Survival", () => survival, v => survival = v, false),
};
int safety = 50;
while (xpToDistribute > 0 && safety-- > 0)
{
var entry = allAttributes[rng.Next(allAttributes.Count)];
int currentLevel = entry.Item2();
//Skip if already maxed
if (currentLevel >= 5)
continue;
int xpRequired = entry.isStat
? currentLevel * 4
: (currentLevel + 1) * 2;
if (xpToDistribute >= xpRequired)
{
xpToDistribute -= xpRequired;
entry.Item3(currentLevel + 1);
//Debug.Log($"{npcName} leveled up {entry.name} to {currentLevel + 1} (cost {xpRequired})");
}
}
if (safety <= 0 && xpToDistribute > 0)
{
//Debug.LogWarning("Safety cap reached. Using fallback deterministic leveling.");
foreach (var entry in allAttributes)
{
while (true)
{
int currentLevel = entry.Item2();
if (currentLevel >= 5)
break;
int xpRequired = entry.isStat
? currentLevel * 4
: (currentLevel + 1) * 2;
if (xpToDistribute >= xpRequired)
{
xpToDistribute -= xpRequired;
entry.Item3(currentLevel + 1);
//Debug.Log($"{npcName} leveled up {entry.name} to {currentLevel + 1} (cost {xpRequired})");
}
else break;
}
if (xpToDistribute <= 0)
break;
}
//Debug.Log($"XP leftover after fallback leveling: {xpToDistribute}");
}
}
private static readonly List<string> maleNames = new()
{
"Liam",
"Noah",
"James",
"Oliver",
"Benjamin",
"Lucas",
"Henry",
"Alexander",
"Mason",
"Ethan",
"Logan",
"Jackson",
"Leo",
"Sebastian",
"Jack",
"Owen",
"Caleb",
"Nathan",
"Aiden",
"Daniel"
};
private static readonly List<string> femaleNames = new()
{
"Emma",
"Olivia",
"Ava",
"Isabella",
"Sophia",
"Charlotte",
"Amelia",
"Mia",
"Harper",
"Evelyn",
"Ella",
"Scarlett",
"Lily",
"Grace",
"Chloe",
"Zoe",
"Nora",
"Aria",
"Abigail",
"Emily"
};
}
Editor is loading...
Leave a Comment