plugins/Eir.dll

Decompiled 12 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using GBV.Eir.Fixes;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Guys Being Vikings")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.3.0.0")]
[assembly: AssemblyInformationalVersion("0.3.0+710031b0010072d25b0164ff19ba523faee8c7d9")]
[assembly: AssemblyProduct("Eir")]
[assembly: AssemblyTitle("Eir")]
[assembly: AssemblyVersion("0.3.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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 GBV.Eir
{
	[BepInPlugin("gbv.valheim.eir", "Eir", "0.3.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class EirPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "gbv.valheim.eir";

		public const string PluginName = "Eir";

		public const string PluginVersion = "0.3.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			try
			{
				Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turn every fix off at once, without uninstalling. Each fix also has its own switch under Fixes. Nothing Eir does is saved anywhere, so this is safe to change at any time; it takes effect the next time you start the game.");
				if (!Enabled.Value)
				{
					Log.LogInfo((object)"Eir 0.3.0 loaded, but every fix is switched off in the config.");
					return;
				}
				_harmony = new Harmony("gbv.valheim.eir");
				FixRegistry.ApplyAll(_harmony, ((BaseUnityPlugin)this).Config);
				Log.LogInfo((object)"Eir 0.3.0 loaded.");
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Eir: startup failed, so no fixes were applied. The game is unaffected otherwise. Please report this.\n" + ex));
			}
		}

		private void Update()
		{
			try
			{
				Drugs.Poll();
				WeedheimCbdTweak.Tick();
				BasementsMaterialFix.Tick();
			}
			catch (Exception ex)
			{
				Safety.Failed("EirPlugin.Update", ex);
			}
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class Safety
	{
		private static readonly HashSet<string> Reported = new HashSet<string>(StringComparer.Ordinal);

		public static void Failed(string where, Exception ex)
		{
			if (Reported.Add(where))
			{
				ManualLogSource log = EirPlugin.Log;
				if (log != null)
				{
					log.LogError((object)("Eir: " + where + " threw and was ignored, so the game keeps its own behaviour. This is a bug in Eir; please report it. It will not be reported again this session.\n" + ex));
				}
			}
		}

		public static void Warn(string where, string message)
		{
			if (Reported.Add(where))
			{
				ManualLogSource log = EirPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Eir: " + message));
				}
			}
		}
	}
}
namespace GBV.Eir.Fixes
{
	internal sealed class BasementsMaterialFix : IFix
	{
		private const string Name = "Basements";

		private const string KnownVersion = "2.0.0";

		private const string ModType = "Basements.BasementsMod";

		private const string SwapMethod = "ReplaceLegacyPrefabMaterials";

		private const string PrefabProperty = "BasementPrefab";

		private static MethodInfo _swap;

		private static PropertyInfo _prefab;

		private static bool _done;

		public string Id => "Basements_ClientMaterials";

		public string Category => "Fixes";

		public string TargetName => "Basements";

		public string TargetGuid => "com.rolopogo.Basement";

		public string Summary => "Basements has no floor or ceiling when you play on a dedicated server. Its fix for Valheim 1.0 only runs on whoever loads the world, so a client never gets it. This runs the same fix on the client.";

		public string AppliedNote => null;

		public void Bind(ConfigFile config)
		{
		}

		public string Detect()
		{
			if (!FixRegistry.PluginLoaded(TargetGuid))
			{
				return TargetName + " is not installed";
			}
			PluginInfo obj = Chainloader.PluginInfos[TargetGuid];
			object obj2;
			if (obj == null)
			{
				obj2 = null;
			}
			else
			{
				BepInPlugin metadata = obj.Metadata;
				obj2 = ((metadata != null) ? metadata.Version : null);
			}
			Version version = (Version)obj2;
			if (version == null || version.ToString() != "2.0.0")
			{
				return TargetName + " reports version " + ((version == null) ? "nothing" : version.ToString()) + " and this fix was written against 2.0.0";
			}
			return null;
		}

		public string Apply(Harmony harmony)
		{
			Type type = AccessTools.TypeByName("Basements.BasementsMod");
			if (type == null)
			{
				return "could not find the type Basements.BasementsMod";
			}
			_swap = AccessTools.DeclaredMethod(type, "ReplaceLegacyPrefabMaterials", new Type[0], (Type[])null);
			if (_swap == null)
			{
				return "found Basements.BasementsMod but not its ReplaceLegacyPrefabMaterials() method";
			}
			_prefab = AccessTools.DeclaredProperty(type, "BasementPrefab");
			if (_prefab == null)
			{
				return "found Basements.BasementsMod but not its BasementPrefab property";
			}
			return null;
		}

		internal static void Tick()
		{
			try
			{
				if (!_done && !(_swap == null) && !(_prefab == null) && !((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer() && !((Object)(object)ZNetScene.instance == (Object)null) && _prefab.GetValue(null) is GameObject)
				{
					_done = true;
					_swap.Invoke(null, null);
					EirPlugin.Log.LogInfo((object)"Eir: ran Basements's material swap on this client. It is wired to a server-only hook, so a basement had no floor or ceiling until now. Basements logs the number of materials it replaced on the next line; zero there would mean the scene was not ready and is worth reporting.");
				}
			}
			catch (Exception ex)
			{
				Safety.Failed("BasementsMaterialFix.Tick", ex);
			}
		}
	}
	internal static class Drugs
	{
		public enum Group
		{
			None,
			Spliff,
			Bong,
			Shroom
		}

		public const string CbdItem = "CBDPotion";

		private static readonly string[] SpliffItems = new string[8] { "jointbadger", "jointcherry", "jointdesire", "jointkush", "jointlights", "jointmystic", "jointpoison", "jointwidow" };

		private const string BongItem = "LokisSpecialBlend";

		private const string ShroomItem = "MagicMushrooms";

		private static readonly string[] SpliffEffects = new string[8] { "BadgerStatusEffect", "CherryStatusEffect", "DesireStatusEffect", "KushStatusEffect", "LightsStatusEffect", "MysticStatusEffect", "PoisonStatusEffect", "WidowStatusEffect" };

		private static readonly string[] BongEffects = new string[1] { "BongStatusEffect" };

		private static readonly string[] ShroomEffects = new string[1] { "LibertyStatusEffect" };

		private static int[] _spliffHashes;

		private static int[] _bongHashes;

		private static int[] _shroomHashes;

		private static readonly float[] EndedAt = new float[4] { -9999f, -9999f, -9999f, -9999f };

		private static readonly bool[] WasActive = new bool[4];

		public static Group GroupOf(string prefabName)
		{
			if (string.IsNullOrEmpty(prefabName))
			{
				return Group.None;
			}
			if (prefabName == "LokisSpecialBlend")
			{
				return Group.Bong;
			}
			if (prefabName == "MagicMushrooms")
			{
				return Group.Shroom;
			}
			if (Array.IndexOf(SpliffItems, prefabName) >= 0)
			{
				return Group.Spliff;
			}
			return Group.None;
		}

		public static bool IsDrug(string prefabName)
		{
			return GroupOf(prefabName) != Group.None;
		}

		public static bool IsDrugEffect(string effectName)
		{
			if (string.IsNullOrEmpty(effectName))
			{
				return false;
			}
			if (Array.IndexOf(SpliffEffects, effectName) < 0 && Array.IndexOf(BongEffects, effectName) < 0)
			{
				return Array.IndexOf(ShroomEffects, effectName) >= 0;
			}
			return true;
		}

		public static float Remaining(Group group)
		{
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null)
				{
					return 0f;
				}
				SEMan sEMan = ((Character)localPlayer).GetSEMan();
				if (sEMan == null)
				{
					return 0f;
				}
				float num = 0f;
				int[] array = HashesFor(group);
				foreach (int num2 in array)
				{
					StatusEffect statusEffect = sEMan.GetStatusEffect(num2);
					if (!((Object)(object)statusEffect == (Object)null))
					{
						float remaningTime = statusEffect.GetRemaningTime();
						if (remaningTime > num)
						{
							num = remaningTime;
						}
					}
				}
				return num;
			}
			catch (Exception ex)
			{
				Safety.Failed("Drugs.Remaining", ex);
				return 0f;
			}
		}

		public static float SinceEnded(Group group)
		{
			return Time.time - EndedAt[(int)group];
		}

		public static bool AnyActive()
		{
			if (!(Remaining(Group.Spliff) > 0f) && !(Remaining(Group.Bong) > 0f))
			{
				return Remaining(Group.Shroom) > 0f;
			}
			return true;
		}

		public static void Poll()
		{
			for (int i = 1; i <= 3; i++)
			{
				bool flag = Remaining((Group)i) > 0f;
				if (WasActive[i] && !flag)
				{
					EndedAt[i] = Time.time;
				}
				WasActive[i] = flag;
			}
		}

		private static int[] HashesFor(Group group)
		{
			if (_spliffHashes == null)
			{
				_spliffHashes = Hash(SpliffEffects);
				_bongHashes = Hash(BongEffects);
				_shroomHashes = Hash(ShroomEffects);
			}
			return group switch
			{
				Group.Spliff => _spliffHashes, 
				Group.Bong => _bongHashes, 
				Group.Shroom => _shroomHashes, 
				_ => new int[0], 
			};
		}

		private static int[] Hash(string[] names)
		{
			int[] array = new int[names.Length];
			for (int i = 0; i < names.Length; i++)
			{
				array[i] = StringExtensionMethods.GetStableHashCode(names[i]);
			}
			return array;
		}

		public static string Describe(Group group)
		{
			return group switch
			{
				Group.Bong => "the bong", 
				Group.Shroom => "mushrooms", 
				_ => "a smoke", 
			};
		}
	}
	internal static class FixRegistry
	{
		private static readonly List<IFix> All = new List<IFix>
		{
			new WeedheimFix(),
			new WeedheimConsumeFix(),
			new WeedheimBongFix(),
			new WeedheimCooldownFix(),
			new WeedheimDamageFix(),
			new WeedheimVfxFix(),
			new BasementsMaterialFix(),
			new WeedheimPriceTweak(),
			new WeedheimCbdTweak(),
			new WeedheimPotheadTweak(),
			new SecondChanceSicknessFix()
		};

		private static readonly Dictionary<string, ConfigEntry<bool>> Toggles = new Dictionary<string, ConfigEntry<bool>>(StringComparer.Ordinal);

		public static bool IsEnabled(string id)
		{
			if (Toggles.TryGetValue(id, out var value))
			{
				return value.Value;
			}
			return false;
		}

		public static void ApplyAll(Harmony harmony, ConfigFile config)
		{
			foreach (IFix item in All)
			{
				bool flag = item.Category == "Tweaks";
				ConfigEntry<bool> val = config.Bind<bool>(item.Category, item.Id, true, item.Summary + "\nDoes nothing unless " + item.TargetName + " is installed. " + (flag ? ("This is a deliberate balance change, not a bug fix - turn it off to leave " + item.TargetName + " playing the way its author intended.") : ("Turn off if this fix ever causes trouble, or once " + item.TargetName + " has fixed it themselves.")));
				Toggles[item.Id] = val;
				item.Bind(config);
				try
				{
					if (!val.Value)
					{
						EirPlugin.Log.LogInfo((object)("Eir: " + item.Id + " - skipped, switched off in the config."));
						continue;
					}
					string text = item.Detect();
					if (text != null)
					{
						EirPlugin.Log.LogInfo((object)("Eir: " + item.Id + " - skipped, " + text + "."));
						continue;
					}
					string text2 = item.Apply(harmony);
					if (text2 == null)
					{
						EirPlugin.Log.LogInfo((object)("Eir: " + item.Id + " - applied."));
						if (!string.IsNullOrEmpty(item.AppliedNote))
						{
							EirPlugin.Log.LogWarning((object)("Eir: " + item.Id + " - " + item.AppliedNote));
						}
						continue;
					}
					EirPlugin.Log.LogError((object)("Eir: " + item.Id + " - FAILED, " + text2 + ". " + item.TargetName + " may have changed since this fix was written, so the fix is doing nothing and the original bug is still there. Please report this."));
				}
				catch (Exception ex)
				{
					EirPlugin.Log.LogError((object)("Eir: " + item.Id + " - FAILED, applying it threw. The fix is doing nothing and the original bug is still there. Please report this.\n" + ex));
				}
			}
		}

		public static bool PluginLoaded(string guid)
		{
			try
			{
				return Chainloader.PluginInfos != null && Chainloader.PluginInfos.ContainsKey(guid);
			}
			catch (Exception ex)
			{
				Safety.Failed("FixRegistry.PluginLoaded(" + guid + ")", ex);
				return false;
			}
		}
	}
	internal interface IFix
	{
		string Id { get; }

		string Category { get; }

		string TargetName { get; }

		string TargetGuid { get; }

		string Summary { get; }

		string AppliedNote { get; }

		string Detect();

		void Bind(ConfigFile config);

		string Apply(Harmony harmony);
	}
	internal static class PotheadSkill
	{
		internal static readonly SkillType Skill = (SkillType)(StringExtensionMethods.GetStableHashCode("gbv.eir.pothead") & 0x7FFFFFFF);

		private static readonly string NameKey = "skill_" + ((int)Skill).ToString().ToLowerInvariant();

		private static readonly string NameToken = "$" + NameKey;

		private const string SkillName = "Pothead";

		private static bool _named;

		internal static int Level
		{
			get
			{
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					Player localPlayer = Player.m_localPlayer;
					return (!((Object)(object)localPlayer == (Object)null)) ? Mathf.FloorToInt(((Character)localPlayer).GetSkills().GetSkillLevel(Skill)) : 0;
				}
				catch (Exception ex)
				{
					Safety.Failed("PotheadSkill.Level", ex);
					return 0;
				}
			}
		}

		internal static float DurationFactor(float atFull)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null)
				{
					return 1f;
				}
				float num = Mathf.Clamp01(((Character)localPlayer).GetSkills().GetSkillLevel(Skill) / 100f);
				return Mathf.Lerp(1f, Mathf.Max(1f, atFull), num);
			}
			catch (Exception ex)
			{
				Safety.Failed("PotheadSkill.DurationFactor", ex);
				return 1f;
			}
		}

		internal static void Raise(float amount)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!(amount <= 0f))
				{
					Player localPlayer = Player.m_localPlayer;
					if (!((Object)(object)localPlayer == (Object)null))
					{
						Skills skills = ((Character)localPlayer).GetSkills();
						Register(skills);
						skills.RaiseSkill(Skill, amount);
					}
				}
			}
			catch (Exception ex)
			{
				Safety.Failed("PotheadSkill.Raise", ex);
			}
		}

		internal static void Register(Skills skills)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			if ((Object)(object)skills == (Object)null || skills.m_skills == null)
			{
				return;
			}
			SkillDef val = null;
			foreach (SkillDef skill in skills.m_skills)
			{
				if (skill != null && skill.m_skill == Skill)
				{
					val = skill;
					break;
				}
			}
			if (val == null)
			{
				val = new SkillDef
				{
					m_skill = Skill,
					m_description = "Practice makes perfect. At 100 every high lasts twice as long.",
					m_increseStep = 1f
				};
				skills.m_skills.Add(val);
			}
			if ((Object)(object)val.m_icon == (Object)null)
			{
				val.m_icon = Icon();
			}
			Name();
		}

		private static Sprite Icon()
		{
			try
			{
				ObjectDB instance = ObjectDB.instance;
				if ((Object)(object)instance == (Object)null)
				{
					return null;
				}
				string[] array = new string[3] { "jointkush", "MushroomYellow", "Mushroom" };
				foreach (string text in array)
				{
					GameObject itemPrefab = instance.GetItemPrefab(text);
					ItemDrop val = (((Object)(object)itemPrefab == (Object)null) ? null : itemPrefab.GetComponent<ItemDrop>());
					Sprite[] array2 = (((Object)(object)val == (Object)null) ? null : val.m_itemData.m_shared.m_icons);
					if (array2 != null && array2.Length != 0)
					{
						return array2[0];
					}
				}
				return null;
			}
			catch
			{
				return null;
			}
		}

		internal static void Name()
		{
			if (_named)
			{
				return;
			}
			try
			{
				Localization instance = Localization.instance;
				if (instance != null && AccessTools.Field(typeof(Localization), "m_translations")?.GetValue(instance) is Dictionary<string, string> dictionary)
				{
					dictionary[NameKey] = "Pothead";
					_named = true;
				}
			}
			catch (Exception ex)
			{
				Safety.Failed("PotheadSkill.Name", ex);
			}
		}

		internal static void Forget()
		{
			_named = false;
		}

		internal static bool Names(string token, out string name)
		{
			if (token != null && token.Length == NameToken.Length && token == NameToken)
			{
				name = "Pothead";
				return true;
			}
			name = null;
			return false;
		}

		internal static void PatchPersistence(Harmony harmony)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Expected O, but got Unknown
			try
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(Skills), "IsSkillValid", new Type[1] { typeof(SkillType) }, (Type[])null);
				if (methodInfo == null)
				{
					ManualLogSource log = EirPlugin.Log;
					if (log != null)
					{
						log.LogError((object)"Eir: could not find Skills.IsSkillValid, so the Pothead skill will NOT survive a logout. A game update has probably renamed it. Everything else still works; please report this.");
					}
				}
				else
				{
					harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(PotheadSkill), "IsSkillValidPostfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
			catch (Exception ex)
			{
				Safety.Failed("PotheadSkill.PatchPersistence", ex);
			}
		}

		private static void IsSkillValidPostfix(SkillType type, ref bool __result)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!__result && type == Skill)
				{
					__result = true;
				}
			}
			catch (Exception ex)
			{
				Safety.Failed("Skills.IsSkillValid", ex);
			}
		}

		internal static void SkillsAwakePostfix(Skills __instance)
		{
			try
			{
				Register(__instance);
			}
			catch (Exception ex)
			{
				Safety.Failed("Skills.Awake", ex);
			}
		}

		internal static void LocalizePostfix(string text, ref string __result)
		{
			try
			{
				if (Names(text, out var name))
				{
					__result = name;
				}
			}
			catch (Exception ex)
			{
				Safety.Failed("Localization.Localize", ex);
			}
		}

		internal static void SetLanguagePostfix()
		{
			try
			{
				Forget();
				Name();
			}
			catch (Exception ex)
			{
				Safety.Failed("Localization.SetLanguage", ex);
			}
		}
	}
	internal sealed class SecondChanceSicknessFix : IFix
	{
		private const string KnownVersion = "1.0.4";

		private const string Sickness = "RessurectCooldown_bal";

		private static ConfigEntry<bool> _keepAsTimer;

		private static bool _reported;

		private const string TargetNameStatic = "SecondChance";

		public string Id => "SecondChance_NoReviveSickness";

		public string Category => "Tweaks";

		public string TargetName => "SecondChance";

		public string TargetGuid => "balrond.astafaraios.BalrondSecondChance";

		public string Summary => "Being revived no longer gives you resurrection sickness. As shipped that sickness stops food healing you at all until it wears off, because it is built from the game's own post-death effect and inherits everything about it.";

		public string AppliedNote => null;

		internal static bool KeepAsTimer
		{
			get
			{
				if (_keepAsTimer != null)
				{
					return _keepAsTimer.Value;
				}
				return false;
			}
		}

		public void Bind(ConfigFile config)
		{
			_keepAsTimer = config.Bind<bool>("Tweaks", "SecondChance_KeepSicknessAsTimer", false, "Keep the resurrection sickness icon as a plain countdown showing when you can be revived again, with its penalties stripped out, instead of removing it altogether. Either way it stops blocking your healing.");
		}

		public string Detect()
		{
			if (!FixRegistry.PluginLoaded(TargetGuid))
			{
				return TargetName + " is not installed";
			}
			PluginInfo obj = Chainloader.PluginInfos[TargetGuid];
			object obj2;
			if (obj == null)
			{
				obj2 = null;
			}
			else
			{
				BepInPlugin metadata = obj.Metadata;
				obj2 = ((metadata != null) ? metadata.Version : null);
			}
			Version version = (Version)obj2;
			if (version == null || version.ToString() != "1.0.4")
			{
				return TargetName + " reports version " + ((version == null) ? "nothing" : version.ToString()) + " and this was written against 1.0.4";
			}
			return null;
		}

		public string Apply(Harmony harmony)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			//IL_00ac: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(SEMan), "AddStatusEffect", new Type[5]
			{
				typeof(StatusEffect),
				typeof(bool),
				typeof(int),
				typeof(float),
				typeof(short)
			}, (Type[])null);
			if (methodInfo == null)
			{
				return "could not find SEMan.AddStatusEffect(StatusEffect, bool, int, float, short)";
			}
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(SecondChanceSicknessFix), "Prefix", (Type[])null, (Type[])null)), new HarmonyMethod(AccessTools.DeclaredMethod(typeof(SecondChanceSicknessFix), "Postfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return null;
		}

		private static bool Prefix(StatusEffect statusEffect, ref StatusEffect __result)
		{
			try
			{
				if ((Object)(object)statusEffect == (Object)null)
				{
					return true;
				}
				if (((Object)statusEffect).name != "RessurectCooldown_bal")
				{
					return true;
				}
				if (KeepAsTimer)
				{
					return true;
				}
				__result = null;
				return false;
			}
			catch (Exception ex)
			{
				Safety.Failed("SecondChanceSicknessFix.Prefix", ex);
				return true;
			}
		}

		private static void Postfix(StatusEffect __result)
		{
			try
			{
				if ((Object)(object)__result == (Object)null || ((Object)__result).name != "RessurectCooldown_bal")
				{
					return;
				}
				SE_Stats val = (SE_Stats)(object)((__result is SE_Stats) ? __result : null);
				if (!((Object)(object)val == (Object)null))
				{
					if (!_reported)
					{
						_reported = true;
						EirPlugin.Log.LogInfo((object)("Eir: SecondChance resurrection sickness inherited health regen x" + val.m_healthRegenMultiplier + ", stamina regen x" + val.m_staminaRegenMultiplier + ", eitr regen x" + val.m_eitrRegenMultiplier + " from the game's post-death effect."));
					}
					if (val.m_healthRegenMultiplier <= 1f)
					{
						val.m_healthRegenMultiplier = 1f;
					}
					if (val.m_staminaRegenMultiplier <= 1f)
					{
						val.m_staminaRegenMultiplier = 1f;
					}
					if (val.m_eitrRegenMultiplier <= 1f)
					{
						val.m_eitrRegenMultiplier = 1f;
					}
					val.m_speedModifier = Mathf.Max(0f, val.m_speedModifier);
				}
			}
			catch (Exception ex)
			{
				Safety.Failed("SecondChanceSicknessFix.Postfix", ex);
			}
		}
	}
	internal sealed class WeedheimBongFix : IFix
	{
		private const string KnownVersion = "2.0.4";

		private const string ConsumeFix = "Weedheim_Consume";

		private const string Blend = "LokisSpecialBlend";

		private const string Bong = "LokisMagicalBong";

		public string Id => "Weedheim_BongRequired";

		public string TargetName => "Weedheim";

		public string TargetGuid => "MagicMike.Weedheim";

		public string Summary => "Loki's Special Blend can only be smoked while holding the Magical Bong, the way Weedheim intended. This puts back a requirement the smoking fix had to remove, so the strongest smoke in the mod costs two purchases rather than one.";

		public string AppliedNote => null;

		public string Category => "Fixes";

		public void Bind(ConfigFile config)
		{
		}

		public string Detect()
		{
			if (!FixRegistry.PluginLoaded(TargetGuid))
			{
				return TargetName + " is not installed";
			}
			PluginInfo obj = Chainloader.PluginInfos[TargetGuid];
			object obj2;
			if (obj == null)
			{
				obj2 = null;
			}
			else
			{
				BepInPlugin metadata = obj.Metadata;
				obj2 = ((metadata != null) ? metadata.Version : null);
			}
			Version version = (Version)obj2;
			if (version == null || version.ToString() != "2.0.4")
			{
				return TargetName + " reports version " + ((version == null) ? "nothing" : version.ToString()) + " and this fix was written against 2.0.4";
			}
			if (!FixRegistry.IsEnabled("Weedheim_Consume"))
			{
				return "the Weedheim_Consume fix is switched off, so Weedheim's own gate is still in place";
			}
			return null;
		}

		public string Apply(Harmony harmony)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(Player), "ConsumeItem", new Type[3]
			{
				typeof(Inventory),
				typeof(ItemData),
				typeof(bool)
			}, (Type[])null);
			if (methodInfo == null)
			{
				return "could not find Player.ConsumeItem(Inventory, ItemData, bool)";
			}
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(WeedheimBongFix), "Prefix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return null;
		}

		private static bool Prefix(Player __instance, ItemData item, ref bool __result)
		{
			try
			{
				if ((Object)(object)__instance == (Object)null || item == null)
				{
					return true;
				}
				if ((Object)(object)item.m_dropPrefab == (Object)null)
				{
					return true;
				}
				if (((Object)item.m_dropPrefab).name != "LokisSpecialBlend")
				{
					return true;
				}
				ItemData rightItem = ((Humanoid)__instance).RightItem;
				if (rightItem != null && (Object)(object)rightItem.m_dropPrefab != (Object)null && ((Object)rightItem.m_dropPrefab).name == "LokisMagicalBong")
				{
					return true;
				}
				((Character)__instance).Message((MessageType)2, "You need Loki's Magical Bong in hand for this.", 0, (Sprite)null, false);
				__result = false;
				return false;
			}
			catch (Exception ex)
			{
				Safety.Failed("WeedheimBongFix.Prefix", ex);
				return true;
			}
		}
	}
	internal sealed class WeedheimCbdTweak : IFix
	{
		private const string KnownVersion = "2.0.4";

		private const string EffectsFix = "Weedheim_Effects";

		private const string TargetType = "Weedheim.EffectPatches+DruggedEffect";

		private const string TargetMethod = "Postfix";

		private const int ExpectedChecks = 11;

		private static ConfigEntry<bool> _untilHighEnds;

		private static bool _clear;

		private static int _gated;

		public string Id => "Weedheim_CbdClearsVisuals";

		public string Category => "Tweaks";

		public string TargetName => "Weedheim";

		public string TargetGuid => "MagicMike.Weedheim";

		public string Summary => "The CBD potion clears the screen effects but keeps your buff running, instead of cancelling the high altogether. Makes it something worth carrying rather than an emergency measure.";

		public string AppliedNote => null;

		public void Bind(ConfigFile config)
		{
			_untilHighEnds = config.Bind<bool>("Tweaks", "Weedheim_CbdLastsUntilHighEnds", true, "Keep the screen clear until your current high runs out. Turn off and the potion only holds until you smoke again, which with one smoke at a time comes to much the same thing.");
		}

		public string Detect()
		{
			if (!FixRegistry.PluginLoaded(TargetGuid))
			{
				return TargetName + " is not installed";
			}
			PluginInfo obj = Chainloader.PluginInfos[TargetGuid];
			object obj2;
			if (obj == null)
			{
				obj2 = null;
			}
			else
			{
				BepInPlugin metadata = obj.Metadata;
				obj2 = ((metadata != null) ? metadata.Version : null);
			}
			Version version = (Version)obj2;
			if (version == null || version.ToString() != "2.0.4")
			{
				return TargetName + " reports version " + ((version == null) ? "nothing" : version.ToString()) + " and this tweak was written against 2.0.4";
			}
			if (!FixRegistry.IsEnabled("Weedheim_Effects"))
			{
				return "the Weedheim_Effects fix is switched off, so there are no screen effects to clear";
			}
			return null;
		}

		public string Apply(Harmony harmony)
		{
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Expected O, but got Unknown
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Expected O, but got Unknown
			Type type = AccessTools.TypeByName("Weedheim.EffectPatches+DruggedEffect");
			if (type == null)
			{
				return "could not find the type Weedheim.EffectPatches+DruggedEffect";
			}
			MethodInfo methodInfo = AccessTools.DeclaredMethod(type, "Postfix", new Type[1] { typeof(Player).MakeByRefType() }, (Type[])null);
			if (methodInfo == null)
			{
				return "found Weedheim.EffectPatches+DruggedEffect but not its Postfix(ref Player) method";
			}
			MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(Player), "ConsumeItem", new Type[3]
			{
				typeof(Inventory),
				typeof(ItemData),
				typeof(bool)
			}, (Type[])null);
			if (methodInfo2 == null)
			{
				return "could not find Player.ConsumeItem(Inventory, ItemData, bool)";
			}
			_gated = -1;
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(WeedheimCbdTweak), "Transpiler", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null);
			if (_gated != 11)
			{
				return "gated " + _gated + " of " + 11 + " status checks in Weedheim.EffectPatches+DruggedEffect.Postfix, so the potion would only half work";
			}
			harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(WeedheimCbdTweak), "OnConsumed", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return null;
		}

		public static bool Gate(bool have)
		{
			if (have)
			{
				return !_clear;
			}
			return false;
		}

		private static void OnConsumed(ItemData item, bool __result)
		{
			try
			{
				if (__result && item != null && !((Object)(object)item.m_dropPrefab == (Object)null))
				{
					string name = ((Object)item.m_dropPrefab).name;
					if (name == "CBDPotion")
					{
						_clear = true;
					}
					else if (Drugs.IsDrug(name))
					{
						_clear = false;
					}
				}
			}
			catch (Exception ex)
			{
				Safety.Failed("WeedheimCbdTweak.OnConsumed", ex);
			}
		}

		public static void Tick()
		{
			if (_clear && _untilHighEnds != null && _untilHighEnds.Value && !Drugs.AnyActive())
			{
				_clear = false;
			}
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			try
			{
				MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(WeedheimCbdTweak), "Gate", (Type[])null, (Type[])null);
				List<CodeInstruction> list2 = new List<CodeInstruction>(list.Count + 16);
				int num = 0;
				foreach (CodeInstruction item in list)
				{
					list2.Add(item);
					MethodBase methodBase = item.operand as MethodBase;
					if (!(methodBase == null) && !(methodBase.Name != "HaveStatusEffect") && !(methodBase.DeclaringType == null) && !(methodBase.DeclaringType.Name != "SEMan"))
					{
						list2.Add(new CodeInstruction(OpCodes.Call, (object)methodInfo));
						num++;
					}
				}
				_gated = num;
				return list2;
			}
			catch (Exception ex)
			{
				Safety.Failed("WeedheimCbdTweak.Transpiler", ex);
				_gated = -1;
				return list;
			}
		}
	}
	internal sealed class WeedheimConsumeFix : IFix
	{
		private const string KnownVersion = "2.0.4";

		private const string TargetType = "Weedheim.EffectPatches+Player_ConsumeItem_Patch";

		private const string TargetMethod = "Prefix";

		public string Id => "Weedheim_Consume";

		public string TargetName => "Weedheim";

		public string TargetGuid => "MagicMike.Weedheim";

		public string Summary => "Makes Weedheim's joints, bong, mushrooms and CBD potion usable again. On Valheim 1.0 right-clicking any of them does nothing at all, because the mod reads a game field that has changed type and the error cancels the whole action.";

		public string AppliedNote => "Weedheim's own cooldowns between smokes are gone with it, along with needing the bong in hand for Loki's Special Blend and the CBD potion's clearing of active effects. None of those worked before this fix either, because the code holding them could not run at all. Turn this fix off to go back to nothing being smokeable.";

		public string Category => "Fixes";

		public void Bind(ConfigFile config)
		{
		}

		public string Detect()
		{
			if (!FixRegistry.PluginLoaded(TargetGuid))
			{
				return TargetName + " is not installed";
			}
			PluginInfo obj = Chainloader.PluginInfos[TargetGuid];
			object obj2;
			if (obj == null)
			{
				obj2 = null;
			}
			else
			{
				BepInPlugin metadata = obj.Metadata;
				obj2 = ((metadata != null) ? metadata.Version : null);
			}
			Version version = (Version)obj2;
			if (version == null || version.ToString() != "2.0.4")
			{
				return TargetName + " reports version " + ((version == null) ? "nothing" : version.ToString()) + " and this fix was written against 2.0.4, so it has been left alone in case the bug is already fixed";
			}
			FieldInfo fieldInfo = AccessTools.DeclaredField(typeof(VisEquipment), "m_rightItem");
			if (fieldInfo != null && fieldInfo.FieldType == typeof(string))
			{
				return "the game's VisEquipment.m_rightItem is a string again, so " + TargetName + "'s own code works";
			}
			return null;
		}

		public string Apply(Harmony harmony)
		{
			Type type = AccessTools.TypeByName("Weedheim.EffectPatches+Player_ConsumeItem_Patch");
			if (type == null)
			{
				return "could not find the type Weedheim.EffectPatches+Player_ConsumeItem_Patch";
			}
			MethodInfo methodInfo = AccessTools.DeclaredMethod(type, "Prefix", new Type[2]
			{
				typeof(Player),
				typeof(ItemData)
			}, (Type[])null);
			if (methodInfo == null)
			{
				return "found Weedheim.EffectPatches+Player_ConsumeItem_Patch but not its Prefix(Player, ItemDrop.ItemData) method";
			}
			MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(Player), "ConsumeItem", new Type[3]
			{
				typeof(Inventory),
				typeof(ItemData),
				typeof(bool)
			}, (Type[])null);
			if (methodInfo2 == null)
			{
				return "could not find Player.ConsumeItem(Inventory, ItemData, bool)";
			}
			if (!IsPrefixOf(methodInfo2, methodInfo))
			{
				return TargetName + "'s prefix is not attached to Player.ConsumeItem, so there is nothing to remove and consuming is broken for some other reason";
			}
			harmony.Unpatch((MethodBase)methodInfo2, methodInfo);
			if (IsPrefixOf(methodInfo2, methodInfo))
			{
				return "removing " + TargetName + "'s prefix from Player.ConsumeItem did not take, and consuming is still broken";
			}
			return null;
		}

		private static bool IsPrefixOf(MethodBase original, MethodInfo patch)
		{
			Patches patchInfo = Harmony.GetPatchInfo(original);
			if (patchInfo?.Prefixes == null)
			{
				return false;
			}
			foreach (Patch prefix in patchInfo.Prefixes)
			{
				if (prefix.PatchMethod == patch)
				{
					return true;
				}
			}
			return false;
		}
	}
	internal sealed class WeedheimCooldownFix : IFix
	{
		private const string KnownVersion = "2.0.4";

		private const string ConsumeFix = "Weedheim_Consume";

		private static ConfigEntry<int> _gap;

		public string Id => "Weedheim_Cooldown";

		public string Category => "Fixes";

		public string TargetName => "Weedheim";

		public string TargetGuid => "MagicMike.Weedheim";

		public string Summary => "One smoke at a time. You cannot light another until the last one wears off, the way Weedheim intended. Without it every strain can be smoked at once and every buff run together. Joints share a timer with each other; the bong and mushrooms keep their own.";

		public string AppliedNote => null;

		public void Bind(ConfigFile config)
		{
			_gap = config.Bind<int>("Fixes", "Weedheim_SmokeGapSeconds", 60, "Extra seconds to wait after a high wears off before you can light another. Weedheim's own value was 60. Set to 0 to be able to smoke again the moment the last one ends.");
		}

		public string Detect()
		{
			if (!FixRegistry.PluginLoaded(TargetGuid))
			{
				return TargetName + " is not installed";
			}
			PluginInfo obj = Chainloader.PluginInfos[TargetGuid];
			object obj2;
			if (obj == null)
			{
				obj2 = null;
			}
			else
			{
				BepInPlugin metadata = obj.Metadata;
				obj2 = ((metadata != null) ? metadata.Version : null);
			}
			Version version = (Version)obj2;
			if (version == null || version.ToString() != "2.0.4")
			{
				return TargetName + " reports version " + ((version == null) ? "nothing" : version.ToString()) + " and this fix was written against 2.0.4";
			}
			if (!FixRegistry.IsEnabled("Weedheim_Consume"))
			{
				return "the Weedheim_Consume fix is switched off, so Weedheim's own cooldown is still in place";
			}
			return null;
		}

		public string Apply(Harmony harmony)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(Player), "ConsumeItem", new Type[3]
			{
				typeof(Inventory),
				typeof(ItemData),
				typeof(bool)
			}, (Type[])null);
			if (methodInfo == null)
			{
				return "could not find Player.ConsumeItem(Inventory, ItemData, bool)";
			}
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(WeedheimCooldownFix), "Prefix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return null;
		}

		private static bool Prefix(Player __instance, ItemData item, ref bool __result)
		{
			try
			{
				if ((Object)(object)__instance == (Object)null || item == null || (Object)(object)item.m_dropPrefab == (Object)null)
				{
					return true;
				}
				Drugs.Group obj = Drugs.GroupOf(((Object)item.m_dropPrefab).name);
				if (obj == Drugs.Group.None)
				{
					return true;
				}
				float num = Drugs.Remaining(obj);
				if (num > 0f)
				{
					Refuse(__instance, num, ref __result);
					return false;
				}
				int num2 = ((_gap != null) ? _gap.Value : 0);
				if (num2 > 0)
				{
					float num3 = Drugs.SinceEnded(obj);
					if (num3 < (float)num2)
					{
						Refuse(__instance, (float)num2 - num3, ref __result);
						return false;
					}
				}
				return true;
			}
			catch (Exception ex)
			{
				Safety.Failed("WeedheimCooldownFix.Prefix", ex);
				return true;
			}
		}

		private static void Refuse(Player player, float seconds, ref bool result)
		{
			int num = (int)Math.Ceiling(seconds);
			if (num < 1)
			{
				num = 1;
			}
			((Character)player).Message((MessageType)2, "You have had enough for now. " + num + "s.", 0, (Sprite)null, false);
			result = false;
		}
	}
	internal sealed class WeedheimDamageFix : IFix
	{
		private const string KnownVersion = "2.0.4";

		private static readonly HashSet<string> Reported = new HashSet<string>(StringComparer.Ordinal);

		public string Id => "Weedheim_DamageModifier";

		public string Category => "Fixes";

		public string TargetName => "Weedheim";

		public string TargetGuid => "MagicMike.Weedheim";

		public string Summary => "Weedheim's damage setting never reaches your attacks, and never appears on the item either, so a strain built around it looks like it does nothing. This makes the number work with every weapon and shows it on the tooltip.";

		public string AppliedNote => null;

		public void Bind(ConfigFile config)
		{
		}

		public string Detect()
		{
			if (!FixRegistry.PluginLoaded(TargetGuid))
			{
				return TargetName + " is not installed";
			}
			PluginInfo obj = Chainloader.PluginInfos[TargetGuid];
			object obj2;
			if (obj == null)
			{
				obj2 = null;
			}
			else
			{
				BepInPlugin metadata = obj.Metadata;
				obj2 = ((metadata != null) ? metadata.Version : null);
			}
			Version version = (Version)obj2;
			if (version == null || version.ToString() != "2.0.4")
			{
				return TargetName + " reports version " + ((version == null) ? "nothing" : version.ToString()) + " and this fix was written against 2.0.4";
			}
			return null;
		}

		public string Apply(Harmony harmony)
		{
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Expected O, but got Unknown
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(SEMan), "AddStatusEffect", new Type[5]
			{
				typeof(StatusEffect),
				typeof(bool),
				typeof(int),
				typeof(float),
				typeof(short)
			}, (Type[])null);
			if (methodInfo == null)
			{
				return "could not find SEMan.AddStatusEffect(StatusEffect, bool, int, float, short)";
			}
			MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(SE_Stats), "GetTooltipString", (Type[])null, (Type[])null);
			if (methodInfo2 == null)
			{
				return "could not find SE_Stats.GetTooltipString()";
			}
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(WeedheimDamageFix), "OnStatusEffectAdded", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(WeedheimDamageFix), "OnTooltip", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return null;
		}

		private unsafe static void OnStatusEffectAdded(StatusEffect __result)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)__result == (Object)null || !Drugs.IsDrugEffect(((Object)__result).name))
				{
					return;
				}
				SE_Stats val = (SE_Stats)(object)((__result is SE_Stats) ? __result : null);
				if (!((Object)(object)val == (Object)null) && !Mathf.Approximately(val.m_damageModifier, 1f))
				{
					SkillType modifyAttackSkill = val.m_modifyAttackSkill;
					val.m_modifyAttackSkill = (SkillType)999;
					if (Reported.Add(((Object)__result).name))
					{
						EirPlugin.Log.LogInfo((object)("Eir: " + ((Object)__result).name + " carries damage x" + val.m_damageModifier + ", which Weedheim limited to " + ((object)(*(SkillType*)(&modifyAttackSkill))/*cast due to .constrained prefix*/).ToString() + ". Now applied to every weapon."));
					}
				}
			}
			catch (Exception ex)
			{
				Safety.Failed("WeedheimDamageFix.OnStatusEffectAdded", ex);
			}
		}

		private static void OnTooltip(SE_Stats __instance, ref string __result)
		{
			try
			{
				if (!((Object)(object)__instance == (Object)null) && Drugs.IsDrugEffect(((Object)__instance).name) && !Mathf.Approximately(__instance.m_damageModifier, 1f))
				{
					string value = "Damage: <color=orange>" + ((__instance.m_damageModifier - 1f) * 100f).ToString("+0;-0") + "%</color>";
					StringBuilder stringBuilder = new StringBuilder(__result ?? string.Empty);
					if (stringBuilder.Length > 0 && stringBuilder[stringBuilder.Length - 1] != '\n')
					{
						stringBuilder.Append('\n');
					}
					stringBuilder.Append(value).Append('\n');
					__result = stringBuilder.ToString();
				}
			}
			catch (Exception ex)
			{
				Safety.Failed("WeedheimDamageFix.OnTooltip", ex);
			}
		}
	}
	internal sealed class WeedheimFix : IFix
	{
		private const string TargetType = "Weedheim.EffectPatches+DruggedEffect";

		private const string TargetMethod = "Postfix";

		private static FieldRef<Character, ZNetView> _nview;

		public string Id => "Weedheim";

		public string TargetName => "Weedheim";

		public string TargetGuid => "MagicMike.Weedheim";

		public string Summary => "Stops Weedheim throwing an error out of the game's player update, which it does every frame while you are spawning, dying, respawning or teleporting. Costs framerate and fills the log; nothing about the mod's effects changes.";

		public string AppliedNote => null;

		public string Category => "Fixes";

		public void Bind(ConfigFile config)
		{
		}

		public string Detect()
		{
			if (!FixRegistry.PluginLoaded(TargetGuid))
			{
				return TargetName + " is not installed";
			}
			return null;
		}

		public string Apply(Harmony harmony)
		{
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Expected O, but got Unknown
			//IL_00bd: Expected O, but got Unknown
			Type type = AccessTools.TypeByName("Weedheim.EffectPatches+DruggedEffect");
			if (type == null)
			{
				return "could not find the type Weedheim.EffectPatches+DruggedEffect";
			}
			MethodInfo methodInfo = AccessTools.DeclaredMethod(type, "Postfix", new Type[1] { typeof(Player).MakeByRefType() }, (Type[])null);
			if (methodInfo == null)
			{
				return "found Weedheim.EffectPatches+DruggedEffect but not its Postfix(ref Player) method";
			}
			FieldInfo fieldInfo = AccessTools.DeclaredField(typeof(Character), "m_nview");
			if (fieldInfo == null)
			{
				return "could not find Character.m_nview, which the fix has to null-check";
			}
			_nview = AccessTools.FieldRefAccess<Character, ZNetView>(fieldInfo);
			harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(WeedheimFix), "Prefix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(WeedheimFix), "Finalizer", (Type[])null, (Type[])null)), (HarmonyMethod)null);
			return null;
		}

		private static bool Prefix(Player __0)
		{
			try
			{
				if ((Object)(object)Player.m_localPlayer == (Object)null)
				{
					return false;
				}
				if ((Object)(object)__0 == (Object)null)
				{
					return false;
				}
				if (_nview == null || (Object)(object)_nview.Invoke((Character)(object)__0) == (Object)null)
				{
					return false;
				}
				if ((Object)(object)GameCamera.instance == (Object)null)
				{
					return false;
				}
				return true;
			}
			catch (Exception ex)
			{
				Safety.Failed("WeedheimFix.Prefix", ex);
				return true;
			}
		}

		private static Exception Finalizer(Exception __exception)
		{
			try
			{
				if (__exception != null)
				{
					Safety.Warn("WeedheimFix.Finalizer", "Weedheim's Player.Update postfix threw for a reason this fix does not cover, and the error was swallowed so it cannot cost you framerate. Weedheim's screen effects may misbehave. This is a bug in Weedheim, not in Eir, but please report it so the fix can be widened. It will not be reported again this session.\n" + __exception);
				}
			}
			catch
			{
			}
			return null;
		}
	}
	internal sealed class WeedheimPotheadTweak : IFix
	{
		private const string KnownVersion = "2.0.4";

		private static ConfigEntry<float> _xp;

		private static ConfigEntry<float> _atFull;

		public string Id => "Weedheim_PotheadSkill";

		public string Category => "Tweaks";

		public string TargetName => "Weedheim";

		public string TargetGuid => "MagicMike.Weedheim";

		public string Summary => "Adds a Pothead skill that levels as you smoke. Every high lasts longer as it rises, up to twice as long at 100. Turning this off leaves the skill dormant; levels already earned are kept.";

		public string AppliedNote => null;

		public void Bind(ConfigFile config)
		{
			_xp = config.Bind<float>("Tweaks", "Weedheim_PotheadXpPerSmoke", 100f, "Experience for each smoke. Reaching 100 takes 20,301 in total, and the accumulator resets at every level so anything past a level is lost. At 100 a smoke that is about 256 smokes, which is roughly 75 hours of play with a buff running - the cooldown means you cannot smoke faster than one per high.");
			_atFull = config.Bind<float>("Tweaks", "Weedheim_PotheadBonusAt100", 2f, "How much longer a high lasts at Pothead 100, as a multiplier. 2 is double. The bonus scales evenly from 1 at level 0, so half the skill is half the bonus.");
		}

		public string Detect()
		{
			if (!FixRegistry.PluginLoaded(TargetGuid))
			{
				return TargetName + " is not installed";
			}
			PluginInfo obj = Chainloader.PluginInfos[TargetGuid];
			object obj2;
			if (obj == null)
			{
				obj2 = null;
			}
			else
			{
				BepInPlugin metadata = obj.Metadata;
				obj2 = ((metadata != null) ? metadata.Version : null);
			}
			Version version = (Version)obj2;
			if (version == null || version.ToString() != "2.0.4")
			{
				return TargetName + " reports version " + ((version == null) ? "nothing" : version.ToString()) + " and this tweak was written against 2.0.4";
			}
			return null;
		}

		public string Apply(Harmony harmony)
		{
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Expected O, but got Unknown
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Expected O, but got Unknown
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Expected O, but got Unknown
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Expected O, but got Unknown
			//IL_01db: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(Player), "ConsumeItem", new Type[3]
			{
				typeof(Inventory),
				typeof(ItemData),
				typeof(bool)
			}, (Type[])null);
			if (methodInfo == null)
			{
				return "could not find Player.ConsumeItem(Inventory, ItemData, bool)";
			}
			MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(SEMan), "AddStatusEffect", new Type[5]
			{
				typeof(StatusEffect),
				typeof(bool),
				typeof(int),
				typeof(float),
				typeof(short)
			}, (Type[])null);
			if (methodInfo2 == null)
			{
				return "could not find SEMan.AddStatusEffect(StatusEffect, bool, int, float, short)";
			}
			MethodInfo methodInfo3 = AccessTools.DeclaredMethod(typeof(Skills), "Awake", (Type[])null, (Type[])null);
			if (methodInfo3 == null)
			{
				return "could not find Skills.Awake, so the skill could not be registered";
			}
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(WeedheimPotheadTweak), "OnConsumed", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(WeedheimPotheadTweak), "OnStatusEffectAdded", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(PotheadSkill), "SkillsAwakePostfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			PotheadSkill.PatchPersistence(harmony);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(Localization), "Localize", new Type[1] { typeof(string) }, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(PotheadSkill), "LocalizePostfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(Localization), "SetLanguage", (Type[])null, (Type[])null), (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(PotheadSkill), "SetLanguagePostfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return null;
		}

		private static void OnConsumed(ItemData item, bool __result)
		{
			try
			{
				if (__result && item != null && !((Object)(object)item.m_dropPrefab == (Object)null) && Drugs.IsDrug(((Object)item.m_dropPrefab).name))
				{
					PotheadSkill.Raise((_xp == null) ? 0f : _xp.Value);
				}
			}
			catch (Exception ex)
			{
				Safety.Failed("WeedheimPotheadTweak.OnConsumed", ex);
			}
		}

		private static void OnStatusEffectAdded(StatusEffect __result)
		{
			try
			{
				if (!((Object)(object)__result == (Object)null) && !(__result.m_ttl <= 0f) && Drugs.IsDrugEffect(((Object)__result).name))
				{
					float num = PotheadSkill.DurationFactor((_atFull == null) ? 2f : _atFull.Value);
					if (!(num <= 1f))
					{
						__result.m_ttl *= num;
					}
				}
			}
			catch (Exception ex)
			{
				Safety.Failed("WeedheimPotheadTweak.OnStatusEffectAdded", ex);
			}
		}
	}
	internal sealed class WeedheimPriceTweak : IFix
	{
		private const string KnownVersion = "2.0.4";

		private const string Blend = "LokisSpecialBlend";

		private static ConfigEntry<int> _price;

		private static readonly HashSet<string> Reported = new HashSet<string>(StringComparer.Ordinal);

		public string Id => "Weedheim_BlendPrice";

		public string Category => "Tweaks";

		public string TargetName => "Weedheim";

		public string TargetGuid => "MagicMike.Weedheim";

		public string Summary => "Sets what Loki charges for one Loki's Special Blend. The Blend is stronger than any strain you can grow, and Loki turns up in the Meadows early, so without a price on it there is little reason to farm.";

		public string AppliedNote => null;

		public void Bind(ConfigFile config)
		{
			_price = config.Bind<int>("Tweaks", "Weedheim_BlendPriceCoins", 100, "Coins for one Loki's Special Blend. Vanilla traders charge roughly 100 to 950 for a single item, so treat 100 as modest and raise it if the Blend still makes growing pointless. Set the Weedheim_BlendPrice switch to false to leave Loki's own price alone.");
		}

		public string Detect()
		{
			if (!FixRegistry.PluginLoaded(TargetGuid))
			{
				return TargetName + " is not installed";
			}
			PluginInfo obj = Chainloader.PluginInfos[TargetGuid];
			object obj2;
			if (obj == null)
			{
				obj2 = null;
			}
			else
			{
				BepInPlugin metadata = obj.Metadata;
				obj2 = ((metadata != null) ? metadata.Version : null);
			}
			Version version = (Version)obj2;
			if (version == null || version.ToString() != "2.0.4")
			{
				return TargetName + " reports version " + ((version == null) ? "nothing" : version.ToString()) + " and this tweak was written against 2.0.4";
			}
			if (_price == null || _price.Value < 0)
			{
				return "no valid price is configured";
			}
			return null;
		}

		public string Apply(Harmony harmony)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(Trader), "GetAvailableItems", (Type[])null, (Type[])null);
			if (methodInfo == null)
			{
				return "could not find Trader.GetAvailableItems()";
			}
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(WeedheimPriceTweak), "Postfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			return null;
		}

		private static void Postfix(List<TradeItem> __result)
		{
			try
			{
				if (__result == null || _price == null)
				{
					return;
				}
				foreach (TradeItem item in __result)
				{
					if ((Object)(object)item?.m_prefab == (Object)null)
					{
						continue;
					}
					string name = ((Object)((Component)item.m_prefab).gameObject).name;
					if (!string.IsNullOrEmpty(name))
					{
						if (Reported.Add(name))
						{
							EirPlugin.Log.LogInfo((object)("Eir: a trader stocks " + name + " at " + item.m_price + " coins (stack of " + item.m_stack + ")."));
						}
						if (!(name != "LokisSpecialBlend") && item.m_price != _price.Value)
						{
							EirPlugin.Log.LogInfo((object)("Eir: LokisSpecialBlend repriced from " + item.m_price + " to " + _price.Value + " coins."));
							item.m_price = _price.Value;
						}
					}
				}
			}
			catch (Exception ex)
			{
				Safety.Failed("WeedheimPriceTweak.Postfix", ex);
			}
		}
	}
	internal sealed class WeedheimVfxFix : IFix
	{
		private const string KnownVersion = "2.0.4";

		private const string TargetType = "Weedheim.EffectPatches+DruggedEffect";

		private const string TargetMethod = "Postfix";

		private const string GuardFix = "Weedheim";

		private static readonly string[] Switches = new string[3] { "enablePlugin", "enableTrippingVFX", "enableStonedVFX" };

		private static int _negated;

		public string Id => "Weedheim_Effects";

		public string TargetName => "Weedheim";

		public string TargetGuid => "MagicMike.Weedheim";

		public string Summary => "Turns on Weedheim's screen effects while you are stoned or tripping - the colour shift, the vignette and the rainbow. Three checks in the mod are written backwards, so the effects switch themselves off whenever the mod is switched on. Smoking currently gives you the buff and nothing to look at.";

		public string AppliedNote => "This one starts something rather than stopping it: Weedheim's screen effects have never run on Valheim 1.0, so anything that looks different while you are stoned is this fix working, not a new bug.";

		public string Category => "Fixes";

		public void Bind(ConfigFile config)
		{
		}

		public string Detect()
		{
			if (!FixRegistry.PluginLoaded(TargetGuid))
			{
				return TargetName + " is not installed";
			}
			PluginInfo obj = Chainloader.PluginInfos[TargetGuid];
			object obj2;
			if (obj == null)
			{
				obj2 = null;
			}
			else
			{
				BepInPlugin metadata = obj.Metadata;
				obj2 = ((metadata != null) ? metadata.Version : null);
			}
			Version version = (Version)obj2;
			if (version == null || version.ToString() != "2.0.4")
			{
				return TargetName + " reports version " + ((version == null) ? "nothing" : version.ToString()) + " and this fix was written against 2.0.4, so it has been left alone in case the checks are already the right way round";
			}
			if (!FixRegistry.IsEnabled("Weedheim"))
			{
				return "the Weedheim fix is switched off, and the effects this turns on are the code that fix guards";
			}
			return null;
		}

		public string Apply(Harmony harmony)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Expected O, but got Unknown
			Type type = AccessTools.TypeByName("Weedheim.EffectPatches+DruggedEffect");
			if (type == null)
			{
				return "could not find the type Weedheim.EffectPatches+DruggedEffect";
			}
			MethodInfo methodInfo = AccessTools.DeclaredMethod(type, "Postfix", new Type[1] { typeof(Player).MakeByRefType() }, (Type[])null);
			if (methodInfo == null)
			{
				return "found Weedheim.EffectPatches+DruggedEffect but not its Postfix(ref Player) method";
			}
			_negated = -1;
			harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(AccessTools.DeclaredMethod(typeof(WeedheimVfxFix), "Transpiler", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null);
			if (_negated != Switches.Length)
			{
				return "rewrote " + _negated + " of " + Switches.Length + " backwards checks in Weedheim.EffectPatches+DruggedEffect.Postfix, so the effects were left as they were";
			}
			return null;
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Expected O, but got Unknown
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			try
			{
				List<CodeInstruction> list2 = new List<CodeInstruction>(list.Count + 8);
				int num = 0;
				for (int i = 0; i < list.Count; i++)
				{
					CodeInstruction val = list[i];
					list2.Add(val);
					if (val.opcode != OpCodes.Ldsfld)
					{
						continue;
					}
					FieldInfo fieldInfo = val.operand as FieldInfo;
					if (!(fieldInfo == null) && !(fieldInfo.DeclaringType == null) && !(fieldInfo.DeclaringType.Name != "MainConfig") && Array.IndexOf(Switches, fieldInfo.Name) >= 0 && i + 1 < list.Count)
					{
						MethodBase methodBase = list[i + 1].operand as MethodBase;
						if (!(methodBase == null) && !(methodBase.Name != "get_Value"))
						{
							list2.Add(list[i + 1]);
							list2.Add(new CodeInstruction(OpCodes.Ldc_I4_0, (object)null));
							list2.Add(new CodeInstruction(OpCodes.Ceq, (object)null));
							i++;
							num++;
						}
					}
				}
				_negated = num;
				return list2;
			}
			catch (Exception ex)
			{
				Safety.Failed("WeedheimVfxFix.Transpiler", ex);
				_negated = -1;
				return list;
			}
		}
	}
}