Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom Crops #103

Merged
merged 11 commits into from
Jan 15, 2025
2 changes: 1 addition & 1 deletion COTL_API.Common.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<TargetFramework>net472</TargetFramework>
<Version>0.2.6</Version>
<Version>0.2.7</Version>
<LangVersion>latest</LangVersion>
<DebugType>portable</DebugType>
<IsPackable>true</IsPackable>
Expand Down
39 changes: 39 additions & 0 deletions COTL_API/CustomInventory/CustomCrops/CustomCrop.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// The States of this crop, the last state should be the fully grown state. Requires at least two states.
/// </summary>
public virtual List<Sprite> CropStates { get; } = [];

/// <summary>
/// The time it takes for this crop to grow, in game ticks.
/// </summary>
public virtual float CropGrowthTime => 9f;

/// <summary>
/// The Crop and Seed that drops when this is harvested. Must be size 2
/// </summary>
public abstract List<InventoryItem.ITEM_TYPE> HarvestResult { get; }

/// <summary>
/// How long (in seconds) it takes to pick this crop.
/// </summary>
public virtual float PickingTime => 2.5f;

/// <summary>
/// The range in how many resources will drop when collecting.
/// </summary>
public virtual Vector2Int CropCountToDropRange => new(3, 4);

/// <summary>
/// Shows when hovering the crop to harvest it.
/// </summary>
public virtual string HarvestText => "Pick <color=#FD1D03>Berries</color>";
}
99 changes: 99 additions & 0 deletions COTL_API/CustomInventory/CustomCrops/CustomCropManager.cs
Original file line number Diff line number Diff line change
@@ -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<InventoryItem.ITEM_TYPE, CustomCrop> CustomCropList { get; } = [];
private static Dictionary<InventoryItem.ITEM_TYPE, CropController> 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<StructureBrain.TYPES>(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>();

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
InfernoDragon0 marked this conversation as resolved.
Show resolved Hide resolved
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)
zelzmiy marked this conversation as resolved.
Show resolved Hide resolved
{
bush.GetComponent<SpriteRenderer>().sprite = crop.CropStates.Last();
bumperBush.GetComponent<SpriteRenderer>().sprite = crop.CropStates.Last();
cropState.GetComponent<SpriteRenderer>().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<SpriteRenderer>().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<CropController>());
}

public static void InitiateCustomCrops()
{
LogInfo("Getting Crop Asset");
var op = Addressables.Instance.LoadAssetAsync<GameObject>(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));
};
}
}
92 changes: 92 additions & 0 deletions COTL_API/CustomInventory/CustomCrops/CustomCropPatches.cs
Original file line number Diff line number Diff line change
@@ -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

Check warning on line 45 in COTL_API/CustomInventory/CustomCrops/CustomCropPatches.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

Harmony non-ref patch parameter Type = crop.StructureType modified. This assignment have no effect.

Check warning on line 45 in COTL_API/CustomInventory/CustomCrops/CustomCropPatches.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

Harmony non-ref patch parameter Type = crop.StructureType modified. This assignment have no effect.

Check warning on line 45 in COTL_API/CustomInventory/CustomCrops/CustomCropPatches.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Harmony non-ref patch parameter Type = crop.StructureType modified. This assignment have no effect.

Check warning on line 45 in COTL_API/CustomInventory/CustomCrops/CustomCropPatches.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Harmony non-ref patch parameter Type = crop.StructureType modified. This assignment have no effect.
};
}

[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<CropController>();

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<CropController>();
if (cropController == null) return;
if (!CustomCropList.TryGetValue(cropController.SeedType, out var crop)) return;

__instance.sLabelName = crop.HarvestText;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/// <summary>
/// The amount of Sin gained by the follower. Range: 0-65
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public abstract class CustomMeal : CustomFood
/// </summary>
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;
}
Expand Down
2 changes: 0 additions & 2 deletions COTL_API/CustomInventory/CustomInventoryItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions COTL_API/CustomInventory/Patches/CustomInventoryPatches.cs
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ private static bool InventoryItem_CapacityString(InventoryItem.ITEM_TYPE type, i
[HarmonyPostfix]
private static void InventoryItem_AllPlantables(ref List<InventoryItem.ITEM_TYPE> __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))]
Expand All @@ -239,7 +239,7 @@ private static bool InventoryItem_GiveToFollowerCallbacks(InventoryItem.ITEM_TYP
[HarmonyPostfix]
private static void InventoryItem_AllSeeds(ref List<InventoryItem.ITEM_TYPE> __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)]
Expand Down
9 changes: 7 additions & 2 deletions COTL_API/Debug/DebugItemClass2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<InventoryItem.ITEM_TYPE> HarvestResult { get; } =
[
Plugin.Instance.DebugItem,

Check warning on line 15 in COTL_API/Debug/DebugItemClass2.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

Dereference of a possibly null reference.

Check warning on line 15 in COTL_API/Debug/DebugItemClass2.cs

View workflow job for this annotation

GitHub Actions / build (macos-latest)

Dereference of a possibly null reference.

Check warning on line 15 in COTL_API/Debug/DebugItemClass2.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Dereference of a possibly null reference.

Check warning on line 15 in COTL_API/Debug/DebugItemClass2.cs

View workflow job for this annotation

GitHub Actions / build (windows-latest)

Dereference of a possibly null reference.
Plugin.Instance.DebugItem2,
];
}
1 change: 0 additions & 1 deletion COTL_API/Debug/DebugItemClass3.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@ public class DebugItemClass3 : CustomInventoryItem
{
public override string InternalName => "DEBUG_ITEM_3";

public override bool IsPlantable => true;
}
3 changes: 1 addition & 2 deletions COTL_API/Debug/DebugItemClass4.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 4 additions & 1 deletion COTL_API/Plugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
{
Expand Down
Loading