using 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.Configuration;
using BepInEx.Logging;
using HarmonyLib;
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.3.0.0")]
[assembly: AssemblyInformationalVersion("0.3.0+1b375adfddd34ea836b8f1edad4108682ce91061")]
[assembly: AssemblyProduct("InstantFertilizer")]
[assembly: AssemblyTitle("InstantFertilizer")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.3.0.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.3.0")]
[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.3.0";
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 remaining time by this amount when fertilizing (in percentage of total growing / respawning time).\nDefault value of 100%: grow / respawn instantaneously.\nA single plant / pickable can be fertilized multiple times, but not more than once with the same fertilizer.";
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 WasVineGrown 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())
{
return false;
}
Vine component = ((Component)pickable).GetComponent<Vine>();
if (component != null && !component.CanSpawnPickable(pickable))
{
return false;
}
return (ZNet.instance.GetTime() - new DateTime(pickable.m_nview.GetZDO().GetLong(ZDOVars.s_pickedTime, 0L))).TotalMinutes <= (double)pickable.m_respawnTimeMinutes;
}
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) && plant.m_nview.IsValid())
{
return plant.TimeSincePlanted() <= (double)plant.GetGrowTime();
}
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();
}
DateTime dateTime = new DateTime(pickable.m_nview.GetZDO().GetLong(ZDOVars.s_pickedTime, 0L));
dateTime -= TimeSpan.FromMinutes(pickable.m_respawnTimeMinutes * Plugin.FertilizePercentage);
pickable.m_nview.GetZDO().Set(ZDOVars.s_pickedTime, dateTime.Ticks);
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();
DateTime dateTime = new DateTime(plant.m_nview.GetZDO().GetLong(ZDOVars.s_plantTime, 0L));
dateTime -= TimeSpan.FromSeconds(plant.GetGrowTime() * Plugin.FertilizePercentage);
plant.m_nview.GetZDO().Set(ZDOVars.s_plantTime, dateTime.Ticks);
Plant obj = plant;
obj.m_updateTime -= 100f;
Plant obj2 = plant;
obj2.m_spawnTime -= 100f;
((SlowUpdate)plant).SUpdate();
});
}
private static bool TryFertilizeInternal(Player player, ZNetView nview, Action onFertilize, bool setWasFertilizedWith = true)
{
//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
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).transform.position);
return true;
}
}
}