Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of LongLastingFertilizer v1.0.0
LongLastingFertilizer.dll
Decompiled 18 hours agousing 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; } } }