diff --git a/S1API/Console/ConsoleHelper.cs b/S1API/Console/ConsoleHelper.cs
index ef60823b..a7bded34 100644
--- a/S1API/Console/ConsoleHelper.cs
+++ b/S1API/Console/ConsoleHelper.cs
@@ -1,15 +1,16 @@
-using System.Collections.Generic;
-
-#if IL2CPPMELON || IL2CPPBEPINEX
+#if IL2CPP
using Il2CppSystem.Collections.Generic;
using static Il2CppScheduleOne.Console;
-
#else
using static ScheduleOne.Console;
+using System.Collections.Generic;
#endif
-namespace S1API.Utils
+namespace S1API.Console
{
+ ///
+ /// This class provides easy access to the in-game console system.
+ ///
public static class ConsoleHelper
{
///
@@ -19,7 +20,7 @@ public static class ConsoleHelper
/// The cash amount to add or remove.
public static void RunCashCommand(int amount)
{
-#if IL2CPPMELON || IL2CPPBEPINEX
+#if IL2CPP
var command = new ChangeCashCommand();
var args = new Il2CppSystem.Collections.Generic.List();
#else
@@ -30,4 +31,4 @@ public static void RunCashCommand(int amount)
command.Execute(args);
}
}
-}
+}
\ No newline at end of file
diff --git a/S1API/Entities/Interfaces/IEntity.cs b/S1API/Entities/Interfaces/IEntity.cs
new file mode 100644
index 00000000..cc551b10
--- /dev/null
+++ b/S1API/Entities/Interfaces/IEntity.cs
@@ -0,0 +1,25 @@
+using UnityEngine;
+
+namespace S1API.Entities.Interfaces
+{
+ ///
+ /// Represents an entity within the game world.
+ ///
+ public interface IEntity
+ {
+ ///
+ /// INTERNAL: Tracking of the GameObject associated with this entity.
+ ///
+ GameObject gameObject { get; }
+
+ ///
+ /// The world position of the entity.
+ ///
+ public Vector3 Position { get; set; }
+
+ ///
+ /// The scale of the entity.
+ ///
+ public float Scale { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/Interfaces/IHealth.cs b/S1API/Entities/Interfaces/IHealth.cs
new file mode 100644
index 00000000..2be07bf1
--- /dev/null
+++ b/S1API/Entities/Interfaces/IHealth.cs
@@ -0,0 +1,57 @@
+using System;
+
+namespace S1API.Entities.Interfaces
+{
+ ///
+ /// Represents an entity that has health associated.
+ ///
+ public interface IHealth
+ {
+ ///
+ /// The current health of the entity.
+ ///
+ public float CurrentHealth { get; }
+
+ ///
+ /// The max health of the entity.
+ ///
+ public float MaxHealth { get; set; }
+
+ ///
+ /// Whether the entity is dead or not.
+ ///
+ public bool IsDead { get; }
+
+ ///
+ /// Whether the entity is invincible.
+ ///
+ public bool IsInvincible { get; set; }
+
+ ///
+ /// Revives the entity.
+ ///
+ public void Revive();
+
+ ///
+ /// Deals damage to the entity.
+ ///
+ /// Amount of damage to deal.
+ public void Damage(int amount);
+
+ ///
+ /// Heals the entity.
+ ///
+ /// Amount of health to heal.
+ public void Heal(int amount);
+
+ ///
+ /// Kills the entity.
+ ///
+ public void Kill();
+
+ ///
+ /// Called when entity's health is less than or equal to 0.
+ ///
+ public event Action OnDeath;
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPC.cs b/S1API/Entities/NPC.cs
new file mode 100644
index 00000000..9d5b92e2
--- /dev/null
+++ b/S1API/Entities/NPC.cs
@@ -0,0 +1,632 @@
+#if (IL2CPP)
+using S1DevUtilities = Il2CppScheduleOne.DevUtilities;
+using S1Interaction = Il2CppScheduleOne.Interaction;
+using S1Messaging = Il2CppScheduleOne.Messaging;
+using S1Noise = Il2CppScheduleOne.Noise;
+using S1Relation = Il2CppScheduleOne.NPCs.Relation;
+using S1Responses = Il2CppScheduleOne.NPCs.Responses;
+using S1PlayerScripts = Il2CppScheduleOne.PlayerScripts;
+using S1ContactApps = Il2CppScheduleOne.UI.Phone.ContactsApp;
+using S1WorkspacePopup = Il2CppScheduleOne.UI.WorldspacePopup;
+using S1AvatarFramework = Il2CppScheduleOne.AvatarFramework;
+using S1Behaviour = Il2CppScheduleOne.NPCs.Behaviour;
+using S1Vehicles = Il2CppScheduleOne.Vehicles;
+using S1Vision = Il2CppScheduleOne.Vision;
+using S1NPCs = Il2CppScheduleOne.NPCs;
+using Il2CppSystem.Collections.Generic;
+#elif (MONO)
+using S1DevUtilities = ScheduleOne.DevUtilities;
+using S1Interaction = ScheduleOne.Interaction;
+using S1Messaging = ScheduleOne.Messaging;
+using S1Noise = ScheduleOne.Noise;
+using S1Relation = ScheduleOne.NPCs.Relation;
+using S1Responses = ScheduleOne.NPCs.Responses;
+using S1PlayerScripts = ScheduleOne.PlayerScripts;
+using S1ContactApps = ScheduleOne.UI.Phone.ContactsApp;
+using S1WorkspacePopup = ScheduleOne.UI.WorldspacePopup;
+using S1AvatarFramework = ScheduleOne.AvatarFramework;
+using S1Behaviour = ScheduleOne.NPCs.Behaviour;
+using S1Vehicles = ScheduleOne.Vehicles;
+using S1Vision = ScheduleOne.Vision;
+using S1NPCs = ScheduleOne.NPCs;
+using System.Collections.Generic;
+#endif
+
+using System;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using HarmonyLib;
+using S1API.Entities.Interfaces;
+using S1API.Internal.Abstraction;
+using S1API.Map;
+using S1API.Messaging;
+using UnityEngine;
+using UnityEngine.Events;
+
+namespace S1API.Entities
+{
+ ///
+ /// An abstract class intended to be derived from for creating custom NPCs in the game.
+ ///
+ public abstract class NPC : Saveable, IEntity, IHealth
+ {
+ // Protected members intended to be used by modders.
+ // Intended to be used from within the class / derived classes ONLY.
+ #region Protected Members
+
+ ///
+ /// A list of text responses you've added to your NPC.
+ ///
+ protected readonly System.Collections.Generic.List Responses = new System.Collections.Generic.List();
+
+ ///
+ /// Base constructor for a new NPC.
+ /// Not intended for instancing your NPC!
+ /// Instead, create your derived class and let S1API handle instancing.
+ ///
+ /// Unique identifier for your NPC.
+ /// The first name for your NPC.
+ /// The last name for your NPC.
+ /// The icon for your NPC for messages, realationships, etc.
+ protected NPC(
+ string id,
+ string? firstName,
+ string? lastName,
+ Sprite? icon = null
+ )
+ {
+ IsCustomNPC = true;
+ gameObject = new GameObject();
+
+ // Deactivate game object til we're done
+ gameObject.SetActive(false);
+
+ // Setup the base NPC class
+ S1NPC = gameObject.AddComponent();
+ S1NPC.FirstName = firstName;
+ S1NPC.LastName = lastName;
+ S1NPC.ID = id;
+ S1NPC.MugshotSprite = icon ?? S1DevUtilities.PlayerSingleton.Instance.AppIcon;
+ S1NPC.BakedGUID = Guid.NewGuid().ToString();
+
+ // ReSharper disable once UseObjectOrCollectionInitializer (IL2CPP COMPAT)
+ S1NPC.ConversationCategories = new List();
+ S1NPC.ConversationCategories.Add(S1Messaging.EConversationCategory.Customer);
+
+ // Create our MessageConversation
+#if (IL2CPP)
+ S1NPC.CreateMessageConversation();
+#elif (MONO)
+ MethodInfo createConvoMethod = AccessTools.Method(typeof(S1NPCs.NPC), "CreateMessageConversation");
+ createConvoMethod.Invoke(S1NPC, null);
+#endif
+
+ // Add UnityEvents for NPCHealth
+ S1NPC.Health = gameObject.GetComponent();
+ S1NPC.Health.onDie = new UnityEvent();
+ S1NPC.Health.onKnockedOut = new UnityEvent();
+ S1NPC.Health.Invincible = true;
+ S1NPC.Health.MaxHealth = 100f;
+
+ // Awareness behaviour
+ GameObject awarenessObject = new GameObject("NPCAwareness");
+ awarenessObject.transform.SetParent(gameObject.transform);
+ S1NPC.awareness = awarenessObject.AddComponent();
+ S1NPC.awareness.onExplosionHeard = new UnityEvent();
+ S1NPC.awareness.onGunshotHeard = new UnityEvent();
+ S1NPC.awareness.onHitByCar = new UnityEvent();
+ S1NPC.awareness.onNoticedDrugDealing = new UnityEvent();
+ S1NPC.awareness.onNoticedGeneralCrime = new UnityEvent();
+ S1NPC.awareness.onNoticedPettyCrime = new UnityEvent();
+ S1NPC.awareness.onNoticedPlayerViolatingCurfew = new UnityEvent();
+ S1NPC.awareness.onNoticedSuspiciousPlayer = new UnityEvent();
+ S1NPC.awareness.Listener = gameObject.AddComponent();
+
+ /////// START BEHAVIOUR CODE ////////
+ // NPCBehaviours behaviour
+ GameObject behaviourObject = new GameObject("NPCBehaviour");
+ behaviourObject.transform.SetParent(gameObject.transform);
+ S1Behaviour.NPCBehaviour behaviour = behaviourObject.AddComponent();
+
+ GameObject cowingBehaviourObject = new GameObject("CowingBehaviour");
+ cowingBehaviourObject.transform.SetParent(behaviourObject.transform);
+ S1Behaviour.CoweringBehaviour coweringBehaviour = cowingBehaviourObject.AddComponent();
+
+ GameObject fleeBehaviourObject = new GameObject("FleeBehaviour");
+ fleeBehaviourObject.transform.SetParent(behaviourObject.transform);
+ S1Behaviour.FleeBehaviour fleeBehaviour = fleeBehaviourObject.AddComponent();
+
+ behaviour.CoweringBehaviour = coweringBehaviour;
+ behaviour.FleeBehaviour = fleeBehaviour;
+ S1NPC.behaviour = behaviour;
+ /////// END BEHAVIOUR CODE ////////
+
+ // Response to actions like gunshots, drug deals, etc.
+ GameObject responsesObject = new GameObject("NPCResponses");
+ responsesObject.transform.SetParent(gameObject.transform);
+ S1NPC.awareness.Responses = responsesObject.AddComponent();
+
+ // Vision cone object and behaviour
+ GameObject visionObject = new GameObject("VisionCone");
+ visionObject.transform.SetParent(gameObject.transform);
+ S1Vision.VisionCone visionCone = visionObject.AddComponent();
+ visionCone.StatesOfInterest.Add(new S1Vision.VisionCone.StateContainer
+ {
+ state = S1PlayerScripts.PlayerVisualState.EVisualState.PettyCrime, RequiredNoticeTime = 0.1f
+ });
+ S1NPC.awareness.VisionCone = visionCone;
+
+
+ // Suspicious ? icon in world space
+ S1NPC.awareness.VisionCone.QuestionMarkPopup = gameObject.AddComponent();
+
+ // Interaction behaviour
+#if (IL2CPP)
+ S1NPC.intObj = gameObject.AddComponent();
+#elif (MONO)
+ FieldInfo intObjField = AccessTools.Field(typeof(S1NPCs.NPC), "intObj");
+ intObjField.SetValue(S1NPC, gameObject.AddComponent());
+#endif
+
+ // Relationship data
+ S1NPC.RelationData = new S1Relation.NPCRelationData();
+
+ // Inventory behaviour
+ S1NPCs.NPCInventory inventory = gameObject.AddComponent();
+
+ // Pickpocket behaviour
+ inventory.PickpocketIntObj = gameObject.AddComponent();
+
+ // Defaulting to the local player for Avatar TODO: Change
+ S1NPC.Avatar = S1AvatarFramework.MugshotGenerator.Instance.MugshotRig;
+
+ // Enable our custom gameObjects so they can initialize
+ gameObject.SetActive(true);
+
+ All.Add(this);
+ }
+
+ ///
+ /// Called when a response is loaded from the save file.
+ /// Override this method for attaching your callbacks to your methods.
+ ///
+ /// The response that was loaded.
+ protected virtual void OnResponseLoaded(Response response) { }
+
+ #endregion
+
+ // Public members intended to be used by modders.
+ // Can be used inside your derived class, or outside via instance reference.
+ #region Public Members
+
+ ///
+ /// INTERNAL: Tracking for the GameObject associated with this NPC.
+ /// Not intended for use by modders!
+ ///
+ public GameObject gameObject { get; }
+
+ ///
+ /// The world position of the NPC.
+ ///
+ public Vector3 Position
+ {
+ get => gameObject.transform.position;
+ set => S1NPC.Movement.Warp(value);
+ }
+
+ ///
+ /// The transform of the NPC.
+ /// Please do not set the properties of this transform.
+ ///
+ public Transform Transform =>
+ gameObject.transform;
+
+ ///
+ /// List of all NPCs within the base game and modded.
+ ///
+ public static readonly System.Collections.Generic.List All = new System.Collections.Generic.List();
+
+ ///
+ /// The first name of this NPC.
+ ///
+ public string FirstName
+ {
+ get => S1NPC.FirstName;
+ set => S1NPC.FirstName = value;
+ }
+
+ ///
+ /// The last name of this NPC.
+ ///
+ public string LastName
+ {
+ get => S1NPC.LastName;
+ set => S1NPC.LastName = value;
+ }
+
+ ///
+ /// The full name of this NPC.
+ /// If there is no last name, it will just return the first name.
+ ///
+ public string FullName =>
+ S1NPC.fullName;
+
+ ///
+ /// The unique identifier to assign to this NPC.
+ /// Used when saving and loading. Probably other things within the base game code.
+ ///
+ public string ID
+ {
+ get => S1NPC.ID;
+ protected set => S1NPC.ID = value;
+ }
+
+ ///
+ /// The icon assigned to this NPC.
+ ///
+ public Sprite Icon
+ {
+ get => S1NPC.MugshotSprite;
+ set => S1NPC.MugshotSprite = value;
+ }
+
+ ///
+ /// Whether the NPC is currently conscious or not.
+ ///
+ public bool IsConscious =>
+ S1NPC.IsConscious;
+
+ ///
+ /// Whether the NPC is currently inside a building or not.
+ ///
+ public bool IsInBuilding =>
+ S1NPC.isInBuilding;
+
+ ///
+ /// Whether the NPC is currently inside a vehicle or not.
+ ///
+ public bool IsInVehicle =>
+ S1NPC.IsInVehicle;
+
+ ///
+ /// Whether the NPC is currently panicking or not.
+ ///
+ public bool IsPanicking =>
+ S1NPC.IsPanicked;
+
+ ///
+ /// Whether the NPC is currently unsettled or not.
+ ///
+ public bool IsUnsettled =>
+ S1NPC.isUnsettled;
+
+ ///
+ /// UNCONFIRMED: Whether the NPC is currently visible to the player or not.
+ /// If you confirm this, please let us know so we can update the documentation!
+ ///
+ public bool IsVisible =>
+ S1NPC.isVisible;
+
+ ///
+ /// How aggressive this NPC is towards others.
+ ///
+ public float Aggressiveness
+ {
+ get => S1NPC.Aggression;
+ set => S1NPC.Aggression = value;
+ }
+
+ ///
+ /// The region the NPC is associated with.
+ /// Note: Not the region they're in currently. Just the region they're designated to.
+ ///
+ public Region Region =>
+ (Region)S1NPC.Region;
+
+ ///
+ /// UNCONFIRMED: How long the NPC will panic for.
+ /// If you confirm this, please let us know so we can update the documentation!
+ ///
+ public float PanicDuration
+ {
+ get => (float)_panicField.GetValue(S1NPC)!;
+ set => _panicField.SetValue(S1NPC, value);
+ }
+
+ ///
+ /// Sets the scale of the NPC.
+ ///
+ public float Scale
+ {
+ get => S1NPC.Scale;
+ set => S1NPC.SetScale(value);
+ }
+
+ ///
+ /// Whether the NPC is knocked out or not.
+ ///
+ public bool IsKnockedOut =>
+ S1NPC.Health.IsKnockedOut;
+
+ ///
+ /// UNCONFIRMED: Whether the NPC requires the region unlocked in order to deal to.
+ /// If you confirm this, please let us know so we can update the documentation!
+ ///
+ public bool RequiresRegionUnlocked
+ {
+ get => (bool)_requiresRegionUnlockedField.GetValue(S1NPC)!;
+ set => _panicField.SetValue(S1NPC, value);
+ }
+
+ // TODO: Add CurrentBuilding (currently missing NPCEnterableBuilding abstraction)
+ // public ??? CurrentBuilding { get; set; }
+
+ // TODO: Add CurrentVehicle (currently missing LandVehicle abstraction)
+ // public ??? CurrentVehicle { get; set; }
+
+ // TODO: Add Inventory (currently missing NPCInventory abstraction)
+ // public ??? Inventory { get; set; }
+
+ ///
+ /// The current health the NPC has.
+ ///
+ public float CurrentHealth =>
+ S1NPC.Health.Health;
+
+ ///
+ /// The maximum health the NPC has.
+ ///
+ public float MaxHealth
+ {
+ get => S1NPC.Health.MaxHealth;
+ set => S1NPC.Health.MaxHealth = value;
+ }
+
+ ///
+ /// Whether the NPC is dead or not.
+ ///
+ public bool IsDead =>
+ S1NPC.Health.IsDead;
+
+ ///
+ /// Whether the NPC is invincible or not.
+ ///
+ public bool IsInvincible
+ {
+ get => S1NPC.Health.Invincible;
+ set => S1NPC.Health.Invincible = value;
+ }
+
+ ///
+ /// Revives the NPC.
+ ///
+ public void Revive() =>
+ S1NPC.Health.Revive();
+
+ ///
+ /// Deals damage to the NPC.
+ ///
+ /// The amount of damage to deal.
+ public void Damage(int amount)
+ {
+ if (amount <= 0)
+ return;
+
+ S1NPC.Health.TakeDamage(amount, true);
+ }
+
+ ///
+ /// Heals the NPC.
+ ///
+ /// The amount of health to heal.
+ public void Heal(int amount)
+ {
+ if (amount <= 0)
+ return;
+
+ float actualHealAmount = Mathf.Min(amount, S1NPC.Health.MaxHealth - S1NPC.Health.Health);
+ S1NPC.Health.TakeDamage(-actualHealAmount, false);
+ }
+
+ ///
+ /// Kills the NPC.
+ ///
+ public void Kill() =>
+ S1NPC.Health.TakeDamage(S1NPC.Health.MaxHealth);
+
+ ///
+ /// Causes the NPC to become unsettled.
+ /// UNCONFIRMED: Will panic them for PanicDuration amount of time.
+ /// If you confirm this, please let us know so we can update the documentation!
+ ///
+ /// Length of time they should stay unsettled.
+ public void Unsettle(float duration) =>
+ _unsettleMethod.Invoke(S1NPC, new object[] { duration });
+
+ ///
+ /// Smoothly scales the NPC over lerpTime.
+ ///
+ /// The scale you want set.
+ /// The time to scale over.
+ public void LerpScale(float scale, float lerpTime) =>
+ S1NPC.SetScale(scale, lerpTime);
+
+ ///
+ /// Causes the NPC to become panicked.
+ ///
+ public void Panic() =>
+ S1NPC.SetPanicked();
+
+ ///
+ /// Causes the NPC to stop panicking, if they are currently.
+ ///
+ public void StopPanicking() =>
+ _removePanicMethod.Invoke(S1NPC, new object[] { });
+
+ ///
+ /// Knocks the NPC out.
+ /// NOTE: Does not work for invincible NPCs.
+ ///
+ public void KnockOut() =>
+ S1NPC.Health.KnockOut();
+
+ ///
+ /// Tells the NPC to travel to a specific position in world space.
+ ///
+ /// The position to travel to.
+ public void Goto(Vector3 position) =>
+ S1NPC.Movement.SetDestination(position);
+
+ // TODO: Add OnEnterVehicle listener (currently missing LandVehicle abstraction)
+ // public event Action OnEnterVehicle { }
+
+ // TODO: Add OnExitVehicle listener (currently missing LandVehicle abstraction)
+ // public event Action OnExitVehicle { }
+
+ // TODO: Add OnExplosionHeard listener (currently missing NoiseEvent abstraction)
+ // public event Action OnExplosionHeard { }
+
+ // TODO: Add OnGunshotHeard listener (currently missing NoiseEvent abstraction)
+ // public event Action OnGunshotHeard { }
+
+ // TODO: Add OnHitByCar listener (currently missing LandVehicle abstraction)
+ // public event Action OnHitByCar { }
+
+ // TODO: Add OnNoticedDrugDealing listener (currently missing Player abstraction)
+ // public event Action OnNoticedDrugDealing { }
+
+ // TODO: Add OnNoticedGeneralCrime listener (currently missing Player abstraction)
+ // public event Action OnNoticedGeneralCrime { }
+
+ // TODO: Add OnNoticedPettyCrime listener (currently missing Player abstraction)
+ // public event Action OnNoticedPettyCrime { }
+
+ // TODO: Add OnPlayerViolatingCurfew listener (currently missing Player abstraction)
+ // public event Action OnPlayerViolatingCurfew { }
+
+ // TODO: Add OnNoticedSuspiciousPlayer listener (currently missing Player abstraction)
+ // public event Action OnNoticedSuspiciousPlayer { }
+
+ ///
+ /// Called when the NPC died.
+ ///
+ public event Action OnDeath
+ {
+ add => EventHelper.AddListener(value, S1NPC.Health.onDie);
+ remove => EventHelper.RemoveListener(value, S1NPC.Health.onDie);
+ }
+
+ ///
+ /// Called when the NPC's inventory contents change.
+ ///
+ public event Action OnInventoryChanged
+ {
+ add => EventHelper.AddListener(value, S1NPC.Inventory.onContentsChanged);
+ remove => EventHelper.RemoveListener(value, S1NPC.Inventory.onContentsChanged);
+ }
+
+ ///
+ /// Sends a text message from this NPC to the players.
+ /// Supports responses with callbacks for additional logic.
+ ///
+ /// The message you want the player to see. Unity rich text is allowed.
+ /// Instances of to display.
+ /// The delay between when the message is sent and when the player can reply.
+ /// Whether this should propagate to all players or not.
+ public void SendTextMessage(string message, Response[]? responses = null, float responseDelay = 1f, bool network = true)
+ {
+ S1NPC.SendTextMessage(message);
+ S1NPC.MSGConversation.ClearResponses();
+
+ if (responses == null || responses.Length == 0)
+ return;
+
+ Responses.Clear();
+
+ List responsesList = new List();
+
+ foreach (Response response in responses)
+ {
+ Responses.Add(response);
+ responsesList.Add(response.S1Response);
+ }
+
+ S1NPC.MSGConversation.ShowResponses(
+ responsesList,
+ responseDelay,
+ network
+ );
+ }
+
+ ///
+ /// Gets the instance of an NPC.
+ /// Supports base NPCs as well as other mod NPCs.
+ /// For base NPCs, .
+ ///
+ /// The NPC class to get the instance of.
+ ///
+ public static NPC? Get() =>
+ All.FirstOrDefault(npc => npc.GetType() == typeof(T));
+
+ #endregion
+
+ // Internal members used by S1API.
+ // Please do not attempt to use these members!
+ #region Internal Members
+
+ ///
+ /// INTERNAL: Reference to the NPC on the S1 side.
+ ///
+ internal readonly S1NPCs.NPC S1NPC;
+
+ ///
+ /// INTERNAL: Constructor used for base game NPCs.
+ ///
+ /// Reference to a base game NPC.
+ internal NPC(S1NPCs.NPC npc)
+ {
+ S1NPC = npc;
+ gameObject = npc.gameObject;
+ IsCustomNPC = false;
+ All.Add(this);
+ }
+
+ ///
+ /// INTERNAL: Initializes the responses that have been added / loaded
+ ///
+ internal override void CreateInternal()
+ {
+ // Assign responses to our tracked responses
+ foreach (S1Messaging.Response s1Response in S1NPC.MSGConversation.currentResponses)
+ {
+ Response response = new Response(s1Response) { Label = s1Response.label, Text = s1Response.text };
+ Responses.Add(response);
+ OnResponseLoaded(response);
+ }
+
+ base.CreateInternal();
+ }
+
+ internal override void SaveInternal(string folderPath, ref List extraSaveables)
+ {
+ string npcPath = Path.Combine(folderPath, S1NPC.SaveFolderName);
+ base.SaveInternal(npcPath, ref extraSaveables);
+ }
+ #endregion
+
+ // Private members used by the NPC class.
+ // Please do not attempt to use these members!
+ #region Private Members
+
+ internal readonly bool IsCustomNPC;
+
+ private readonly FieldInfo _panicField = AccessTools.Field(typeof(S1NPCs.NPC), "PANIC_DURATION");
+ private readonly FieldInfo _requiresRegionUnlockedField = AccessTools.Field(typeof(S1NPCs.NPC), "RequiresRegionUnlocked");
+
+ private readonly MethodInfo _unsettleMethod = AccessTools.Method(typeof(S1NPCs.NPC), "SetUnsettled");
+ private readonly MethodInfo _removePanicMethod = AccessTools.Method(typeof(S1NPCs.NPC), "RemovePanicked");
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/DanSamwell.cs b/S1API/Entities/NPCs/DanSamwell.cs
new file mode 100644
index 00000000..e3f3b0aa
--- /dev/null
+++ b/S1API/Entities/NPCs/DanSamwell.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs
+{
+ ///
+ /// UNCONFIRMED: Dan Samwell is a customer.
+ /// He is the NPC that owns Dan's Hardware!
+ /// If you confirm this, please let us know so we can update the documentation!
+ ///
+ public class DanSamwell : NPC
+ {
+ internal DanSamwell() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "dan_samwell")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Docks/AnnaChesterfield.cs b/S1API/Entities/NPCs/Docks/AnnaChesterfield.cs
new file mode 100644
index 00000000..716271c2
--- /dev/null
+++ b/S1API/Entities/NPCs/Docks/AnnaChesterfield.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Docks
+{
+ ///
+ /// Anna Chesterfield is a customer.
+ /// She lives in the Docks region.
+ /// Anna also works at the Barbershop.
+ ///
+ public class AnnaChesterfield : NPC
+ {
+ internal AnnaChesterfield() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "anna_chesterfield")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Docks/BillyKramer.cs b/S1API/Entities/NPCs/Docks/BillyKramer.cs
new file mode 100644
index 00000000..1195c3ba
--- /dev/null
+++ b/S1API/Entities/NPCs/Docks/BillyKramer.cs
@@ -0,0 +1,18 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Docks
+{
+ ///
+ /// Billy Kramer is a customer.
+ /// He lives in the Docks region.
+ ///
+ public class BillyKramer : NPC
+ {
+ internal BillyKramer() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "billy_kramer")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Docks/CrankyFrank.cs b/S1API/Entities/NPCs/Docks/CrankyFrank.cs
new file mode 100644
index 00000000..b5b91835
--- /dev/null
+++ b/S1API/Entities/NPCs/Docks/CrankyFrank.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Docks
+{
+ ///
+ /// Cranky Frank is a customer.
+ /// He lives in the Docks region.
+ /// Frank is the NPC with a pot on his head!
+ ///
+ public class CrankyFrank : NPC
+ {
+ internal CrankyFrank() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "cranky_frank")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Docks/GenghisBarn.cs b/S1API/Entities/NPCs/Docks/GenghisBarn.cs
new file mode 100644
index 00000000..3f0053a9
--- /dev/null
+++ b/S1API/Entities/NPCs/Docks/GenghisBarn.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Docks
+{
+ ///
+ /// Genghis Barn is a customer.
+ /// He lives in the Docks region.
+ /// Genghis is the NPC with a mohawk!
+ ///
+ public class GenghisBarn : NPC
+ {
+ internal GenghisBarn() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "genghis_barn")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Docks/JaneLucero.cs b/S1API/Entities/NPCs/Docks/JaneLucero.cs
new file mode 100644
index 00000000..92dfb778
--- /dev/null
+++ b/S1API/Entities/NPCs/Docks/JaneLucero.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Docks
+{
+ ///
+ /// Jane Lucero is a dealer.
+ /// She lives in the Docks region.
+ /// Jane is the dealer with a tear tattoo!
+ ///
+ public class JaneLucero : NPC
+ {
+ internal JaneLucero() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "jane_lucero")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Docks/JavierPerez.cs b/S1API/Entities/NPCs/Docks/JavierPerez.cs
new file mode 100644
index 00000000..d60491cd
--- /dev/null
+++ b/S1API/Entities/NPCs/Docks/JavierPerez.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Docks
+{
+ ///
+ /// Javier Perez is a customer.
+ /// He lives in the Docks region.
+ /// Javier works night shift at the Gas-Mart!
+ ///
+ public class JavierPerez : NPC
+ {
+ internal JavierPerez() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "javier_perez")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Docks/LisaGardener.cs b/S1API/Entities/NPCs/Docks/LisaGardener.cs
new file mode 100644
index 00000000..25ff3c57
--- /dev/null
+++ b/S1API/Entities/NPCs/Docks/LisaGardener.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Docks
+{
+ ///
+ /// Lisa Gardener is a customer.
+ /// She lives in the Docks region.
+ /// Lisa is the NPC wearing blue scrubs!
+ ///
+ public class LisaGardener : NPC
+ {
+ internal LisaGardener() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "lisa_gardener")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Docks/MacCooper.cs b/S1API/Entities/NPCs/Docks/MacCooper.cs
new file mode 100644
index 00000000..417812a9
--- /dev/null
+++ b/S1API/Entities/NPCs/Docks/MacCooper.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Docks
+{
+ ///
+ /// Mac Cooper is a customer.
+ /// He lives in the Docks region.
+ /// Mac is the NPC with a blonde mohawk and gold shades!
+ ///
+ public class MacCooper : NPC
+ {
+ internal MacCooper() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "mac_cooper")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Docks/MarcoBaron.cs b/S1API/Entities/NPCs/Docks/MarcoBaron.cs
new file mode 100644
index 00000000..acc1e4b9
--- /dev/null
+++ b/S1API/Entities/NPCs/Docks/MarcoBaron.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Docks
+{
+ ///
+ /// Marco Baron is a customer.
+ /// He lives in the Docks region.
+ /// Marco is the NPC that runs the Auto Shop!
+ ///
+ public class MarcoBaron : NPC
+ {
+ internal MarcoBaron() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "marco_baron")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Docks/MelissaWood.cs b/S1API/Entities/NPCs/Docks/MelissaWood.cs
new file mode 100644
index 00000000..f951d422
--- /dev/null
+++ b/S1API/Entities/NPCs/Docks/MelissaWood.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Docks
+{
+ ///
+ /// Melissa Wood is a customer.
+ /// She lives in the Docks region.
+ /// Melissa is the Blackjack dealer at the casino!
+ ///
+ public class MelissaWood : NPC
+ {
+ internal MelissaWood() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "melissa_wood")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Docks/SalvadorMoreno.cs b/S1API/Entities/NPCs/Docks/SalvadorMoreno.cs
new file mode 100644
index 00000000..5874de36
--- /dev/null
+++ b/S1API/Entities/NPCs/Docks/SalvadorMoreno.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Docks
+{
+ ///
+ /// Salvador Moreno is a supplier.
+ /// He lives in the Docks region.
+ /// Salvador is the NPC that supplies coca seeds to the player!
+ ///
+ public class SalvadorMoreno : NPC
+ {
+ internal SalvadorMoreno() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "salvador_moreno")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Downtown/BradCrosby.cs b/S1API/Entities/NPCs/Downtown/BradCrosby.cs
new file mode 100644
index 00000000..193ff48c
--- /dev/null
+++ b/S1API/Entities/NPCs/Downtown/BradCrosby.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Downtown
+{
+ ///
+ /// Brad Crosby is a dealer.
+ /// He lives in the Downtown region.
+ /// Brad lives in a tent at the parking garage next to the casino!
+ ///
+ public class BradCrosby : NPC
+ {
+ internal BradCrosby() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "brad_crosby")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Downtown/ElizabethHomley.cs b/S1API/Entities/NPCs/Downtown/ElizabethHomley.cs
new file mode 100644
index 00000000..cc5dc2e9
--- /dev/null
+++ b/S1API/Entities/NPCs/Downtown/ElizabethHomley.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Downtown
+{
+ ///
+ /// Elizabeth Homley is a customer.
+ /// She lives in the Downtown region.
+ /// Elizabeth is the NPC is lightning blue hair!
+ ///
+ public class ElizabethHomley : NPC
+ {
+ internal ElizabethHomley() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "elizabeth_homley")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Downtown/EugeneBuckley.cs b/S1API/Entities/NPCs/Downtown/EugeneBuckley.cs
new file mode 100644
index 00000000..e497a5cc
--- /dev/null
+++ b/S1API/Entities/NPCs/Downtown/EugeneBuckley.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Downtown
+{
+ ///
+ /// Eugene Buckley is a customer.
+ /// He lives in the Downtown region.
+ /// Eugene is the NPC with light brown hair, freckles, and black glasses!
+ ///
+ public class EugeneBuckley : NPC
+ {
+ internal EugeneBuckley() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "eugene_buckley")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Downtown/GregFliggle.cs b/S1API/Entities/NPCs/Downtown/GregFliggle.cs
new file mode 100644
index 00000000..6645f34a
--- /dev/null
+++ b/S1API/Entities/NPCs/Downtown/GregFliggle.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Downtown
+{
+ ///
+ /// Greg Fliggle is a customer.
+ /// He lives in the Downtown region.
+ /// Greg is the NPC with a teardrop tattoo and wrinkles!
+ ///
+ public class GregFliggle : NPC
+ {
+ internal GregFliggle() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "greg_fliggle")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Downtown/JeffGilmore.cs b/S1API/Entities/NPCs/Downtown/JeffGilmore.cs
new file mode 100644
index 00000000..8b7ca3d8
--- /dev/null
+++ b/S1API/Entities/NPCs/Downtown/JeffGilmore.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Downtown
+{
+ ///
+ /// Jeff Gilmore is a customer.
+ /// He lives in the Downtown region.
+ /// Jeff is the NPC that runs the skateboard shop!
+ ///
+ public class JeffGilmore : NPC
+ {
+ internal JeffGilmore() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "jeff_gilmore")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Downtown/JenniferRivera.cs b/S1API/Entities/NPCs/Downtown/JenniferRivera.cs
new file mode 100644
index 00000000..7df65be4
--- /dev/null
+++ b/S1API/Entities/NPCs/Downtown/JenniferRivera.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Downtown
+{
+ ///
+ /// Jennifer Rivera is a customer.
+ /// She lives in the Downtown region.
+ /// Jennifer is the NPC with blonde haired buns!
+ ///
+ public class JenniferRivera : NPC
+ {
+ internal JenniferRivera() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "jennifer_rivera")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Downtown/KevinOakley.cs b/S1API/Entities/NPCs/Downtown/KevinOakley.cs
new file mode 100644
index 00000000..6d841940
--- /dev/null
+++ b/S1API/Entities/NPCs/Downtown/KevinOakley.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Downtown
+{
+ ///
+ /// Kevin Oakley is a customer.
+ /// He lives in the Downtown region.
+ /// Kevin is the NPC wearing a green apron!
+ ///
+ public class KevinOakley : NPC
+ {
+ internal KevinOakley() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "kevin_oakley")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Downtown/LouisFourier.cs b/S1API/Entities/NPCs/Downtown/LouisFourier.cs
new file mode 100644
index 00000000..6012bb2c
--- /dev/null
+++ b/S1API/Entities/NPCs/Downtown/LouisFourier.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Downtown
+{
+ ///
+ /// Louis Fourier is a customer.
+ /// He lives in the Downtown region.
+ /// Louis is the NPC with a chef's hat!
+ ///
+ public class LouisFourier : NPC
+ {
+ internal LouisFourier() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "louis_fourier")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Downtown/LucyPennington.cs b/S1API/Entities/NPCs/Downtown/LucyPennington.cs
new file mode 100644
index 00000000..97fde0e6
--- /dev/null
+++ b/S1API/Entities/NPCs/Downtown/LucyPennington.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Downtown
+{
+ ///
+ /// Lucy Pennington is a customer.
+ /// She lives in the Downtown region.
+ /// Lucy is the NPC with blonde haired buns up high!
+ ///
+ public class LucyPennington : NPC
+ {
+ internal LucyPennington() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "lucy_pennington")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Downtown/PhilipWentworth.cs b/S1API/Entities/NPCs/Downtown/PhilipWentworth.cs
new file mode 100644
index 00000000..42d715a3
--- /dev/null
+++ b/S1API/Entities/NPCs/Downtown/PhilipWentworth.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Downtown
+{
+ ///
+ /// Philip Wentworth is a customer.
+ /// He lives in the Downtown region.
+ /// Philip is the bald NPC with a goatee!
+ ///
+ public class PhilipWentworth : NPC
+ {
+ internal PhilipWentworth() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "philip_wentworth")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Downtown/RandyCaulfield.cs b/S1API/Entities/NPCs/Downtown/RandyCaulfield.cs
new file mode 100644
index 00000000..3a8fecca
--- /dev/null
+++ b/S1API/Entities/NPCs/Downtown/RandyCaulfield.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Downtown
+{
+ ///
+ /// Randy Caulfield is a customer.
+ /// He lives in the Downtown region.
+ /// Randy is the NPC wearing a green hat!
+ ///
+ public class RandyCaulfield : NPC
+ {
+ internal RandyCaulfield() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "randy_caulfield")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/IgorRomanovich.cs b/S1API/Entities/NPCs/IgorRomanovich.cs
new file mode 100644
index 00000000..4723d905
--- /dev/null
+++ b/S1API/Entities/NPCs/IgorRomanovich.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs
+{
+ ///
+ /// Igor Romanovich is a npc.
+ /// He is Manny's bodyguard.
+ /// Igor can be found inside the Warehouse!
+ ///
+ public class IgorRomanovich : NPC
+ {
+ internal IgorRomanovich() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "igor_romanovich")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/MannyOakfield.cs b/S1API/Entities/NPCs/MannyOakfield.cs
new file mode 100644
index 00000000..403897a4
--- /dev/null
+++ b/S1API/Entities/NPCs/MannyOakfield.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs
+{
+ ///
+ /// Manny is a NPC.
+ /// He provides workers to the player.
+ /// Manny can be found in the Warehouse!
+ ///
+ public class MannyOakfield : NPC
+ {
+ internal MannyOakfield() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "manny_oakfield")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/NPC_LIST.md b/S1API/Entities/NPCs/NPC_LIST.md
new file mode 100644
index 00000000..334576fa
--- /dev/null
+++ b/S1API/Entities/NPCs/NPC_LIST.md
@@ -0,0 +1,260 @@
+**manny_oakfield**
+ScheduleOne.NPCs.CharacterClasses.Fixer
+---
+**jessi_waters**
+ScheduleOne.NPCs.CharacterClasses.Jessi
+---
+**brad_crosby**
+ScheduleOne.NPCs.CharacterClasses.Brad
+---
+**donna_martin**
+ScheduleOne.NPCs.NPC
+---
+**keith_wagner**
+ScheduleOne.NPCs.CharacterClasses.Keith
+---
+**kevin_oakley**
+ScheduleOne.NPCs.CharacterClasses.Kevin
+---
+**billy_kramer**
+ScheduleOne.NPCs.Billy
+---
+**marco_baron**
+ScheduleOne.NPCs.CharacterClasses.Marco
+---
+**officergreen**
+ScheduleOne.Police.PoliceOfficer
+---
+**stan_carney**
+ScheduleOne.NPCs.Stan
+---
+**kathy_henderson**
+ScheduleOne.NPCs.CharacterClasses.Kathy
+---
+**jane_lucero**
+ScheduleOne.NPCs.CharacterClasses.Jane
+---
+**beth_penn**
+ScheduleOne.NPCs.CharacterClasses.Beth
+---
+**officerhoward**
+ScheduleOne.Police.PoliceOfficer
+---
+**officerlopez**
+ScheduleOne.Police.PoliceOfficer
+---
+**tobias_wentworth**
+ScheduleOne.NPCs.CharacterClasses.Tobias
+---
+**kim_delaney**
+ScheduleOne.NPCs.CharacterClasses.Kim
+---
+**officerdavis**
+ScheduleOne.Police.PoliceOfficer
+---
+**officermurphy**
+ScheduleOne.Police.PoliceOfficer
+---
+**lisa_gardener**
+ScheduleOne.NPCs.CharacterClasses.Lisa
+---
+**eugene_buckley**
+ScheduleOne.NPCs.CharacterClasses.Eugene
+---
+**karen_kennedy**
+ScheduleOne.NPCs.CharacterClasses.Karen
+---
+**jen_heard**
+ScheduleOne.NPCs.CharacterClasses.Jen
+---
+**michael_boog**
+ScheduleOne.NPCs.CharacterClasses.Michael
+---
+**dean_webster**
+ScheduleOne.NPCs.CharacterClasses.Dean
+---
+**elizabeth_homley**
+ScheduleOne.NPCs.CharacterClasses.Elizabeth
+---
+**herbert_bleuball**
+ScheduleOne.NPCs.CharacterClasses.Herbert
+---
+**mick_lubbin**
+ScheduleOne.NPCs.CharacterClasses.Mick
+---
+**mac_cooper**
+ScheduleOne.NPCs.CharacterClasses.Mac
+---
+**sam_thompson**
+ScheduleOne.NPCs.CharacterClasses.Sam
+---
+**molly_presley**
+ScheduleOne.Economy.Dealer
+---
+**greg_fliggle**
+ScheduleOne.NPCs.CharacterClasses.Greg
+---
+**jeff_gilmore**
+ScheduleOne.NPCs.CharacterClasses.Jeff
+---
+**benji_coleman**
+ScheduleOne.NPCs.CharacterClasses.Benji
+---
+**albert_hoover**
+ScheduleOne.NPCs.CharacterClasses.Albert
+---
+**chris_sullivan**
+ScheduleOne.NPCs.CharacterClasses.Chris
+---
+**oscar_holland**
+ScheduleOne.NPCs.CharacterClasses.Oscar
+---
+**peter_file**
+ScheduleOne.NPCs.CharacterClasses.Peter
+---
+**jennifer_rivera**
+ScheduleOne.NPCs.CharacterClasses.Jennifer
+---
+**hank_stevenson**
+ScheduleOne.NPCs.CharacterClasses.Steve
+---
+**officerjackson**
+ScheduleOne.Police.PoliceOfficer
+---
+**louis_fourier**
+ScheduleOne.NPCs.CharacterClasses.Louis
+---
+**officerlee**
+ScheduleOne.Police.PoliceOfficer
+---
+**melissa_wood**
+ScheduleOne.NPCs.CharacterClasses.Melissa
+---
+**jackie_stevenson**
+ScheduleOne.NPCs.CharacterClasses.Jackie
+---
+**lucy_pennington**
+ScheduleOne.NPCs.NPC
+---
+**peggy_myers**
+ScheduleOne.NPCs.CharacterClasses.Peggy
+---
+**joyce_ball**
+ScheduleOne.NPCs.NPC
+---
+**meg_cooley**
+ScheduleOne.NPCs.Meg
+---
+**trent_sherman**
+ScheduleOne.NPCs.NPC
+---
+**charles_rowland**
+ScheduleOne.NPCs.CharacterClasses.Charles
+---
+**george_greene**
+ScheduleOne.NPCs.CharacterClasses.George
+---
+**jerry_montero**
+ScheduleOne.NPCs.Jerry
+---
+**doris_lubbin**
+ScheduleOne.NPCs.Doris
+---
+**dan_samwell**
+ScheduleOne.NPCs.CharacterClasses.Dan
+---
+**uncle_nelson**
+ScheduleOne.NPCs.CharacterClasses.UncleNelson
+---
+**igor_romanovich**
+ScheduleOne.NPCs.CharacterClasses.Igor
+---
+**wei_long**
+ScheduleOne.NPCs.CharacterClasses.Wei
+---
+**leo_rivers**
+ScheduleOne.NPCs.CharacterClasses.Leo
+---
+**officerbailey**
+ScheduleOne.Police.PoliceOfficer
+---
+**officercooper**
+ScheduleOne.Police.PoliceOfficer
+---
+**officeroakley**
+ScheduleOne.Police.PoliceOfficer
+---
+**lily_turner**
+ScheduleOne.NPCs.CharacterClasses.Lily
+---
+**ray_hoffman**
+ScheduleOne.NPCs.CharacterClasses.Ray
+---
+**walter_cussler**
+ScheduleOne.NPCs.CharacterClasses.Walter
+---
+**pearl_moore**
+ScheduleOne.NPCs.CharacterClasses.Pearl
+---
+**fiona_hancock**
+ScheduleOne.NPCs.CharacterClasses.Fiona
+---
+**genghis_barn**
+ScheduleOne.NPCs.CharacterClasses.Genghis
+---
+**anna_chesterfield**
+ScheduleOne.NPCs.CharacterClasses.Anna
+---
+**javier_perez**
+ScheduleOne.NPCs.CharacterClasses.Javier
+---
+**cranky_frank**
+ScheduleOne.NPCs.CharacterClasses.Frank
+---
+**randy_caulfield**
+ScheduleOne.NPCs.CharacterClasses.Randy
+---
+**philip_wentworth**
+ScheduleOne.NPCs.CharacterClasses.Philip
+---
+**jeremy_wilkinson**
+ScheduleOne.NPCs.CharacterClasses.Jeremy
+---
+**alison_knight**
+ScheduleOne.NPCs.CharacterClasses.Alison
+---
+**carl_bundy**
+ScheduleOne.NPCs.CharacterClasses.Carl
+---
+**harold_colt**
+ScheduleOne.NPCs.CharacterClasses.Harold
+---
+**jack_knight**
+ScheduleOne.NPCs.CharacterClasses.Jack
+---
+**dennis_kennedy**
+ScheduleOne.NPCs.CharacterClasses.Dennis
+---
+**shirley_watts**
+ScheduleOne.NPCs.CharacterClasses.Shirley
+---
+**salvador_moreno**
+ScheduleOne.NPCs.CharacterClasses.Salvador
+---
+**kyle_cooley**
+ScheduleOne.NPCs.CharacterClasses.Kyle
+---
+**ludwig_meyer**
+ScheduleOne.NPCs.NPC
+---
+**austin_steiner**
+ScheduleOne.NPCs.CharacterClasses.Austin
+---
+**chloe_bowers**
+ScheduleOne.NPCs.Chloe
+---
+**ming**
+ScheduleOne.NPCs.CharacterClasses.Ming
+---
+**geraldine_poon**
+ScheduleOne.NPCs.CharacterClasses.Geraldine
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Northtown/AlbertHoover.cs b/S1API/Entities/NPCs/Northtown/AlbertHoover.cs
new file mode 100644
index 00000000..162bcfb8
--- /dev/null
+++ b/S1API/Entities/NPCs/Northtown/AlbertHoover.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Northtown
+{
+ ///
+ /// Albert Hoover is a supplier.
+ /// He lives in the Northtown region.
+ /// Albert is the supplier for weed seeds!
+ ///
+ public class AlbertHoover : NPC
+ {
+ internal AlbertHoover() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "albert_hoover")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Northtown/AustinSteiner.cs b/S1API/Entities/NPCs/Northtown/AustinSteiner.cs
new file mode 100644
index 00000000..393ebf90
--- /dev/null
+++ b/S1API/Entities/NPCs/Northtown/AustinSteiner.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Northtown
+{
+ ///
+ /// Austin Steiner is a customer.
+ /// He lives in the Northtown region.
+ /// Austin is the NPC with a red/orange afro and black glasses!
+ ///
+ public class AustinSteiner : NPC
+ {
+ internal AustinSteiner() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "austin_steiner")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Northtown/BenjiColeman.cs b/S1API/Entities/NPCs/Northtown/BenjiColeman.cs
new file mode 100644
index 00000000..b0c6528b
--- /dev/null
+++ b/S1API/Entities/NPCs/Northtown/BenjiColeman.cs
@@ -0,0 +1,20 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Northtown
+{
+ ///
+ /// Benji Coleman is a dealer.
+ /// He lives in the Northtown region.
+ /// Benji lives at the motel in room #2.
+ /// He is the first dealer the player unlocks!
+ ///
+ public class BenjiColeman : NPC
+ {
+ internal BenjiColeman() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "benji_coleman")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Northtown/BethPenn.cs b/S1API/Entities/NPCs/Northtown/BethPenn.cs
new file mode 100644
index 00000000..e5538920
--- /dev/null
+++ b/S1API/Entities/NPCs/Northtown/BethPenn.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Northtown
+{
+ ///
+ /// Beth Penn is a customer.
+ /// She lives in the Northtown region.
+ /// Beth is the NPC with a blonde bowl cut and wears green glasses!
+ ///
+ public class BethPenn : NPC
+ {
+ internal BethPenn() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "beth_penn")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Northtown/ChloeBowers.cs b/S1API/Entities/NPCs/Northtown/ChloeBowers.cs
new file mode 100644
index 00000000..03ab02b8
--- /dev/null
+++ b/S1API/Entities/NPCs/Northtown/ChloeBowers.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Northtown
+{
+ ///
+ /// Chloe Bowers is a customer.
+ /// She lives in the Northtown region.
+ /// Chloe is the NPC with long, straight, red hair!
+ ///
+ public class ChloeBowers : NPC
+ {
+ internal ChloeBowers() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "chloe_bowers")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Northtown/DonnaMartin.cs b/S1API/Entities/NPCs/Northtown/DonnaMartin.cs
new file mode 100644
index 00000000..ef38ed01
--- /dev/null
+++ b/S1API/Entities/NPCs/Northtown/DonnaMartin.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Northtown
+{
+ ///
+ /// Donna Martin is a customer.
+ /// She lives in the Northtown region.
+ /// Donna is the attendant of the Motel!
+ ///
+ public class DonnaMartin : NPC
+ {
+ internal DonnaMartin() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "donna_martin")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Northtown/GeraldinePoon.cs b/S1API/Entities/NPCs/Northtown/GeraldinePoon.cs
new file mode 100644
index 00000000..3b04d209
--- /dev/null
+++ b/S1API/Entities/NPCs/Northtown/GeraldinePoon.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Northtown
+{
+ ///
+ /// Geraldine Poon is a customer.
+ /// He lives in the Northtown region.
+ /// Geraldine is the balding NPC with small gold glasses!
+ ///
+ public class GeraldinePoon : NPC
+ {
+ internal GeraldinePoon() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "geraldine_poon")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Northtown/JessiWaters.cs b/S1API/Entities/NPCs/Northtown/JessiWaters.cs
new file mode 100644
index 00000000..7ed78aaa
--- /dev/null
+++ b/S1API/Entities/NPCs/Northtown/JessiWaters.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Northtown
+{
+ ///
+ /// Jessi Waters is a customer.
+ /// She lives in the Northtown region.
+ /// Jessi is the purple haired NPC with face tattoos!
+ ///
+ public class JessiWaters : NPC
+ {
+ internal JessiWaters() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "jessi_waters")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Northtown/KathyHenderson.cs b/S1API/Entities/NPCs/Northtown/KathyHenderson.cs
new file mode 100644
index 00000000..58a9135b
--- /dev/null
+++ b/S1API/Entities/NPCs/Northtown/KathyHenderson.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Northtown
+{
+ ///
+ /// Kathy Henderson is a customer.
+ /// She lives in the Northtown region.
+ /// Kathy is the NPC with long blonde hair with bangs!
+ ///
+ public class KathyHenderson : NPC
+ {
+ internal KathyHenderson() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "kathy_henderson")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Northtown/KyleCooley.cs b/S1API/Entities/NPCs/Northtown/KyleCooley.cs
new file mode 100644
index 00000000..b4104f2c
--- /dev/null
+++ b/S1API/Entities/NPCs/Northtown/KyleCooley.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Northtown
+{
+ ///
+ /// Kyle Cooley is a customer.
+ /// He lives in the Northtown region.
+ /// Kyle is the NPC that works at Taco Ticklers!
+ ///
+ public class KyleCooley : NPC
+ {
+ internal KyleCooley() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "kyle_cooley")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Northtown/LudwigMeyer.cs b/S1API/Entities/NPCs/Northtown/LudwigMeyer.cs
new file mode 100644
index 00000000..e3b77d8c
--- /dev/null
+++ b/S1API/Entities/NPCs/Northtown/LudwigMeyer.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Northtown
+{
+ ///
+ /// Ludwig Meyer is a customer.
+ /// He lives in the Northtown region.
+ /// Ludwig is the NPC with spiky hair and gold glasses!
+ ///
+ public class LudwigMeyer : NPC
+ {
+ internal LudwigMeyer() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "ludwig_meyer")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Northtown/MickLubbin.cs b/S1API/Entities/NPCs/Northtown/MickLubbin.cs
new file mode 100644
index 00000000..293e7a80
--- /dev/null
+++ b/S1API/Entities/NPCs/Northtown/MickLubbin.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Northtown
+{
+ ///
+ /// Mick Lubbin is a customer.
+ /// He lives in the Northtown region.
+ /// Mick is the owner of the pawn shop!
+ ///
+ public class MickLubbin : NPC
+ {
+ internal MickLubbin() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "mick_lubbin")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Northtown/Ming.cs b/S1API/Entities/NPCs/Northtown/Ming.cs
new file mode 100644
index 00000000..5c5b3e11
--- /dev/null
+++ b/S1API/Entities/NPCs/Northtown/Ming.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Northtown
+{
+ ///
+ /// Mrs. Ming is a customer.
+ /// She lives in the Northtown region.
+ /// Ming is the NPC that owns the chinese restaurant!
+ ///
+ public class Ming : NPC
+ {
+ internal Ming() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "ming")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Northtown/PeggyMyers.cs b/S1API/Entities/NPCs/Northtown/PeggyMyers.cs
new file mode 100644
index 00000000..875983ab
--- /dev/null
+++ b/S1API/Entities/NPCs/Northtown/PeggyMyers.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Northtown
+{
+ ///
+ /// Peggy Myers is a customer.
+ /// She lives in the Northtown region.
+ /// Peggy is the NPC with freckles and brown hair pulled back!
+ ///
+ public class PeggyMyers : NPC
+ {
+ internal PeggyMyers() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "peggy_myers")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Northtown/PeterFile.cs b/S1API/Entities/NPCs/Northtown/PeterFile.cs
new file mode 100644
index 00000000..e2af8748
--- /dev/null
+++ b/S1API/Entities/NPCs/Northtown/PeterFile.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Northtown
+{
+ ///
+ /// Peter File is a customer.
+ /// He lives in the Northtown region.
+ /// Peter is the NPC with a black bowl cut and black glasses!
+ ///
+ public class PeterFile : NPC
+ {
+ internal PeterFile() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "peter_file")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Northtown/SamThompson.cs b/S1API/Entities/NPCs/Northtown/SamThompson.cs
new file mode 100644
index 00000000..d9a49e98
--- /dev/null
+++ b/S1API/Entities/NPCs/Northtown/SamThompson.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Northtown
+{
+ ///
+ /// Sam Thompson is a customer.
+ /// He lives in the Northtown region.
+ /// Sam is the NPC with a green hair and wrinkles!
+ ///
+ public class SamThompson : NPC
+ {
+ internal SamThompson() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "sam_thompson")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/OscarHolland.cs b/S1API/Entities/NPCs/OscarHolland.cs
new file mode 100644
index 00000000..5a557110
--- /dev/null
+++ b/S1API/Entities/NPCs/OscarHolland.cs
@@ -0,0 +1,18 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs
+{
+ ///
+ /// Oscar Holland is a NPC.
+ /// He is a supplier located in the Warehouse!
+ ///
+ public class OscarHolland : NPC
+ {
+ internal OscarHolland() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "oscar_holland")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/PoliceOfficers/OfficerBailey.cs b/S1API/Entities/NPCs/PoliceOfficers/OfficerBailey.cs
new file mode 100644
index 00000000..a5203922
--- /dev/null
+++ b/S1API/Entities/NPCs/PoliceOfficers/OfficerBailey.cs
@@ -0,0 +1,18 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.PoliceOfficers
+{
+ ///
+ /// Officer Bailey is a police officer.
+ /// He is the bald officer with a swirling mustache!
+ ///
+ public class OfficerBailey : NPC
+ {
+ internal OfficerBailey() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "officerbailey")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/PoliceOfficers/OfficerCooper.cs b/S1API/Entities/NPCs/PoliceOfficers/OfficerCooper.cs
new file mode 100644
index 00000000..c2c40ba9
--- /dev/null
+++ b/S1API/Entities/NPCs/PoliceOfficers/OfficerCooper.cs
@@ -0,0 +1,18 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.PoliceOfficers
+{
+ ///
+ /// Officer Cooper is a police officer.
+ /// She is the officer with two high black buns and black glasses!
+ ///
+ public class OfficerCooper : NPC
+ {
+ internal OfficerCooper() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "officercooper")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/PoliceOfficers/OfficerGreen.cs b/S1API/Entities/NPCs/PoliceOfficers/OfficerGreen.cs
new file mode 100644
index 00000000..a5950489
--- /dev/null
+++ b/S1API/Entities/NPCs/PoliceOfficers/OfficerGreen.cs
@@ -0,0 +1,18 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.PoliceOfficers
+{
+ ///
+ /// Officer Green is a police officer.
+ /// She is the officer with light brown hair in a bun!
+ ///
+ public class OfficerGreen : NPC
+ {
+ internal OfficerGreen() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "officergreen")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/PoliceOfficers/OfficerHoward.cs b/S1API/Entities/NPCs/PoliceOfficers/OfficerHoward.cs
new file mode 100644
index 00000000..b077a19b
--- /dev/null
+++ b/S1API/Entities/NPCs/PoliceOfficers/OfficerHoward.cs
@@ -0,0 +1,18 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.PoliceOfficers
+{
+ ///
+ /// Officer Howard is a police officer.
+ /// He is the officer with a light brown afro and goatee!
+ ///
+ public class OfficerHoward : NPC
+ {
+ internal OfficerHoward() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "officerhoward")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/PoliceOfficers/OfficerJackson.cs b/S1API/Entities/NPCs/PoliceOfficers/OfficerJackson.cs
new file mode 100644
index 00000000..3c3c4a4e
--- /dev/null
+++ b/S1API/Entities/NPCs/PoliceOfficers/OfficerJackson.cs
@@ -0,0 +1,18 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.PoliceOfficers
+{
+ ///
+ /// Officer Jackson is a police officer.
+ /// He is the officer with a light brown goatee and police hat!
+ ///
+ public class OfficerJackson : NPC
+ {
+ internal OfficerJackson() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "officerjackson")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/PoliceOfficers/OfficerLee.cs b/S1API/Entities/NPCs/PoliceOfficers/OfficerLee.cs
new file mode 100644
index 00000000..ce448bdf
--- /dev/null
+++ b/S1API/Entities/NPCs/PoliceOfficers/OfficerLee.cs
@@ -0,0 +1,18 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.PoliceOfficers
+{
+ ///
+ /// Officer Lee is a police officer.
+ /// He is the officer with a button-up shirt and black hair!
+ ///
+ public class OfficerLee : NPC
+ {
+ internal OfficerLee() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "officerlee")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/PoliceOfficers/OfficerLopez.cs b/S1API/Entities/NPCs/PoliceOfficers/OfficerLopez.cs
new file mode 100644
index 00000000..6d2235b0
--- /dev/null
+++ b/S1API/Entities/NPCs/PoliceOfficers/OfficerLopez.cs
@@ -0,0 +1,18 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.PoliceOfficers
+{
+ ///
+ /// Officer Lopez is a police officer.
+ /// She is the officer with a blue button-up and long black hair!
+ ///
+ public class OfficerLopez : NPC
+ {
+ internal OfficerLopez() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "officerlopez")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/PoliceOfficers/OfficerMurphy.cs b/S1API/Entities/NPCs/PoliceOfficers/OfficerMurphy.cs
new file mode 100644
index 00000000..206b2eea
--- /dev/null
+++ b/S1API/Entities/NPCs/PoliceOfficers/OfficerMurphy.cs
@@ -0,0 +1,18 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.PoliceOfficers
+{
+ ///
+ /// Officer Murphy is a police officer.
+ /// He is the balding officer with grey hair and wrinkles!
+ ///
+ public class OfficerMurphy : NPC
+ {
+ internal OfficerMurphy() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "officermurphy")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/PoliceOfficers/OfficerOakley.cs b/S1API/Entities/NPCs/PoliceOfficers/OfficerOakley.cs
new file mode 100644
index 00000000..11724460
--- /dev/null
+++ b/S1API/Entities/NPCs/PoliceOfficers/OfficerOakley.cs
@@ -0,0 +1,18 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.PoliceOfficers
+{
+ ///
+ /// Officer Oakley is a police officer.
+ /// He is the officer with light brown spiky hair and a goatee!
+ ///
+ public class OfficerOakley : NPC
+ {
+ internal OfficerOakley() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "officeroakley")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/StanCarney.cs b/S1API/Entities/NPCs/StanCarney.cs
new file mode 100644
index 00000000..017983c5
--- /dev/null
+++ b/S1API/Entities/NPCs/StanCarney.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs
+{
+ ///
+ /// Stan Carney is a NPC.
+ /// He is the NPC that sells weapons.
+ /// Stan can be found in the Warehouse!
+ ///
+ public class StanCarney : NPC
+ {
+ internal StanCarney() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "stan_carney")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Suburbia/AlisonKnight.cs b/S1API/Entities/NPCs/Suburbia/AlisonKnight.cs
new file mode 100644
index 00000000..a534cddc
--- /dev/null
+++ b/S1API/Entities/NPCs/Suburbia/AlisonKnight.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Suburbia
+{
+ ///
+ /// Alison Knight is a customer.
+ /// She lives in the Suburbia region.
+ /// Alison is the NPC with long light brown hair!
+ ///
+ public class AlisonKnight : NPC
+ {
+ internal AlisonKnight() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "alison_knight")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Suburbia/CarlBundy.cs b/S1API/Entities/NPCs/Suburbia/CarlBundy.cs
new file mode 100644
index 00000000..e9fcbbb6
--- /dev/null
+++ b/S1API/Entities/NPCs/Suburbia/CarlBundy.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Suburbia
+{
+ ///
+ /// Carl Bundy is a customer.
+ /// He lives in the Suburbia region.
+ /// Carl is the NPC with a brown apron!
+ ///
+ public class CarlBundy : NPC
+ {
+ internal CarlBundy() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "carl_bundy")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Suburbia/ChrisSullivan.cs b/S1API/Entities/NPCs/Suburbia/ChrisSullivan.cs
new file mode 100644
index 00000000..5c24211c
--- /dev/null
+++ b/S1API/Entities/NPCs/Suburbia/ChrisSullivan.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Suburbia
+{
+ ///
+ /// Chris Sullivan is a customer.
+ /// He lives in the Suburbia region.
+ /// Chris is the NPC with black spiky hair and black glasses!
+ ///
+ public class ChrisSullivan : NPC
+ {
+ internal ChrisSullivan() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "chris_sullivan")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Suburbia/DennisKennedy.cs b/S1API/Entities/NPCs/Suburbia/DennisKennedy.cs
new file mode 100644
index 00000000..d733d06a
--- /dev/null
+++ b/S1API/Entities/NPCs/Suburbia/DennisKennedy.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Suburbia
+{
+ ///
+ /// Dennis Kennedy is a customer.
+ /// He lives in the Suburbia region.
+ /// Dennis is the NPC with light blonde spiky hair and a thick mustache!
+ ///
+ public class DennisKennedy : NPC
+ {
+ internal DennisKennedy() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "dennis_kennedy")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Suburbia/HankStevenson.cs b/S1API/Entities/NPCs/Suburbia/HankStevenson.cs
new file mode 100644
index 00000000..9440e3ec
--- /dev/null
+++ b/S1API/Entities/NPCs/Suburbia/HankStevenson.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Suburbia
+{
+ ///
+ /// Hank Stevenson is a customer.
+ /// He lives in the Suburbia region.
+ /// Hank is the balding NPC with greying brown hair and a goatee!
+ ///
+ public class HankStevenson : NPC
+ {
+ internal HankStevenson() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "hank_stevenson")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Suburbia/HaroldColt.cs b/S1API/Entities/NPCs/Suburbia/HaroldColt.cs
new file mode 100644
index 00000000..e313c440
--- /dev/null
+++ b/S1API/Entities/NPCs/Suburbia/HaroldColt.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Suburbia
+{
+ ///
+ /// Harold Colt is a customer.
+ /// He lives in the Suburbia region.
+ /// Harold is the NPC with grey spiky hair and wrinkles!
+ ///
+ public class HaroldColt : NPC
+ {
+ internal HaroldColt() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "harold_colt")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Suburbia/JackKnight.cs b/S1API/Entities/NPCs/Suburbia/JackKnight.cs
new file mode 100644
index 00000000..921ef9d2
--- /dev/null
+++ b/S1API/Entities/NPCs/Suburbia/JackKnight.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Suburbia
+{
+ ///
+ /// Jack Knight is a customer.
+ /// He lives in the Suburbia region.
+ /// Jack is the balding NPC with small gold glasses!
+ ///
+ public class JackKnight : NPC
+ {
+ internal JackKnight() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "jack_knight")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Suburbia/JackieStevenson.cs b/S1API/Entities/NPCs/Suburbia/JackieStevenson.cs
new file mode 100644
index 00000000..ca504988
--- /dev/null
+++ b/S1API/Entities/NPCs/Suburbia/JackieStevenson.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Suburbia
+{
+ ///
+ /// Jackie Stevenson is a customer.
+ /// He lives in the Suburbia region.
+ /// Jackie is the NPC with short brown hair and light freckles!
+ ///
+ public class JackieStevenson : NPC
+ {
+ internal JackieStevenson() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "jackie_stevenson")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Suburbia/JeremyWilkinson.cs b/S1API/Entities/NPCs/Suburbia/JeremyWilkinson.cs
new file mode 100644
index 00000000..4a7ebe04
--- /dev/null
+++ b/S1API/Entities/NPCs/Suburbia/JeremyWilkinson.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Suburbia
+{
+ ///
+ /// Jeremy Wilkinson is a customer.
+ /// He lives in the Suburbia region.
+ /// Jeremy is the NPC that works at Hyland Auto!
+ ///
+ public class JeremyWilkinson : NPC
+ {
+ internal JeremyWilkinson() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "jeremy_wilkinson")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Suburbia/KarenKennedy.cs b/S1API/Entities/NPCs/Suburbia/KarenKennedy.cs
new file mode 100644
index 00000000..0df4bccd
--- /dev/null
+++ b/S1API/Entities/NPCs/Suburbia/KarenKennedy.cs
@@ -0,0 +1,20 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Suburbia
+{
+ ///
+ /// Karen Kennedy is a customer.
+ /// She lives in the Suburbia region.
+ /// Karen is the NPC with wavy blonde hair and purple eyelids!
+ /// She can be found at the casino upstairs when it's open.
+ ///
+ public class KarenKennedy : NPC
+ {
+ internal KarenKennedy() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "karen_kennedy")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Suburbia/WeiLong.cs b/S1API/Entities/NPCs/Suburbia/WeiLong.cs
new file mode 100644
index 00000000..bec8f78f
--- /dev/null
+++ b/S1API/Entities/NPCs/Suburbia/WeiLong.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Suburbia
+{
+ ///
+ /// Wei Long is a dealer.
+ /// He lives in the Suburbia region.
+ /// Wei is the dealer with a black bowl cut and gold glasses!
+ ///
+ public class WeiLong : NPC
+ {
+ internal WeiLong() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "wei_long")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/UncleNelson.cs b/S1API/Entities/NPCs/UncleNelson.cs
new file mode 100644
index 00000000..15e6b06b
--- /dev/null
+++ b/S1API/Entities/NPCs/UncleNelson.cs
@@ -0,0 +1,18 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs
+{
+ ///
+ /// Uncle Nelson is a NPC.
+ /// He is the uncle of the main character!
+ ///
+ public class UncleNelson : NPC
+ {
+ internal UncleNelson() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "uncle_nelson")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Uptown/FionaHancock.cs b/S1API/Entities/NPCs/Uptown/FionaHancock.cs
new file mode 100644
index 00000000..59a3b968
--- /dev/null
+++ b/S1API/Entities/NPCs/Uptown/FionaHancock.cs
@@ -0,0 +1,20 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+using NPC = S1API.Entities.NPC;
+
+namespace S1API.Entities.NPCs.Uptown
+{
+ ///
+ /// Fiona Hancock is a customer.
+ /// She lives in the Uptown region.
+ /// Fiona is the NPC with light brown buns and green glasses!
+ ///
+ public class FionaHancock : NPC
+ {
+ internal FionaHancock() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "fiona_hancock")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Uptown/HerbertBleuball.cs b/S1API/Entities/NPCs/Uptown/HerbertBleuball.cs
new file mode 100644
index 00000000..0361d983
--- /dev/null
+++ b/S1API/Entities/NPCs/Uptown/HerbertBleuball.cs
@@ -0,0 +1,20 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+using NPC = S1API.Entities.NPC;
+
+namespace S1API.Entities.NPCs.Uptown
+{
+ ///
+ /// Herbert Bleuball is a customer.
+ /// He lives in the Uptown region.
+ /// Herbert is the NPC that owns Bleuball's Boutique!
+ ///
+ public class HerbertBleuball : NPC
+ {
+ internal HerbertBleuball() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "herbert_bleuball")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Uptown/JenHeard.cs b/S1API/Entities/NPCs/Uptown/JenHeard.cs
new file mode 100644
index 00000000..d2ffe6b5
--- /dev/null
+++ b/S1API/Entities/NPCs/Uptown/JenHeard.cs
@@ -0,0 +1,20 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+using NPC = S1API.Entities.NPC;
+
+namespace S1API.Entities.NPCs.Uptown
+{
+ ///
+ /// Jen Heard is a customer.
+ /// She lives in the Uptown region.
+ /// Jen is the NPC with low orange buns!
+ ///
+ public class JenHeard : NPC
+ {
+ internal JenHeard() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "jen_heard")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Uptown/LeoRivers.cs b/S1API/Entities/NPCs/Uptown/LeoRivers.cs
new file mode 100644
index 00000000..6f22c063
--- /dev/null
+++ b/S1API/Entities/NPCs/Uptown/LeoRivers.cs
@@ -0,0 +1,20 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+using NPC = S1API.Entities.NPC;
+
+namespace S1API.Entities.NPCs.Uptown
+{
+ ///
+ /// Leo Rivers is a dealer.
+ /// He lives in the Uptown region.
+ /// Leo is the dealer wearing a black hat and gold shades!
+ ///
+ public class LeoRivers : NPC
+ {
+ internal LeoRivers() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "leo_rivers")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Uptown/LilyTurner.cs b/S1API/Entities/NPCs/Uptown/LilyTurner.cs
new file mode 100644
index 00000000..a2d6b77f
--- /dev/null
+++ b/S1API/Entities/NPCs/Uptown/LilyTurner.cs
@@ -0,0 +1,20 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+using NPC = S1API.Entities.NPC;
+
+namespace S1API.Entities.NPCs.Uptown
+{
+ ///
+ /// Lily Turner is a customer.
+ /// She lives in the Uptown region.
+ /// Lily is the NPC with long brown hair with bangs!
+ ///
+ public class LilyTurner : NPC
+ {
+ internal LilyTurner() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "lily_turner")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Uptown/MichaelBoog.cs b/S1API/Entities/NPCs/Uptown/MichaelBoog.cs
new file mode 100644
index 00000000..d8d160c2
--- /dev/null
+++ b/S1API/Entities/NPCs/Uptown/MichaelBoog.cs
@@ -0,0 +1,20 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+using NPC = S1API.Entities.NPC;
+
+namespace S1API.Entities.NPCs.Uptown
+{
+ ///
+ /// Michael Boog is a customer.
+ /// He lives in the Uptown region.
+ /// Michael is the NPC with a bright blue flat cap and black glasses!
+ ///
+ public class MichaelBoog : NPC
+ {
+ internal MichaelBoog() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "michael_boog")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Uptown/PearlMoore.cs b/S1API/Entities/NPCs/Uptown/PearlMoore.cs
new file mode 100644
index 00000000..902efae0
--- /dev/null
+++ b/S1API/Entities/NPCs/Uptown/PearlMoore.cs
@@ -0,0 +1,20 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+using NPC = S1API.Entities.NPC;
+
+namespace S1API.Entities.NPCs.Uptown
+{
+ ///
+ /// Pearl Moore is a customer.
+ /// She lives in the Uptown region.
+ /// Pearl is the NPC with long white hair with bangs!
+ ///
+ public class PearlMoore : NPC
+ {
+ internal PearlMoore() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "pearl_moore")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Uptown/RayHoffman.cs b/S1API/Entities/NPCs/Uptown/RayHoffman.cs
new file mode 100644
index 00000000..0716d33e
--- /dev/null
+++ b/S1API/Entities/NPCs/Uptown/RayHoffman.cs
@@ -0,0 +1,20 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+using NPC = S1API.Entities.NPC;
+
+namespace S1API.Entities.NPCs.Uptown
+{
+ ///
+ /// Ray Hoffman is a customer.
+ /// He lives in the Uptown region.
+ /// Ray is the NPC that owns Ray's Realty!
+ ///
+ public class RayHoffman : NPC
+ {
+ internal RayHoffman() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "ray_hoffman")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Uptown/TobiasWentworth.cs b/S1API/Entities/NPCs/Uptown/TobiasWentworth.cs
new file mode 100644
index 00000000..b4950553
--- /dev/null
+++ b/S1API/Entities/NPCs/Uptown/TobiasWentworth.cs
@@ -0,0 +1,20 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+using NPC = S1API.Entities.NPC;
+
+namespace S1API.Entities.NPCs.Uptown
+{
+ ///
+ /// Tobias Wentworth is a customer.
+ /// He lives in the Uptown region.
+ /// Tobias is the balding NPC with extremely light brown hair and small black glasses!
+ ///
+ public class TobiasWentworth : NPC
+ {
+ internal TobiasWentworth() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "tobias_wentworth")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Uptown/WalterCussler.cs b/S1API/Entities/NPCs/Uptown/WalterCussler.cs
new file mode 100644
index 00000000..ee6b726e
--- /dev/null
+++ b/S1API/Entities/NPCs/Uptown/WalterCussler.cs
@@ -0,0 +1,20 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+using NPC = S1API.Entities.NPC;
+
+namespace S1API.Entities.NPCs.Uptown
+{
+ ///
+ /// Walter Cussler is a customer.
+ /// He lives in the Uptown region.
+ /// Walter is the NPC with white hair and dressed as a priest!
+ ///
+ public class WalterCussler : NPC
+ {
+ internal WalterCussler() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "walter_cussler")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Westville/CharlesRowland.cs b/S1API/Entities/NPCs/Westville/CharlesRowland.cs
new file mode 100644
index 00000000..a82f5374
--- /dev/null
+++ b/S1API/Entities/NPCs/Westville/CharlesRowland.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Westville
+{
+ ///
+ /// Charles Rowland is a customer.
+ /// He lives in the Westville region.
+ /// Charles is the bald NPC with black glasses!
+ ///
+ public class CharlesRowland : NPC
+ {
+ internal CharlesRowland() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "charles_rowland")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Westville/DeanWebster.cs b/S1API/Entities/NPCs/Westville/DeanWebster.cs
new file mode 100644
index 00000000..00d9da03
--- /dev/null
+++ b/S1API/Entities/NPCs/Westville/DeanWebster.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Westville
+{
+ ///
+ /// Dean Webster is a customer.
+ /// He lives in the Westville region.
+ /// Dean is the NPC that owns Top Tattoo!
+ ///
+ public class DeanWebster : NPC
+ {
+ internal DeanWebster() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "dean_webster")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Westville/DorisLubbin.cs b/S1API/Entities/NPCs/Westville/DorisLubbin.cs
new file mode 100644
index 00000000..a30fd86a
--- /dev/null
+++ b/S1API/Entities/NPCs/Westville/DorisLubbin.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Westville
+{
+ ///
+ /// Doris Lubbin is a customer.
+ /// She lives in the Westville region.
+ /// Doris is the NPC with light brown, wavy hair and black glasses!
+ ///
+ public class DorisLubbin : NPC
+ {
+ internal DorisLubbin() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "doris_lubbin")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Westville/GeorgeGreene.cs b/S1API/Entities/NPCs/Westville/GeorgeGreene.cs
new file mode 100644
index 00000000..0aa2e27a
--- /dev/null
+++ b/S1API/Entities/NPCs/Westville/GeorgeGreene.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Westville
+{
+ ///
+ /// George Greene is a customer.
+ /// He lives in the Westville region.
+ /// George is the NPC with light brown, spiky hair and gold glasses!
+ ///
+ public class GeorgeGreene : NPC
+ {
+ internal GeorgeGreene() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "george_greene")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Westville/JerryMontero.cs b/S1API/Entities/NPCs/Westville/JerryMontero.cs
new file mode 100644
index 00000000..8100e54c
--- /dev/null
+++ b/S1API/Entities/NPCs/Westville/JerryMontero.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Westville
+{
+ ///
+ /// Jerry Montero is a customer.
+ /// He lives in the Westville region.
+ /// Jerry is the NPC with a green hat and black glasses!
+ ///
+ public class JerryMontero : NPC
+ {
+ internal JerryMontero() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "jerry_montero")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Westville/JoyceBall.cs b/S1API/Entities/NPCs/Westville/JoyceBall.cs
new file mode 100644
index 00000000..83452668
--- /dev/null
+++ b/S1API/Entities/NPCs/Westville/JoyceBall.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Westville
+{
+ ///
+ /// Joyce Ball is a customer.
+ /// She lives in the Westville region.
+ /// Joyce is the NPC with light brown hair and wrinkles!
+ ///
+ public class JoyceBall : NPC
+ {
+ internal JoyceBall() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "joyce_ball")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Westville/KeithWagner.cs b/S1API/Entities/NPCs/Westville/KeithWagner.cs
new file mode 100644
index 00000000..98b0b302
--- /dev/null
+++ b/S1API/Entities/NPCs/Westville/KeithWagner.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Westville
+{
+ ///
+ /// Keith Wagner is a customer.
+ /// He lives in the Westville region.
+ /// Keith is the NPC with blonde spiky hair and always angry!
+ ///
+ public class KeithWagner : NPC
+ {
+ internal KeithWagner() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "keith_wagner")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Westville/KimDelaney.cs b/S1API/Entities/NPCs/Westville/KimDelaney.cs
new file mode 100644
index 00000000..abbc07a4
--- /dev/null
+++ b/S1API/Entities/NPCs/Westville/KimDelaney.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Westville
+{
+ ///
+ /// Kim Delaney is a customer.
+ /// She lives in the Westville region.
+ /// Kim is the NPC with long, black hair with bangs!
+ ///
+ public class KimDelaney : NPC
+ {
+ internal KimDelaney() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "kim_delaney")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Westville/MegCooley.cs b/S1API/Entities/NPCs/Westville/MegCooley.cs
new file mode 100644
index 00000000..b60df006
--- /dev/null
+++ b/S1API/Entities/NPCs/Westville/MegCooley.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Westville
+{
+ ///
+ /// Meg Cooley is a customer.
+ /// She lives in the Westville region.
+ /// Meg is the npc with a mustard yellow bowl cut hairstyle!
+ ///
+ public class MegCooley : NPC
+ {
+ internal MegCooley() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "meg_cooley")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Westville/MollyPresley.cs b/S1API/Entities/NPCs/Westville/MollyPresley.cs
new file mode 100644
index 00000000..7f55b99f
--- /dev/null
+++ b/S1API/Entities/NPCs/Westville/MollyPresley.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Westville
+{
+ ///
+ /// Molly Presley is a dealer.
+ /// She lives in the Westville region.
+ /// Molly is the dealer with gold shades and a red backward cap!
+ ///
+ public class MollyPresley : NPC
+ {
+ internal MollyPresley() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "molly_presley")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Westville/ShirleyWatts.cs b/S1API/Entities/NPCs/Westville/ShirleyWatts.cs
new file mode 100644
index 00000000..705bb0a2
--- /dev/null
+++ b/S1API/Entities/NPCs/Westville/ShirleyWatts.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Westville
+{
+ ///
+ /// Shirley Watts is a supplier.
+ /// She lives in the Westville region.
+ /// Shirley is the supplier for pseudo!
+ ///
+ public class ShirleyWatts : NPC
+ {
+ internal ShirleyWatts() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "shirley_watts")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/NPCs/Westville/TrentSherman.cs b/S1API/Entities/NPCs/Westville/TrentSherman.cs
new file mode 100644
index 00000000..e46f1c2b
--- /dev/null
+++ b/S1API/Entities/NPCs/Westville/TrentSherman.cs
@@ -0,0 +1,19 @@
+#if IL2CPP
+using Il2CppScheduleOne.NPCs;
+#else
+using ScheduleOne.NPCs;
+#endif
+using System.Linq;
+
+namespace S1API.Entities.NPCs.Westville
+{
+ ///
+ /// Trent Sherman is a customer.
+ /// He lives in the Westville region.
+ /// Trent is the NPC with short black hair and dark-colored skin!
+ ///
+ public class TrentSherman : NPC
+ {
+ internal TrentSherman() : base(NPCManager.NPCRegistry.ToArray().First(n => n.ID == "trent_sherman")) { }
+ }
+}
\ No newline at end of file
diff --git a/S1API/Entities/Player.cs b/S1API/Entities/Player.cs
new file mode 100644
index 00000000..a8abc0e6
--- /dev/null
+++ b/S1API/Entities/Player.cs
@@ -0,0 +1,192 @@
+#if IL2CPP
+using S1PlayerScripts = Il2CppScheduleOne.PlayerScripts;
+using S1Health = Il2CppScheduleOne.PlayerScripts.Health;
+#else
+using S1PlayerScripts = ScheduleOne.PlayerScripts;
+using S1Health = ScheduleOne.PlayerScripts.Health;
+#endif
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using HarmonyLib;
+using MelonLoader;
+using S1API.Entities.Interfaces;
+using S1API.Internal.Abstraction;
+using UnityEngine;
+
+namespace S1API.Entities
+{
+ ///
+ /// Represents a player within the game.
+ ///
+ public class Player : IEntity, IHealth
+ {
+ ///
+ /// Health when the player is invincible.
+ /// Invincibility isn't baked in the base game.
+ /// Hence, why we're doing it this way :).
+ ///
+ private const float InvincibleHealth = 1000000000f;
+
+ ///
+ /// The standard MAX_HEALTH of a player.
+ ///
+ private const float MortalHealth = 100f;
+
+ ///
+ /// All players currently in the game.
+ ///
+ public static readonly List All = new List();
+
+ ///
+ /// INTERNAL: Tracking of the S1 instance of the player.
+ ///
+ internal S1PlayerScripts.Player S1Player;
+
+ ///
+ /// INTERNAL: Constructor to create a new player from an S1 instance.
+ ///
+ ///
+ internal Player(S1PlayerScripts.Player player)
+ {
+ S1Player = player;
+ All.Add(this);
+ }
+
+ ///
+ /// The current client player (player executing your code).
+ ///
+ public static Player Local =>
+ All.FirstOrDefault(player => player.IsLocal)!;
+
+ ///
+ /// Whether this player is the client player or a networked player.
+ ///
+ public bool IsLocal =>
+ S1Player.IsLocalPlayer;
+
+ ///
+ /// The name of the player.
+ /// For single player, this appears to always return `Player`.
+ ///
+ public string Name =>
+ S1Player.PlayerName;
+
+ ///
+ /// INTERNAL: The game object associated with this player.
+ ///
+ GameObject IEntity.gameObject =>
+ S1Player.gameObject;
+
+ ///
+ /// The world position of the player.
+ ///
+ public Vector3 Position
+ {
+ get => ((IEntity)this).gameObject.transform.position;
+ set => ((IEntity)this).gameObject.transform.position = value;
+ }
+
+ ///
+ /// The transform of the player.
+ /// Please do not set the properties of the Transform.
+ ///
+ public Transform Transform =>
+ ((IEntity)this).gameObject.transform;
+
+ ///
+ /// The scale of the player.
+ ///
+ public float Scale
+ {
+ get => S1Player.Scale;
+ set => S1Player.SetScale(value);
+ }
+
+ ///
+ /// The current health of the player.
+ ///
+ public float CurrentHealth =>
+ S1Player.Health.CurrentHealth;
+
+ ///
+ /// The maximum health of the player.
+ ///
+ public float MaxHealth
+ {
+ get => (float)_maxHealthField.GetValue(S1Player.Health)!;
+ set => _maxHealthField.SetValue(S1Player.Health, value);
+ }
+
+ ///
+ /// Whether the player is dead or not.
+ ///
+ public bool IsDead =>
+ !S1Player.Health.IsAlive;
+
+ ///
+ /// Whether the player is invincible or not.
+ ///
+ public bool IsInvincible
+ {
+ get => MaxHealth == InvincibleHealth;
+ set
+ {
+ MaxHealth = value ? InvincibleHealth : MortalHealth;
+ S1Player.Health.SetHealth(MaxHealth);
+ }
+ }
+
+ ///
+ /// Revives the player.
+ ///
+ public void Revive() =>
+ S1Player.Health.Revive(Position, Quaternion.identity);
+
+ ///
+ /// Deals damage to the player.
+ ///
+ /// The amount of damage to deal.
+ public void Damage(int amount)
+ {
+ if (amount <= 0)
+ return;
+
+ S1Player.Health.TakeDamage(amount);
+ }
+
+ ///
+ /// Heals the player.
+ ///
+ /// The amount of healing to apply to the player.
+ public void Heal(int amount)
+ {
+ if (amount <= 0)
+ return;
+
+ S1Player.Health.SetHealth(CurrentHealth + amount);
+ }
+
+ ///
+ /// Kills the player.
+ ///
+ public void Kill() =>
+ S1Player.Health.SetHealth(0f);
+
+ ///
+ /// Called when the player dies.
+ ///
+ public event Action OnDeath
+ {
+ add => EventHelper.AddListener(value, S1Player.Health.onDie);
+ remove => EventHelper.RemoveListener(value, S1Player.Health.onDie);
+ }
+
+ ///
+ /// INTERNAL: Field access for the MAX_HEALTH const.
+ ///
+ private readonly FieldInfo _maxHealthField = AccessTools.Field(typeof(S1Health.PlayerHealth), "MAX_HEALTH");
+ }
+}
diff --git a/S1API/GameTime/Day.cs b/S1API/GameTime/Day.cs
index 61311920..38b2fa45 100644
--- a/S1API/GameTime/Day.cs
+++ b/S1API/GameTime/Day.cs
@@ -5,12 +5,39 @@
///
public enum Day
{
+ ///
+ /// Represents the first day of the week.
+ ///
Monday,
+
+ ///
+ /// Represents the second day of the week.
+ ///
Tuesday,
+
+ ///
+ /// Represents the third day of the week.
+ ///
Wednesday,
+
+ ///
+ /// Represents the fourth day of the week.
+ ///
Thursday,
+
+ ///
+ /// Represents the fifth day of the week.
+ ///
Friday,
+
+ ///
+ /// Represents the sixth day of the week.
+ ///
Saturday,
+
+ ///
+ /// Represents the seventh day of the week.
+ ///
Sunday
}
}
\ No newline at end of file
diff --git a/S1API/Internal/Abstraction/EventHelper.cs b/S1API/Internal/Abstraction/EventHelper.cs
index 1dd94e81..900eeaee 100644
--- a/S1API/Internal/Abstraction/EventHelper.cs
+++ b/S1API/Internal/Abstraction/EventHelper.cs
@@ -36,7 +36,7 @@ internal static void AddListener(Action listener, UnityEvent unityEvent)
/// The event you want to unsubscribe from.
internal static void RemoveListener(Action listener, UnityEvent unityEvent)
{
- SubscribedActions.TryGetValue(listener, out UnityAction wrappedAction);
+ SubscribedActions.TryGetValue(listener, out UnityAction? wrappedAction);
SubscribedActions.Remove(listener);
unityEvent.RemoveListener(wrappedAction);
}
diff --git a/S1API/Internal/Abstraction/Saveable.cs b/S1API/Internal/Abstraction/Saveable.cs
index 4dd23602..7afa79aa 100644
--- a/S1API/Internal/Abstraction/Saveable.cs
+++ b/S1API/Internal/Abstraction/Saveable.cs
@@ -33,7 +33,7 @@ internal virtual void LoadInternal(string folderPath)
FieldInfo[] saveableFields = GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (FieldInfo saveableField in saveableFields)
{
- SaveableField saveableFieldAttribute = saveableField.GetCustomAttribute();
+ SaveableField? saveableFieldAttribute = saveableField.GetCustomAttribute();
if (saveableFieldAttribute == null)
continue;
@@ -68,7 +68,7 @@ internal virtual void SaveInternal(string folderPath, ref List extraSave
FieldInfo[] saveableFields = ReflectionUtils.GetAllFields(GetType(), BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
foreach (FieldInfo saveableField in saveableFields)
{
- SaveableField saveableFieldAttribute = saveableField.GetCustomAttribute();
+ SaveableField? saveableFieldAttribute = saveableField.GetCustomAttribute();
if (saveableFieldAttribute == null)
continue;
@@ -78,7 +78,7 @@ internal virtual void SaveInternal(string folderPath, ref List extraSave
string saveDataPath = Path.Combine(folderPath, saveFileName);
- object value = saveableField.GetValue(this);
+ object? value = saveableField.GetValue(this);
if (value == null)
// Remove the save if the field is null
File.Delete(saveDataPath);
diff --git a/S1API/Internal/Patches/NPCPatches.cs b/S1API/Internal/Patches/NPCPatches.cs
index b00bba50..0c06a5bf 100644
--- a/S1API/Internal/Patches/NPCPatches.cs
+++ b/S1API/Internal/Patches/NPCPatches.cs
@@ -11,9 +11,10 @@
using System;
using System.IO;
using System.Linq;
+using System.Reflection;
using HarmonyLib;
+using S1API.Entities;
using S1API.Internal.Utils;
-using S1API.NPCs;
namespace S1API.Internal.Patches
{
@@ -23,13 +24,6 @@ namespace S1API.Internal.Patches
[HarmonyPatch]
internal class NPCPatches
{
-
- // ReSharper disable once RedundantNameQualifier
- ///
- /// List of all custom NPCs currently created.
- ///
- private static readonly System.Collections.Generic.List NPCs = new System.Collections.Generic.List();
-
///
/// Patching performed for when game NPCs are loaded.
///
@@ -41,13 +35,19 @@ private static void NPCsLoadersLoad(S1Loaders.NPCsLoader __instance, string main
{
foreach (Type type in ReflectionUtils.GetDerivedClasses())
{
- NPC customNPC = (NPC)Activator.CreateInstance(type);
- NPCs.Add(customNPC);
+ NPC? customNPC = (NPC)Activator.CreateInstance(type, true)!;
+ if (customNPC == null)
+ throw new Exception($"Unable to create instance of {type.FullName}!");
+
+ // We skip any S1API NPCs, as they are base NPC wrappers.
+ if (type.Assembly == Assembly.GetExecutingAssembly())
+ continue;
+
string npcPath = Path.Combine(mainPath, customNPC.S1NPC.SaveFolderName);
customNPC.LoadInternal(npcPath);
}
}
-
+
///
/// Patching performed for when a single NPC starts (including modded in NPCs).
///
@@ -55,7 +55,8 @@ private static void NPCsLoadersLoad(S1Loaders.NPCsLoader __instance, string main
[HarmonyPatch(typeof(S1NPCs.NPC), "Start")]
[HarmonyPostfix]
private static void NPCStart(S1NPCs.NPC __instance) =>
- NPCs.FirstOrDefault(npc => npc.S1NPC == __instance)?.CreateInternal();
+ NPC.All.FirstOrDefault(npc => npc.IsCustomNPC && npc.S1NPC == __instance)?.CreateInternal();
+
///
/// Patching performed for when an NPC calls to save data.
@@ -66,7 +67,7 @@ private static void NPCStart(S1NPCs.NPC __instance) =>
[HarmonyPatch(typeof(S1NPCs.NPC), "WriteData")]
[HarmonyPostfix]
private static void NPCWriteData(S1NPCs.NPC __instance, string parentFolderPath, ref List __result) =>
- NPCs.FirstOrDefault(npc => npc.S1NPC == __instance)?.SaveInternal(parentFolderPath, ref __result);
+ NPC.All.FirstOrDefault(npc => npc.IsCustomNPC && npc.S1NPC == __instance)?.SaveInternal(parentFolderPath, ref __result);
///
/// Patching performed for when an NPC is destroyed.
@@ -74,14 +75,7 @@ private static void NPCWriteData(S1NPCs.NPC __instance, string parentFolderPath,
/// Instance of the NPC
[HarmonyPatch(typeof(S1NPCs.NPC), "OnDestroy")]
[HarmonyPostfix]
- private static void NPCOnDestroy(S1NPCs.NPC __instance)
- {
- NPCs.RemoveAll(npc => npc.S1NPC == __instance);
- NPC? npc = NPCs.FirstOrDefault(npc => npc.S1NPC == __instance);
- if (npc == null)
- return;
-
- NPCs.Remove(npc);
- }
+ private static void NPCOnDestroy(S1NPCs.NPC __instance) =>
+ NPC.All.Remove(NPC.All.First(npc => npc.S1NPC == __instance));
}
}
diff --git a/S1API/Internal/Patches/PhoneAppPatches.cs b/S1API/Internal/Patches/PhoneAppPatches.cs
index 7d48da32..9c9bf913 100644
--- a/S1API/Internal/Patches/PhoneAppPatches.cs
+++ b/S1API/Internal/Patches/PhoneAppPatches.cs
@@ -3,12 +3,11 @@
using UnityEngine.SceneManagement;
using S1API.Internal.Utils;
using S1API.Internal.Abstraction;
-using S1API.Logging;
using S1API.PhoneApp;
namespace S1API.Internal.Patches
-{
-#if IL2CPPMELON || IL2CPPBEPINEX
+{
+#if IL2CPP
[HarmonyPatch(typeof(SceneManager), nameof(SceneManager.Internal_SceneLoaded))]
#else
[HarmonyPatch(typeof(SceneManager), "Internal_SceneLoaded", new Type[] { typeof(Scene), typeof(LoadSceneMode) })]
@@ -16,8 +15,7 @@ namespace S1API.Internal.Patches
#endif
internal static class PhoneAppPatches
{
- private static readonly Log _loggerInstance = new Log("PhoneAppPatches");
-
+ // TODO (@omar-akermi): Can you look into if this is still needed pls?
private static bool _loaded = false;
///
@@ -44,9 +42,9 @@ static void Postfix(Scene scene, LoadSceneMode mode)
}
catch (System.Exception e)
{
- _loggerInstance.Warning($"[PhoneApp] Failed to register {type.FullName}: {e.Message}");
+ MelonLoader.MelonLogger.Warning($"[PhoneApp] Failed to register {type.FullName}: {e.Message}");
}
}
}
}
-}
+}
\ No newline at end of file
diff --git a/S1API/Internal/Patches/PlayerPatches.cs b/S1API/Internal/Patches/PlayerPatches.cs
new file mode 100644
index 00000000..d3f24c65
--- /dev/null
+++ b/S1API/Internal/Patches/PlayerPatches.cs
@@ -0,0 +1,39 @@
+#if IL2CPP
+using S1PlayerScripts = Il2CppScheduleOne.PlayerScripts;
+#else
+using S1PlayerScripts = ScheduleOne.PlayerScripts;
+#endif
+
+
+using System.Linq;
+using HarmonyLib;
+using S1API.Entities;
+
+namespace S1API.Internal.Patches
+{
+ ///
+ /// INTERNAL: Patches to apply to the Players for tracking.
+ ///
+ [HarmonyPatch]
+ internal class PlayerPatches
+ {
+ ///
+ /// INTERNAL: Adds players to the player list upon wake.
+ ///
+ /// The player to add.
+ [HarmonyPatch(typeof(S1PlayerScripts.Player), "Awake")]
+ [HarmonyPostfix]
+ private static void PlayerAwake(S1PlayerScripts.Player __instance) =>
+ new Player(__instance);
+
+
+ ///
+ /// INTERNAL: Removes players from the player list upon destruction.
+ ///
+ /// The player to remove.
+ [HarmonyPatch(typeof(S1PlayerScripts.Player), "OnDestroy")]
+ [HarmonyPostfix]
+ private static void PlayerOnDestroy(S1PlayerScripts.Player __instance) =>
+ Player.All.Remove(Player.All.First(player => player.S1Player == __instance));
+ }
+}
\ No newline at end of file
diff --git a/S1API/Internal/Patches/QuestPatches.cs b/S1API/Internal/Patches/QuestPatches.cs
index 6b825e21..035bbe64 100644
--- a/S1API/Internal/Patches/QuestPatches.cs
+++ b/S1API/Internal/Patches/QuestPatches.cs
@@ -59,8 +59,8 @@ private static void QuestsLoaderLoad(S1Loaders.QuestsLoader __instance, string m
string[] questDirectories = Directory.GetDirectories(mainPath)
.Select(Path.GetFileName)
- .Where(directory => directory.StartsWith("Quest_"))
- .ToArray();
+ .Where(directory => directory != null && directory.StartsWith("Quest_"))
+ .ToArray()!;
foreach (string questDirectory in questDirectories)
{
diff --git a/S1API/Internal/Utils/ButtonListener.cs b/S1API/Internal/Utils/ButtonListener.cs
index 75f6a75a..8f8d6ab3 100644
--- a/S1API/Internal/Utils/ButtonListener.cs
+++ b/S1API/Internal/Utils/ButtonListener.cs
@@ -1,69 +1,76 @@
-using UnityEngine.UI;
using System;
using S1API.Internal.Abstraction;
using UnityEngine;
+using UnityEngine.UI;
-public static class ButtonUtils
+namespace S1API.Internal.Utils
{
///
- /// Adds a click listener to the specified button, ensuring compatibility with IL2CPP and Mono.
+ /// Utility class for managing UI Buttons.
+ /// TODO (@omar-akermi): Is this intended to be internal instead of public??
///
- public static void AddListener(Button button, Action action)
+ public static class ButtonUtils
{
- if (button == null || action == null) return;
- EventHelper.AddListener(action, button.onClick);
- }
+ ///
+ /// Adds a click listener to the specified button, ensuring compatibility with IL2CPP and Mono.
+ ///
+ public static void AddListener(Button button, Action action)
+ {
+ if (button == null || action == null) return;
+ EventHelper.AddListener(action, button.onClick);
+ }
- ///
- /// Removes a previously added click listener from the specified button.
- ///
- public static void RemoveListener(Button button, Action action)
- {
- if (button == null || action == null) return;
- EventHelper.RemoveListener(action, button.onClick);
- }
+ ///
+ /// Removes a previously added click listener from the specified button.
+ ///
+ public static void RemoveListener(Button button, Action action)
+ {
+ if (button == null || action == null) return;
+ EventHelper.RemoveListener(action, button.onClick);
+ }
- ///
- /// Removes all listeners from the specified button safely.
- ///
- public static void ClearListeners(Button button)
- {
- if (button == null) return;
- button.onClick.RemoveAllListeners();
- }
+ ///
+ /// Removes all listeners from the specified button safely.
+ ///
+ public static void ClearListeners(Button button)
+ {
+ if (button == null) return;
+ button.onClick.RemoveAllListeners();
+ }
- ///
- /// Enables the button and optionally updates the label.
- ///
- public static void Enable(Button button, Text label = null, string text = null)
- {
- if (button != null) button.interactable = true;
- if (label != null && !string.IsNullOrEmpty(text)) label.text = text;
- }
+ ///
+ /// Enables the button and optionally updates the label.
+ ///
+ public static void Enable(Button button, Text? label = null, string? text = null)
+ {
+ if (button != null) button.interactable = true;
+ if (label != null && !string.IsNullOrEmpty(text)) label.text = text;
+ }
- ///
- /// Disables the button and optionally updates the label.
- ///
- public static void Disable(Button button, Text label = null, string text = null)
- {
- if (button != null) button.interactable = false;
- if (label != null && !string.IsNullOrEmpty(text)) label.text = text;
- }
+ ///
+ /// Disables the button and optionally updates the label.
+ ///
+ public static void Disable(Button button, Text? label = null, string? text = null)
+ {
+ if (button != null) button.interactable = false;
+ if (label != null && !string.IsNullOrEmpty(text)) label.text = text;
+ }
- ///
- /// Sets the label text of a button with a known Text child.
- ///
- public static void SetLabel(Text label, string text)
- {
- if (label != null) label.text = text;
- }
- ///
- /// Sets the button label and background color.
- ///
- public static void SetStyle(Button button, Text label, string text, Color bg)
- {
- if (button == null || label == null) return;
- label.text = text;
- button.image.color = bg;
+ ///
+ /// Sets the label text of a button with a known Text child.
+ ///
+ public static void SetLabel(Text label, string text)
+ {
+ if (label != null) label.text = text;
+ }
+ ///
+ /// Sets the button label and background color.
+ ///
+ public static void SetStyle(Button button, Text label, string text, Color bg)
+ {
+ if (button == null || label == null) return;
+ label.text = text;
+ button.image.color = bg;
+ }
}
}
\ No newline at end of file
diff --git a/S1API/Internal/Utils/ImageUtils.cs b/S1API/Internal/Utils/ImageUtils.cs
index 8ca166e1..62365758 100644
--- a/S1API/Internal/Utils/ImageUtils.cs
+++ b/S1API/Internal/Utils/ImageUtils.cs
@@ -4,6 +4,10 @@
namespace S1API.Internal.Utils
{
+ ///
+ /// A utility class to assist with loading images into the game.
+ /// Useful for icons such as on phone apps, custom NPCs, quests, etc.
+ ///
public static class ImageUtils
{
private static readonly Log _loggerInstance = new Log("ImageUtils");
@@ -15,7 +19,7 @@ public static class ImageUtils
///
/// A Sprite object representing the loaded image, or null if the image could not be loaded or the file does not exist.
///
- public static Sprite LoadImage(string fileName)
+ public static Sprite? LoadImage(string fileName)
{
string fullPath = Path.Combine(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location) ?? string.Empty, fileName);
if (!File.Exists(fullPath))
diff --git a/S1API/Internal/Utils/RandomUtils.cs b/S1API/Internal/Utils/RandomUtils.cs
index a1d984ac..f57dc030 100644
--- a/S1API/Internal/Utils/RandomUtils.cs
+++ b/S1API/Internal/Utils/RandomUtils.cs
@@ -1,8 +1,7 @@
using System;
using System.Collections.Generic;
-using UnityEngine;
-namespace S1API.Utils
+namespace S1API.Internal.Utils
{
///
/// A utility class providing random selection functionality for lists and numeric ranges.
@@ -17,7 +16,7 @@ public static class RandomUtils
public static T PickOne(this IList list)
{
if (list == null || list.Count == 0)
- return default;
+ return default!;
return list[UnityEngine.Random.Range(0, list.Count)];
}
@@ -32,8 +31,8 @@ public static T PickOne(this IList list)
/// A randomly selected item that satisfies the condition, or the default value of the type if no valid item is found.
public static T PickUnique(this IList list, Func isDuplicate, int maxTries = 10)
{
- if (list == null || list.Count == 0)
- return default;
+ if (list.Count == 0)
+ return default!;
for (int i = 0; i < maxTries; i++)
{
@@ -42,7 +41,7 @@ public static T PickUnique(this IList list, Func isDuplicate, int
return item;
}
- return default;
+ return default!;
}
///
@@ -54,7 +53,9 @@ public static T PickUnique(this IList list, Func isDuplicate, int
/// A list containing the selected random items, or an empty list if the input list is null or empty.
public static List PickMany(this IList list, int count)
{
- if (list == null || list.Count == 0) return new List();
+ if (list.Count == 0)
+ return new List();
+
var copy = new List(list);
var result = new List();
diff --git a/S1API/Internal/Utils/ReflectionUtils.cs b/S1API/Internal/Utils/ReflectionUtils.cs
index ff22e152..664c6e7d 100644
--- a/S1API/Internal/Utils/ReflectionUtils.cs
+++ b/S1API/Internal/Utils/ReflectionUtils.cs
@@ -19,12 +19,12 @@ internal static List GetDerivedClasses()
{
List derivedClasses = new List();
Assembly[] applicableAssemblies = AppDomain.CurrentDomain.GetAssemblies()
- .Where(assembly => !assembly.FullName.StartsWith("System") &&
- !assembly.FullName.StartsWith("Unity") &&
- !assembly.FullName.StartsWith("Il2Cpp") &&
- !assembly.FullName.StartsWith("mscorlib") &&
- !assembly.FullName.StartsWith("Mono.") &&
- !assembly.FullName.StartsWith("netstandard"))
+ .Where(assembly => !assembly.FullName!.StartsWith("System") &&
+ !assembly.FullName!.StartsWith("Unity") &&
+ !assembly.FullName!.StartsWith("Il2Cpp") &&
+ !assembly.FullName!.StartsWith("mscorlib") &&
+ !assembly.FullName!.StartsWith("Mono.") &&
+ !assembly.FullName!.StartsWith("netstandard"))
.ToArray();
foreach (Assembly assembly in applicableAssemblies)
derivedClasses.AddRange(assembly.GetTypes()
diff --git a/S1API/Items/ItemCategory.cs b/S1API/Items/ItemCategory.cs
index 483a6ed2..a4e88df8 100644
--- a/S1API/Items/ItemCategory.cs
+++ b/S1API/Items/ItemCategory.cs
@@ -5,17 +5,66 @@
///
public enum ItemCategory
{
+ ///
+ /// Represents items such as Cocaine, Weed, etc.
+ /// Oddly, SpeedGrow is in this category as of (v0.3.4f8).
+ ///
Product,
+
+ ///
+ /// Represents items such as Baggies, Bricks, Jars, etc.
+ ///
Packaging,
+
+ ///
+ /// Represents items such as Soil, Fertilizer, Pots, etc.
+ ///
Growing,
+
+ ///
+ /// Represents equipment tools such as the clippers.
+ /// Oddly, trash bags is in this category as of (v0.3.4f8).
+ ///
Tools,
+
+ ///
+ /// Represents items such as TV, Trash Can, Bed, etc.
+ ///
Furniture,
+
+ ///
+ /// Represents items such as Floor Lamps, Halogen Lights, etc.
+ ///
Lighting,
+
+ ///
+ /// Represents cash-based items.
+ ///
Cash,
+
+ ///
+ /// Represents items such as Cuke, Energy Drink, etc.
+ ///
Consumable,
+
+ ///
+ /// Represents items such as Drying Rack, Brick Press, Mixing Station, etc.
+ ///
Equipment,
+
+ ///
+ /// Represents items such as Acid, Banana, Chili, etc.
+ ///
Ingredient,
+
+ ///
+ /// Represents items such as GoldBar, WallClock, WoodSign, etc.
+ ///
Decoration,
+
+ ///
+ /// Represents clothing items.
+ ///
Clothing
}
}
\ No newline at end of file
diff --git a/S1API/Leveling/Rank.cs b/S1API/Leveling/Rank.cs
index 1582ee51..9144b28f 100644
--- a/S1API/Leveling/Rank.cs
+++ b/S1API/Leveling/Rank.cs
@@ -5,16 +5,59 @@
///
public enum Rank
{
+ ///
+ /// Represents the first rank for the player.
+ ///
StreetRat,
+
+ ///
+ /// Represents the second rank for the player.
+ ///
Hoodlum,
+
+ ///
+ /// Represents the third rank for the player.
+ ///
Peddler,
+
+ ///
+ /// Represents the fourth rank for the player.
+ ///
Hustler,
+
+ ///
+ /// Represents the fifth rank for the player.
+ ///
Bagman,
+
+ ///
+ /// Represents the sixth rank for the player.
+ ///
Enforcer,
+
+ ///
+ /// Represents the seventh rank for the player.
+ ///
ShotCaller,
+
+ ///
+ /// Represents the eighth rank for the player.
+ ///
BlockBoss,
+
+ ///
+ /// Represents the ninth rank for the player.
+ ///
Underlord,
+
+ ///
+ /// Represents the tenth rank for the player.
+ ///
Baron,
+
+ ///
+ /// Represents the eleventh rank for the player.
+ ///
Kingpin
}
}
\ No newline at end of file
diff --git a/S1API/Map/Region.cs b/S1API/Map/Region.cs
new file mode 100644
index 00000000..1ed6785b
--- /dev/null
+++ b/S1API/Map/Region.cs
@@ -0,0 +1,38 @@
+namespace S1API.Map
+{
+ ///
+ /// The regions available in the game.
+ ///
+ public enum Region
+ {
+ ///
+ /// The first region in the game.
+ ///
+ Northtown,
+
+ ///
+ /// The second region in the game.
+ ///
+ Westville,
+
+ ///
+ /// The third region in the game.
+ ///
+ Downtown,
+
+ ///
+ /// The fourth region in the game.
+ ///
+ Docks,
+
+ ///
+ /// The fifth region in the game.
+ ///
+ Suburbia,
+
+ ///
+ /// The sixth region in the game.
+ ///
+ Uptown
+ }
+}
\ No newline at end of file
diff --git a/S1API/NPCs/Response.cs b/S1API/Messaging/Response.cs
similarity index 94%
rename from S1API/NPCs/Response.cs
rename to S1API/Messaging/Response.cs
index 9ab12e45..1e9028c6 100644
--- a/S1API/NPCs/Response.cs
+++ b/S1API/Messaging/Response.cs
@@ -1,11 +1,11 @@
-#if (IL2CPPMELON || IL2CPPBEPINEX)
+#if (IL2CPP)
using S1Messaging = Il2CppScheduleOne.Messaging;
-#elif (MONOMELON || MONOBEPINEX)
+#elif (MONO)
using S1Messaging = ScheduleOne.Messaging;
#endif
using System;
-namespace S1API.NPCs
+namespace S1API.Messaging
{
///
/// Represents a message response displayed for the player.
@@ -55,4 +55,4 @@ public string Text
set => S1Response.text = value;
}
}
-}
+}
\ No newline at end of file
diff --git a/S1API/NPCs/NPC.cs b/S1API/NPCs/NPC.cs
deleted file mode 100644
index f39611db..00000000
--- a/S1API/NPCs/NPC.cs
+++ /dev/null
@@ -1,270 +0,0 @@
-#if (IL2CPPMELON || IL2CPPBEPINEX)
-using S1DevUtilities = Il2CppScheduleOne.DevUtilities;
-using S1Interaction = Il2CppScheduleOne.Interaction;
-using S1Messaging = Il2CppScheduleOne.Messaging;
-using S1Noise = Il2CppScheduleOne.Noise;
-using S1Relation = Il2CppScheduleOne.NPCs.Relation;
-using S1Responses = Il2CppScheduleOne.NPCs.Responses;
-using S1PlayerScripts = Il2CppScheduleOne.PlayerScripts;
-using S1ContactApps = Il2CppScheduleOne.UI.Phone.ContactsApp;
-using S1WorkspacePopup = Il2CppScheduleOne.UI.WorldspacePopup;
-using S1AvatarFramework = Il2CppScheduleOne.AvatarFramework;
-using S1Behaviour = Il2CppScheduleOne.NPCs.Behaviour;
-using S1Vehicles = Il2CppScheduleOne.Vehicles;
-using S1Vision = Il2CppScheduleOne.Vision;
-using S1NPCs = Il2CppScheduleOne.NPCs;
-using Il2CppSystem.Collections.Generic;
-#elif (MONOMELON || MONOBEPINEX)
-using S1DevUtilities = ScheduleOne.DevUtilities;
-using S1Interaction = ScheduleOne.Interaction;
-using S1Messaging = ScheduleOne.Messaging;
-using S1Noise = ScheduleOne.Noise;
-using S1Relation = ScheduleOne.NPCs.Relation;
-using S1Responses = ScheduleOne.NPCs.Responses;
-using S1PlayerScripts = ScheduleOne.PlayerScripts;
-using S1ContactApps = ScheduleOne.UI.Phone.ContactsApp;
-using S1WorkspacePopup = ScheduleOne.UI.WorldspacePopup;
-using S1AvatarFramework = ScheduleOne.AvatarFramework;
-using S1Behaviour = ScheduleOne.NPCs.Behaviour;
-using S1Vehicles = ScheduleOne.Vehicles;
-using S1Vision = ScheduleOne.Vision;
-using S1NPCs = ScheduleOne.NPCs;
-using System.Collections.Generic;
-using System.Reflection;
-using HarmonyLib;
-#endif
-
-using System;
-using System.IO;
-using S1API.Internal.Abstraction;
-using UnityEngine;
-using UnityEngine.Events;
-
-namespace S1API.NPCs
-{
- ///
- /// An abstract class intended to be derived from for creating custom NPCs in the game.
- ///
- public abstract class NPC : Saveable
- {
- ///
- /// A list of text responses you've added to your NPC.
- ///
- protected readonly System.Collections.Generic.List Responses =
- new System.Collections.Generic.List();
-
- internal readonly S1NPCs.NPC S1NPC;
-
- private readonly GameObject _gameObject;
- ///
- /// Optional custom mugshot icon sprite. If null, defaults to ContactsApp icon.
- /// protected override Sprite? NPCIcon => ImageUtils.LoadImage("icon.png");
- ///
- protected virtual Sprite? NPCIcon => null;
-
- ///
- /// Base constructor for a new NPC.
- /// Intended to be wrapped in your derived class constructor such as:
- /// public class MyNPC : NPC ...
- /// public MyNPC() : base(id, fname, lname) { ... }
- ///
- /// The unique identifier for this NPC
- ///
- ///
- public NPC(string guid, string firstName, string lastName)
- {
- _gameObject = new GameObject("NPC");
-
- // Deactivate game object til we're done
- _gameObject.SetActive(false);
-
- // Setup the base NPC class
- S1NPC = _gameObject.AddComponent();
- S1NPC.FirstName = firstName;
- S1NPC.LastName = lastName;
- S1NPC.ID = guid;
- S1NPC.BakedGUID = Guid.NewGuid().ToString();
- S1NPC.MugshotSprite = NPCIcon ?? S1DevUtilities.PlayerSingleton.Instance.AppIcon;
-
- // ReSharper disable once UseObjectOrCollectionInitializer
- S1NPC.ConversationCategories = new List();
- S1NPC.ConversationCategories.Add(S1Messaging.EConversationCategory.Customer);
-
- // Create our MessageConversation
-#if (IL2CPPMELON || IL2CPPBEPINEX)
- S1NPC.CreateMessageConversation();
-#elif (MONOMELON || MONOBEPINEX)
- MethodInfo createConvoMethod = AccessTools.Method(typeof(S1NPCs.NPC), "CreateMessageConversation");
- createConvoMethod.Invoke(S1NPC, null);
-#endif
-
- // Add UnityEvents for NPCHealth
- S1NPC.Health = _gameObject.GetComponent();
- S1NPC.Health.onDie = new UnityEvent();
- S1NPC.Health.onKnockedOut = new UnityEvent();
- S1NPC.Health.Invincible = true;
- S1NPC.Health.MaxHealth = 100f;
-
- // Awareness behaviour
- GameObject awarenessObject = new GameObject("NPCAwareness");
- awarenessObject.SetActive(false);
- awarenessObject.transform.SetParent(_gameObject.transform);
- S1NPC.awareness = awarenessObject.AddComponent();
- S1NPC.awareness.onExplosionHeard = new UnityEvent();
- S1NPC.awareness.onGunshotHeard = new UnityEvent();
- S1NPC.awareness.onHitByCar = new UnityEvent();
- S1NPC.awareness.onNoticedDrugDealing = new UnityEvent();
- S1NPC.awareness.onNoticedGeneralCrime = new UnityEvent();
- S1NPC.awareness.onNoticedPettyCrime = new UnityEvent();
- S1NPC.awareness.onNoticedPlayerViolatingCurfew = new UnityEvent();
- S1NPC.awareness.onNoticedSuspiciousPlayer = new UnityEvent();
- S1NPC.awareness.Listener = _gameObject.AddComponent();
-
- /////// START BEHAVIOUR CODE ////////
- // NPCBehaviours behaviour
- GameObject behaviourObject = new GameObject("NPCBehaviour");
- behaviourObject.SetActive(false);
- behaviourObject.transform.SetParent(_gameObject.transform);
- S1Behaviour.NPCBehaviour behaviour = behaviourObject.AddComponent();
-
- GameObject cowingBehaviourObject = new GameObject("CowingBehaviour");
- cowingBehaviourObject.transform.SetParent(behaviourObject.transform);
- S1Behaviour.CoweringBehaviour coweringBehaviour = cowingBehaviourObject.AddComponent();
-
- GameObject fleeBehaviourObject = new GameObject("FleeBehaviour");
- fleeBehaviourObject.transform.SetParent(behaviourObject.transform);
- S1Behaviour.FleeBehaviour fleeBehaviour = fleeBehaviourObject.AddComponent();
-
- behaviour.CoweringBehaviour = coweringBehaviour;
- behaviour.FleeBehaviour = fleeBehaviour;
- S1NPC.behaviour = behaviour;
- /////// END BEHAVIOUR CODE ////////
-
- // Response to actions like gunshots, drug deals, etc.
- GameObject responsesObject = new GameObject("NPCResponses");
- responsesObject.SetActive(false);
- responsesObject.transform.SetParent(_gameObject.transform);
- S1NPC.awareness.Responses = responsesObject.AddComponent();
-
- // Vision cone object and behaviour
- GameObject visionObject = new GameObject("VisionCone");
- visionObject.SetActive(false);
- visionObject.transform.SetParent(_gameObject.transform);
- S1Vision.VisionCone visionCone = visionObject.AddComponent();
- visionCone.StatesOfInterest.Add(new S1Vision.VisionCone.StateContainer
- {
- state = S1PlayerScripts.PlayerVisualState.EVisualState.PettyCrime, RequiredNoticeTime = 0.1f
- });
- S1NPC.awareness.VisionCone = visionCone;
-
-
- // Suspicious ? icon in world space
- S1NPC.awareness.VisionCone.QuestionMarkPopup = _gameObject.AddComponent();
-
- // Interaction behaviour
-#if (IL2CPPMELON || IL2CPPBEPINEX)
- S1NPC.intObj = _gameObject.AddComponent();
-#elif (MONOMELON || MONOBEPINEX)
- FieldInfo intObjField = AccessTools.Field(typeof(S1NPCs.NPC), "intObj");
- intObjField.SetValue(S1NPC, _gameObject.AddComponent());
-#endif
-
- // Relationship data
- S1NPC.RelationData = new S1Relation.NPCRelationData();
-
- // void OnUnlockAction(S1Relation.NPCRelationData.EUnlockType unlockType, bool notify)
- // {
- // if (!string.IsNullOrEmpty(S1NPC.NPCUnlockedVariable))
- // {
- // S1DevUtilities.NetworkSingleton.Instance.SetVariableValue(S1NPC.NPCUnlockedVariable, true.ToString());
- // }
- // }
-
- // S1NPC.RelationData.onUnlocked += (Action)OnUnlockAction;
-
- // Inventory behaviour
- S1NPCs.NPCInventory inventory = _gameObject.AddComponent();
-
- // Pickpocket behaviour
- inventory.PickpocketIntObj = _gameObject.AddComponent();
-
- // Defaulting to the local player for Avatar TODO: Change
- S1NPC.Avatar = S1AvatarFramework.MugshotGenerator.Instance.MugshotRig;
-
-
- // NetworkObject networkObject = gameObject.AddComponent();
- // // networkObject.NetworkBehaviours = InstanceFinder.NetworkManager;
- // PropertyInfo networkBehavioursProperty = AccessTools.Property(typeof(NetworkObject), "NetworkBehaviours");
- // networkBehavioursProperty.SetValue(networkObject, new [] { this });
-
- // Enable our custom gameObjects so they can initialize
- _gameObject.SetActive(true);
- visionObject.SetActive(true);
- responsesObject.SetActive(true);
- awarenessObject.SetActive(true);
- behaviourObject.SetActive(true);
- }
-
- ///
- /// INTERNAL: Initializes the responses that have been added / loaded
- ///
- internal override void CreateInternal()
- {
- // Assign responses to our tracked responses
- foreach (S1Messaging.Response s1Response in S1NPC.MSGConversation.currentResponses)
- {
- Response response = new Response(s1Response) { Label = s1Response.label, Text = s1Response.text };
- Responses.Add(response);
- OnResponseLoaded(response);
- }
-
- base.CreateInternal();
- }
-
- internal override void SaveInternal(string folderPath, ref List extraSaveables)
- {
- string npcPath = Path.Combine(folderPath, S1NPC.SaveFolderName);
- base.SaveInternal(npcPath, ref extraSaveables);
- }
-
- ///
- /// Sends a text message from this NPC to the players.
- /// Supports responses with callbacks for additional logic.
- ///
- /// The message you want the player to see. Unity rich text is allowed.
- /// Instances of to display.
- /// The delay between when the message is sent and when the player can reply.
- /// Whether this should propagate to all players or not.
- public void SendTextMessage(string message, Response[]? responses = null, float responseDelay = 1f, bool network = true)
- {
- S1NPC.SendTextMessage(message);
- S1NPC.MSGConversation.ClearResponses();
-
- if (responses == null || responses.Length == 0)
- return;
-
- Responses.Clear();
-
- List responsesList = new List();
-
- foreach (Response response in responses)
- {
- Responses.Add(response);
- responsesList.Add(response.S1Response);
- }
-
- S1NPC.MSGConversation.ShowResponses(
- responsesList,
- responseDelay,
- network
- );
- }
-
- ///
- /// Called when a response is loaded from the save file.
- /// Override this method for attaching your callbacks to your methods.
- ///
- /// The response that was loaded.
- protected virtual void OnResponseLoaded(Response response) { }
- }
-}
diff --git a/S1API/NPCs/NPCInstance.cs b/S1API/NPCs/NPCInstance.cs
deleted file mode 100644
index 27354028..00000000
--- a/S1API/NPCs/NPCInstance.cs
+++ /dev/null
@@ -1,13 +0,0 @@
-#if (IL2CPPMELON || IL2CPPBEPINEX)
-using S1NPCs = Il2CppScheduleOne.NPCs;
-#elif (MONOMELON || MONOBEPINEX)
-using S1NPCs = ScheduleOne.NPCs;
-#endif
-
-namespace S1API.NPCs
-{
- public class NPCInstance
- {
-
- }
-}
diff --git a/S1API/NPCs/NPCManager.cs b/S1API/NPCs/NPCManager.cs
deleted file mode 100644
index b8fa86dc..00000000
--- a/S1API/NPCs/NPCManager.cs
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace S1API.NPCs
-{
- public class NPCManager
- {
-
- }
-}
\ No newline at end of file
diff --git a/S1API/PhoneApp/MyAwesomeApp.cs b/S1API/PhoneApp/MyAwesomeApp.cs
index 54ef4495..31d3c30f 100644
--- a/S1API/PhoneApp/MyAwesomeApp.cs
+++ b/S1API/PhoneApp/MyAwesomeApp.cs
@@ -5,13 +5,15 @@
namespace S1API.PhoneApp
{
- ///
- /// Defines the MyAwesomeApp, a specialized application integrated into an in-game phone system.
- ///
- ///
- /// This class leverages the PhoneApp framework to specify application-specific properties like name, title,
- /// icon label, and icon file name. It also overrides the method for defining the user interface layout upon creation.
- ///
+ // TODO(@omar-akermi): Can we move this into markdown pls <3
+
+ // ///
+ // /// Defines the MyAwesomeApp, a specialized application integrated into an in-game phone system.
+ // ///
+ // ///
+ // /// This class leverages the PhoneApp framework to specify application-specific properties like name, title,
+ // /// icon label, and icon file name. It also overrides the method for defining the user interface layout upon creation.
+ // ///
/*
public class MyAwesomeApp : PhoneApp
{
diff --git a/S1API/PhoneApp/PhoneApp.cs b/S1API/PhoneApp/PhoneApp.cs
index 4b9dbb33..7f59e4e7 100644
--- a/S1API/PhoneApp/PhoneApp.cs
+++ b/S1API/PhoneApp/PhoneApp.cs
@@ -1,25 +1,11 @@
-#if IL2CPPMELON || IL2CPPBEPINEX
-using UnityEngine;
-using UnityEngine.UI;
-using UnityEngine.Events;
-#elif MONOMELON || MONOBEPINEX
-using UnityEngine;
-using UnityEngine.UI;
-using UnityEngine.Events;
-#endif
-
-using FishNet;
-using System.Collections;
+using System.Collections;
using System.IO;
-#if MONOMELON || IL2CPPMELON
using MelonLoader;
-using MelonLoader.Utils;
-#elif MONOBEPINEX || IL2CPPBEPINEX
-using BepInEx.Logging;
-#endif
+using UnityEngine;
+using UnityEngine.UI;
using Object = UnityEngine.Object;
+using MelonLoader.Utils;
using S1API.Internal.Abstraction;
-using S1API.Logging;
namespace S1API.PhoneApp
{
@@ -33,27 +19,31 @@ namespace S1API.PhoneApp
///
public abstract class PhoneApp : Registerable
{
- protected static readonly Log LoggerInstance = new Log("PhoneApp");
+ ///
+ /// INTERNAL: A dedicated logger for all custom phone apps.
+ ///
+ protected static readonly MelonLogger.Instance LoggerInstance = new MelonLogger.Instance("PhoneApp");
///
/// The player object in the scene.
///
- protected GameObject Player;
+ private GameObject? _player;
///
/// The in-game UI panel representing the app.
///
- protected GameObject AppPanel;
+ private GameObject? _appPanel;
+ // TODO (@omar-akermi): Can you look into if this is still needed pls?
///
/// Whether the app was successfully created.
///
- protected bool AppCreated;
+ private bool _appCreated;
///
/// Whether the app icon was already modified.
///
- protected bool IconModified;
+ private bool _iconModified;
///
/// Unique GameObject name of the app (e.g. "SilkroadApp").
@@ -64,7 +54,7 @@ public abstract class PhoneApp : Registerable
/// The title shown at the top of the app UI.
///
protected abstract string AppTitle { get; }
-
+
///
/// The label shown under the app icon on the home screen.
///
@@ -86,10 +76,7 @@ public abstract class PhoneApp : Registerable
///
protected override void OnCreated()
{
- // @TODO: Find out if this actually is the proper way of starting a coroutine
- // for both BepInEx and MelonLoader
- // Old code: MelonCoroutines.Start(InitApp());
- InstanceFinder.TimeManager.StartCoroutine(InitApp());
+ MelonCoroutines.Start(InitApp());
}
///
@@ -97,14 +84,14 @@ protected override void OnCreated()
///
protected override void OnDestroyed()
{
- if (AppPanel != null)
+ if (_appPanel != null)
{
- Object.Destroy(AppPanel);
- AppPanel = null;
+ Object.Destroy(_appPanel);
+ _appPanel = null;
}
- AppCreated = false;
- IconModified = false;
+ _appCreated = false;
+ _iconModified = false;
}
///
@@ -114,8 +101,8 @@ private IEnumerator InitApp()
{
yield return new WaitForSeconds(5f);
- Player = GameObject.Find("Player_Local");
- if (Player == null)
+ _player = GameObject.Find("Player_Local");
+ if (_player == null)
{
LoggerInstance.Error("Player_Local not found.");
yield break;
@@ -131,8 +118,8 @@ private IEnumerator InitApp()
Transform existingApp = appsCanvas.transform.Find(AppName);
if (existingApp != null)
{
- AppPanel = existingApp.gameObject;
- SetupExistingAppPanel(AppPanel);
+ _appPanel = existingApp.gameObject;
+ SetupExistingAppPanel(_appPanel);
}
else
{
@@ -143,10 +130,10 @@ private IEnumerator InitApp()
yield break;
}
- AppPanel = Object.Instantiate(templateApp.gameObject, appsCanvas.transform);
- AppPanel.name = AppName;
+ _appPanel = Object.Instantiate(templateApp.gameObject, appsCanvas.transform);
+ _appPanel.name = AppName;
- Transform containerTransform = AppPanel.transform.Find("Container");
+ Transform containerTransform = _appPanel.transform.Find("Container");
if (containerTransform != null)
{
GameObject container = containerTransform.gameObject;
@@ -154,14 +141,14 @@ private IEnumerator InitApp()
OnCreatedUI(container);
}
- AppCreated = true;
+ _appCreated = true;
}
- AppPanel.SetActive(true);
+ _appPanel.SetActive(true);
- if (!IconModified)
+ if (!_iconModified)
{
- IconModified = ModifyAppIcon(IconLabel, IconFileName);
+ _iconModified = ModifyAppIcon(IconLabel, IconFileName);
}
}
@@ -182,7 +169,7 @@ private void SetupExistingAppPanel(GameObject panel)
}
}
- AppCreated = true;
+ _appCreated = true;
}
private void ClearContainer(GameObject container)
@@ -206,14 +193,14 @@ private bool ModifyAppIcon(string labelText, string fileName)
GameObject parent = GameObject.Find("Player_Local/CameraContainer/Camera/OverlayCamera/GameplayMenu/Phone/phone/HomeScreen/AppIcons/");
if (parent == null)
{
- LoggerInstance.Error("AppIcons not found.");
+ LoggerInstance?.Error("AppIcons not found.");
return false;
}
- Transform lastIcon = parent.transform.childCount > 0 ? parent.transform.GetChild(parent.transform.childCount - 1) : null;
+ Transform? lastIcon = parent.transform.childCount > 0 ? parent.transform.GetChild(parent.transform.childCount - 1) : null;
if (lastIcon == null)
{
- LoggerInstance.Error("No icon found to clone.");
+ LoggerInstance?.Error("No icon found to clone.");
return false;
}
@@ -221,8 +208,9 @@ private bool ModifyAppIcon(string labelText, string fileName)
iconObj.name = AppName;
Transform labelTransform = iconObj.transform.Find("Label");
- Text label = labelTransform?.GetComponent();
- if (label != null) label.text = labelText;
+ Text? label = labelTransform?.GetComponent();
+ if (label != null)
+ label.text = labelText;
return ChangeAppIconImage(iconObj, fileName);
}
@@ -237,18 +225,14 @@ private bool ModifyAppIcon(string labelText, string fileName)
private bool ChangeAppIconImage(GameObject iconObj, string filename)
{
Transform imageTransform = iconObj.transform.Find("Mask/Image");
- Image image = imageTransform?.GetComponent();
+ Image? image = imageTransform?.GetComponent();
if (image == null)
{
LoggerInstance?.Error("Image component not found in icon.");
return false;
}
-#if MONOMELON || IL2CPPMELON
string path = Path.Combine(MelonEnvironment.ModsDirectory, filename);
-#elif MONOBEPINEX || IL2CPPBEPINEX
- string path = Path.Combine(BepInEx.Paths.PluginPath, filename);
-#endif
if (!File.Exists(path))
{
LoggerInstance?.Error("Icon file not found: " + path);
diff --git a/S1API/Player/Player.cs b/S1API/Player/Player.cs
new file mode 100644
index 00000000..34f0a683
--- /dev/null
+++ b/S1API/Player/Player.cs
@@ -0,0 +1,10 @@
+// using UnityEngine;
+//
+// namespace S1API.Player
+// {
+// public class Player
+// {
+// public static Player Local => new Player();
+// public Vector3 Position = Vector3.zero;
+// }
+// }
\ No newline at end of file
diff --git a/S1API/Products/ProductDefinitionWrapper.cs b/S1API/Products/ProductDefinitionWrapper.cs
index a8374c83..a6fb9eb5 100644
--- a/S1API/Products/ProductDefinitionWrapper.cs
+++ b/S1API/Products/ProductDefinitionWrapper.cs
@@ -7,9 +7,12 @@
namespace S1API.Products
{
- public static class ProductDefinitionWrapper
+ ///
+ /// INTERNAL: A wrapper class for converting a product definition to its proper dedicated class.
+ ///
+ internal static class ProductDefinitionWrapper
{
- public static ProductDefinition Wrap(ProductDefinition def)
+ internal static ProductDefinition Wrap(ProductDefinition def)
{
var item = def.S1ItemDefinition;
if (CrossType.Is(item, out var weed))
diff --git a/S1API/Quests/QuestManager.cs b/S1API/Quests/QuestManager.cs
index bf15845d..36e694f9 100644
--- a/S1API/Quests/QuestManager.cs
+++ b/S1API/Quests/QuestManager.cs
@@ -29,7 +29,10 @@ public static Quest CreateQuest(string? guid = null) where T : Quest =>
///
public static Quest CreateQuest(Type questType, string? guid = null)
{
- Quest quest = (Quest)Activator.CreateInstance(questType);
+ Quest? quest = (Quest)Activator.CreateInstance(questType)!;
+ if (quest == null)
+ throw new Exception($"Unable to create quest instance of {questType.FullName}!");
+
Quests.Add(quest);
return quest;
}
diff --git a/S1API/Quests/QuestState.cs b/S1API/Quests/QuestState.cs
index 8f02c675..d907b328 100644
--- a/S1API/Quests/QuestState.cs
+++ b/S1API/Quests/QuestState.cs
@@ -5,11 +5,34 @@
///
public enum QuestState
{
+ ///
+ /// Represents a quest / quest entry that has not been started yet.
+ ///
Inactive,
+
+ ///
+ /// Represents a quest / quest entry that has been started but not ended.
+ ///
Active,
+
+ ///
+ /// Represents a quest / quest entry that has been completed successfully by the player.
+ ///
Completed,
+
+ ///
+ /// Represents a quest / quest entry that has been failed by the played.
+ ///
Failed,
+
+ ///
+ /// Represents a quest / quest entry that has been expired.
+ ///
Expired,
+
+ ///
+ /// Represents a quest / quest entry that has been cancelled.
+ ///
Cancelled
}
}
\ No newline at end of file
diff --git a/S1API/S1API.cs b/S1API/S1API.cs
index b3c3936b..847adb45 100644
--- a/S1API/S1API.cs
+++ b/S1API/S1API.cs
@@ -5,6 +5,9 @@
namespace S1API
{
+ ///
+ /// Not currently utilized by S1API.
+ ///
public class S1API : MelonMod
{
}
diff --git a/S1API/S1API.csproj b/S1API/S1API.csproj
index 51a19069..223cc89b 100644
--- a/S1API/S1API.csproj
+++ b/S1API/S1API.csproj
@@ -7,7 +7,6 @@
- netstandard2.1
KaBooMa
S1API
A Schedule One Mono / Il2Cpp Cross Compatibility Layer
@@ -17,6 +16,15 @@
MonoMelon;MonoBepInEx;Il2CppMelon;Il2CppBepInEx
AnyCPU
S1API
+ true
+
+
+
+ netstandard2.1
+
+
+
+ net6.0
@@ -86,15 +94,15 @@
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
diff --git a/S1API/Saveables/SaveableField.cs b/S1API/Saveables/SaveableField.cs
index ec5be204..1df462af 100644
--- a/S1API/Saveables/SaveableField.cs
+++ b/S1API/Saveables/SaveableField.cs
@@ -4,6 +4,8 @@ namespace S1API.Saveables
{
///
/// Marks a field to be saved alongside the class instance.
+ /// This attribute is intended to work across all custom game elements.
+ /// (For example, custom NPCs, quests, etc.)
///
[AttributeUsage(AttributeTargets.Field)]
public class SaveableField : Attribute
@@ -13,6 +15,10 @@ public class SaveableField : Attribute
///
internal string SaveName { get; }
+ ///
+ /// Base constructor for initializing a SaveableField.
+ ///
+ ///
public SaveableField(string saveName)
{
SaveName = saveName;
diff --git a/S1API/UI/UIFactory.cs b/S1API/UI/UIFactory.cs
index f97eab43..4ae6d900 100644
--- a/S1API/UI/UIFactory.cs
+++ b/S1API/UI/UIFactory.cs
@@ -1,15 +1,12 @@
-#if IL2CPPMELON || IL2CPPBEPINEX
-using UnityEngine;
-using UnityEngine.UI;
+#if IL2CPP
using Il2CppInterop.Runtime.InteropTypes.Arrays;
-#else
-using UnityEngine;
-using UnityEngine.UI;
#endif
using System;
+using S1API.Internal.Utils;
+using UnityEngine;
using UnityEngine.Events;
-using Object = UnityEngine.Object;
+using UnityEngine.UI;
namespace S1API.UI
{
@@ -239,33 +236,15 @@ public static void CreateRowButton(GameObject go, UnityAction clickHandler, bool
/// The transform whose child objects will be destroyed.
public static void ClearChildren(Transform parent)
{
- if (parent == null)
- {
- return;
- }
-
- try
- {
- int count = parent.childCount;
- for (int i = count - 1; i >= 0; i--)
- {
- var child = parent.GetChild(i);
- if (child != null)
- Object.Destroy(child.gameObject);
- }
-
- }
- catch (System.Exception e)
- {
- return;
- }
+ foreach (Transform child in parent)
+ GameObject.Destroy(child.gameObject);
}
/// Configures a GameObject to use a VerticalLayoutGroup with specified spacing and padding.
/// The GameObject to which a VerticalLayoutGroup will be added or configured.
/// The spacing between child objects within the VerticalLayoutGroup. Default is 10.
/// The padding around the edges of the VerticalLayoutGroup. If null, a default RectOffset of (10, 10, 10, 10) will be used.
- public static void VerticalLayoutOnGO(GameObject go, int spacing = 10, RectOffset padding = null)
+ public static void VerticalLayoutOnGO(GameObject go, int spacing = 10, RectOffset? padding = null)
{
var layout = go.AddComponent();
layout.spacing = spacing;
@@ -288,8 +267,8 @@ public static GameObject CreateQuestRow(string name, Transform parent, out GameO
rowRT.sizeDelta = new Vector2(0f, 90f); // Let layout handle width
row.AddComponent().minHeight = 50f;
row.AddComponent().effectColor = new Color(0, 0, 0, 0.2f); // or Image line separator below
-
-
+
+
var line = UIFactory.Panel("Separator", row.transform, new Color(1,1,1,0.05f));
line.GetComponent().sizeDelta = new Vector2(300f, 1f);
@@ -338,41 +317,57 @@ public static GameObject CreateQuestRow(string name, Transform parent, out GameO
/// The height of the button, if displayed.
/// An optional action to be invoked when the button is clicked. If null, the button will not be created.
/// The text to display on the optional button. Defaults to "Action" if not specified.
+ /// The size of the top bar.
+ /// The left position of the bar.
+ /// The right position of the bar.
+ /// The top position of the bar.
+ /// The bottom position of the bar.
/// The created GameObject representing the top bar.
- public static GameObject TopBar(string name, Transform parent, string title,float buttonWidth,float buttonHeight,float topbarSize,int rectleft,int rectright,int recttop,int rectbottom,
- Action onRightButtonClick = null,
- string rightButtonText = "Action")
-{
- var topBar = Panel(name, parent, new Color(0.15f, 0.15f, 0.15f),
- new Vector2(0f, topbarSize), new Vector2(1f, 1f));
-
- var layout = topBar.AddComponent();
- layout.padding = new RectOffset(rectleft,rectright,recttop,rectbottom);;
- layout.spacing = 20;
- layout.childAlignment = TextAnchor.MiddleCenter;
- layout.childForceExpandWidth = false;
- layout.childForceExpandHeight = true;
-
- // Title
- var titleText = Text("TopBarTitle", title, topBar.transform, 26, TextAnchor.MiddleLeft, FontStyle.Bold);
- var titleLayout = titleText.gameObject.AddComponent();
- titleLayout.minWidth = 300;
- titleLayout.flexibleWidth = 1;
-
- // Button (if any)
- if (onRightButtonClick != null)
- {
- var (btnGO, btn, label) = ButtonWithLabel("TopBarButton", rightButtonText, topBar.transform, new Color(0.25f, 0.5f, 1f), buttonWidth, buttonHeight);
- ButtonUtils.AddListener(btn, onRightButtonClick);
-
- var btnLayout = btnGO.AddComponent();
- btnLayout.minWidth = buttonWidth;
- btnLayout.preferredHeight = buttonHeight;
- btnLayout.flexibleWidth = 0;
- }
+ public static GameObject TopBar(
+ string name,
+ Transform parent,
+ string title,
+ float buttonWidth,
+ float buttonHeight,
+ float topBarSize,
+ int rectLeft,
+ int rectRight,
+ int rectTop,
+ int rectBottom,
+ Action? onRightButtonClick = null,
+ string rightButtonText = "Action"
+ )
+ {
+ var topBar = Panel(name, parent, new Color(0.15f, 0.15f, 0.15f),
+ new Vector2(0f, topBarSize), new Vector2(1f, 1f));
- return topBar;
-}
+ var layout = topBar.AddComponent();
+ layout.padding = new RectOffset(rectLeft,rectRight,rectTop,rectBottom);;
+ layout.spacing = 20;
+ layout.childAlignment = TextAnchor.MiddleCenter;
+ layout.childForceExpandWidth = false;
+ layout.childForceExpandHeight = true;
+
+ // Title
+ var titleText = Text("TopBarTitle", title, topBar.transform, 26, TextAnchor.MiddleLeft, FontStyle.Bold);
+ var titleLayout = titleText.gameObject.AddComponent();
+ titleLayout.minWidth = 300;
+ titleLayout.flexibleWidth = 1;
+
+ if (onRightButtonClick == null)
+ return topBar;
+
+ // Create the button element if we have an Action to invoke
+ var (btnGO, btn, label) = ButtonWithLabel("TopBarButton", rightButtonText, topBar.transform, new Color(0.25f, 0.5f, 1f), buttonWidth, buttonHeight);
+ ButtonUtils.AddListener(btn, onRightButtonClick);
+
+ var btnLayout = btnGO.AddComponent();
+ btnLayout.minWidth = buttonWidth;
+ btnLayout.preferredHeight = buttonHeight;
+ btnLayout.flexibleWidth = 0;
+
+ return topBar;
+ }
/// Binds an action to a button and updates its label text.
@@ -387,35 +382,35 @@ public static void BindAcceptButton(Button btn, Text label, string text, UnityAc
btn.onClick.AddListener(callback);
}
}
-}
-
-///
-/// Represents a handler that encapsulates a callback action to be invoked when a click event occurs.
-///
-///
-/// This class provides a mechanism to handle and execute logic when a click event is triggered.
-/// It associates an action defined by a UnityAction delegate with the click event.
-///
-public class ClickHandler
-{
- ///
- /// A private field that stores the UnityAction delegate to be invoked during a specific click event.
- ///
- private readonly UnityAction _callback;
+ ///
/// Represents a handler that encapsulates a callback action to be invoked when a click event occurs.
- public ClickHandler(UnityAction callback)
- {
- _callback = callback;
- }
-
- /// Invokes the callback action associated with a click event.
+ ///
///
- /// Executes the UnityAction delegate provided during the creation of the ClickHandler instance.
- /// This method is used to process and handle click events associated with the handler.
+ /// This class provides a mechanism to handle and execute logic when a click event is triggered.
+ /// It associates an action defined by a UnityAction delegate with the click event.
///
- public void OnClick()
+ public class ClickHandler
{
- _callback.Invoke();
+ ///
+ /// A private field that stores the UnityAction delegate to be invoked during a specific click event.
+ ///
+ private readonly UnityAction _callback;
+
+ /// Represents a handler that encapsulates a callback action to be invoked when a click event occurs.
+ public ClickHandler(UnityAction callback)
+ {
+ _callback = callback;
+ }
+
+ /// Invokes the callback action associated with a click event.
+ ///
+ /// Executes the UnityAction delegate provided during the creation of the ClickHandler instance.
+ /// This method is used to process and handle click events associated with the handler.
+ ///
+ public void OnClick()
+ {
+ _callback.Invoke();
+ }
}
-}
+}
\ No newline at end of file