Decompiled source of Persistent Nutrients v1.0.1

PersistentNutrients.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using HarmonyLib;
using Il2CppFishNet.Object;
using Il2CppInterop.Runtime.InteropTypes;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppScheduleOne.Employees;
using Il2CppScheduleOne.EntityFramework;
using Il2CppScheduleOne.Growing;
using Il2CppScheduleOne.ItemFramework;
using Il2CppScheduleOne.ObjectScripts;
using Il2CppScheduleOne.Persistence;
using Il2CppSystem;
using Il2CppSystem.Collections.Generic;
using MelonLoader;
using MelonLoader.Utils;
using Newtonsoft.Json;
using PersistentNutrients;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(global::PersistentNutrients.PersistentNutrients), "PersistentNutrients Mod", "1.0.1", "Shootex20", null)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("0.0.0.0")]
namespace PersistentNutrients;

public class PersistentNutrients : MelonMod
{
	[HarmonyPatch(typeof(Pot), "OnPlantFullyHarvested")]
	public static class OnPlantFullyHarvested_Patch
	{
		[HarmonyPrefix]
		public static void Prefix(Pot __instance)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			_ = ((BuildableItem)__instance).GUID;
			if (0 == 0)
			{
				Guid gUID = ((BuildableItem)__instance).GUID;
				Guid guid = new Guid(Il2CppArrayBase<byte>.op_Implicit((Il2CppArrayBase<byte>)(object)((Guid)(ref gUID)).ToByteArray()));
				if (((GrowContainer)__instance)._remainingSoilUses > 0 && ((GrowContainer)__instance).AppliedAdditives != null && (Object)(object)__instance.Plant != (Object)null)
				{
					SaveFertilizers(__instance, guid);
				}
				else if (((GrowContainer)__instance)._remainingSoilUses <= 0 && persistentFertilizers.ContainsKey(guid))
				{
					persistentFertilizers.Remove(guid);
					MelonLogger.Msg($"[PersistentNutrientsMod] Soil depleted, cleared fertilizer data for pot {guid}");
				}
			}
		}
	}

	[HarmonyPatch(typeof(Pot), "PlantSeed_Server")]
	public static class PlantSeed_Patch
	{
		[CompilerGenerated]
		private sealed class <WaitAndRestore>d__1 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public Pot pot;

			public SavedPotFertilizerData fertData;

			public Guid potGuid;

			private int <attempts>5__1;

			private int <i>5__2;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <WaitAndRestore>d__1(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<attempts>5__1 = 0;
					goto IL_0060;
				case 1:
					<>1__state = -1;
					<attempts>5__1++;
					goto IL_0060;
				case 2:
					{
						<>1__state = -1;
						<i>5__2++;
						goto IL_00ea;
					}
					IL_0060:
					if (<attempts>5__1 < 100 && ((Object)(object)pot == (Object)null || (Object)(object)pot.Plant == (Object)null))
					{
						<>2__current = null;
						<>1__state = 1;
						return true;
					}
					if ((Object)(object)pot != (Object)null && (Object)(object)pot.Plant != (Object)null)
					{
						<i>5__2 = 0;
						goto IL_00ea;
					}
					MelonLogger.Warning($"[PersistentNutrients Mod] Failed to restore fertilizers for pot {potGuid} - Plant not created after {<attempts>5__1} frames");
					break;
					IL_00ea:
					if (<i>5__2 < 10)
					{
						<>2__current = null;
						<>1__state = 2;
						return true;
					}
					RestoreFertilizers(pot, fertData);
					break;
				}
				return false;
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[HarmonyPostfix]
		public static void Postfix(Pot __instance, string seedID, float normalizedSeedProgress)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			_ = ((BuildableItem)__instance).GUID;
			if (((NetworkBehaviour)__instance).IsServer)
			{
				Guid gUID = ((BuildableItem)__instance).GUID;
				Guid guid = new Guid(Il2CppArrayBase<byte>.op_Implicit((Il2CppArrayBase<byte>)(object)((Guid)(ref gUID)).ToByteArray()));
				if (persistentFertilizers.TryGetValue(guid, out var value))
				{
					MelonLogger.Msg($"[PersistentNutrients Mod] Found saved fertilizer data for pot {guid}, scheduling restoration...");
					MelonCoroutines.Start(WaitAndRestore(__instance, value, guid));
				}
			}
		}

		[IteratorStateMachine(typeof(<WaitAndRestore>d__1))]
		private static IEnumerator WaitAndRestore(Pot pot, SavedPotFertilizerData fertData, Guid potGuid)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <WaitAndRestore>d__1(0)
			{
				pot = pot,
				fertData = fertData,
				potGuid = potGuid
			};
		}
	}

	[HarmonyPatch(typeof(Pot), "CanApplyAdditive")]
	public static class CanApplyAdditive_Patch
	{
		[HarmonyPrefix]
		public static bool Prefix(Pot __instance, AdditiveDefinition additiveDef, ref string invalidReason, ref bool __result)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)__instance == (Object)null))
			{
				_ = ((BuildableItem)__instance).GUID;
				if (!((Object)(object)additiveDef == (Object)null))
				{
					if (!((ItemDefinition)additiveDef).Name.ToLower().Contains("fertilizer"))
					{
						return true;
					}
					Guid gUID = ((BuildableItem)__instance).GUID;
					Guid key = new Guid(Il2CppArrayBase<byte>.op_Implicit((Il2CppArrayBase<byte>)(object)((Guid)(ref gUID)).ToByteArray()));
					if (persistentFertilizers.ContainsKey(key))
					{
						invalidReason = "This soil is already fertilized!";
						__result = false;
						return false;
					}
					return true;
				}
			}
			return true;
		}
	}

	[HarmonyPatch(typeof(Botanist), "GetGrowContainersForAdditives")]
	public static class BotanistGetGrowContainersForAdditives_Patch
	{
		[HarmonyPostfix]
		public static void Postfix(ref List<GrowContainer> __result)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			if (__result == null || __result.Count == 0)
			{
				return;
			}
			List<GrowContainer> list = new List<GrowContainer>();
			Enumerator<GrowContainer> enumerator = __result.GetEnumerator();
			while (enumerator.MoveNext())
			{
				GrowContainer current = enumerator.Current;
				if ((Object)(object)current == (Object)null)
				{
					continue;
				}
				_ = ((BuildableItem)current).GUID;
				if (false)
				{
					continue;
				}
				Pot val = ((Il2CppObjectBase)current).TryCast<Pot>();
				if ((Object)(object)val != (Object)null)
				{
					Guid gUID = ((BuildableItem)val).GUID;
					Guid guid = new Guid(Il2CppArrayBase<byte>.op_Implicit((Il2CppArrayBase<byte>)(object)((Guid)(ref gUID)).ToByteArray()));
					if (persistentFertilizers.ContainsKey(guid))
					{
						list.Add(current);
						MelonLogger.Msg($"[PersistentNutrients Mod] Prevented botanist from fertilizing pot {guid} - soil already fertilized");
					}
				}
			}
			foreach (GrowContainer item in list)
			{
				__result.Remove(item);
			}
		}
	}

	[HarmonyPatch(typeof(SaveManager), "Save", new Type[] { typeof(string) })]
	public static class SaveManager_SaveWithPath_Patch
	{
		[HarmonyPrefix]
		public static void Prefix(string saveFolderPath)
		{
			MelonLogger.Msg("[PersistentNutrients Mod] Game saving, persisting fertilizer data...");
			SaveFertilizerData();
		}
	}

	[HarmonyPatch(typeof(SaveManager), "Save", new Type[] { })]
	public static class SaveManager_SaveNoArgs_Patch
	{
		[HarmonyPrefix]
		public static void Prefix()
		{
			MelonLogger.Msg("[PersistentNutrients Mod] Game saving, persisting fertilizer data...");
			SaveFertilizerData();
		}
	}

	[HarmonyPatch(typeof(LoadManager), "StartGame")]
	public static class LoadManager_StartGame_Patch
	{
		[HarmonyPostfix]
		public static void Postfix(SaveInfo info, bool allowLoadStacking)
		{
			if (DebugMode)
			{
				MelonLogger.Msg("[PersistentNutrients Mod] LoadManager.StartGame Postfix triggered.");
			}
			if (info != null)
			{
				string text = $"SaveGame_{info.SaveSlotNumber}";
				if (DebugMode)
				{
					MelonLogger.Msg("[PersistentNutrients Mod] Using derived slotIdentifier: '" + text + "'");
				}
				currentSaveSlot = text;
				LoadFertilizerData();
			}
			else
			{
				if (DebugMode)
				{
					MelonLogger.Msg("[PersistentNutrients Mod] LoadManager.StartGame: SaveInfo was null. Using 'NewGame_Initial'.");
				}
				currentSaveSlot = "NewGame_Initial";
				persistentFertilizers.Clear();
			}
		}
	}

	private static readonly Dictionary<Guid, SavedPotFertilizerData> persistentFertilizers = new Dictionary<Guid, SavedPotFertilizerData>();

	private static string currentSaveSlot = "default_slot";

	public static bool DebugMode = false;

	private static string DataFilePath
	{
		get
		{
			string text = currentSaveSlot;
			char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
			foreach (char oldChar in invalidFileNameChars)
			{
				text = text.Replace(oldChar, '_');
			}
			return Path.Combine(MelonEnvironment.UserDataDirectory, "PersistentNutrients_Fertilizers_" + text + ".json");
		}
	}

	public override void OnInitializeMelon()
	{
		((MelonBase)this).LoggerInstance.Msg("PersistentNutrients Mod initialized - Fertilizer will now persist with soil!");
	}

	public override void OnApplicationQuit()
	{
		SaveFertilizerData();
	}

	private static void SaveFertilizers(Pot pot, Guid potGuid)
	{
		if (((GrowContainer)pot).AppliedAdditives == null || ((GrowContainer)pot).AppliedAdditives.Count == 0 || (Object)(object)pot.Plant == (Object)null)
		{
			return;
		}
		List<AdditiveData> list = new List<AdditiveData>();
		bool flag = false;
		Enumerator<AdditiveDefinition> enumerator = ((GrowContainer)pot).AppliedAdditives.GetEnumerator();
		while (enumerator.MoveNext())
		{
			AdditiveDefinition current = enumerator.Current;
			if ((Object)(object)current != (Object)null)
			{
				if (((ItemDefinition)current).Name.ToLower().Contains("fertilizer"))
				{
					list.Add(new AdditiveData
					{
						Name = ((ItemDefinition)current).Name,
						YieldChange = current.YieldMultiplier,
						QualityChange = current.QualityChange,
						InstantGrowth = current.InstantGrowth
					});
				}
				if (current.InstantGrowth > 0f)
				{
					flag = true;
				}
			}
		}
		if (list.Count > 0)
		{
			persistentFertilizers[potGuid] = new SavedPotFertilizerData
			{
				Fertilizers = list,
				YieldLevel = pot.Plant.YieldMultiplier,
				QualityLevel = pot.Plant.QualityLevel,
				HadSpeedGrow = flag
			};
			MelonLogger.Msg($"[PersistentNutrients Mod] Saved {list.Count} fertilizer(s) for pot {potGuid} (Yield: {pot.Plant.YieldMultiplier:F2}, Quality: {pot.Plant.QualityLevel:F2}, SpeedGrow: {flag})");
		}
	}

	private static void RestoreFertilizers(Pot pot, SavedPotFertilizerData fertData)
	{
		if (!((Object)(object)pot.Plant == (Object)null) && fertData?.Fertilizers != null)
		{
			pot.Plant.YieldMultiplier = fertData.YieldLevel;
			pot.Plant.QualityLevel = fertData.QualityLevel;
		}
	}

	private static void SaveFertilizerData()
	{
		try
		{
			FertilizerDataCollection fertilizerDataCollection = new FertilizerDataCollection();
			foreach (KeyValuePair<Guid, SavedPotFertilizerData> persistentFertilizer in persistentFertilizers)
			{
				fertilizerDataCollection.Entries.Add(new FertilizerEntry
				{
					PotGUID = persistentFertilizer.Key.ToString(),
					Data = persistentFertilizer.Value
				});
			}
			string contents = JsonConvert.SerializeObject((object)fertilizerDataCollection, (Formatting)1);
			File.WriteAllText(DataFilePath, contents);
			MelonLogger.Msg($"[PersistentNutrients Mod] Saved {fertilizerDataCollection.Entries.Count} pot(s) with persistent fertilizers to {DataFilePath}");
		}
		catch (Exception ex)
		{
			MelonLogger.Error("[PersistentNutrients Mod] Error saving fertilizer data: " + ex.Message);
		}
	}

	private static void LoadFertilizerData()
	{
		try
		{
			if (!File.Exists(DataFilePath))
			{
				MelonLogger.Msg("[PersistentNutrients Mod] No save file found at " + DataFilePath + ", starting fresh.");
				persistentFertilizers.Clear();
				return;
			}
			string text = File.ReadAllText(DataFilePath);
			FertilizerDataCollection fertilizerDataCollection = JsonConvert.DeserializeObject(text, typeof(FertilizerDataCollection)) as FertilizerDataCollection;
			persistentFertilizers.Clear();
			if (fertilizerDataCollection?.Entries != null)
			{
				foreach (FertilizerEntry entry in fertilizerDataCollection.Entries)
				{
					if (Guid.TryParse(entry.PotGUID, out var result))
					{
						persistentFertilizers[result] = entry.Data;
					}
				}
			}
			MelonLogger.Msg($"[PersistentNutrients Mod] Loaded {persistentFertilizers.Count} pot(s) with persistent fertilizers from {DataFilePath}");
		}
		catch (Exception ex)
		{
			MelonLogger.Error("[PersistentNutrients Mod] Error loading fertilizer data: " + ex.Message);
			persistentFertilizers.Clear();
		}
	}
}
[Serializable]
public class AdditiveData
{
	public string Name { get; set; }

	public float YieldChange { get; set; }

	public float QualityChange { get; set; }

	public float InstantGrowth { get; set; }
}
[Serializable]
public class SavedPotFertilizerData
{
	public List<AdditiveData> Fertilizers { get; set; }

	public float YieldLevel { get; set; }

	public float QualityLevel { get; set; }

	public bool HadSpeedGrow { get; set; }
}
[Serializable]
public class FertilizerEntry
{
	public string PotGUID { get; set; }

	public SavedPotFertilizerData Data { get; set; }
}
[Serializable]
public class FertilizerDataCollection
{
	public List<FertilizerEntry> Entries { get; set; } = new List<FertilizerEntry>();

}