d
This commit is contained in:
@@ -0,0 +1,19 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<AssemblyName>AbsorbentSoil</AssemblyName>
|
||||||
|
<RootNamespace>AbsorbentSoil</RootNamespace>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<Nullable>disable</Nullable>
|
||||||
|
<GameDir>/home/attila/.local/share/Steam/steamapps/common/Schedule I</GameDir>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Reference Include="$(GameDir)/MelonLoader/net6/*.dll" Private="false" />
|
||||||
|
<Reference Include="$(GameDir)/MelonLoader/Il2CppAssemblies/*.dll" Private="false" />
|
||||||
|
<Reference Include="$(GameDir)/MelonLoader/Managed/*.dll" Private="false" />
|
||||||
|
<Reference Include="$(GameDir)/UserLibs/*.dll" Private="false" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -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.
@@ -0,0 +1,77 @@
|
|||||||
|
{
|
||||||
|
"format": 1,
|
||||||
|
"restore": {
|
||||||
|
"/home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/AbsorbentSoil.csproj": {}
|
||||||
|
},
|
||||||
|
"projects": {
|
||||||
|
"/home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/AbsorbentSoil.csproj": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"restore": {
|
||||||
|
"projectUniqueName": "/home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/AbsorbentSoil.csproj",
|
||||||
|
"projectName": "AbsorbentSoil",
|
||||||
|
"projectPath": "/home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/AbsorbentSoil.csproj",
|
||||||
|
"packagesPath": "/home/attila/.nuget/packages/",
|
||||||
|
"outputPath": "/home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/obj/",
|
||||||
|
"projectStyle": "PackageReference",
|
||||||
|
"configFilePaths": [
|
||||||
|
"/home/attila/.nuget/NuGet/NuGet.Config"
|
||||||
|
],
|
||||||
|
"originalTargetFrameworks": [
|
||||||
|
"net6.0"
|
||||||
|
],
|
||||||
|
"sources": {
|
||||||
|
"https://api.nuget.org/v3/index.json": {}
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net6.0": {
|
||||||
|
"targetAlias": "net6.0",
|
||||||
|
"projectReferences": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"warningProperties": {
|
||||||
|
"warnAsError": [
|
||||||
|
"NU1605"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"restoreAuditProperties": {
|
||||||
|
"enableAudit": "true",
|
||||||
|
"auditLevel": "low",
|
||||||
|
"auditMode": "direct"
|
||||||
|
},
|
||||||
|
"SdkAnalysisLevel": "10.0.100"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net6.0": {
|
||||||
|
"targetAlias": "net6.0",
|
||||||
|
"imports": [
|
||||||
|
"net461",
|
||||||
|
"net462",
|
||||||
|
"net47",
|
||||||
|
"net471",
|
||||||
|
"net472",
|
||||||
|
"net48",
|
||||||
|
"net481"
|
||||||
|
],
|
||||||
|
"assetTargetFallback": true,
|
||||||
|
"warn": true,
|
||||||
|
"downloadDependencies": [
|
||||||
|
{
|
||||||
|
"name": "Microsoft.AspNetCore.App.Ref",
|
||||||
|
"version": "[6.0.36, 6.0.36]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Microsoft.NETCore.App.Ref",
|
||||||
|
"version": "[6.0.36, 6.0.36]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"frameworkReferences": {
|
||||||
|
"Microsoft.NETCore.App": {
|
||||||
|
"privateAssets": "all"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtimeIdentifierGraphPath": "/usr/share/dotnet/sdk/10.0.104/RuntimeIdentifierGraph.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
|
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||||
|
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
|
||||||
|
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
|
||||||
|
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
|
||||||
|
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">/home/attila/.nuget/packages/</NuGetPackageRoot>
|
||||||
|
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">/home/attila/.nuget/packages/</NuGetPackageFolders>
|
||||||
|
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
|
||||||
|
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">7.0.0</NuGetToolVersion>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
|
||||||
|
<SourceRoot Include="/home/attila/.nuget/packages/" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
|
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" />
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// <autogenerated />
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// <auto-generated>
|
||||||
|
// This code was generated by a tool.
|
||||||
|
//
|
||||||
|
// Changes to this file may cause incorrect behavior and will be lost if
|
||||||
|
// the code is regenerated.
|
||||||
|
// </auto-generated>
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
[assembly: System.Reflection.AssemblyCompanyAttribute("AbsorbentSoil")]
|
||||||
|
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
|
||||||
|
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
|
||||||
|
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0")]
|
||||||
|
[assembly: System.Reflection.AssemblyProductAttribute("AbsorbentSoil")]
|
||||||
|
[assembly: System.Reflection.AssemblyTitleAttribute("AbsorbentSoil")]
|
||||||
|
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
|
||||||
|
|
||||||
|
// Generated by the MSBuild WriteCodeFragment class.
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
54ce364b284ea9f7c7e326b3938578be61e76fb507bfb64c59c1c67e28888f74
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
is_global = true
|
||||||
|
build_property.TargetFramework = net6.0
|
||||||
|
build_property.TargetFrameworkIdentifier = .NETCoreApp
|
||||||
|
build_property.TargetFrameworkVersion = v6.0
|
||||||
|
build_property.TargetPlatformMinVersion =
|
||||||
|
build_property.UsingMicrosoftNETSdkWeb =
|
||||||
|
build_property.ProjectTypeGuids =
|
||||||
|
build_property.InvariantGlobalization =
|
||||||
|
build_property.PlatformNeutralAssembly =
|
||||||
|
build_property.EnforceExtendedAnalyzerRules =
|
||||||
|
build_property._SupportedPlatformList = Linux,macOS,Windows
|
||||||
|
build_property.RootNamespace = AbsorbentSoil
|
||||||
|
build_property.ProjectDir = /home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/
|
||||||
|
build_property.EnableComHosting =
|
||||||
|
build_property.EnableGeneratedComInterfaceComImportInterop =
|
||||||
|
build_property.EffectiveAnalysisLevelStyle = 6.0
|
||||||
|
build_property.EnableCodeStyleSeverity =
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
|||||||
|
ddb36bf3fc2e77af19df7cc3d5e652bab5098b8451766fb4c9483694133ef288
|
||||||
@@ -0,0 +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.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.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.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.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,82 @@
|
|||||||
|
{
|
||||||
|
"version": 3,
|
||||||
|
"targets": {
|
||||||
|
"net6.0": {}
|
||||||
|
},
|
||||||
|
"libraries": {},
|
||||||
|
"projectFileDependencyGroups": {
|
||||||
|
"net6.0": []
|
||||||
|
},
|
||||||
|
"packageFolders": {
|
||||||
|
"/home/attila/.nuget/packages/": {}
|
||||||
|
},
|
||||||
|
"project": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"restore": {
|
||||||
|
"projectUniqueName": "/home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/AbsorbentSoil.csproj",
|
||||||
|
"projectName": "AbsorbentSoil",
|
||||||
|
"projectPath": "/home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/AbsorbentSoil.csproj",
|
||||||
|
"packagesPath": "/home/attila/.nuget/packages/",
|
||||||
|
"outputPath": "/home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/obj/",
|
||||||
|
"projectStyle": "PackageReference",
|
||||||
|
"configFilePaths": [
|
||||||
|
"/home/attila/.nuget/NuGet/NuGet.Config"
|
||||||
|
],
|
||||||
|
"originalTargetFrameworks": [
|
||||||
|
"net6.0"
|
||||||
|
],
|
||||||
|
"sources": {
|
||||||
|
"https://api.nuget.org/v3/index.json": {}
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net6.0": {
|
||||||
|
"targetAlias": "net6.0",
|
||||||
|
"projectReferences": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"warningProperties": {
|
||||||
|
"warnAsError": [
|
||||||
|
"NU1605"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"restoreAuditProperties": {
|
||||||
|
"enableAudit": "true",
|
||||||
|
"auditLevel": "low",
|
||||||
|
"auditMode": "direct"
|
||||||
|
},
|
||||||
|
"SdkAnalysisLevel": "10.0.100"
|
||||||
|
},
|
||||||
|
"frameworks": {
|
||||||
|
"net6.0": {
|
||||||
|
"targetAlias": "net6.0",
|
||||||
|
"imports": [
|
||||||
|
"net461",
|
||||||
|
"net462",
|
||||||
|
"net47",
|
||||||
|
"net471",
|
||||||
|
"net472",
|
||||||
|
"net48",
|
||||||
|
"net481"
|
||||||
|
],
|
||||||
|
"assetTargetFallback": true,
|
||||||
|
"warn": true,
|
||||||
|
"downloadDependencies": [
|
||||||
|
{
|
||||||
|
"name": "Microsoft.AspNetCore.App.Ref",
|
||||||
|
"version": "[6.0.36, 6.0.36]"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Microsoft.NETCore.App.Ref",
|
||||||
|
"version": "[6.0.36, 6.0.36]"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"frameworkReferences": {
|
||||||
|
"Microsoft.NETCore.App": {
|
||||||
|
"privateAssets": "all"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"runtimeIdentifierGraphPath": "/usr/share/dotnet/sdk/10.0.104/RuntimeIdentifierGraph.json"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"dgSpecHash": "72I/i8FVP4Q=",
|
||||||
|
"success": true,
|
||||||
|
"projectFilePath": "/home/attila/Projects/Schedule-1/AbsorbentSoilMod/AbsorbentSoil/AbsorbentSoil.csproj",
|
||||||
|
"expectedPackageFiles": [
|
||||||
|
"/home/attila/.nuget/packages/microsoft.netcore.app.ref/6.0.36/microsoft.netcore.app.ref.6.0.36.nupkg.sha512",
|
||||||
|
"/home/attila/.nuget/packages/microsoft.aspnetcore.app.ref/6.0.36/microsoft.aspnetcore.app.ref.6.0.36.nupkg.sha512"
|
||||||
|
],
|
||||||
|
"logs": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,335 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using HarmonyLib;
|
||||||
|
using MelonLoader;
|
||||||
|
using ScheduleOne.Growing;
|
||||||
|
using ScheduleOne.ItemFramework;
|
||||||
|
using ScheduleOne.ObjectScripts;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
[assembly: MelonInfo(typeof(AbsorbentSoil.AbsorbentSoilMod), "Absorbent Soil", "0.1.0", "AttilaG")]
|
||||||
|
[assembly: MelonGame(null, "Schedule I")]
|
||||||
|
|
||||||
|
namespace AbsorbentSoil
|
||||||
|
{
|
||||||
|
public sealed class AbsorbentSoilMod : MelonMod
|
||||||
|
{
|
||||||
|
internal static bool VerboseLogging = false;
|
||||||
|
|
||||||
|
public override void OnInitializeMelon()
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(sceneName) &&
|
||||||
|
(sceneName.IndexOf("menu", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||||
|
sceneName.IndexOf("main", StringComparison.OrdinalIgnoreCase) >= 0 ||
|
||||||
|
sceneName.IndexOf("load", StringComparison.OrdinalIgnoreCase) >= 0))
|
||||||
|
{
|
||||||
|
AdditiveMemory.Clear();
|
||||||
|
MelonLogger.Msg($"Cleared additive memory on scene load: {sceneName}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class AdditiveMemory
|
||||||
|
{
|
||||||
|
private static readonly Dictionary<string, HashSet<string>> AdditivesByPotKey = new();
|
||||||
|
|
||||||
|
public static void Remember(Pot pot, string additiveId)
|
||||||
|
{
|
||||||
|
if (pot == null || string.IsNullOrWhiteSpace(additiveId))
|
||||||
|
return;
|
||||||
|
|
||||||
|
string key = PotKeyHelper.GetPotKey(pot);
|
||||||
|
if (string.IsNullOrWhiteSpace(key))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!AdditivesByPotKey.TryGetValue(key, out HashSet<string> set))
|
||||||
|
{
|
||||||
|
set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
AdditivesByPotKey[key] = set;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (set.Add(additiveId) && AbsorbentSoilMod.VerboseLogging)
|
||||||
|
MelonLogger.Msg($"Remembered additive '{additiveId}' for pot '{key}'.");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IReadOnlyList<string> Get(Pot pot)
|
||||||
|
{
|
||||||
|
if (pot == null)
|
||||||
|
return Array.Empty<string>();
|
||||||
|
|
||||||
|
string key = PotKeyHelper.GetPotKey(pot);
|
||||||
|
if (string.IsNullOrWhiteSpace(key))
|
||||||
|
return Array.Empty<string>();
|
||||||
|
|
||||||
|
return AdditivesByPotKey.TryGetValue(key, out HashSet<string> set)
|
||||||
|
? set.ToArray()
|
||||||
|
: Array.Empty<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Forget(Pot pot)
|
||||||
|
{
|
||||||
|
if (pot == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
string key = PotKeyHelper.GetPotKey(pot);
|
||||||
|
if (!string.IsNullOrWhiteSpace(key))
|
||||||
|
AdditivesByPotKey.Remove(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Clear() => AdditivesByPotKey.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class PotKeyHelper
|
||||||
|
{
|
||||||
|
public static string GetPotKey(Pot pot)
|
||||||
|
{
|
||||||
|
if (pot == null)
|
||||||
|
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");
|
||||||
|
if (guid != null && !string.IsNullOrWhiteSpace(guid.ToString()))
|
||||||
|
return guid.ToString();
|
||||||
|
|
||||||
|
// Fallback for testing only. Not stable across reloads, but better than hard failing.
|
||||||
|
return $"scene:{pot.GetInstanceID()}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object ReadMember(object instance, string name)
|
||||||
|
{
|
||||||
|
if (instance == null || string.IsNullOrWhiteSpace(name))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Type type = instance.GetType();
|
||||||
|
|
||||||
|
PropertyInfo prop = AccessTools.Property(type, name);
|
||||||
|
if (prop != null)
|
||||||
|
{
|
||||||
|
try { return prop.GetValue(instance); } catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldInfo field = AccessTools.Field(type, name);
|
||||||
|
if (field != null)
|
||||||
|
{
|
||||||
|
try { return field.GetValue(instance); } catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class SoilHelper
|
||||||
|
{
|
||||||
|
// Adjust these after checking the actual IDs/names from item definitions or Melon logs.
|
||||||
|
// The helper checks IDs/names case-insensitively and allows partial matches.
|
||||||
|
private static readonly string[] RetainingSoilTokens =
|
||||||
|
{
|
||||||
|
"longlife",
|
||||||
|
"long_life",
|
||||||
|
"long-life",
|
||||||
|
"long life",
|
||||||
|
"extralonglife",
|
||||||
|
"extra_long_life",
|
||||||
|
"extra-long-life",
|
||||||
|
"extra long life"
|
||||||
|
};
|
||||||
|
|
||||||
|
public static bool IsRetainingSoil(Pot pot)
|
||||||
|
{
|
||||||
|
if (pot == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// In current Pot.cs, soil identity is probably on GrowContainer/PotData/base state rather than Pot itself.
|
||||||
|
// Try several likely member names so the mod remains resilient to EA renames.
|
||||||
|
foreach (string memberName in new[]
|
||||||
|
{
|
||||||
|
"SoilID", "soilID", "SoilId", "soilId",
|
||||||
|
"SoilDefinition", "soilDefinition", "SoilItem", "soilItem",
|
||||||
|
"Soil", "soil", "CurrentSoil", "currentSoil"
|
||||||
|
})
|
||||||
|
{
|
||||||
|
object value = ReadMemberRecursive(pot, memberName);
|
||||||
|
if (MatchesRetainingSoil(value))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Last resort: inspect chunks/transforms/names. This is intentionally broad but only affects whether
|
||||||
|
// additives are remembered/reapplied; if it is too broad, tighten RetainingSoilTokens above.
|
||||||
|
string potName = SafeLower(pot.name);
|
||||||
|
if (ContainsAnyToken(potName))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool MatchesRetainingSoil(object value)
|
||||||
|
{
|
||||||
|
if (value == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (ContainsAnyToken(SafeLower(value.ToString())))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
foreach (string memberName in new[] { "ID", "Id", "Name", "name", "ItemID", "itemID", "Definition", "definition" })
|
||||||
|
{
|
||||||
|
object nested = ReadMemberRecursive(value, memberName);
|
||||||
|
if (nested != null && ContainsAnyToken(SafeLower(nested.ToString())))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static object ReadMemberRecursive(object instance, string name)
|
||||||
|
{
|
||||||
|
if (instance == null || string.IsNullOrWhiteSpace(name))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
Type type = instance.GetType();
|
||||||
|
|
||||||
|
while (type != null)
|
||||||
|
{
|
||||||
|
PropertyInfo prop = AccessTools.Property(type, name);
|
||||||
|
if (prop != null)
|
||||||
|
{
|
||||||
|
try { return prop.GetValue(instance); } catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
FieldInfo field = AccessTools.Field(type, name);
|
||||||
|
if (field != null)
|
||||||
|
{
|
||||||
|
try { return field.GetValue(instance); } catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
type = type.BaseType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool ContainsAnyToken(string text)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(text))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return RetainingSoilTokens.Any(token => text.Contains(token));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string SafeLower(string value) => value == null ? string.Empty : value.ToLowerInvariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(Pot), "ApplyAdditive")]
|
||||||
|
internal static class Pot_ApplyAdditive_Patch
|
||||||
|
{
|
||||||
|
private static bool _suppressCapture;
|
||||||
|
|
||||||
|
public static void Postfix(Pot __instance, AdditiveDefinition __result, string additiveID, bool isInitialApplication)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (_suppressCapture)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (__instance == null || __result == null || string.IsNullOrWhiteSpace(additiveID))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!SoilHelper.IsRetainingSoil(__instance))
|
||||||
|
return;
|
||||||
|
|
||||||
|
AdditiveMemory.Remember(__instance, additiveID);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MelonLogger.Warning($"ApplyAdditive postfix failed: {ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void ReapplyWithoutRecapture(Pot pot, string additiveID)
|
||||||
|
{
|
||||||
|
if (pot == null || string.IsNullOrWhiteSpace(additiveID))
|
||||||
|
return;
|
||||||
|
|
||||||
|
MethodInfo applyAdditive = AccessTools.Method(typeof(Pot), "ApplyAdditive");
|
||||||
|
if (applyAdditive == null)
|
||||||
|
{
|
||||||
|
MelonLogger.Warning("Could not find Pot.ApplyAdditive via reflection.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_suppressCapture = true;
|
||||||
|
applyAdditive.Invoke(pot, new object[] { additiveID, true });
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MelonLogger.Warning($"Failed to reapply additive '{additiveID}': {ex}");
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
_suppressCapture = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(Plant), nameof(Plant.Initialize))]
|
||||||
|
internal static class Plant_Initialize_Patch
|
||||||
|
{
|
||||||
|
public static void Postfix(Plant __instance)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (__instance == null || __instance.Pot == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Pot pot = __instance.Pot;
|
||||||
|
if (!SoilHelper.IsRetainingSoil(pot))
|
||||||
|
return;
|
||||||
|
|
||||||
|
IReadOnlyList<string> additiveIds = AdditiveMemory.Get(pot);
|
||||||
|
if (additiveIds.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (string additiveId in additiveIds)
|
||||||
|
Pot_ApplyAdditive_Patch.ReapplyWithoutRecapture(pot, additiveId);
|
||||||
|
|
||||||
|
MelonLogger.Msg($"Reapplied {additiveIds.Count} retained additive(s) to new plant in pot '{PotKeyHelper.GetPotKey(pot)}'.");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
MelonLogger.Warning($"Plant.Initialize postfix failed: {ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(Pot), "OnPlantFullyHarvested")]
|
||||||
|
internal static class Pot_OnPlantFullyHarvested_Patch
|
||||||
|
{
|
||||||
|
public static void Postfix(Pot __instance)
|
||||||
|
{
|
||||||
|
// Intentionally do nothing. This patch exists as a reminder that retained additives should survive harvest.
|
||||||
|
// Do NOT clear AdditiveMemory here, because long-life/extra-long-life soil should carry additives forward.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HarmonyPatch(typeof(Pot), "Destroy")]
|
||||||
|
internal static class Pot_Destroy_Patch
|
||||||
|
{
|
||||||
|
public static void Prefix(Pot __instance)
|
||||||
|
{
|
||||||
|
// If the pot itself is destroyed/sold, stop tracking its additives.
|
||||||
|
try { AdditiveMemory.Forget(__instance); }
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user