Decompiled source of LongLastingFertilizer v1.0.0

LongLastingFertilizer.dll

Decompiled 18 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using HarmonyLib;
using Il2CppFishNet;
using Il2CppInterop.Runtime.InteropTypes;
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 LongLastingFertilizer;
using MelonLoader;
using MelonLoader.Utils;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
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(Mod), "LongLastingFertilizer", "1.0.0", "Sensanaty", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("LongLastingFertilizer")]
[assembly: AssemblyConfiguration("IL2CPP")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+524fd4448e3e4095f9e360ad4d043cec4aa25157")]
[assembly: AssemblyProduct("LongLastingFertilizer")]
[assembly: AssemblyTitle("LongLastingFertilizer")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace LongLastingFertilizer
{
	internal static class FertilizerStore
	{
		private static readonly Dictionary<string, PotFertilizerData> Data = new Dictionary<string, PotFertilizerData>();

		private static string _saveSlot = "default";

		private static string FilePath
		{
			get
			{
				string text = Path.GetInvalidFileNameChars().Aggregate(_saveSlot, (string current, char c) => current.Replace(c, '_'));
				return Path.Combine(MelonEnvironment.UserDataDirectory, "LongLastingFertilizer_" + text + ".json");
			}
		}

		internal static bool Has(string potId)
		{
			return Data.ContainsKey(potId);
		}

		internal static bool TryGet(string potId, out PotFertilizerData data)
		{
			return Data.TryGetValue(potId, out data);
		}

		internal static void Remove(string potId, string reason)
		{
			if (Data.Remove(potId))
			{
				Melon<Mod>.Logger.Msg("Cleared pot " + potId + ": " + reason);
			}
		}

		internal static void CaptureState(Pot pot, string potId)
		{
			if (((GrowContainer)pot).AppliedAdditives == null || ((GrowContainer)pot).AppliedAdditives.Count == 0 || (Object)(object)pot.Plant == (Object)null)
			{
				return;
			}
			List<FertilizerEffect> list = new List<FertilizerEffect>();
			bool hasSpeedGrow = false;
			int count = ((GrowContainer)pot).AppliedAdditives.Count;
			for (int i = 0; i < count; i++)
			{
				try
				{
					AdditiveDefinition val = ((GrowContainer)pot).AppliedAdditives[i];
					if (!((Object)(object)val == (Object)null))
					{
						if (PotHelper.IsFertilizer(val))
						{
							list.Add(new FertilizerEffect
							{
								YieldMultiplier = val.YieldMultiplier,
								QualityChange = val.QualityChange,
								InstantGrowth = val.InstantGrowth
							});
						}
						if (val.InstantGrowth > 0f)
						{
							hasSpeedGrow = true;
						}
					}
				}
				catch (Exception ex)
				{
					Melon<Mod>.Logger.Error($"Error reading additive [{i}]: {ex.Message}");
				}
			}
			if (list.Count == 0)
			{
				return;
			}
			try
			{
				Data[potId] = new PotFertilizerData
				{
					Effects = list,
					YieldLevel = pot.Plant.YieldMultiplier,
					QualityLevel = pot.Plant.QualityLevel,
					HasSpeedGrow = hasSpeedGrow
				};
				Melon<Mod>.Logger.Msg($"Captured {list.Count} fertilizer(s) for pot {potId} (yield={pot.Plant.YieldMultiplier:F2}, quality={pot.Plant.QualityLevel:F2})");
			}
			catch (Exception ex2)
			{
				Melon<Mod>.Logger.Error("Error capturing plant values: " + ex2.Message);
			}
		}

		internal static bool ValidateOrClean(Pot pot, string potId, string context)
		{
			if (!Has(potId))
			{
				return false;
			}
			if (PotHelper.HasSoilRemaining(pot))
			{
				return true;
			}
			Remove(potId, "stale data (" + context + ")");
			return false;
		}

		internal static void SetSaveSlot(string slot)
		{
			_saveSlot = slot;
		}

		internal static void Clear()
		{
			Data.Clear();
		}

		internal static void Save()
		{
			try
			{
				FertilizerSaveFile fertilizerSaveFile = new FertilizerSaveFile();
				foreach (KeyValuePair<string, PotFertilizerData> datum in Data)
				{
					fertilizerSaveFile.Entries.Add(new FertilizerSaveEntry
					{
						PotId = datum.Key,
						Data = datum.Value
					});
				}
				File.WriteAllText(FilePath, JsonConvert.SerializeObject((object)fertilizerSaveFile, (Formatting)1));
				Melon<Mod>.Logger.Msg($"Saved {fertilizerSaveFile.Entries.Count} pot(s) to {FilePath}");
			}
			catch (Exception ex)
			{
				Melon<Mod>.Logger.Error("Save failed: " + ex.Message);
			}
		}

		internal static void Load()
		{
			Data.Clear();
			try
			{
				if (!File.Exists(FilePath))
				{
					Melon<Mod>.Logger.Msg("No save file found, starting fresh.");
					return;
				}
				FertilizerSaveFile fertilizerSaveFile = JsonConvert.DeserializeObject<FertilizerSaveFile>(File.ReadAllText(FilePath));
				if (fertilizerSaveFile?.Entries == null)
				{
					return;
				}
				foreach (FertilizerSaveEntry item in fertilizerSaveFile.Entries.Where((FertilizerSaveEntry e) => !string.IsNullOrEmpty(e.PotId)))
				{
					Data[item.PotId] = item.Data;
				}
				Melon<Mod>.Logger.Msg($"Loaded {Data.Count} pot(s) from {FilePath}");
			}
			catch (Exception ex)
			{
				Melon<Mod>.Logger.Error("Load failed: " + ex.Message);
				Data.Clear();
			}
		}
	}
	public class Mod : MelonMod
	{
		public override void OnInitializeMelon()
		{
			((MelonBase)this).LoggerInstance.Msg("LongLastingFertilizer loaded.");
		}

		public override void OnApplicationQuit()
		{
			FertilizerStore.Save();
		}
	}
	[Serializable]
	public class FertilizerEffect
	{
		public float YieldMultiplier { get; set; }

		public float QualityChange { get; set; }

		public float InstantGrowth { get; set; }
	}
	[Serializable]
	public class PotFertilizerData
	{
		public List<FertilizerEffect> Effects { get; set; } = new List<FertilizerEffect>();


		public float YieldLevel { get; set; }

		public float QualityLevel { get; set; }

		public bool HasSpeedGrow { get; set; }
	}
	[Serializable]
	public class FertilizerSaveEntry
	{
		public string PotId { get; set; } = "";


		public PotFertilizerData Data { get; set; } = new PotFertilizerData();

	}
	[Serializable]
	public class FertilizerSaveFile
	{
		public List<FertilizerSaveEntry> Entries { get; set; } = new List<FertilizerSaveEntry>();

	}
	[HarmonyPatch(typeof(Pot), "OnPlantFullyHarvested")]
	internal static class Patch_Harvest
	{
		[HarmonyPrefix]
		public static void Prefix(Pot __instance)
		{
			try
			{
				if (!((Object)(object)((__instance != null) ? __instance.Plant : null) == (Object)null))
				{
					string id = PotHelper.GetId(__instance);
					if (PotHelper.HasSoilRemaining(__instance) && PotHelper.HasAdditives(__instance))
					{
						FertilizerStore.CaptureState(__instance, id);
					}
					else
					{
						FertilizerStore.Remove(id, "soil depleted at harvest");
					}
				}
			}
			catch (Exception ex)
			{
				Melon<Mod>.Logger.Error("Harvest prefix error: " + ex.Message);
			}
		}
	}
	[HarmonyPatch(typeof(Pot), "PlantSeed_Server")]
	internal static class Patch_Plant
	{
		[CompilerGenerated]
		private sealed class <WaitThenRestore>d__2 : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public Pot pot;

			public PotFertilizerData data;

			public string potId;

			private int <i>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 <WaitThenRestore>d__2(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

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

			private bool MoveNext()
			{
				Pot obj2;
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<i>5__1 = 0;
					goto IL_005e;
				case 1:
					<>1__state = -1;
					<i>5__1++;
					goto IL_005e;
				case 2:
					{
						<>1__state = -1;
						<i>5__2++;
						break;
					}
					IL_005e:
					if (<i>5__1 < 100)
					{
						Pot obj = pot;
						if ((Object)(object)((obj != null) ? obj.Plant : null) == (Object)null)
						{
							<>2__current = null;
							<>1__state = 1;
							return true;
						}
					}
					obj2 = pot;
					if ((Object)(object)((obj2 != null) ? obj2.Plant : null) == (Object)null)
					{
						Melon<Mod>.Logger.Warning("Plant never appeared for pot " + potId + ", skipping restore.");
						return false;
					}
					<i>5__2 = 0;
					break;
				}
				if (<i>5__2 < 10)
				{
					<>2__current = null;
					<>1__state = 2;
					return true;
				}
				RestorePlantValues(pot, data, potId);
				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)
		{
			if ((Object)(object)__instance == (Object)null || !InstanceFinder.IsServer)
			{
				return;
			}
			string id = PotHelper.GetId(__instance);
			if (FertilizerStore.TryGet(id, out var data))
			{
				if ((Object)(object)__instance.Plant != (Object)null)
				{
					RestorePlantValues(__instance, data, id);
				}
				else
				{
					MelonCoroutines.Start(WaitThenRestore(__instance, data, id));
				}
			}
		}

		private static void RestorePlantValues(Pot pot, PotFertilizerData data, string potId)
		{
			try
			{
				pot.Plant.YieldMultiplier = data.YieldLevel;
				pot.Plant.QualityLevel = data.QualityLevel;
				Melon<Mod>.Logger.Msg($"Restored yield={data.YieldLevel:F2}, quality={data.QualityLevel:F2} on pot {potId}");
			}
			catch (Exception ex)
			{
				Melon<Mod>.Logger.Error("Restore error on pot " + potId + ": " + ex.Message);
			}
		}

		[IteratorStateMachine(typeof(<WaitThenRestore>d__2))]
		private static IEnumerator WaitThenRestore(Pot pot, PotFertilizerData data, string potId)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <WaitThenRestore>d__2(0)
			{
				pot = pot,
				data = data,
				potId = potId
			};
		}
	}
	[HarmonyPatch(typeof(Pot), "CanApplyAdditive")]
	internal static class Patch_CanApply
	{
		[HarmonyPrefix]
		public static bool Prefix(Pot __instance, AdditiveDefinition additiveDef, ref string invalidReason, ref bool __result)
		{
			if ((Object)(object)__instance == (Object)null || (Object)(object)additiveDef == (Object)null)
			{
				return true;
			}
			if (!PotHelper.IsFertilizer(additiveDef))
			{
				return true;
			}
			string id = PotHelper.GetId(__instance);
			if (!FertilizerStore.ValidateOrClean(__instance, id, "CanApplyAdditive"))
			{
				return true;
			}
			invalidReason = "This soil is already fertilized!";
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(Botanist), "GetGrowContainersForAdditives")]
	internal static class Patch_Botanist
	{
		[HarmonyPostfix]
		public static void Postfix(ref List<GrowContainer> __result)
		{
			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;
				Pot val = ((current != null) ? ((Il2CppObjectBase)current).TryCast<Pot>() : null);
				if (!((Object)(object)val == (Object)null))
				{
					string id = PotHelper.GetId(val);
					if (FertilizerStore.ValidateOrClean(val, id, "botanist filter"))
					{
						list.Add(current);
					}
				}
			}
			foreach (GrowContainer item in list)
			{
				__result.Remove(item);
			}
		}
	}
	[HarmonyPatch(typeof(SaveManager), "Save", new Type[] { typeof(string) })]
	internal static class Patch_SaveWithPath
	{
		[HarmonyPrefix]
		public static void Prefix()
		{
			FertilizerStore.Save();
		}
	}
	[HarmonyPatch(typeof(SaveManager), "Save", new Type[] { })]
	internal static class Patch_SaveNoArgs
	{
		[HarmonyPrefix]
		public static void Prefix()
		{
			FertilizerStore.Save();
		}
	}
	[HarmonyPatch(typeof(LoadManager), "StartGame")]
	internal static class Patch_Load
	{
		[HarmonyPostfix]
		public static void Postfix(SaveInfo info)
		{
			if (info != null)
			{
				FertilizerStore.SetSaveSlot($"SaveGame_{info.SaveSlotNumber}");
				FertilizerStore.Load();
			}
			else
			{
				FertilizerStore.SetSaveSlot("NewGame");
				FertilizerStore.Clear();
			}
		}
	}
	internal static class PotHelper
	{
		internal static string GetId(Pot pot)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			Guid gUID = ((BuildableItem)pot).GUID;
			return ((object)(Guid)(ref gUID)).ToString();
		}

		internal static bool HasSoilRemaining(Pot pot)
		{
			return ((GrowContainer)pot)._remainingSoilUses > 0;
		}

		internal static bool HasAdditives(Pot pot)
		{
			List<AdditiveDefinition> appliedAdditives = ((GrowContainer)pot).AppliedAdditives;
			return appliedAdditives != null && appliedAdditives.Count > 0;
		}

		internal static bool IsFertilizer(AdditiveDefinition additive)
		{
			return additive.YieldMultiplier != 0f || additive.QualityChange != 0f;
		}
	}
}