diff --git a/Growing/PlantInstance.cs b/Growing/PlantInstance.cs
new file mode 100644
index 00000000..8bcfe5b6
--- /dev/null
+++ b/Growing/PlantInstance.cs
@@ -0,0 +1,77 @@
+#if (IL2CPPMELON || IL2CPPBEPINEX)
+using S1Growing = Il2CppScheduleOne.Growing;
+#elif (MONOMELON || MONOBEPINEX)
+using S1Growing = ScheduleOne.Growing;
+#endif
+
+using S1API.Internal.Utils;
+using S1API.Items;
+using UnityEngine;
+
+namespace S1API.Growing
+{
+ ///
+ /// Represents an instance of a growing plant in the world.
+ ///
+ public class PlantInstance
+ {
+ ///
+ /// INTERNAL: The in-game Plant object.
+ ///
+ internal readonly S1Growing.Plant S1Plant;
+
+ ///
+ /// INTERNAL: Create a wrapper around an existing Plant.
+ ///
+ /// The in-game Plant to wrap.
+ internal PlantInstance(S1Growing.Plant plant)
+ {
+ S1Plant = plant;
+ }
+
+ ///
+ /// The current growth stage as a float from 0.0 to 1.0.
+ ///
+ public float NormalizedGrowth =>
+ S1Plant.NormalizedGrowthProgress;
+
+ ///
+ /// Whether the plant is fully grown.
+ ///
+ public bool IsFullyGrown =>
+ S1Plant.IsFullyGrown;
+
+ ///
+ /// The SeedDefinition that this plant originated from.
+ ///
+ public SeedDefinition SeedDefinition =>
+ new SeedDefinition(S1Plant.SeedDefinition);
+
+ ///
+ /// The quality level of this plant.
+ ///
+ public float Quality =>
+ S1Plant.QualityLevel;
+
+ ///
+ /// The yield level (amount) of this plant.
+ ///
+ public float Yield =>
+ S1Plant.YieldLevel;
+
+ ///
+ /// The GameObject of the plant.
+ ///
+ public GameObject GameObject =>
+ S1Plant.gameObject;
+
+ ///
+ /// Destroys this plant in-game.
+ ///
+ /// Whether to drop trash scraps.
+ public void Destroy(bool dropScraps = false)
+ {
+ S1Plant.Destroy(dropScraps);
+ }
+ }
+}
diff --git a/Growing/SeedCreator.cs b/Growing/SeedCreator.cs
new file mode 100644
index 00000000..8729a58f
--- /dev/null
+++ b/Growing/SeedCreator.cs
@@ -0,0 +1,57 @@
+#if (IL2CPPMELON || IL2CPPBEPINEX)
+using S1Growing = Il2CppScheduleOne.Growing;
+using S1ItemFramework = Il2CppScheduleOne.ItemFramework;
+using S1Registry = Il2CppScheduleOne.Registry;
+#elif (MONOMELON || MONOBEPINEX)
+using S1Growing = ScheduleOne.Growing;
+using S1ItemFramework = ScheduleOne.ItemFramework;
+using S1Registry = ScheduleOne.Registry;
+#endif
+
+using UnityEngine;
+using System.Linq;
+
+
+namespace S1API.Growing
+{
+ ///
+ /// The seed Creator for custom seeds to be added.
+ ///
+ public static class SeedCreator
+ {
+ public static SeedDefinition CreateSeed(
+ string id,
+ string name,
+ string description,
+ int stackLimit = 10,
+ GameObject functionSeedPrefab = null,
+ GameObject plantPrefab = null,
+ Sprite icon = null)
+ {
+ S1Growing.SeedDefinition seed = ScriptableObject.CreateInstance();
+
+ seed.ID = id;
+ seed.Name = name;
+ seed.Description = description;
+ seed.StackLimit = stackLimit;
+ seed.Category = S1ItemFramework.EItemCategory.Growing;
+
+ // if (icon != null)
+ // {
+ // seed.Icon = icon;
+ // }
+ // commented out for more test later.
+
+ if (functionSeedPrefab != null)
+ seed.FunctionSeedPrefab = functionSeedPrefab.GetComponent();
+
+ if (plantPrefab != null)
+ seed.PlantPrefab = plantPrefab.GetComponent();
+
+ S1Registry.Instance.AddToRegistry(seed);
+
+ return new SeedDefinition(seed);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Growing/SeedDefinition.cs b/Growing/SeedDefinition.cs
new file mode 100644
index 00000000..745a8e17
--- /dev/null
+++ b/Growing/SeedDefinition.cs
@@ -0,0 +1,54 @@
+#if (IL2CPPMELON || IL2CPPBEPINEX)
+using S1Growing = Il2CppScheduleOne.Growing;
+#elif (MONOMELON || MONOBEPINEX)
+using S1Growing = ScheduleOne.Growing;
+#endif
+
+using S1API.Internal.Utils;
+using S1API.Items;
+
+namespace S1API.Growing
+{
+ ///
+ /// Represents the definition of a Seed item (what you buy in shops).
+ ///
+ public class SeedDefinition : ItemDefinition
+ {
+ ///
+ /// INTERNAL: Stored reference to the SeedDefinition.
+ ///
+ internal S1Growing.SeedDefinition S1SeedDefinition =>
+ CrossType.As(S1ItemDefinition);
+
+ ///
+ /// INTERNAL: Create a new wrapper around an existing SeedDefinition.
+ ///
+ /// The in-game SeedDefinition to wrap.
+ internal SeedDefinition(S1Growing.SeedDefinition definition) : base(definition) { }
+
+ ///
+ /// The prefab that is spawned when planting this seed.
+ ///
+ public UnityEngine.GameObject FunctionalSeedPrefab =>
+ S1SeedDefinition.FunctionSeedPrefab?.gameObject;
+
+ ///
+ /// The plant prefab this seed grows into.
+ ///
+ public UnityEngine.GameObject PlantPrefab =>
+ S1SeedDefinition.PlantPrefab?.gameObject;
+
+ ///
+ /// Creates an instance of this seed in the world (FunctionalSeed prefab).
+ ///
+ public UnityEngine.GameObject CreateSeedInstance()
+ {
+ if (S1SeedDefinition.FunctionSeedPrefab != null)
+ return UnityEngine.Object.Instantiate(S1SeedDefinition.FunctionSeedPrefab).gameObject;
+
+ throw new System.NullReferenceException("No FunctionalSeedPrefab assigned to this SeedDefinition!");
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/Growing/SeedInstance.cs b/Growing/SeedInstance.cs
new file mode 100644
index 00000000..97bafe45
--- /dev/null
+++ b/Growing/SeedInstance.cs
@@ -0,0 +1,55 @@
+#if (IL2CPPMELON || IL2CPPBEPINEX)
+using S1Growing = Il2CppScheduleOne.Growing;
+#elif (MONOMELON || MONOBEPINEX)
+using S1Growing = ScheduleOne.Growing;
+#endif
+
+using UnityEngine;
+using S1API.Internal.Utils;
+
+namespace S1API.Growing
+{
+ ///
+ /// Represents an instance of a functional seed in the world.
+ /// (Not just the definition — this is the physical object you interact with.)
+ ///
+ public class SeedInstance
+ {
+ ///
+ /// INTERNAL: Reference to the in-game FunctionalSeed object.
+ ///
+ internal readonly S1Growing.FunctionalSeed S1FunctionalSeed;
+
+ ///
+ /// INTERNAL: Creates a wrapper around the existing FunctionalSeed.
+ ///
+ /// The FunctionalSeed object to wrap.
+ internal SeedInstance(S1Growing.FunctionalSeed functionalSeed)
+ {
+ S1FunctionalSeed = functionalSeed;
+ }
+
+ ///
+ /// The underlying GameObject of this seed.
+ ///
+ private GameObject GameObject =>
+ S1FunctionalSeed.gameObject;
+
+ ///
+ /// Whether the seed currently has exited its vial.
+ ///
+ public bool HasExitedVial { get; private set; } = false;
+
+ ///
+ /// Force the seed to exit the vial manually.
+ ///
+ public void ForceExitVial()
+ {
+ if (S1FunctionalSeed.Vial != null)
+ {
+ S1FunctionalSeed.TriggerExit(S1FunctionalSeed.Vial.GetComponent());
+ HasExitedVial = true;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Items/ItemCategory.cs b/Items/ItemCategory.cs
new file mode 100644
index 00000000..a4e88df8
--- /dev/null
+++ b/Items/ItemCategory.cs
@@ -0,0 +1,70 @@
+namespace S1API.Items
+{
+ ///
+ /// A list of item categories available in-game.
+ ///
+ 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/Items/ItemDefinition.cs b/Items/ItemDefinition.cs
new file mode 100644
index 00000000..c2f3b4db
--- /dev/null
+++ b/Items/ItemDefinition.cs
@@ -0,0 +1,156 @@
+#if (IL2CPPMELON || IL2CPPBEPINEX)
+using S1ItemFramework = Il2CppScheduleOne.ItemFramework;
+#elif (MONOMELON || MONOBEPINEX)
+using S1ItemFramework = ScheduleOne.ItemFramework;
+#endif
+
+using UnityEngine;
+using S1API.Internal.Abstraction;
+
+namespace S1API.Items
+{
+ ///
+ /// Represents an item definition in-game.
+ /// Use this class to read and create new item definitions dynamically.
+ ///
+ public class ItemDefinition : IGUIDReference
+ {
+ ///
+ /// INTERNAL: A reference to the native game item definition.
+ ///
+ internal S1ItemFramework.ItemDefinition S1ItemDefinition { get; }
+
+ ///
+ /// INTERNAL: Wraps an existing native item definition.
+ ///
+ internal ItemDefinition(S1ItemFramework.ItemDefinition definition)
+ {
+ S1ItemDefinition = definition;
+ }
+
+ ///
+ /// The unique ID of this item.
+ ///
+ public string ID
+ {
+ get => S1ItemDefinition.ID;
+ set => S1ItemDefinition.ID = value;
+ }
+
+ ///
+ /// The display name for this item.
+ ///
+ public string Name
+ {
+ get => S1ItemDefinition.Name;
+ set => S1ItemDefinition.Name = value;
+ }
+
+ ///
+ /// A short description for this item.
+ ///
+ public string Description
+ {
+ get => S1ItemDefinition.Description;
+ set => S1ItemDefinition.Description = value;
+ }
+
+ ///
+ /// Stack limit for this item (max quantity per slot).
+ ///
+ public int StackLimit
+ {
+ get => S1ItemDefinition.StackLimit;
+ set => S1ItemDefinition.StackLimit = value;
+ }
+
+ ///
+ /// The category for inventory sorting.
+ ///
+ public ItemCategory Category
+ {
+ get => (ItemCategory)S1ItemDefinition.Category;
+ set => S1ItemDefinition.Category = (S1ItemFramework.EItemCategory)value;
+ }
+
+ ///
+ /// The icon for this item.
+ ///
+ public Sprite Icon
+ {
+ get => S1ItemDefinition.Icon;
+ set => S1ItemDefinition.Icon = value;
+ }
+
+ ///
+ /// Whether this item is available in the demo version of the game.
+ ///
+ public bool AvailableInDemo
+ {
+ get => S1ItemDefinition.AvailableInDemo;
+ set => S1ItemDefinition.AvailableInDemo = value;
+ }
+
+ ///
+ /// Legal status of the item (e.g., illegal drugs).
+ ///
+ public LegalStatus LegalStatus
+ {
+ get => (LegalStatus)S1ItemDefinition.legalStatus;
+ set => S1ItemDefinition.legalStatus = (S1ItemFramework.ELegalStatus)value;
+ }
+
+
+ ///
+ /// The color of the label shown in UI.
+ ///
+ public Color LabelDisplayColor
+ {
+ get => S1ItemDefinition.LabelDisplayColor;
+ set => S1ItemDefinition.LabelDisplayColor = value;
+ }
+
+ ///
+ /// Any keywords used to filter/search this item.
+ ///
+ public string[] Keywords
+ {
+ get => S1ItemDefinition.Keywords;
+ set => S1ItemDefinition.Keywords = value;
+ }
+
+ ///
+ /// Creates a new item instance with the specified quantity.
+ ///
+ public virtual ItemInstance CreateInstance(int quantity = 1)
+ {
+ var inst = S1ItemDefinition.GetDefaultInstance(quantity);
+ return new ItemInstance(inst);
+ }
+
+
+ public string GUID => ID;
+
+ public override bool Equals(object? obj) =>
+ obj is ItemDefinition other && S1ItemDefinition == other.S1ItemDefinition;
+
+ public override int GetHashCode() =>
+ S1ItemDefinition?.GetHashCode() ?? 0;
+
+ public static bool operator ==(ItemDefinition? a, ItemDefinition? b) =>
+ ReferenceEquals(a, b) || (a is not null && b is not null && a.S1ItemDefinition == b.S1ItemDefinition);
+
+ public static bool operator !=(ItemDefinition? a, ItemDefinition? b) =>
+ !(a == b);
+ }
+
+ ///
+ /// Represents the legal status of an item (e.g., legal or illegal).
+ ///
+ public enum LegalStatus
+ {
+ Legal,
+ Illegal,
+ // More if needed
+ }
+}
diff --git a/Items/ItemInstance.cs b/Items/ItemInstance.cs
new file mode 100644
index 00000000..df388f4e
--- /dev/null
+++ b/Items/ItemInstance.cs
@@ -0,0 +1,52 @@
+#if (IL2CPPMELON || IL2CPPBEPINEX)
+using S1ItemFramework = Il2CppScheduleOne.ItemFramework;
+#elif (MONOMELON || MONOBEPINEX)
+using S1ItemFramework = ScheduleOne.ItemFramework;
+#endif
+
+using S1API.Items;
+using S1API.Internal.Utils;
+
+namespace S1API.Items
+{
+ ///
+ /// Represents an item instance in the game world (physical item you own).
+ ///
+ public class ItemInstance
+ {
+ ///
+ /// INTERNAL: Reference to the in-game item instance.
+ ///
+ internal readonly S1ItemFramework.ItemInstance S1ItemInstance;
+
+ ///
+ /// INTERNAL: Creates an ItemInstance wrapper.
+ ///
+ /// In-game item instance
+ internal ItemInstance(S1ItemFramework.ItemInstance itemInstance) =>
+ S1ItemInstance = itemInstance;
+
+ // ====== Properties ======
+
+ ///
+ /// The definition (template) this instance was created from.
+ ///
+ public ItemDefinition Definition =>
+ new ItemDefinition(S1ItemInstance.Definition);
+
+ ///
+ /// Current quantity of this item (stacks).
+ ///
+ public int Quantity
+ {
+ get => S1ItemInstance.Quantity;
+ set => S1ItemInstance.SetQuantity(value);
+ }
+
+ ///
+ /// Whether this instance is stackable (based on StackLimit).
+ ///
+ public bool IsStackable =>
+ Definition.StackLimit > 1;
+ }
+}
diff --git a/Items/ItemManager.cs b/Items/ItemManager.cs
new file mode 100644
index 00000000..e886c6df
--- /dev/null
+++ b/Items/ItemManager.cs
@@ -0,0 +1,42 @@
+#if (IL2CPPMELON || IL2CPPBEPINEX)
+using S1 = Il2CppScheduleOne;
+using S1ItemFramework = Il2CppScheduleOne.ItemFramework;
+using S1Product = Il2CppScheduleOne.Product;
+#elif (MONOMELON || MONOBEPINEX)
+using S1 = ScheduleOne;
+using S1ItemFramework = ScheduleOne.ItemFramework;
+using S1Product = ScheduleOne.Product;
+#endif
+
+using S1API.Internal.Utils;
+using S1API.Money;
+using S1API.Products;
+
+namespace S1API.Items
+{
+ ///
+ /// Provides access to managing items across the game.
+ ///
+ public static class ItemManager
+ {
+ ///
+ /// Gets the definition of an item by its ID.
+ ///
+ /// The ID of the item.
+ /// An instance of the item definition.
+ public static ItemDefinition GetItemDefinition(string itemID)
+ {
+ S1ItemFramework.ItemDefinition itemDefinition = S1.Registry.GetItem(itemID);
+
+ if (CrossType.Is(itemDefinition,
+ out S1Product.ProductDefinition productDefinition))
+ return new ProductDefinition(productDefinition);
+
+ if (CrossType.Is(itemDefinition,
+ out S1ItemFramework.CashDefinition cashDefinition))
+ return new CashDefinition(cashDefinition);
+
+ return new ItemDefinition(itemDefinition);
+ }
+ }
+}
diff --git a/Items/ItemSlotInstance.cs b/Items/ItemSlotInstance.cs
new file mode 100644
index 00000000..ad51429d
--- /dev/null
+++ b/Items/ItemSlotInstance.cs
@@ -0,0 +1,70 @@
+#if (IL2CPPMELON || IL2CPPBEPINEX)
+using S1ItemFramework = Il2CppScheduleOne.ItemFramework;
+using S1Product = Il2CppScheduleOne.Product;
+#elif (MONOMELON || MONOBEPINEX)
+using S1ItemFramework = ScheduleOne.ItemFramework;
+using S1Product = ScheduleOne.Product;
+#endif
+
+using S1API.Internal.Utils;
+using S1API.Money;
+using S1API.Products;
+
+namespace S1API.Items
+{
+ ///
+ /// Represents an item slot within the game.
+ /// These are present within storage, the hot bar, etc.
+ ///
+ public class ItemSlotInstance
+ {
+ ///
+ /// INTERNAL: The reference to the item slot in the game.
+ ///
+ internal readonly S1ItemFramework.ItemSlot S1ItemSlot;
+
+ ///
+ /// Creates an item slot instance from the in game slot.
+ ///
+ ///
+ internal ItemSlotInstance(S1ItemFramework.ItemSlot itemSlot) =>
+ S1ItemSlot = itemSlot;
+
+ ///
+ /// The quantity of item in this slot.
+ ///
+ public int Quantity =>
+ S1ItemSlot.Quantity;
+
+ ///
+ /// The item instance the slot contains.
+ ///
+ public ItemInstance? ItemInstance
+ {
+ get
+ {
+ if (CrossType.Is(S1ItemSlot.ItemInstance,
+ out S1Product.ProductItemInstance productItemInstance))
+ return new ProductInstance(productItemInstance);
+
+ if (CrossType.Is(S1ItemSlot.ItemInstance,
+ out S1ItemFramework.CashInstance cashInstance))
+ return new CashInstance(cashInstance);
+
+ if (CrossType.Is(S1ItemSlot.ItemInstance,
+ out S1ItemFramework.ItemInstance itemInstance))
+ return new ItemInstance(itemInstance);
+
+ return null;
+ }
+ }
+
+ ///
+ /// Adds a quantity to the item in this slot.
+ /// NOTE: Negative numbers are supported and allowed.
+ ///
+ ///
+ public void AddQuantity(int amount) =>
+ S1ItemSlot.ChangeQuantity(amount);
+ }
+}
diff --git a/S1API/Growing/PlantInstance.cs b/S1API/Growing/PlantInstance.cs
new file mode 100644
index 00000000..b706b05d
--- /dev/null
+++ b/S1API/Growing/PlantInstance.cs
@@ -0,0 +1,77 @@
+#if (IL2CPPMELON)
+using S1Growing = Il2CppScheduleOne.Growing;
+#elif (MONOMELON || MONOBEPINEX || IL2CPPBEPINEX)
+using S1Growing = ScheduleOne.Growing;
+#endif
+
+using S1API.Internal.Utils;
+using S1API.Items;
+using UnityEngine;
+
+namespace S1API.Growing
+{
+ ///
+ /// Represents an instance of a growing plant in the world.
+ ///
+ public class PlantInstance
+ {
+ ///
+ /// INTERNAL: The in-game Plant object.
+ ///
+ internal readonly S1Growing.Plant S1Plant;
+
+ ///
+ /// INTERNAL: Create a wrapper around an existing Plant.
+ ///
+ /// The in-game Plant to wrap.
+ internal PlantInstance(S1Growing.Plant plant)
+ {
+ S1Plant = plant;
+ }
+
+ ///
+ /// The current growth stage as a float from 0.0 to 1.0.
+ ///
+ public float NormalizedGrowth =>
+ S1Plant.NormalizedGrowthProgress;
+
+ ///
+ /// Whether the plant is fully grown.
+ ///
+ public bool IsFullyGrown =>
+ S1Plant.IsFullyGrown;
+
+ ///
+ /// The SeedDefinition that this plant originated from.
+ ///
+ public SeedDefinition SeedDefinition =>
+ new SeedDefinition(S1Plant.SeedDefinition);
+
+ ///
+ /// The quality level of this plant.
+ ///
+ public float Quality =>
+ S1Plant.QualityLevel;
+
+ ///
+ /// The yield level (amount) of this plant.
+ ///
+ public float Yield =>
+ S1Plant.YieldLevel;
+
+ ///
+ /// The GameObject of the plant.
+ ///
+ private GameObject GameObject =>
+ S1Plant.gameObject;
+
+ ///
+ /// Destroys this plant in-game.
+ ///
+ /// Whether to drop trash scraps.
+ public void Destroy(bool dropScraps = false)
+ {
+ S1Plant.Destroy(dropScraps);
+ }
+ }
+}
diff --git a/S1API/Growing/SeedCreator.cs b/S1API/Growing/SeedCreator.cs
new file mode 100644
index 00000000..18b7a556
--- /dev/null
+++ b/S1API/Growing/SeedCreator.cs
@@ -0,0 +1,55 @@
+#if (IL2CPPMELON)
+using S1Growing = Il2CppScheduleOne.Growing;
+using S1ItemFramework = Il2CppScheduleOne.ItemFramework;
+using S1Registry = Il2CppScheduleOne.Registry;
+#elif (MONOMELON || MONOBEPINEX || IL2CPPBEPINEX)
+using S1Growing = ScheduleOne.Growing;
+using S1ItemFramework = ScheduleOne.ItemFramework;
+using S1Registry = ScheduleOne.Registry;
+#endif
+
+using UnityEngine;
+
+namespace S1API.Growing
+{
+ ///
+ /// The seed Creator for custom seeds to be added.
+ ///
+ public static class SeedCreator
+ {
+ public static SeedDefinition CreateSeed(
+ string id,
+ string name,
+ string description,
+ int stackLimit = 10,
+ GameObject? functionSeedPrefab = null,
+ GameObject? plantPrefab = null,
+ Sprite? icon = null)
+ {
+ S1Growing.SeedDefinition seed = ScriptableObject.CreateInstance();
+
+ seed.ID = id;
+ seed.Name = name;
+ seed.Description = description;
+ seed.StackLimit = stackLimit;
+ seed.Category = S1ItemFramework.EItemCategory.Growing;
+
+ // if (icon != null)
+ // {
+ // seed.Icon = icon;
+ // }
+ // commented out for more test later.
+
+ if (functionSeedPrefab != null)
+ seed.FunctionSeedPrefab = functionSeedPrefab.GetComponent();
+
+ if (plantPrefab != null)
+ seed.PlantPrefab = plantPrefab.GetComponent();
+
+ S1Registry.Instance.AddToRegistry(seed);
+
+ return new SeedDefinition(seed);
+ }
+
+ }
+}
diff --git a/S1API/Growing/SeedDefinition.cs b/S1API/Growing/SeedDefinition.cs
new file mode 100644
index 00000000..d3e0886c
--- /dev/null
+++ b/S1API/Growing/SeedDefinition.cs
@@ -0,0 +1,55 @@
+#if (IL2CPPMELON)
+using S1Growing = Il2CppScheduleOne.Growing;
+#elif (MONOMELON || MONOBEPINEX || IL2CPPBEPINEX)
+using S1Growing = ScheduleOne.Growing;
+#endif
+
+using System;
+using UnityEngine;
+
+using S1API.Internal.Utils;
+using S1API.Items;
+
+namespace S1API.Growing
+{
+ ///
+ /// Represents the definition of a Seed item (what you buy in shops).
+ ///
+ public class SeedDefinition : ItemDefinition
+ {
+ ///
+ /// INTERNAL: Stored reference to the SeedDefinition.
+ ///
+ internal S1Growing.SeedDefinition S1SeedDefinition =>
+ CrossType.As(S1ItemDefinition);
+
+ ///
+ /// INTERNAL: Create a new wrapper around an existing SeedDefinition.
+ ///
+ /// The in-game SeedDefinition to wrap.
+ internal SeedDefinition(S1Growing.SeedDefinition definition) : base(definition) { }
+
+ ///
+ /// The prefab that is spawned when planting this seed.
+ ///
+ public GameObject? FunctionalSeedPrefab => S1SeedDefinition.FunctionSeedPrefab?.gameObject;
+
+ ///
+ /// The plant prefab this seed grows into.
+ ///
+ public GameObject? PlantPrefab => S1SeedDefinition.PlantPrefab?.gameObject;
+
+ ///
+ /// Creates an instance of this seed in the world (FunctionalSeed prefab).
+ ///
+ public GameObject CreateSeedInstance()
+ {
+ if (S1SeedDefinition.FunctionSeedPrefab != null)
+ return UnityEngine.Object.Instantiate(S1SeedDefinition.FunctionSeedPrefab).gameObject;
+
+ throw new NullReferenceException("No FunctionalSeedPrefab assigned to this SeedDefinition!");
+ }
+
+
+ }
+}
diff --git a/S1API/Growing/SeedInstance.cs b/S1API/Growing/SeedInstance.cs
new file mode 100644
index 00000000..f9767b78
--- /dev/null
+++ b/S1API/Growing/SeedInstance.cs
@@ -0,0 +1,55 @@
+#if (IL2CPPMELON)
+using S1Growing = Il2CppScheduleOne.Growing;
+#elif (MONOMELON || MONOBEPINEX || IL2CPPBEPINEX)
+using S1Growing = ScheduleOne.Growing;
+#endif
+
+using UnityEngine;
+using S1API.Internal.Utils;
+
+namespace S1API.Growing
+{
+ ///
+ /// Represents an instance of a functional seed in the world.
+ /// (Not just the definition — this is the physical object you interact with.)
+ ///
+ public class SeedInstance
+ {
+ ///
+ /// INTERNAL: Reference to the in-game FunctionalSeed object.
+ ///
+ internal readonly S1Growing.FunctionalSeed S1FunctionalSeed;
+
+ ///
+ /// INTERNAL: Creates a wrapper around the existing FunctionalSeed.
+ ///
+ /// The FunctionalSeed object to wrap.
+ internal SeedInstance(S1Growing.FunctionalSeed functionalSeed)
+ {
+ S1FunctionalSeed = functionalSeed;
+ }
+
+ ///
+ /// The underlying GameObject of this seed.
+ ///
+ private GameObject GameObject =>
+ S1FunctionalSeed.gameObject;
+
+ ///
+ /// Whether the seed currently has exited its vial.
+ ///
+ public bool HasExitedVial { get; private set; } = false;
+
+ ///
+ /// Force the seed to exit the vial manually.
+ ///
+ public void ForceExitVial()
+ {
+ if (S1FunctionalSeed.Vial != null)
+ {
+ S1FunctionalSeed.TriggerExit(S1FunctionalSeed.Vial.GetComponent());
+ HasExitedVial = true;
+ }
+ }
+ }
+}
diff --git a/S1API/Items/ItemDefinition.cs b/S1API/Items/ItemDefinition.cs
index b4ba934e..24b6e213 100644
--- a/S1API/Items/ItemDefinition.cs
+++ b/S1API/Items/ItemDefinition.cs
@@ -4,115 +4,174 @@
using S1ItemFramework = ScheduleOne.ItemFramework;
#endif
+using UnityEngine;
using S1API.Internal.Abstraction;
namespace S1API.Items
{
///
/// Represents an item definition in-game.
- /// NOTE: A definition is "what" the item is. For example, "This is a `Soda`".
- /// Any instanced items in the game will be a instead.
+ /// Use this class to read and create new item definitions dynamically.
///
public class ItemDefinition : IGUIDReference
{
///
- /// INTERNAL: A reference to the item definition in the game.
+ /// INTERNAL: A reference to the native game item definition.
///
- internal readonly S1ItemFramework.ItemDefinition S1ItemDefinition;
+ internal S1ItemFramework.ItemDefinition S1ItemDefinition { get; }
///
- /// Creates a new item definition from the game item definition instance.
+ /// INTERNAL: Wraps an existing native item definition.
///
- ///
- internal ItemDefinition(S1ItemFramework.ItemDefinition s1ItemDefinition) =>
- S1ItemDefinition = s1ItemDefinition;
+ internal ItemDefinition(S1ItemFramework.ItemDefinition definition)
+ {
+ S1ItemDefinition = definition;
+ }
///
- /// INTERNAL: Gets an item definition from a GUID.
+ /// The unique ID of this item.
///
- /// The GUID to look for
- /// The applicable item definition, if found.
- internal static ItemDefinition GetFromGUID(string guid) =>
- ItemManager.GetItemDefinition(guid);
+ public string ID
+ {
+ get => S1ItemDefinition.ID;
+ set => S1ItemDefinition.ID = value;
+ }
///
- /// Performs an equals check on the game item definition instance.
+ /// The display name for this item.
///
- /// The item definition you want to compare against.
- /// Whether the item definitions are the same or not.
- public override bool Equals(object? obj) =>
- obj is ItemDefinition other && S1ItemDefinition == other.S1ItemDefinition;
+ public string Name
+ {
+ get => S1ItemDefinition.Name;
+ set => S1ItemDefinition.Name = value;
+ }
///
- /// Snags the hash code from the game instance versus this instance.
+ /// A short description for this item.
///
- /// The game intance hash code
- public override int GetHashCode() =>
- S1ItemDefinition?.GetHashCode() ?? 0;
+ public string Description
+ {
+ get => S1ItemDefinition.Description;
+ set => S1ItemDefinition.Description = value;
+ }
///
- /// Performs an == check on the game item definition instance.
+ /// Stack limit for this item (max quantity per slot).
///
- /// The first item definition to compare.
- /// The second item definition to compare.
- /// Whether the item definitions are the same or not.
- public static bool operator ==(ItemDefinition? left, ItemDefinition? right)
+ public int StackLimit
{
- if (ReferenceEquals(left, right)) return true;
- return left?.S1ItemDefinition == right?.S1ItemDefinition;
+ get => S1ItemDefinition.StackLimit;
+ set => S1ItemDefinition.StackLimit = value;
}
///
- /// Performs an != check on the game item definition instance.
+ /// The category for inventory sorting.
///
- /// The first item definition to compare.
- /// The second item definition to compare.
- /// Whether the item definitions are different or not.
- public static bool operator !=(ItemDefinition left, ItemDefinition right) =>
- !(left == right);
+ public ItemCategory Category
+ {
+ get => (ItemCategory)S1ItemDefinition.Category;
+ set => S1ItemDefinition.Category = (S1ItemFramework.EItemCategory)value;
+ }
///
- /// The unique identifier assigned to this item definition.
+ /// The icon for this item.
///
- public virtual string GUID =>
- S1ItemDefinition.ID;
+ public Sprite Icon
+ {
+ get => S1ItemDefinition.Icon;
+ set => S1ItemDefinition.Icon = value;
+ }
///
- /// The unique identifier assigned to this item definition.
+ /// Whether this item is available in the demo version of the game.
///
- public string ID =>
- S1ItemDefinition.ID;
+ public bool AvailableInDemo
+ {
+ get => S1ItemDefinition.AvailableInDemo;
+ set => S1ItemDefinition.AvailableInDemo = value;
+ }
///
- /// The display name for this item.
+ /// Legal status of the item (e.g., illegal drugs).
+ ///
+ public LegalStatus LegalStatus
+ {
+ get => (LegalStatus)S1ItemDefinition.legalStatus;
+ set => S1ItemDefinition.legalStatus = (S1ItemFramework.ELegalStatus)value;
+ }
+
+
+ ///
+ /// The color of the label shown in UI.
+ ///
+ public Color LabelDisplayColor
+ {
+ get => S1ItemDefinition.LabelDisplayColor;
+ set => S1ItemDefinition.LabelDisplayColor = value;
+ }
+
+ ///
+ /// Any keywords used to filter/search this item.
///
- public string Name =>
- S1ItemDefinition.Name;
+ public string[] Keywords
+ {
+ get => S1ItemDefinition.Keywords;
+ set => S1ItemDefinition.Keywords = value;
+ }
///
- /// The description used for this item.
+ /// Creates a new item instance with the specified quantity.
///
- public string Description =>
- S1ItemDefinition.Description;
+ public virtual ItemInstance CreateInstance(int quantity = 1)
+ {
+ var inst = S1ItemDefinition.GetDefaultInstance(quantity);
+ return new ItemInstance(inst);
+ }
///
- /// The category this item is assigned to.
+ /// Gets the globally unique identifier (GUID) of the item, which is equivalent to the ID.
///
- public ItemCategory Category =>
- (ItemCategory)S1ItemDefinition.Category;
+ public string GUID => ID;
///
- /// The stack limit for this item.
+ /// Determines whether the specified object is equal to the current object.
///
- public int StackLimit =>
- S1ItemDefinition.StackLimit;
+ /// The object to compare with the current object.
+ /// true if the specified object is an and has the same S1ItemDefinition; otherwise, false.
+ public override bool Equals(object? obj) =>
+ obj is ItemDefinition other && S1ItemDefinition == other.S1ItemDefinition;
///
- /// Creates an instance of this item from the definition.
+ /// Serves as the default hash function.
///
- /// How many of the item the instance will have.
- /// A new item instance within the game.
- public virtual ItemInstance CreateInstance(int quantity = 1) =>
- new ItemInstance(S1ItemDefinition.GetDefaultInstance(quantity));
+ /// A hash code for the current object based on S1ItemDefinition.
+ public override int GetHashCode() => S1ItemDefinition.GetHashCode();
+
+ ///
+ /// Determines whether two instances are equal.
+ ///
+ /// The first to compare.
+ /// The second to compare.
+ /// true if both instances are equal or have the same S1ItemDefinition; otherwise, false.
+ public static bool operator ==(ItemDefinition? a, ItemDefinition? b) =>
+ ReferenceEquals(a, b) || a != null && b != null && a.S1ItemDefinition == b.S1ItemDefinition;
+
+ ///
+ /// Determines whether two instances are not equal.
+ ///
+ /// The first to compare.
+ /// The second to compare.
+ /// true if the instances are not equal; otherwise, false.
+ public static bool operator !=(ItemDefinition? a, ItemDefinition? b) => !(a == b);
+ }
+
+ ///
+ /// Represents the legal status of an item (e.g., legal or illegal).
+ ///
+ public enum LegalStatus
+ {
+ Legal,
+ Illegal,
+ // More if needed
}
}
diff --git a/S1API/Items/ItemInstance.cs b/S1API/Items/ItemInstance.cs
index 3f9bb0c2..d3776c63 100644
--- a/S1API/Items/ItemInstance.cs
+++ b/S1API/Items/ItemInstance.cs
@@ -4,32 +4,49 @@
using S1ItemFramework = ScheduleOne.ItemFramework;
#endif
+using S1API.Items;
+using S1API.Internal.Utils;
namespace S1API.Items
{
///
- /// Represents an item instance in the game.
- /// NOTE: A instance is the item existing in the game world. For example, "I have five sodas in my hand.".
- /// The definition for items in the game will be a instead.
+ /// Represents an item instance in the game world (physical item you own).
///
public class ItemInstance
{
///
- /// INTERNAL: The reference to the instance of this item.
+ /// INTERNAL: Reference to the in-game item instance.
///
internal readonly S1ItemFramework.ItemInstance S1ItemInstance;
///
- /// INTERNAL: Creates an item instance
+ /// INTERNAL: Creates an ItemInstance wrapper.
///
- /// The instance of the item instance in-game.
+ /// In-game item instance
internal ItemInstance(S1ItemFramework.ItemInstance itemInstance) =>
S1ItemInstance = itemInstance;
+ // ====== Properties ======
+
///
- /// The item definition of this item.
+ /// The definition (template) this instance was created from.
///
public ItemDefinition Definition =>
new ItemDefinition(S1ItemInstance.Definition);
+
+ ///
+ /// Current quantity of this item (stacks).
+ ///
+ public int Quantity
+ {
+ get => S1ItemInstance.Quantity;
+ set => S1ItemInstance.SetQuantity(value);
+ }
+
+ ///
+ /// Whether this instance is stackable (based on StackLimit).
+ ///
+ public bool IsStackable =>
+ Definition.StackLimit > 1;
}
}
diff --git a/S1API/S1API.csproj b/S1API/S1API.csproj
index 51ca67e3..cc105058 100644
--- a/S1API/S1API.csproj
+++ b/S1API/S1API.csproj
@@ -56,6 +56,7 @@
+
@@ -73,6 +74,7 @@
+
@@ -90,7 +92,7 @@
-
+