Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Instant Fertilizer v0.4.3
plugins/InstantFertilizer.dll
Decompiled 7 months agousing System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using InstantFertilizer.Compatibility; using InstantFertilizer.Model; using Jotunn.Utils; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")] [assembly: AssemblyCompany("InstantFertilizer")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.4.3.0")] [assembly: AssemblyInformationalVersion("0.4.3+1291ca81f61e91e5a06229bddd0f5264f77ee8ca")] [assembly: AssemblyProduct("InstantFertilizer")] [assembly: AssemblyTitle("InstantFertilizer")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.4.3.0")] [module: UnverifiableCode] [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 InstantFertilizer { [BepInPlugin("nbusseneau.InstantFertilizer", "InstantFertilizer", "0.4.3")] [BepInDependency(/*Could not decode attribute arguments.*/)] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] public class Plugin : BaseUnityPlugin { internal const string ModGUID = "nbusseneau.InstantFertilizer"; private const string ModName = "InstantFertilizer"; private const string ModVersion = "0.4.3"; private static readonly List<Fertilizer> s_defaultFertilizers = new List<Fertilizer>(2) { new Fertilizer("$item_ancientseed", 3, "defeated_eikthyr"), new Fertilizer("$item_ymirremains", 1, "defeated_gdking") }; private static ConfigEntry<string> s_fertilizers; private static ConfigEntry<int> s_fertilizePercentage; internal static ManualLogSource Logger { get; private set; } public static List<Fertilizer> Fertilizers { get; private set; } public static float FertilizePercentage => (float)s_fertilizePercentage.Value / 100f; private static List<Fertilizer> ParseFertilizers(string serializedFertilizers) { return (from f in serializedFertilizers.Split(new char[1] { ',' }).Select(Fertilizer.FromString) where f != null select f).ToList(); } public void Awake() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Expected O, but got Unknown //IL_00f3: Unknown result type (might be due to invalid IL or missing references) Logger = ((BaseUnityPlugin)this).Logger; ConfigurationManagerAttributes val = new ConfigurationManagerAttributes { IsAdminOnly = true }; string text = "Comma-separated list of fertilizers that may be used.\nFertilizer format: " + Fertilizer.SerializedFormat + "\nSee https://valheim.fandom.com/wiki/Global_Keys for quick reference of available global keys, or use `none` if you do not want to gate fertilizing behind a global key.\nNote that the mod is not able to determine in advance if an item or global key actually exists. If a fertilizer appears to be ignored, double check item names and global keys."; s_fertilizers = ((BaseUnityPlugin)this).Config.Bind<string>("Behaviour", "Fertilizer list", GeneralExtensions.Join<Fertilizer>((IEnumerable<Fertilizer>)s_defaultFertilizers, (Func<Fertilizer, string>)null, ", "), new ConfigDescription(text, (AcceptableValueBase)null, new object[1] { val })); Fertilizers = ParseFertilizers(s_fertilizers.Value); s_fertilizers.SettingChanged += delegate { Fertilizers = ParseFertilizers(s_fertilizers.Value); }; string text2 = "Reduce current remaining time by this percentage when fertilizing.\nDefault value of 100%: grow / respawn instantaneously.\nIf set to a value lower than 100%, a single plant / pickable can be fertilized multiple times with diminishing returns (but not more than once with the same fertilizer).\nFor example: if set at 50%, 50% of original time remain after first fertilization (reduction of 50%), and 25% of original time remain after second fertilization (reduction of 50% again)."; AcceptableValueRange<int> val2 = new AcceptableValueRange<int>(1, 100); s_fertilizePercentage = ((BaseUnityPlugin)this).Config.Bind<int>("Behaviour", "Fertilize percentage", 100, new ConfigDescription(text2, (AcceptableValueBase)(object)val2, new object[1] { val })); SetUpConfigWatcher(); Assembly executingAssembly = Assembly.GetExecutingAssembly(); new Harmony("nbusseneau.InstantFertilizer").PatchAll(executingAssembly); } public void OnDestroy() { ((BaseUnityPlugin)this).Config.Save(); } private void SetUpConfigWatcher() { FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(Paths.ConfigPath, Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath)); fileSystemWatcher.Changed += ReadConfigValues; fileSystemWatcher.Created += ReadConfigValues; fileSystemWatcher.Renamed += ReadConfigValues; fileSystemWatcher.IncludeSubdirectories = true; fileSystemWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; fileSystemWatcher.EnableRaisingEvents = true; } private void ReadConfigValues(object sender, FileSystemEventArgs e) { if (!File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath)) { return; } try { Logger.LogDebug((object)"Attempting to reload configuration..."); ((BaseUnityPlugin)this).Config.Reload(); } catch { Logger.LogError((object)("There was an issue loading " + ((BaseUnityPlugin)this).Config.ConfigFilePath)); } } } } namespace InstantFertilizer.Patches { [HarmonyPatch(typeof(Pickable))] public class PickablePatches { [HarmonyPostfix] [HarmonyPatch("GetHoverText")] private static void PickableGetFertilizeHoverText(Pickable __instance, ref string __result) { bool num = FertilizerManager.CanFertilize(__instance); Vine component = ((Component)__instance).GetComponent<Vine>(); bool flag = component != null && FertilizerManager.CanFertilize(component); if (num || flag) { __result += FertilizerManager.GetFertilizeHoverText(__instance.m_nview); } } [HarmonyPostfix] [HarmonyPatch("SetPicked")] private static void PickableResetFertilizedWith(Pickable __instance, bool picked) { if (picked && Object.op_Implicit((Object)(object)__instance.m_nview) && __instance.m_nview.IsValid() && __instance.m_nview.GetZDO().GetBool(ZDOVars.s_picked, false)) { Plugin.Fertilizers.ForEach(delegate(Fertilizer fertilizer) { FertilizerManager.SetWasFertilizedWith(fertilizer, __instance.m_nview, value: false); }); } } } [HarmonyPatch(typeof(Plant))] public class PlantPatches { [HarmonyPostfix] [HarmonyPatch("GetHoverText")] private static void PlantGetFertilizeHoverText(Plant __instance, ref string __result) { if (FertilizerManager.CanFertilize(__instance)) { __result += FertilizerManager.GetFertilizeHoverText(__instance.m_nview); } } } [HarmonyPatch(typeof(Player))] public class PlayerPatches { [HarmonyPrefix] [HarmonyPatch("Interact")] private static void Fertilize(Player __instance, GameObject go, bool hold, ref bool __runOriginal) { if (((Character)__instance).InAttack() || ((Character)__instance).InDodge() || hold) { return; } bool flag = false; Plant componentInParent = go.GetComponentInParent<Plant>(); if (componentInParent != null && FertilizerManager.CanFertilize(componentInParent)) { flag = FertilizerManager.TryFertilize(__instance, componentInParent); } else { Vine componentInParent2 = go.GetComponentInParent<Vine>(); if (componentInParent2 != null && FertilizerManager.CanFertilize(componentInParent2)) { flag = FertilizerManager.TryFertilize(__instance, componentInParent2); } else { Pickable componentInParent3 = go.GetComponentInParent<Pickable>(); if (componentInParent3 != null && FertilizerManager.CanFertilize(componentInParent3)) { flag = FertilizerManager.TryFertilize(__instance, componentInParent3); } } } if (flag) { __runOriginal = false; } } } [HarmonyPatch(typeof(Vine))] public class VinePatches { [HarmonyTranspiler] [HarmonyPatch("Grow")] private static IEnumerable<CodeInstruction> SetWasVineFertilizedOnChildVines(IEnumerable<CodeInstruction> instructions) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Expected O, but got Unknown //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Expected O, but got Unknown //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Expected O, but got Unknown //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Expected O, but got Unknown //IL_0141: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Expected O, but got Unknown //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Expected O, but got Unknown return new CodeMatcher(instructions, (ILGenerator)null).MatchEndForward((CodeMatch[])(object)new CodeMatch[8] { new CodeMatch((OpCode?)OpCodes.Callvirt, (object)AccessTools.Method(typeof(ZNetView), "GetZDO", (Type[])null, (Type[])null), (string)null), new CodeMatch((OpCode?)OpCodes.Ldsfld, (object)AccessTools.Field(typeof(ZDOVars), "s_plantTime"), (string)null), new CodeMatch((OpCode?)OpCodes.Call, (object)null, (string)null), new CodeMatch((OpCode?)OpCodes.Callvirt, (object)AccessTools.Method(typeof(ZNet), "GetTime", (Type[])null, (Type[])null), (string)null), new CodeMatch((OpCode?)OpCodes.Stloc_2, (object)null, (string)null), new CodeMatch((OpCode?)OpCodes.Ldloca_S, (object)null, (string)null), new CodeMatch((OpCode?)OpCodes.Call, (object)null, (string)null), new CodeMatch((OpCode?)OpCodes.Callvirt, (object)AccessTools.Method(typeof(ZDO), "Set", new Type[2] { typeof(int), typeof(long) }, (Type[])null), (string)null) }).ThrowIfInvalid("Could not inject WasVineFertilized status to child vine in Vine.Grow(...)").Advance(1) .InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Ldarg_0, (object)null) }) .InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Ldloc_0, (object)null) }) .InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { Transpilers.EmitDelegate<Action<Vine, Vine>>((Action<Vine, Vine>)delegate(Vine parentVine, Vine childVine) { if (FertilizerManager.WasVineFertilized(parentVine)) { FertilizerManager.SetWasVineFertilized(childVine, value: true); } }) }) .InstructionEnumeration(); } } [HarmonyPatch(typeof(ObjectDB))] public class ZNetScenePatches { [HarmonyPostfix] [HarmonyPatch("Awake")] [HarmonyPatch("CopyOtherDB")] private static void ClearCachedGlobalKeys() { FertilizerManager.ClearCachedGlobalKeys(); } } } namespace InstantFertilizer.Model { public class Fertilizer { public const char Delimiter = ':'; public static readonly string SerializedFormat = $"itemName{':'}requiredAmount{':'}requiredGlobalKey"; public string ItemName { get; } public int RequiredAmount { get; } public string RequiredGlobalKey { get; } public Fertilizer(string itemName, int requiredAmount, string requiredGlobalKey) { ItemName = itemName; RequiredAmount = requiredAmount; RequiredGlobalKey = requiredGlobalKey; base..ctor(); } public override string ToString() { return $"{ItemName}{':'}{RequiredAmount}{':'}{RequiredGlobalKey}"; } public static Fertilizer FromString(string serializedFertilizer) { string[] array = serializedFertilizer.Trim().Split(new char[1] { ':' }); if (array.Length != 3) { Plugin.Logger.LogError((object)("Could not deserialize the following fertilizer entry: " + serializedFertilizer + "\r\nInvalid format: must be `" + SerializedFormat + "`")); return null; } string text = array[0]; string s = array[1]; string requiredGlobalKey = array[2]; string itemName = text; if (!int.TryParse(s, out var result)) { Plugin.Logger.LogError((object)("Could not deserialize the following fertilizer entry: " + serializedFertilizer + "\r\nInvalid amount: must be a valid integer")); return null; } return new Fertilizer(itemName, result, requiredGlobalKey); } } public static class FertilizerManager { public const string GlobalKeyIgnore = "none"; private static readonly Dictionary<string, bool> s_cachedGlobalKeys = new Dictionary<string, bool>(); private static readonly string WasFertilizedWithZDOKey = "nbusseneau.InstantFertilizer.WasFertilizedWith"; public static void ClearCachedGlobalKeys() { s_cachedGlobalKeys.Clear(); } private static bool HasGlobalKey(string globalKey) { if (globalKey == "none") { return true; } if (s_cachedGlobalKeys.TryGetValue(globalKey, out var value)) { return value; } value = ZoneSystem.instance.GetGlobalKey(globalKey); if (value) { s_cachedGlobalKeys[globalKey] = true; } return value; } private static bool WasFertilizedWith(Fertilizer fertilizer, ZNetView nview) { return nview.GetZDO().GetBool(WasFertilizedWithZDOKey + "." + fertilizer.ItemName, false); } public static void SetWasFertilizedWith(Fertilizer fertilizer, ZNetView nview, bool value) { nview.GetZDO().Set(WasFertilizedWithZDOKey + "." + fertilizer.ItemName, value); } public static bool WasVineFertilized(Vine vine) { return vine.m_nview.GetZDO().GetBool(WasFertilizedWithZDOKey, false); } public static void SetWasVineFertilized(Vine vine, bool value) { vine.m_nview.GetZDO().Set(WasFertilizedWithZDOKey, value); } public static string GetFertilizeHoverText(ZNetView nview) { string text = string.Empty; IEnumerable<string> wasFertilizedWith = from fertilizer in Plugin.Fertilizers where WasFertilizedWith(fertilizer, nview) select fertilizer.ItemName; if (wasFertilizedWith.Any()) { text = text + "\n$InstantFertilizer_FertilizedWith " + string.Join(" / ", wasFertilizedWith); } IEnumerable<string> enumerable = from fertilizer in Plugin.Fertilizers where HasGlobalKey(fertilizer.RequiredGlobalKey) && !wasFertilizedWith.Contains(fertilizer.ItemName) select $"{fertilizer.RequiredAmount} {fertilizer.ItemName}"; if (enumerable.Any()) { text = text + "\n[<color=yellow><b>$KEY_Use</b></color>] $InstantFertilizer_Fertilize (" + string.Join(" / ", enumerable) + ")"; } return Localization.instance.Localize(text); } public static bool CanFertilize(Pickable pickable) { if (pickable.CanBePicked() || !Object.op_Implicit((Object)(object)pickable.m_nview) || !pickable.m_nview.IsValid() || pickable.m_respawnTimeMinutes <= 0f) { return false; } Vine component = ((Component)pickable).GetComponent<Vine>(); if (component != null && !component.CanSpawnPickable(pickable)) { return false; } return true; } public static bool CanFertilize(Vine vine) { if (!vine.IsDoneGrowing && Object.op_Implicit((Object)(object)vine.m_nview) && vine.m_nview.IsValid() && !WasVineFertilized(vine)) { return !vine.m_pickable.CanBePicked(); } return false; } public static bool CanFertilize(Plant plant) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if ((int)plant.m_status == 0 && Object.op_Implicit((Object)(object)plant.m_nview)) { return plant.m_nview.IsValid(); } return false; } public static bool TryFertilize(Player player, Pickable pickable) { return TryFertilizeInternal(player, pickable.m_nview, delegate { pickable.m_nview.ClaimOwnership(); if (pickable.m_pickedTime == 0L) { pickable.UpdateRespawn(); } long @long = pickable.m_nview.GetZDO().GetLong(ZDOVars.s_pickedTime, 0L); double value = GetRemainingSeconds(pickable) * (double)Plugin.FertilizePercentage; @long -= TimeSpan.FromSeconds(value).Ticks; if (@long < 0) { @long = 1L; } pickable.m_nview.GetZDO().Set(ZDOVars.s_pickedTime, @long); pickable.UpdateRespawn(); }); } public static bool TryFertilize(Player player, Vine vine) { return TryFertilizeInternal(player, vine.m_nview, delegate { vine.m_nview.ClaimOwnership(); SetWasVineFertilized(vine, value: true); vine.m_initialGrowItterations = 25; }, setWasFertilizedWith: false); } public static bool TryFertilize(Player player, Plant plant) { return TryFertilizeInternal(player, plant.m_nview, delegate { plant.m_nview.ClaimOwnership(); long @long = plant.m_nview.GetZDO().GetLong(ZDOVars.s_plantTime, 0L); if (Plugin.FertilizePercentage == 1f) { plant.Grow(); } else { double value = GetRemainingSeconds(plant) * (double)Plugin.FertilizePercentage; @long -= TimeSpan.FromSeconds(value).Ticks; if (@long < 0) { @long = 1L; } plant.m_nview.GetZDO().Set(ZDOVars.s_plantTime, @long); } }); } private static bool TryFertilizeInternal(Player player, ZNetView nview, Action onFertilize, bool setWasFertilizedWith = true) { player.m_lastHoverInteractTime = Time.time; IEnumerable<Fertilizer> enumerable = Plugin.Fertilizers.Where((Fertilizer fertilizer) => HasGlobalKey(fertilizer.RequiredGlobalKey) && !WasFertilizedWith(fertilizer, nview)); if (!enumerable.Any()) { return false; } bool flag = false; foreach (Fertilizer item2 in enumerable) { ItemData item = ((Humanoid)player).m_inventory.GetItem(item2.ItemName, -1, false); flag = item != null && ((Humanoid)player).m_inventory.CountItems(item2.ItemName, -1, true) >= item2.RequiredAmount && ((Humanoid)player).m_inventory.RemoveItem(item, item2.RequiredAmount); if (flag && setWasFertilizedWith) { SetWasFertilizedWith(item2, nview, value: true); break; } } if (!flag) { ((Character)player).Message((MessageType)2, "$InstantFertilizer_FertilizerRequired", 0, (Sprite)null); return false; } onFertilize(); ((Humanoid)player).DoInteractAnimation(((Component)nview).gameObject); return true; } private static double GetRemainingSeconds(Pickable pickable) { return (SeasonsCompatibility.IsReady ? SeasonsCompatibility.GetSecondsToRespawnPickable(pickable) : ((double)(pickable.m_respawnTimeMinutes * 60f))) - (ZNet.instance.GetTime() - new DateTime(pickable.m_nview.GetZDO().GetLong(ZDOVars.s_pickedTime, 0L))).TotalSeconds; } private static double GetRemainingSeconds(Plant plant) { if (!SeasonsCompatibility.IsReady) { return (double)plant.GetGrowTime() - plant.TimeSincePlanted(); } return SeasonsCompatibility.GetSecondsToGrowPlant(plant); } } } namespace InstantFertilizer.Compatibility { public static class SeasonsCompatibility { private static bool _isInitialized; private static bool _isReady; private const string GUID = "shudnal.Seasons"; private static Assembly _assembly; private static FieldInfo _seasonState; private static MethodInfo _getSecondsToRespawnPickable; private static MethodInfo _getSecondsToGrowPlant; public static bool IsReady { get { if (!_isInitialized) { Initialize(); } return _isReady; } } public static double GetSecondsToRespawnPickable(Pickable pickable) { return (double)_getSecondsToRespawnPickable.Invoke(_seasonState.GetValue(_seasonState), new object[1] { pickable }); } public static double GetSecondsToGrowPlant(Plant plant) { return (double)_getSecondsToGrowPlant.Invoke(_seasonState.GetValue(_seasonState), new object[1] { plant }); } private static void Initialize() { _isInitialized = true; if (Chainloader.PluginInfos.TryGetValue("shudnal.Seasons", out var value)) { _assembly = Assembly.GetAssembly(((object)value.Instance).GetType()); if (_assembly != null) { _seasonState = AccessTools.Field(_assembly.GetType("Seasons.Seasons"), "seasonState"); _getSecondsToRespawnPickable = AccessTools.Method(_seasonState.FieldType, "GetSecondsToRespawnPickable", (Type[])null, (Type[])null); _getSecondsToGrowPlant = AccessTools.Method(_seasonState.FieldType, "GetSecondsToGrowPlant", (Type[])null, (Type[])null); } } _isReady = _seasonState != null && _getSecondsToRespawnPickable != null && _getSecondsToGrowPlant != null; } } }