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 3 months 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; } } }