going to stop reading remaining soil uses

This commit is contained in:
2026-05-18 20:42:30 -04:00
parent 3c7ea09f73
commit 7eba0a03f0
10 changed files with 111 additions and 61 deletions
+79 -59
View File
@@ -4,11 +4,13 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using HarmonyLib; using HarmonyLib;
using MelonLoader; using MelonLoader;
using Il2CppInterop.Runtime;
using Il2CppScheduleOne.Growing; using Il2CppScheduleOne.Growing;
using Il2CppScheduleOne.ItemFramework; using Il2CppScheduleOne.ItemFramework;
using Il2CppScheduleOne.ObjectScripts; using Il2CppScheduleOne.ObjectScripts;
using UnityEngine;
[assembly: MelonInfo(typeof(AbsorbentSoil.AbsorbentSoilMod), "Absorbent Soil", "0.1.1", "AttilaG")] [assembly: MelonInfo(typeof(AbsorbentSoil.AbsorbentSoilMod), "Absorbent Soil", "0.1.0", "AttilaG")]
[assembly: MelonGame(null, "Schedule I")] [assembly: MelonGame(null, "Schedule I")]
namespace AbsorbentSoil namespace AbsorbentSoil
@@ -22,15 +24,16 @@ namespace AbsorbentSoil
MelonLogger.Msg("Absorbent Soil loaded. Additive retention is active for long-life soils."); MelonLogger.Msg("Absorbent Soil loaded. Additive retention is active for long-life soils.");
} }
// This keeps slot/session bleed low if the player backs out and loads another save without restarting.
// It does NOT persist anything to disk. It only tracks pots currently seen in the running game session.
public override void OnSceneWasLoaded(int buildIndex, string sceneName) public override void OnSceneWasLoaded(int buildIndex, string sceneName)
{ {
// Prevent save-slot/session bleed if the player returns to menu and loads another save.
if (!string.IsNullOrWhiteSpace(sceneName) && if (!string.IsNullOrWhiteSpace(sceneName) &&
(sceneName.IndexOf("menu", StringComparison.OrdinalIgnoreCase) >= 0 || (sceneName.IndexOf("menu", StringComparison.OrdinalIgnoreCase) >= 0 ||
sceneName.IndexOf("main", StringComparison.OrdinalIgnoreCase) >= 0 || sceneName.IndexOf("main", StringComparison.OrdinalIgnoreCase) >= 0 ||
sceneName.IndexOf("load", StringComparison.OrdinalIgnoreCase) >= 0)) sceneName.IndexOf("load", StringComparison.OrdinalIgnoreCase) >= 0))
{ {
AdditiveMemory.ClearAll(); AdditiveMemory.Clear();
MelonLogger.Msg($"Cleared additive memory on scene load: {sceneName}"); MelonLogger.Msg($"Cleared additive memory on scene load: {sceneName}");
} }
} }
@@ -73,27 +76,28 @@ namespace AbsorbentSoil
: Array.Empty<string>(); : Array.Empty<string>();
} }
public static bool HasStored(Pot pot)
{
if (pot == null)
return false;
string key = PotKeyHelper.GetPotKey(pot);
return !string.IsNullOrWhiteSpace(key) &&
AdditivesByPotKey.TryGetValue(key, out HashSet<string> set) &&
set.Count > 0;
}
public static void Forget(Pot pot) public static void Forget(Pot pot)
{ {
if (pot == null) if (pot == null)
return; return;
Forget(PotKeyHelper.GetPotKey(pot)); string key = PotKeyHelper.GetPotKey(pot);
if (!string.IsNullOrWhiteSpace(key))
AdditivesByPotKey.Remove(key);
} }
public static void Forget(string potKey) public static void Clear() => AdditivesByPotKey.Clear();
{
if (string.IsNullOrWhiteSpace(potKey))
return;
if (AdditivesByPotKey.Remove(potKey) && AbsorbentSoilMod.VerboseLogging)
MelonLogger.Msg($"Cleared retained additives for pot '{potKey}'.");
}
public static void ClearAll()
{
AdditivesByPotKey.Clear();
}
} }
internal static class PotKeyHelper internal static class PotKeyHelper
@@ -103,10 +107,13 @@ namespace AbsorbentSoil
if (pot == null) if (pot == null)
return string.Empty; return string.Empty;
// Most Schedule I buildable/grid items have a GUID inherited from a base class.
// Use reflection so this survives minor EA API shifts.
object guid = ReadMember(pot, "GUID") ?? ReadMember(pot, "Guid") ?? ReadMember(pot, "guid"); object guid = ReadMember(pot, "GUID") ?? ReadMember(pot, "Guid") ?? ReadMember(pot, "guid");
if (guid != null && !string.IsNullOrWhiteSpace(guid.ToString())) if (guid != null && !string.IsNullOrWhiteSpace(guid.ToString()))
return guid.ToString(); return guid.ToString();
// Fallback for testing only. Not stable across reloads, but better than hard failing.
return $"scene:{pot.GetInstanceID()}"; return $"scene:{pot.GetInstanceID()}";
} }
@@ -116,21 +123,17 @@ namespace AbsorbentSoil
return null; return null;
Type type = instance.GetType(); Type type = instance.GetType();
while (type != null)
PropertyInfo prop = AccessTools.Property(type, name);
if (prop != null)
{ {
PropertyInfo prop = AccessTools.Property(type, name); try { return prop.GetValue(instance); } catch { }
if (prop != null) }
{
try { return prop.GetValue(instance); } catch { }
}
FieldInfo field = AccessTools.Field(type, name); FieldInfo field = AccessTools.Field(type, name);
if (field != null) if (field != null)
{ {
try { return field.GetValue(instance); } catch { } try { return field.GetValue(instance); } catch { }
}
type = type.BaseType;
} }
return null; return null;
@@ -142,28 +145,30 @@ namespace AbsorbentSoil
private static readonly FieldInfo RemainingSoilUsesField = private static readonly FieldInfo RemainingSoilUsesField =
AccessTools.Field(typeof(GrowContainer), "_remainingSoilUses"); AccessTools.Field(typeof(GrowContainer), "_remainingSoilUses");
public static bool IsRetainingSoil(Pot pot)
{
if (pot == null || string.IsNullOrWhiteSpace(pot.SoilID))
return false;
string soilId = pot.SoilID.ToLowerInvariant();
return soilId.Contains("longlifesoil") ||
soilId.Contains("extralonglifesoil");
}
public static int GetRemainingSoilUses(Pot pot) public static int GetRemainingSoilUses(Pot pot)
{ {
if (pot == null || RemainingSoilUsesField == null) if (pot == null || RemainingSoilUsesField == null)
return 0; return 0;
return (int)RemainingSoilUsesField.GetValue(pot); try
{
return (int)RemainingSoilUsesField.GetValue(pot);
}
catch
{
return 0;
}
} }
public static bool CanRetainAdditives(Pot pot) public static bool CanCaptureNewAdditives(Pot pot)
{ {
return IsRetainingSoil(pot) && GetRemainingSoilUses(pot) > 0; // Normal soil should have 1 use, long-life should have more than 1.
return GetRemainingSoilUses(pot) > 1;
}
public static bool CanReapplyRetainedAdditives(Pot pot)
{
return GetRemainingSoilUses(pot) > 0;
} }
} }
@@ -171,7 +176,6 @@ namespace AbsorbentSoil
internal static class Pot_ApplyAdditive_Patch internal static class Pot_ApplyAdditive_Patch
{ {
private static bool _suppressCapture; private static bool _suppressCapture;
private static readonly MethodInfo ApplyAdditiveMethod = AccessTools.Method(typeof(Pot), "ApplyAdditive");
public static void Postfix(Pot __instance, AdditiveDefinition __result, string additiveID, bool isInitialApplication) public static void Postfix(Pot __instance, AdditiveDefinition __result, string additiveID, bool isInitialApplication)
{ {
@@ -183,7 +187,7 @@ namespace AbsorbentSoil
if (__instance == null || __result == null || string.IsNullOrWhiteSpace(additiveID)) if (__instance == null || __result == null || string.IsNullOrWhiteSpace(additiveID))
return; return;
if (!SoilHelper.IsRetainingSoil(__instance)) if (!SoilHelper.CanCaptureNewAdditives(__instance))
return; return;
AdditiveMemory.Remember(__instance, additiveID); AdditiveMemory.Remember(__instance, additiveID);
@@ -199,7 +203,8 @@ namespace AbsorbentSoil
if (pot == null || string.IsNullOrWhiteSpace(additiveID)) if (pot == null || string.IsNullOrWhiteSpace(additiveID))
return; return;
if (ApplyAdditiveMethod == null) MethodInfo applyAdditive = AccessTools.Method(typeof(Pot), "ApplyAdditive");
if (applyAdditive == null)
{ {
MelonLogger.Warning("Could not find Pot.ApplyAdditive via reflection."); MelonLogger.Warning("Could not find Pot.ApplyAdditive via reflection.");
return; return;
@@ -208,7 +213,7 @@ namespace AbsorbentSoil
try try
{ {
_suppressCapture = true; _suppressCapture = true;
ApplyAdditiveMethod.Invoke(pot, new object[] { additiveID, true }); applyAdditive.Invoke(pot, new object[] { additiveID, true });
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -232,12 +237,8 @@ namespace AbsorbentSoil
return; return;
Pot pot = __instance.Pot; Pot pot = __instance.Pot;
if (!SoilHelper.CanReapplyRetainedAdditives(pot))
if (!SoilHelper.CanRetainAdditives(pot))
{
AdditiveMemory.Forget(pot);
return; return;
}
IReadOnlyList<string> additiveIds = AdditiveMemory.Get(pot); IReadOnlyList<string> additiveIds = AdditiveMemory.Get(pot);
if (additiveIds.Count == 0) if (additiveIds.Count == 0)
@@ -258,15 +259,33 @@ namespace AbsorbentSoil
[HarmonyPatch(typeof(Pot), "OnPlantFullyHarvested")] [HarmonyPatch(typeof(Pot), "OnPlantFullyHarvested")]
internal static class Pot_OnPlantFullyHarvested_Patch internal static class Pot_OnPlantFullyHarvested_Patch
{ {
private static void Postfix(Pot __instance) public static void Postfix(Pot __instance)
{ {
if (__instance == null) // Intentionally do nothing. This patch exists as a reminder that retained additives should survive harvest.
return; // Do NOT clear AdditiveMemory here, because long-life/extra-long-life soil should carry additives forward.
}
}
if (!SoilHelper.CanRetainAdditives(__instance)) [HarmonyPatch(typeof(GrowContainer), "SetRemainingSoilUses")]
internal static class GrowContainer_SetRemainingSoilUses_Patch
{
private static void Postfix(GrowContainer __instance, int uses)
{
try
{ {
AdditiveMemory.Forget(__instance); if (__instance == null || uses > 0)
MelonLogger.Msg("[Absorbent Soil] Cleared retained additives because soil has no retaining uses left."); return;
Pot pot = __instance.TryCast<Pot>();
if (pot == null)
return;
AdditiveMemory.Forget(pot);
MelonLogger.Msg("[Absorbent Soil] Cleared retained additives because soil uses reached zero.");
}
catch (Exception ex)
{
MelonLogger.Warning($"SetRemainingSoilUses postfix failed: {ex}");
} }
} }
} }
@@ -276,6 +295,7 @@ namespace AbsorbentSoil
{ {
public static void Prefix(Pot __instance) public static void Prefix(Pot __instance)
{ {
// If the pot itself is destroyed/sold, stop tracking its additives.
try { AdditiveMemory.Forget(__instance); } try { AdditiveMemory.Forget(__instance); }
catch { } catch { }
} }
@@ -0,0 +1,23 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v6.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v6.0": {
"AbsorbentSoil/1.0.0": {
"runtime": {
"AbsorbentSoil.dll": {}
}
}
}
},
"libraries": {
"AbsorbentSoil/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}
Binary file not shown.
@@ -13,7 +13,7 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("AbsorbentSoil")] [assembly: System.Reflection.AssemblyCompanyAttribute("AbsorbentSoil")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+57f6a7917f7640bd65cefb3f66b24ea34b817e0c")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+3c7ea09f730c3ca38d3c004961f111503e0905c8")]
[assembly: System.Reflection.AssemblyProductAttribute("AbsorbentSoil")] [assembly: System.Reflection.AssemblyProductAttribute("AbsorbentSoil")]
[assembly: System.Reflection.AssemblyTitleAttribute("AbsorbentSoil")] [assembly: System.Reflection.AssemblyTitleAttribute("AbsorbentSoil")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
@@ -1 +1 @@
8a3855f139cd66387b0e747834f0fb38e6416f2dc381e733183dbdeaa1f21465 b331521ed94e421a5866942ca81a55cff8e0735dcb4bc556f934ca310386ca0f
@@ -1,5 +1,12 @@
/home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/bin/Release/net6.0/AbsorbentSoil.deps.json
/home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/bin/Release/net6.0/AbsorbentSoil.dll
/home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/bin/Release/net6.0/AbsorbentSoil.pdb
/home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.csproj.AssemblyReference.cache /home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.csproj.AssemblyReference.cache
/home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.GeneratedMSBuildEditorConfig.editorconfig /home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.GeneratedMSBuildEditorConfig.editorconfig
/home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.AssemblyInfoInputs.cache /home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.AssemblyInfoInputs.cache
/home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.AssemblyInfo.cs /home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.AssemblyInfo.cs
/home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.csproj.CoreCompileInputs.cache /home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.csproj.CoreCompileInputs.cache
/home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.dll
/home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/obj/Release/net6.0/refint/AbsorbentSoil.dll
/home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/obj/Release/net6.0/AbsorbentSoil.pdb
/home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/obj/Release/net6.0/ref/AbsorbentSoil.dll
Binary file not shown.
Binary file not shown.