diff --git a/COTL_API.Common.props b/COTL_API.Common.props index 02b37a8..18274d7 100644 --- a/COTL_API.Common.props +++ b/COTL_API.Common.props @@ -1,7 +1,7 @@ net472 - 0.2.6 + 0.2.7 latest portable true diff --git a/COTL_API/CustomInventory/CustomCrops/CustomCrop.cs b/COTL_API/CustomInventory/CustomCrops/CustomCrop.cs new file mode 100644 index 0000000..9b201e2 --- /dev/null +++ b/COTL_API/CustomInventory/CustomCrops/CustomCrop.cs @@ -0,0 +1,39 @@ +using UnityEngine; + +namespace COTL_API.CustomInventory; + +public abstract class CustomCrop : CustomInventoryItem +{ + internal StructureBrain.TYPES StructureType { get; set; } + internal int CropStatesCount => CropStates.Count; + + /// + /// The States of this crop, the last state should be the fully grown state. Requires at least two states. + /// + public virtual List CropStates { get; } = []; + + /// + /// The time it takes for this crop to grow, in game ticks. + /// + public virtual float CropGrowthTime => 9f; + + /// + /// The Crop and Seed that drops when this is harvested. Must be size 2 + /// + public abstract List HarvestResult { get; } + + /// + /// How long (in seconds) it takes to pick this crop. + /// + public virtual float PickingTime => 2.5f; + + /// + /// The range in how many resources will drop when collecting. + /// + public virtual Vector2Int CropCountToDropRange => new(3, 4); + + /// + /// Shows when hovering the crop to harvest it. + /// + public virtual string HarvestText => "Pick Berries"; +} \ No newline at end of file diff --git a/COTL_API/CustomInventory/CustomCrops/CustomCropManager.cs b/COTL_API/CustomInventory/CustomCrops/CustomCropManager.cs new file mode 100644 index 0000000..2642e84 --- /dev/null +++ b/COTL_API/CustomInventory/CustomCrops/CustomCropManager.cs @@ -0,0 +1,99 @@ +using COTL_API.Guid; +using HarmonyLib; +using UnityEngine; +using UnityEngine.AddressableAssets; +using UnityEngine.ResourceManagement.AsyncOperations; +using static UnityEngine.Object; + +namespace COTL_API.CustomInventory; + +public static partial class CustomItemManager +{ + internal static GameObject CropPrefab = null!; + + public static Dictionary CustomCropList { get; } = []; + private static Dictionary CropObjectList { get; } = []; + + private const string AssetPath = "Prefabs/Structures/Crops/Berry Crop"; + + public static InventoryItem.ITEM_TYPE Add(CustomCrop crop) + { + var item = Add(crop as CustomInventoryItem); + crop.ItemType = item; + crop.StructureType = + GuidManager.GetEnumValue(CustomItemList[item].ModPrefix, crop.InternalName); + + CustomCropList.Add(item, crop); + + return item; + } + + private static void CreateCropObject(CustomCrop crop) + { + if (CropPrefab == null) + throw new NullReferenceException("This REALLY shouldn't happen, send a bug report!"); + + var duplicate = Instantiate(CropPrefab); + + if (duplicate == null) + throw new NullReferenceException("Somehow, the Crop Prefab could not be instantiated, send a bug report"); + + duplicate.transform.name = $"{crop.Name()} Crop"; + + var cropController = duplicate.GetComponent(); + + cropController.CropStates = []; + cropController.SeedType = crop.ItemType; + + // remove extra growth stages in case there are less than 4 + DestroyImmediate(duplicate.transform.GetChild(1).gameObject); // Stage 2 + DestroyImmediate(duplicate.transform.GetChild(1).gameObject); // Stage 3 + DestroyImmediate(duplicate.transform.GetChild(1).gameObject); // Stage 4 + + var harvest = duplicate.transform.GetChild(1); + var bush = harvest.GetChild(2).GetChild(0).GetChild(0); + + var bumperHarvest = duplicate.transform.GetChild(2); + var bumperBush = bumperHarvest.GetChild(3).GetChild(0).GetChild(0); + + var cropState = duplicate.transform.GetChild(0); + cropController.CropStates.Add(cropState.gameObject); + + if (crop.CropStates.Count > 0) + { + bush.GetComponent().sprite = crop.CropStates.Last(); + bumperBush.GetComponent().sprite = crop.CropStates.Last(); + cropState.GetComponent().sprite = crop.CropStates[0]; + } + + for (var i = 1; i < crop.CropStates.Count - 1; i++) + { + var newState = Instantiate(cropState, duplicate.transform); + newState.name = $"Crop {i + 1}"; + newState.SetSiblingIndex(i); + newState.GetComponent().sprite = crop.CropStates[i]; + cropController.CropStates.Add(newState.gameObject); + } + + cropController.CropStates.Add(harvest.gameObject); + + // Ensures that the object doesn't get deleted between scene loads + duplicate.hideFlags = HideFlags.HideAndDontSave; + + CropObjectList.Add(crop.ItemType, duplicate.GetComponent()); + } + + public static void InitiateCustomCrops() + { + LogInfo("Getting Crop Asset"); + var op = Addressables.Instance.LoadAssetAsync(AssetPath); + op.Completed += (handle) => + { + if (op.Status != AsyncOperationStatus.Succeeded) + throw new NullReferenceException("Couldn't Find Berry Crop Object, Send a bug report!"); + + CropPrefab = handle.Result; + CustomCropList.Do(x => CreateCropObject(x.Value)); + }; + } +} \ No newline at end of file diff --git a/COTL_API/CustomInventory/CustomCrops/CustomCropPatches.cs b/COTL_API/CustomInventory/CustomCrops/CustomCropPatches.cs new file mode 100644 index 0000000..2738085 --- /dev/null +++ b/COTL_API/CustomInventory/CustomCrops/CustomCropPatches.cs @@ -0,0 +1,92 @@ +using HarmonyLib; +using MonoMod.Utils; +using UnityEngine; +using Object = UnityEngine.Object; + +namespace COTL_API.CustomInventory; + +[HarmonyPatch] +public static partial class CustomItemManager +{ + [HarmonyPatch(typeof(CropController), nameof(CropController.CropStatesForSeedType))] + [HarmonyPostfix] + private static void CropController_CropStatesForSeedType(InventoryItem.ITEM_TYPE seedType, ref int __result) + { + if (!CustomCropList.TryGetValue(seedType, out var item)) return; + + __result = item.CropStatesCount; + } + + [HarmonyPatch(typeof(CropController), nameof(CropController.CropGrowthTimes))] + [HarmonyPostfix] + private static void CropController_CropGrowthTimes(InventoryItem.ITEM_TYPE seedType, ref float __result) + { + if (!CustomCropList.TryGetValue(seedType, out var item)) return; + + __result = item.CropGrowthTime; + } + + + [HarmonyPatch(typeof(StructuresData), nameof(StructuresData.GetInfoByType))] + [HarmonyPostfix] + private static void StructureData_GetInfoByType(StructureBrain.TYPES Type, ref StructuresData __result) + { + if (CustomCropList.Values.All(x => x.StructureType != Type)) return; + + var crop = CustomCropList.Values.First(x => x.StructureType == Type); + // Not sure that this is necessary but just in case? + __result = new StructuresData + { + PrefabPath = "Prefabs/Structures/Other/Berry Bush", + DontLoadMe = true, + ProgressTarget = crop.PickingTime, + MultipleLootToDrop = crop.HarvestResult, + LootCountToDropRange = crop.CropCountToDropRange, + Type = crop.StructureType + }; + } + + [HarmonyPatch(typeof(StructureBrain), nameof(StructureBrain.CreateBrain))] + [HarmonyPostfix] + private static void StructureBrain_CreateBrain(StructuresData data, ref StructureBrain __result) + { + if (CustomCropList.Values.All(x => x.StructureType != data.Type)) return; + + __result = new Structures_BerryBush(); + } + + [HarmonyPatch(typeof(FarmPlot), nameof(FarmPlot.Awake))] + [HarmonyPostfix] + private static void FarmPlot_Awake(FarmPlot __instance) + { + foreach(var kvp in CropObjectList) + { + __instance._cropPrefabsBySeedType.Add(kvp.Key, kvp.Value); + } + } + + [HarmonyPatch(typeof(Interaction_Berries), nameof(Interaction_Berries.OnBrainAssigned))] + [HarmonyPostfix] + private static void Interaction_Berries_OnBrainAssigned(Interaction_Berries __instance) + { + var cropController = __instance.GetComponentInParent(); + + if (cropController == null) return; + if (!CustomCropList.TryGetValue(cropController.SeedType, out var crop)) return; + + __instance.StructureBrain.Data.MultipleLootToDrop = crop.HarvestResult; + __instance.StructureBrain.Data.LootCountToDropRange = crop.CropCountToDropRange; + __instance.BerryPickingIncrements = 1.25f / crop.PickingTime; + } + + [HarmonyPatch(typeof(Interaction_Berries), nameof(Interaction_Berries.UpdateLocalisation))] + [HarmonyPostfix] + private static void Interaction_Berries_UpdateLocalisation(Interaction_Berries __instance) + { + var cropController = __instance.GetComponentInParent(); + if (cropController == null) return; + if (!CustomCropList.TryGetValue(cropController.SeedType, out var crop)) return; + + __instance.sLabelName = crop.HarvestText; + } +} \ No newline at end of file diff --git a/COTL_API/CustomInventory/CustomFood/CustomDrinks/CustomDrink.cs b/COTL_API/CustomInventory/CustomFood/CustomDrinks/CustomDrink.cs index 54c37dd..07c6f9c 100644 --- a/COTL_API/CustomInventory/CustomFood/CustomDrinks/CustomDrink.cs +++ b/COTL_API/CustomInventory/CustomFood/CustomDrinks/CustomDrink.cs @@ -3,7 +3,7 @@ namespace COTL_API.CustomInventory; public abstract class CustomDrink : CustomFood { internal FollowerBrain.PleasureActions PleasureAction { get; set; } - public override InventoryItem.ITEM_TYPE ItemPickUpToImitate { get; } = InventoryItem.ITEM_TYPE.DRINK_GIN; + public sealed override InventoryItem.ITEM_TYPE ItemPickUpToImitate { get; } = InventoryItem.ITEM_TYPE.DRINK_GIN; /// /// The amount of Sin gained by the follower. Range: 0-65 diff --git a/COTL_API/CustomInventory/CustomFood/CustomMeals/CustomMeal.cs b/COTL_API/CustomInventory/CustomFood/CustomMeals/CustomMeal.cs index 2aaf8e1..dac1b6c 100644 --- a/COTL_API/CustomInventory/CustomFood/CustomMeals/CustomMeal.cs +++ b/COTL_API/CustomInventory/CustomFood/CustomMeals/CustomMeal.cs @@ -8,7 +8,7 @@ public abstract class CustomMeal : CustomFood /// public abstract float TummyRating { get; } - public override InventoryItem.ITEM_TYPE ItemPickUpToImitate { get; } = InventoryItem.ITEM_TYPE.MEAL; + public sealed override InventoryItem.ITEM_TYPE ItemPickUpToImitate { get; } = InventoryItem.ITEM_TYPE.MEAL; public virtual MealQuality Quality { get; } = MealQuality.NORMAL; public virtual bool MealSafeToEat { get; } = true; } diff --git a/COTL_API/CustomInventory/CustomInventoryItem.cs b/COTL_API/CustomInventory/CustomInventoryItem.cs index 7fd274e..7e8f939 100644 --- a/COTL_API/CustomInventory/CustomInventoryItem.cs +++ b/COTL_API/CustomInventory/CustomInventoryItem.cs @@ -29,8 +29,6 @@ public abstract class CustomInventoryItem public virtual bool IsFood => false; public virtual bool IsBigFish => false; public virtual bool IsCurrency => false; - public virtual bool IsSeed => false; - public virtual bool IsPlantable => false; public virtual bool IsBurnableFuel => false; public virtual bool CanBeGivenToFollower => false; diff --git a/COTL_API/CustomInventory/Patches/CustomInventoryPatches.cs b/COTL_API/CustomInventory/Patches/CustomInventoryPatches.cs index 10bacef..63cb612 100644 --- a/COTL_API/CustomInventory/Patches/CustomInventoryPatches.cs +++ b/COTL_API/CustomInventory/Patches/CustomInventoryPatches.cs @@ -217,7 +217,7 @@ private static bool InventoryItem_CapacityString(InventoryItem.ITEM_TYPE type, i [HarmonyPostfix] private static void InventoryItem_AllPlantables(ref List __result) { - __result.AddRange(CustomItemList.Where(x => x.Value.IsPlantable).Select(x => x.Key)); + __result.AddRange(CustomCropList.Select(x => x.Key)); } [HarmonyPatch(typeof(InventoryItem), nameof(InventoryItem.GiveToFollowerCallbacks))] @@ -239,7 +239,7 @@ private static bool InventoryItem_GiveToFollowerCallbacks(InventoryItem.ITEM_TYP [HarmonyPostfix] private static void InventoryItem_AllSeeds(ref List __result) { - __result.AddRange(CustomItemList.Where(x => x.Value.IsSeed).Select(x => x.Key)); + __result.AddRange(CustomCropList.Select(x => x.Key)); } [HarmonyPatch(typeof(InventoryItem), nameof(InventoryItem.AllBurnableFuel), MethodType.Getter)] diff --git a/COTL_API/Debug/DebugItemClass2.cs b/COTL_API/Debug/DebugItemClass2.cs index e7d6ac8..0ecf4d7 100644 --- a/COTL_API/Debug/DebugItemClass2.cs +++ b/COTL_API/Debug/DebugItemClass2.cs @@ -2,12 +2,17 @@ namespace COTL_API.Debug; -public class DebugItemClass2 : CustomInventoryItem +public class DebugItemClass2 : CustomCrop { public override string InternalName => "DEBUG_ITEM_2"; public override CustomInventoryItemType InventoryItemType => CustomInventoryItemType.FOOD; public override bool IsFood => true; - public override bool IsSeed => true; + + public override List HarvestResult { get; } = + [ + Plugin.Instance.DebugItem, + Plugin.Instance.DebugItem2, + ]; } \ No newline at end of file diff --git a/COTL_API/Debug/DebugItemClass3.cs b/COTL_API/Debug/DebugItemClass3.cs index 30fd486..34ff69a 100644 --- a/COTL_API/Debug/DebugItemClass3.cs +++ b/COTL_API/Debug/DebugItemClass3.cs @@ -6,5 +6,4 @@ public class DebugItemClass3 : CustomInventoryItem { public override string InternalName => "DEBUG_ITEM_3"; - public override bool IsPlantable => true; } \ No newline at end of file diff --git a/COTL_API/Debug/DebugItemClass4.cs b/COTL_API/Debug/DebugItemClass4.cs index b3da21f..181d7f5 100644 --- a/COTL_API/Debug/DebugItemClass4.cs +++ b/COTL_API/Debug/DebugItemClass4.cs @@ -6,8 +6,7 @@ namespace COTL_API.Debug; public class DebugItemClass4 : CustomInventoryItem { public override string InternalName => "DEBUG_ITEM_4"; - - public override bool IsPlantable => true; + public override InventoryItem.ITEM_TYPE ItemPickUpToImitate => InventoryItem.ITEM_TYPE.BLACK_GOLD; public override CustomItemManager.ItemRarity Rarity => CustomItemManager.ItemRarity.COMMON; diff --git a/COTL_API/Plugin.cs b/COTL_API/Plugin.cs index 005b2c2..631292e 100644 --- a/COTL_API/Plugin.cs +++ b/COTL_API/Plugin.cs @@ -251,7 +251,10 @@ private void OnDisable() LogInfo($"{MyPluginInfo.PLUGIN_NAME} unloaded!"); } - internal static event Action OnStart = delegate { }; + internal static event Action OnStart = delegate + { + CustomItemManager.InitiateCustomCrops(); + }; private void RunSavePatch() {