Decompiled source of ElPopeAdventures v1.1.0

ElPopeAdventures.dll

Decompiled 10 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ElPopeAdventures.Deer;
using ElPopeAdventures.Deer.Patches;
using ElPopeAdventures.InventoryQoL;
using ElPopeAdventures.InventoryQoL.Patches;
using ElPopeAdventures.Mage;
using ElPopeAdventures.Mage.Patches;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("ElPopeAdventures")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0")]
[assembly: AssemblyProduct("ElPopeAdventures")]
[assembly: AssemblyTitle("ElPopeAdventures")]
[assembly: AssemblyVersion("1.1.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 ElPopeAdventures
{
	internal interface IModule
	{
		string Name { get; }

		string Description { get; }

		Type[] PatchClasses { get; }

		void Initialize();

		void Bind(ConfigFile config);

		void OnSettingChanged();

		void Update();
	}
	[BepInPlugin("ElPope.ElPopeAdventures", "ElPopeAdventures", "1.1.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string ModGuid = "ElPope.ElPopeAdventures";

		public const string ModName = "ElPopeAdventures";

		public const string ModVersion = "1.1.0";

		internal static ManualLogSource Log;

		private readonly List<IModule> _active = new List<IModule>();

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			_harmony = new Harmony("ElPope.ElPopeAdventures");
			IModule[] array = new IModule[3]
			{
				new DeerModule(),
				new MageModule(),
				new InventoryModule()
			};
			foreach (IModule module in array)
			{
				if (!((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enable" + module.Name, true, new ConfigDescription(module.Description + " Requires a game restart. In multiplayer, everyone should use the same setting.", (AcceptableValueBase)null, Array.Empty<object>())).Value)
				{
					Log.LogInfo((object)(module.Name + " module is switched off in the config."));
					continue;
				}
				try
				{
					module.Bind(((BaseUnityPlugin)this).Config);
					module.Initialize();
				}
				catch (Exception arg)
				{
					Log.LogError((object)$"{module.Name} module could not start and stays off: {arg}");
					continue;
				}
				int num = 0;
				Type[] patchClasses = module.PatchClasses;
				foreach (Type type in patchClasses)
				{
					try
					{
						_harmony.CreateClassProcessor(type).Patch();
						num++;
					}
					catch (Exception ex)
					{
						Log.LogWarning((object)(module.Name + ": patch " + type.Name + " was not applied (" + ex.GetType().Name + ": " + ex.Message + "). The feature that needs it stays off; the rest of the game is unaffected."));
					}
				}
				_active.Add(module);
				Log.LogInfo((object)$"{module.Name} module loaded ({num}/{module.PatchClasses.Length} patches applied).");
			}
			((BaseUnityPlugin)this).Config.SettingChanged += OnSettingChanged;
			Log.LogInfo((object)"ElPopeAdventures 1.1.0 loaded.");
		}

		private void Update()
		{
			foreach (IModule item in _active)
			{
				try
				{
					item.Update();
				}
				catch (Exception arg)
				{
					Log.LogError((object)$"{item.Name}: {arg}");
				}
			}
		}

		private void OnDestroy()
		{
			((BaseUnityPlugin)this).Config.SettingChanged -= OnSettingChanged;
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void OnSettingChanged(object sender, SettingChangedEventArgs e)
		{
			foreach (IModule item in _active)
			{
				try
				{
					item.OnSettingChanged();
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)(item.Name + ": could not apply the changed settings: " + ex.Message));
				}
			}
		}
	}
}
namespace ElPopeAdventures.Mage
{
	internal static class MageClass
	{
		private const string ClassKey = "FrostMage.Class";

		private const string ClassValue = "Mage";

		private static readonly MethodInfo s_updateFood = AccessTools.Method(typeof(Player), "UpdateFood", new Type[2]
		{
			typeof(float),
			typeof(bool)
		}, (Type[])null);

		private static readonly List<string> s_bossKeys = new List<string>();

		private static float s_nextRefusal;

		internal static bool IsMage(Player player)
		{
			if ((Object)(object)player != (Object)null && player.m_customData != null && player.m_customData.TryGetValue("FrostMage.Class", out var value))
			{
				return value == "Mage";
			}
			return false;
		}

		internal static bool StaffsLocked(Player player)
		{
			if (Settings.StaffsRequireMage && (Object)(object)player != (Object)null)
			{
				return !IsMage(player);
			}
			return false;
		}

		internal static bool RefuseStaff(Player player)
		{
			if ((Object)(object)player != (Object)(object)Player.m_localPlayer || !StaffsLocked(player) || !MageItems.IsStaff(((Humanoid)player).GetCurrentWeapon()))
			{
				return false;
			}
			if (Time.time >= s_nextRefusal)
			{
				s_nextRefusal = Time.time + 2f;
				((Character)player).Message((MessageType)2, "Only a Mage can use this staff. Read a Mage Tome first.", 0, (Sprite)null, false);
			}
			return true;
		}

		internal static float BonusMana(Player player)
		{
			if (!IsMage(player))
			{
				return 0f;
			}
			return Settings.BaseMana + Settings.ManaPerBoss * (float)BossesDefeated(player);
		}

		internal static void FindBosses(ZNetScene scene)
		{
			s_bossKeys.Clear();
			foreach (GameObject prefab in scene.m_prefabs)
			{
				Character val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<Character>() : null);
				if ((Object)(object)val != (Object)null && val.m_boss && !string.IsNullOrEmpty(val.m_defeatSetGlobalKey) && !s_bossKeys.Contains(val.m_defeatSetGlobalKey))
				{
					s_bossKeys.Add(val.m_defeatSetGlobalKey);
				}
			}
			MageModule.Log.LogInfo((object)string.Format("Mage mana grows with {0} bosses: {1}.", s_bossKeys.Count, string.Join(", ", s_bossKeys.ToArray())));
		}

		private static int BossesDefeated(Player player)
		{
			ZoneSystem instance = ZoneSystem.instance;
			int num = 0;
			foreach (string s_bossKey in s_bossKeys)
			{
				if (((Object)(object)instance != (Object)null && instance.GetGlobalKey(s_bossKey)) || ((Humanoid)player).HaveUniqueKey(s_bossKey))
				{
					num++;
				}
			}
			return num;
		}

		internal static bool RefuseTome(Player player, ItemData item)
		{
			if (!MageItems.IsTome(item) || !IsMage(player))
			{
				return false;
			}
			((Character)player).Message((MessageType)2, "You are already a Mage.", 0, (Sprite)null, false);
			return true;
		}

		internal static bool RefuseCocktail(Player player, ItemData item)
		{
			if (!MageItems.IsCocktail(item) || ((Character)player).GetMaxEitr() > 0f)
			{
				return false;
			}
			((Character)player).Message((MessageType)2, "You have no mana to restore. Read a Mage Tome first.", 0, (Sprite)null, false);
			return true;
		}

		internal static void OnConsumed(Player player, ItemData item)
		{
			if (MageItems.IsTome(item) && !IsMage(player))
			{
				player.m_customData["FrostMage.Class"] = "Mage";
				float num = BonusMana(player);
				try
				{
					s_updateFood?.Invoke(player, new object[2] { 0f, true });
					((Character)player).AddEitr(num);
				}
				catch (Exception ex)
				{
					MageModule.Log.LogWarning((object)("Mana pool will appear within a second (" + ex.Message + ")."));
				}
				((Character)player).Message((MessageType)2, $"You have awakened as a Mage! ({num:0} mana)", 0, (Sprite)null, false);
				MageModule.Log.LogInfo((object)(player.GetPlayerName() + " became a Mage."));
			}
		}
	}
	internal enum ItemKind
	{
		Tome,
		Staff,
		Cocktail,
		Armor
	}
	internal enum ArmorSlot
	{
		Hood,
		Robe,
		Trousers
	}
	internal sealed class School
	{
		internal readonly string Name;

		internal readonly (string Prefab, int NativeTier)[] Sources;

		internal readonly Func<DamageTypes, DamageTypes> Convert;

		internal School(string name, (string, int)[] sources, Func<DamageTypes, DamageTypes> convert = null)
		{
			Name = name;
			Sources = sources;
			Convert = convert;
		}
	}
	internal sealed class MageItemDef
	{
		internal string Id;

		internal string Name;

		internal string Description;

		internal ItemKind Kind;

		internal int Tier;

		internal School School;

		internal string Station;

		internal int StationLevel = 1;

		internal string DefaultRecipe;

		internal int CraftAmount = 1;

		internal float Mana;

		internal string ArmorSource;

		internal float Armor;

		internal float EitrRegen;

		internal DamageType Resistance;

		internal string ConfigKey => Id.Substring("FrostMage_".Length);
	}
	internal static class MageContent
	{
		internal const string IdPrefix = "FrostMage_";

		internal static readonly string[] Biomes = new string[8] { "Meadows", "Black Forest", "Swamp", "Mountain", "Plains", "Mistlands", "Ashlands", "Deep North" };

		internal static readonly float[] TierDamage = new float[8] { 0.17f, 0.29f, 0.46f, 0.63f, 0.79f, 1f, 1.21f, 1.45f };

		internal static readonly float[] TierManaCost = new float[8] { 0.33f, 0.47f, 0.6f, 0.73f, 0.87f, 1f, 1f, 1f };

		internal static readonly string[] ArmorSetNames = new string[5] { "Apprentice", "Mossweave", "Mirewitch", "Frostbound", "Sunweave" };

		internal static readonly float[] TierArmor = new float[5] { 2f, 4f, 7f, 10f, 13f };

		internal static readonly float[] TierSetEitrRegen = new float[5] { 0.25f, 0.4f, 0.55f, 0.7f, 0.85f };

		internal static readonly int[] TierSetBonus = new int[5] { 5, 5, 10, 10, 15 };

		private const string Workbench = "$piece_workbench";

		private const string Forge = "$piece_forge";

		private const string GaldrTable = "$piece_magetable";

		private const string Cauldron = "$piece_cauldron";

		internal static readonly School Frost = new School("Frost", new(string, int)[1] { ("StaffIceShards", 5) });

		internal static readonly School Fire = new School("Fire", new(string, int)[1] { ("StaffFireball", 5) });

		internal static readonly School Arcane = new School("Arcane", new(string, int)[3]
		{
			("StaffThunderBlood", 7),
			("StaffLightning", 6),
			("StaffFireball", 5)
		}, delegate(DamageTypes d)
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			float totalDamage = ((DamageTypes)(ref d)).GetTotalDamage();
			return new DamageTypes
			{
				m_lightning = totalDamage * 0.6f,
				m_spirit = totalDamage * 0.4f
			};
		});

		internal static readonly string[] TomeSources = new string[3] { "Crystal", "FreezeGland", "Resin" };

		internal static readonly string[] CocktailSources = new string[1] { "MeadEitrMinor" };

		internal static readonly MageItemDef[] Items = new MageItemDef[33]
		{
			new MageItemDef
			{
				Id = "FrostMage_Tome",
				Kind = ItemKind.Tome,
				Name = "Mage Tome",
				Description = "Frozen runes of the old mages. Read it to awaken as a Mage: you gain a pool of mana (eitr) that grows with every boss you defeat, and learn to craft and wield mage staffs.",
				Station = "$piece_workbench",
				DefaultRecipe = "DeerHide:2,Resin:5,Dandelion:3"
			},
			Staff("FrostStaff", "Frost Staff", 0, Frost, "$piece_workbench", 1, "Wood:10,DeerHide:4,TrophyDeer:1", "An apprentice's staff that hurls shards of ice, chilling whatever they hit."),
			Staff("GlacialStaff", "Glacial Staff", 1, Frost, "$piece_forge", 1, "RoundLog:10,Bronze:6,GreydwarfEye:10,TrophyGreydwarfShaman:1", "Bronze-bound and bitterly cold. Its shards bite deeper than the apprentice's staff."),
			Staff("EmberStaff", "Ember Staff", 2, Fire, "$piece_forge", 2, "ElderBark:10,Iron:8,SurtlingCore:4,TrophyDraugr:1", "A surtling's heart smoulders in its tip. Hurls balls of fire that burst on impact."),
			Staff("BlazeStaff", "Blaze Staff", 3, Fire, "$piece_forge", 3, "ElderBark:10,Silver:10,Obsidian:6,SurtlingCore:6,TrophyCultist:1", "Taken from the fire cults of the mountain caves. Its fireballs burn hot enough to melt frost."),
			Staff("ArcaneStaff", "Arcane Staff", 4, Arcane, "$piece_forge", 4, "FineWood:10,BlackMetal:10,LinenThread:6,TrophyGoblinShaman:1", "Fuling shamans guarded this secret: raw magic, shaped into a bolt of lightning and spirit."),
			Staff("RimeStaff", "Rime Staff", 5, Frost, "$piece_magetable", 1, "YggdrasilWood:20,FreezeGland:4,Eitr:16", "Frost magic refined with eitr. Unleashes a storm of ice shards."),
			Staff("InfernoStaff", "Inferno Staff", 5, Fire, "$piece_magetable", 1, "YggdrasilWood:20,SurtlingCore:4,Eitr:16", "Fire magic refined with eitr. Its fireballs level anything caught in the blast."),
			Staff("MistruneStaff", "Mistrune Staff", 5, Arcane, "$piece_magetable", 1, "YggdrasilWood:20,BlackCore:2,Eitr:16", "Dvergr runes channel arcane bolts of lightning and spirit."),
			Staff("HoarfrostStaff", "Hoarfrost Staff", 6, Frost, "$piece_magetable", 2, "Blackwood:10,FlametalNew:4,CelestialFeather:3,FreezeGland:6", "Cold that even the Ashlands cannot thaw."),
			Staff("SurtrStaff", "Surtr's Staff", 6, Fire, "$piece_magetable", 2, "Blackwood:10,FlametalNew:4,CelestialFeather:3,MoltenCore:2", "Forged in flametal, burning with the fury of the fire giant."),
			Staff("StormStaff", "Stormcaller Staff", 6, Arcane, "$piece_magetable", 2, "Blackwood:10,FlametalNew:4,CelestialFeather:3,TrophyCharredMage:1", "Arcane lightning wrested from the charred warlocks."),
			Staff("JotunStaff", "Jotun Frost Staff", 7, Frost, "$piece_magetable", 3, "Frostwood:10,Gold:10,NornThread:5,Ice:10", "The deep cold of the north, bound in gold."),
			Staff("MuspelStaff", "Muspel Staff", 7, Fire, "$piece_magetable", 3, "Frostwood:10,Gold:10,NornThread:5,MoltenCore:4", "A fire that burns even in the endless northern winter."),
			Staff("NornStaff", "Norn Staff", 7, Arcane, "$piece_magetable", 3, "Frostwood:10,Gold:10,NornThread:5,TrophyJotunWitch:1", "Spun from the threads of fate. The mightiest arcane bolt a mage can cast."),
			Cocktail("BerryCocktail", "Meadow Berry Cocktail", 0, "$piece_workbench", "Raspberry:6,Dandelion:2", 40f, "Crushed raspberries and a sprig of dandelion. Tart, sweet, and it refills a mage's mana at once."),
			Cocktail("BlueberryCocktail", "Blueberry Cocktail", 1, "$piece_cauldron", "Blueberries:6,Thistle:2", 70f, "Blueberries muddled with thistle. The glow of the forest restores more mana."),
			Cocktail("CloudberryCocktail", "Cloudberry Cocktail", 4, "$piece_cauldron", "Cloudberry:6,Barley:2", 110f, "Golden cloudberries over barley. A mage's favourite on the plains."),
			Armor(ArmorSlot.Hood, 0, "DeerHide:4,Dandelion:3", "A soft deer-hide hood, stitched with meadow flowers.", (DamageType)0),
			Armor(ArmorSlot.Robe, 0, "DeerHide:6,LeatherScraps:4,Resin:5", "Every mage starts somewhere. This is where.", (DamageType)0),
			Armor(ArmorSlot.Trousers, 0, "DeerHide:5,LeatherScraps:4", "Plain, warm, and light enough to run in.", (DamageType)0),
			Armor(ArmorSlot.Hood, 1, "TrollHide:3,GreydwarfEye:6", "Troll hide lined with moss. The greydwarves' eyes still glow faintly.", (DamageType)0),
			Armor(ArmorSlot.Robe, 1, "TrollHide:5,GreydwarfEye:10,Bronze:2", "A heavy forest robe fastened with bronze.", (DamageType)0),
			Armor(ArmorSlot.Trousers, 1, "TrollHide:4,GreydwarfEye:8", "Mossy leggings that muffle a mage's steps.", (DamageType)0),
			Armor(ArmorSlot.Hood, 2, "Guck:8,Root:4,LeatherScraps:4", "Root-woven and soaked in guck. The swamp's poison slides off it.", (DamageType)256),
			Armor(ArmorSlot.Robe, 2, "Guck:10,Bloodbag:6,Iron:4", "Iron-clasped robes stained dark with the swamp.", (DamageType)0),
			Armor(ArmorSlot.Trousers, 2, "Guck:8,Bloodbag:4,Iron:2", "Waterproofed with guck, at a price to the nose.", (DamageType)0),
			Armor(ArmorSlot.Hood, 3, "WolfPelt:3,FreezeGland:3,Silver:2", "A wolf-pelt hood with a silver circlet.", (DamageType)0),
			Armor(ArmorSlot.Robe, 3, "WolfPelt:5,WolfHairBundle:10,Silver:6,FreezeGland:4", "Lined with freeze glands, it keeps the mountain's cold out.", (DamageType)64),
			Armor(ArmorSlot.Trousers, 3, "WolfPelt:4,WolfHairBundle:8,Silver:4", "Thick wolf fur, bound with silver thread.", (DamageType)0),
			Armor(ArmorSlot.Hood, 4, "LinenThread:12,Feathers:4,BlackMetal:2", "A sun-bleached linen hood with a black metal brow.", (DamageType)0),
			Armor(ArmorSlot.Robe, 4, "LinenThread:20,LoxPelt:3,BlackMetal:4", "Fine linen over lox pelt. The robe of a seasoned mage.", (DamageType)0),
			Armor(ArmorSlot.Trousers, 4, "LinenThread:16,LoxPelt:2,BlackMetal:3", "Linen and lox, made for long marches across the plains.", (DamageType)0)
		};

		internal static string ArmorSetId(int tier)
		{
			return "FrostMage_Set_" + ArmorSetNames[tier];
		}

		private unsafe static MageItemDef Armor(ArmorSlot slot, int tier, string recipe, string flavour, DamageType resistance = (DamageType)0)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			string text = ArmorSetNames[tier];
			float num = ((slot == ArmorSlot.Hood) ? 0.2f : 0.4f);
			float num2 = (float)Math.Round(TierSetEitrRegen[tier] * num, 2);
			string armorSource = slot switch
			{
				ArmorSlot.Robe => "ArmorMageChest", 
				ArmorSlot.Hood => "HelmetMage", 
				_ => "ArmorMageLegs", 
			};
			string text2 = (((int)resistance == 0) ? "" : (" Resistant to " + ((object)(*(DamageType*)(&resistance))/*cast due to .constrained prefix*/).ToString().ToLowerInvariant() + "."));
			MageItemDef mageItemDef = new MageItemDef();
			mageItemDef.Id = "FrostMage_" + text + slot;
			mageItemDef.Kind = ItemKind.Armor;
			mageItemDef.Name = $"{text} {slot}";
			mageItemDef.Tier = tier;
			mageItemDef.Station = "$piece_workbench";
			mageItemDef.StationLevel = 2;
			mageItemDef.DefaultRecipe = recipe;
			mageItemDef.ArmorSource = armorSource;
			mageItemDef.Armor = TierArmor[tier];
			mageItemDef.EitrRegen = num2;
			mageItemDef.Resistance = resistance;
			mageItemDef.Description = $"{flavour} Mage armour ({Biomes[tier]}): +{num2 * 100f:0}% eitr regeneration.{text2} " + $"Wear the {text} hood, robe and trousers together for +{TierSetBonus[tier]} Elemental Magic.";
			return mageItemDef;
		}

		private static MageItemDef Staff(string key, string name, int tier, School school, string station, int level, string recipe, string flavour)
		{
			MageItemDef mageItemDef = new MageItemDef();
			mageItemDef.Id = "FrostMage_" + key;
			mageItemDef.Kind = ItemKind.Staff;
			mageItemDef.Name = name;
			mageItemDef.Tier = tier;
			mageItemDef.School = school;
			mageItemDef.Station = station;
			mageItemDef.StationLevel = level;
			mageItemDef.DefaultRecipe = recipe;
			mageItemDef.Description = flavour + " " + school.Name + " magic (" + Biomes[tier] + "). Spends mana (eitr) with every cast.";
			return mageItemDef;
		}

		private static MageItemDef Cocktail(string key, string name, int tier, string station, string recipe, float mana, string flavour)
		{
			return new MageItemDef
			{
				Id = "FrostMage_" + key,
				Kind = ItemKind.Cocktail,
				Name = name,
				Tier = tier,
				Station = station,
				DefaultRecipe = recipe,
				Mana = mana,
				Description = flavour
			};
		}
	}
	internal static class MageItems
	{
		private sealed class Built
		{
			internal MageItemDef Def;

			internal ItemDrop Item;

			internal int NativeTier;

			internal DamageTypes VanillaDamage;

			internal DamageTypes VanillaDamagePerLevel;

			internal float VanillaAttackEitr;

			internal float VanillaReloadEitr;

			internal float VanillaDrawEitr;

			internal SE_Stats Effect;

			internal SE_Stats SetEffect;
		}

		private const string RecipePrefix = "Recipe_FrostMage_";

		private const int StaffMaxQuality = 4;

		private static readonly MethodInfo s_updateRegisters = AccessTools.Method(typeof(ObjectDB), "UpdateRegisters", (Type[])null, (Type[])null);

		private static readonly FieldInfo s_namedPrefabs = AccessTools.Field(typeof(ZNetScene), "m_namedPrefabs");

		private static readonly FieldInfo s_seNameHash = AccessTools.Field(typeof(StatusEffect), "m_nameHash");

		private static readonly HashSet<string> s_warned = new HashSet<string>();

		private static readonly Dictionary<int, SE_Stats> s_setEffects = new Dictionary<int, SE_Stats>();

		private static readonly Dictionary<string, Built> s_built = new Dictionary<string, Built>();

		private static GameObject s_container;

		private static IEnumerable<Built> AllBuilt => s_built.Values.Where((Built b) => (Object)(object)b.Item != (Object)null);

		internal static bool IsTome(ItemData item)
		{
			string id = MageContent.Items[0].Id;
			if (item != null && s_built.TryGetValue(id, out var value))
			{
				if (item.m_shared != value.Item.m_itemData.m_shared)
				{
					if ((Object)(object)item.m_dropPrefab != (Object)null)
					{
						return ((Object)item.m_dropPrefab).name == id;
					}
					return false;
				}
				return true;
			}
			return false;
		}

		internal static bool IsCocktail(ItemData item)
		{
			if (item == null)
			{
				return false;
			}
			foreach (Built value in s_built.Values)
			{
				if ((Object)(object)value.Item != (Object)null && value.Def.Kind == ItemKind.Cocktail && (item.m_shared == value.Item.m_itemData.m_shared || ((Object)(object)item.m_dropPrefab != (Object)null && ((Object)item.m_dropPrefab).name == value.Def.Id)))
				{
					return true;
				}
			}
			return false;
		}

		internal static bool IsStaff(ItemData item)
		{
			if (item == null)
			{
				return false;
			}
			foreach (Built value in s_built.Values)
			{
				if ((Object)(object)value.Item != (Object)null && value.Def.Kind == ItemKind.Staff && (item.m_shared == value.Item.m_itemData.m_shared || ((Object)(object)item.m_dropPrefab != (Object)null && ((Object)item.m_dropPrefab).name == value.Def.Id)))
				{
					return true;
				}
			}
			return false;
		}

		internal static bool IsMageOnlyRecipe(Recipe recipe)
		{
			if ((Object)(object)recipe == (Object)null || (Object)(object)recipe.m_item == (Object)null)
			{
				return false;
			}
			string name = ((Object)recipe.m_item).name;
			if (s_built.TryGetValue(name ?? "", out var value))
			{
				if (value.Def.Kind != ItemKind.Staff)
				{
					return value.Def.Kind == ItemKind.Armor;
				}
				return true;
			}
			return false;
		}

		internal static void RegisterInObjectDB(ObjectDB db)
		{
			if ((Object)(object)db == (Object)null || db.m_items == null || db.m_items.Count == 0)
			{
				return;
			}
			EnsureBuilt((Func<string, GameObject>)db.GetItemPrefab);
			bool flag = false;
			foreach (Built item in AllBuilt)
			{
				if (!db.m_items.Contains(((Component)item.Item).gameObject))
				{
					db.m_items.Add(((Component)item.Item).gameObject);
					flag = true;
				}
				if ((Object)(object)item.Effect != (Object)null && !db.m_StatusEffects.Contains((StatusEffect)(object)item.Effect))
				{
					db.m_StatusEffects.Add((StatusEffect)(object)item.Effect);
				}
				if ((Object)(object)item.SetEffect != (Object)null && !db.m_StatusEffects.Contains((StatusEffect)(object)item.SetEffect))
				{
					db.m_StatusEffects.Add((StatusEffect)(object)item.SetEffect);
				}
			}
			if (flag)
			{
				s_updateRegisters.Invoke(db, null);
			}
			ApplySettings();
			RebuildRecipes(db);
		}

		internal static void RegisterInScene(ZNetScene scene)
		{
			EnsureBuilt((Func<string, GameObject>)scene.GetPrefab);
			MageClass.FindBosses(scene);
			if (!(s_namedPrefabs?.GetValue(scene) is Dictionary<int, GameObject> dictionary))
			{
				Warn("scene", "Could not add the mage items to the game's network prefab list; they cannot be dropped on the ground.");
				return;
			}
			foreach (Built item in AllBuilt)
			{
				if (!scene.m_prefabs.Contains(((Component)item.Item).gameObject))
				{
					scene.m_prefabs.Add(((Component)item.Item).gameObject);
				}
				dictionary[StringExtensionMethods.GetStableHashCode(((Object)item.Item).name)] = ((Component)item.Item).gameObject;
			}
		}

		private static void EnsureBuilt(Func<string, GameObject> find)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			if ((Object)(object)s_container == (Object)null)
			{
				s_container = new GameObject("FrostMage_Prefabs");
				s_container.SetActive(false);
				Object.DontDestroyOnLoad((Object)(object)s_container);
			}
			MageItemDef[] items = MageContent.Items;
			foreach (MageItemDef mageItemDef in items)
			{
				if (!s_built.ContainsKey(mageItemDef.Id))
				{
					Built built = null;
					switch (mageItemDef.Kind)
					{
					case ItemKind.Tome:
						built = BuildTome(mageItemDef, find);
						break;
					case ItemKind.Staff:
						built = BuildStaff(mageItemDef, find);
						break;
					case ItemKind.Cocktail:
						built = BuildCocktail(mageItemDef, find);
						break;
					case ItemKind.Armor:
						built = BuildArmor(mageItemDef, find);
						break;
					}
					if (built != null)
					{
						s_built[mageItemDef.Id] = built;
					}
				}
			}
		}

		private static Built BuildTome(MageItemDef def, Func<string, GameObject> find)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			string used;
			ItemDrop val = CloneItem(find, MageContent.TomeSources, def, out used);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			SharedData shared = val.m_itemData.m_shared;
			shared.m_itemType = (ItemType)2;
			shared.m_food = 0f;
			shared.m_foodStamina = 0f;
			shared.m_foodEitr = 0f;
			shared.m_consumeStatusEffect = null;
			shared.m_questItem = false;
			shared.m_teleportable = true;
			shared.m_maxQuality = 1;
			return new Built
			{
				Def = def,
				Item = val
			};
		}

		private static Built BuildStaff(MageItemDef def, Func<string, GameObject> find)
		{
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			string[] sources = def.School.Sources.Select(((string Prefab, int NativeTier) s) => s.Prefab).ToArray();
			string used;
			ItemDrop val = CloneItem(find, sources, def, out used);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			SharedData shared = val.m_itemData.m_shared;
			Built result = new Built
			{
				Def = def,
				Item = val,
				NativeTier = def.School.Sources.First(((string Prefab, int NativeTier) s) => s.Prefab == used).NativeTier,
				VanillaDamage = shared.m_damages,
				VanillaDamagePerLevel = shared.m_damagesPerLevel,
				VanillaAttackEitr = (shared.m_attack?.m_attackEitr ?? 0f),
				VanillaReloadEitr = (shared.m_attack?.m_reloadEitrDrain ?? 0f),
				VanillaDrawEitr = (shared.m_attack?.m_drawEitrDrain ?? 0f)
			};
			shared.m_maxQuality = 4;
			return result;
		}

		private static Built BuildCocktail(MageItemDef def, Func<string, GameObject> find)
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			string used;
			ItemDrop val = CloneItem(find, MageContent.CocktailSources, def, out used);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			SharedData shared = val.m_itemData.m_shared;
			StatusEffect consumeStatusEffect = shared.m_consumeStatusEffect;
			SE_Stats val2 = (SE_Stats)(object)((consumeStatusEffect is SE_Stats) ? consumeStatusEffect : null);
			SE_Stats val3 = (((Object)(object)val2 != (Object)null) ? Object.Instantiate<SE_Stats>(val2) : ScriptableObject.CreateInstance<SE_Stats>());
			((Object)val3).name = def.Id + "_Effect";
			s_seNameHash?.SetValue(val3, 0);
			((Object)val3).hideFlags = (HideFlags)(((Object)val3).hideFlags | 0x20);
			((StatusEffect)val3).m_name = def.Name;
			if (string.IsNullOrEmpty(((StatusEffect)val3).m_category))
			{
				((StatusEffect)val3).m_category = "FrostMage_Cocktail";
			}
			if ((Object)(object)((StatusEffect)val3).m_icon == (Object)null && shared.m_icons != null && shared.m_icons.Length != 0)
			{
				((StatusEffect)val3).m_icon = shared.m_icons[0];
			}
			val3.m_healthUpFront = 0f;
			val3.m_healthOverTime = 0f;
			val3.m_staminaUpFront = 0f;
			val3.m_staminaOverTime = 0f;
			val3.m_eitrOverTime = 0f;
			val3.m_eitrRegenMultiplier = 1f;
			val3.m_healthRegenMultiplier = 1f;
			val3.m_staminaRegenMultiplier = 1f;
			shared.m_consumeStatusEffect = (StatusEffect)(object)val3;
			shared.m_maxQuality = 1;
			return new Built
			{
				Def = def,
				Item = val,
				Effect = val3
			};
		}

		private static Built BuildArmor(MageItemDef def, Func<string, GameObject> find)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			string used;
			ItemDrop val = CloneItem(find, new string[1] { def.ArmorSource }, def, out used);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			SharedData shared = val.m_itemData.m_shared;
			shared.m_armor = def.Armor;
			shared.m_eitrRegenModifier = def.EitrRegen;
			shared.m_damageModifiers = new List<DamageModPair>();
			if ((int)def.Resistance != 0)
			{
				shared.m_damageModifiers.Add(new DamageModPair
				{
					m_type = def.Resistance,
					m_modifier = (DamageModifier)1
				});
			}
			shared.m_equipStatusEffect = null;
			shared.m_setName = MageContent.ArmorSetId(def.Tier);
			shared.m_setSize = 3;
			shared.m_setStatusEffect = (StatusEffect)(object)SetEffect(def.Tier, shared);
			return new Built
			{
				Def = def,
				Item = val,
				SetEffect = (SE_Stats)/*isinst with value type is only supported in some contexts*/
			};
		}

		private static SE_Stats SetEffect(int tier, SharedData iconFrom)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			if (s_setEffects.TryGetValue(tier, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			int num = MageContent.TierSetBonus[tier];
			SE_Stats val = ScriptableObject.CreateInstance<SE_Stats>();
			((Object)val).name = MageContent.ArmorSetId(tier) + "_Effect";
			((Object)val).hideFlags = (HideFlags)(((Object)val).hideFlags | 0x20);
			((StatusEffect)val).m_name = MageContent.ArmorSetNames[tier] + " set";
			((StatusEffect)val).m_tooltip = $"+{num} Elemental Magic";
			val.m_skillLevel = (SkillType)9;
			val.m_skillLevelModifier = num;
			if (iconFrom.m_icons != null && iconFrom.m_icons.Length != 0)
			{
				((StatusEffect)val).m_icon = iconFrom.m_icons[0];
			}
			s_setEffects[tier] = val;
			return val;
		}

		private static ItemDrop CloneItem(Func<string, GameObject> find, string[] sources, MageItemDef def, out string used)
		{
			used = null;
			foreach (string text in sources)
			{
				GameObject val = find(text);
				ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
				if (val2?.m_itemData?.m_shared != null)
				{
					GameObject val3 = Object.Instantiate<GameObject>(val, s_container.transform, false);
					((Object)val3).name = def.Id;
					ItemDrop component = val3.GetComponent<ItemDrop>();
					SharedData shared = component.m_itemData.m_shared;
					SharedData shared2 = val2.m_itemData.m_shared;
					if (shared == shared2 || (shared.m_attack != null && shared.m_attack == shared2.m_attack) || (shared.m_secondaryAttack != null && shared.m_secondaryAttack == shared2.m_secondaryAttack))
					{
						Object.DestroyImmediate((Object)(object)val3);
						Warn(def.Id, "Could not make an independent copy of '" + text + "', so " + def.Name + " is unavailable.");
						return null;
					}
					component.m_itemData.m_dropPrefab = val3;
					shared.m_name = def.Name;
					shared.m_description = def.Description;
					used = text;
					return component;
				}
			}
			Warn(def.Id, "None of the vanilla items " + string.Join(", ", sources) + " exist in this game version, so " + def.Name + " is unavailable.");
			return null;
		}

		private static void ApplySettings()
		{
			foreach (Built item in AllBuilt)
			{
				if (item.Def.Kind == ItemKind.Staff)
				{
					ApplyStaffSettings(item);
				}
				else if (item.Def.Kind == ItemKind.Armor)
				{
					MageItemDef def = item.Def;
					MageModule.Log.LogInfo((object)($"{def.Name} ({MageContent.Biomes[def.Tier]}): {def.Armor:0} armour, " + $"+{def.EitrRegen * 100f:0}% eitr regeneration."));
				}
				else if ((Object)(object)item.Effect != (Object)null)
				{
					item.Effect.m_eitrUpFront = item.Def.Mana * Settings.CocktailManaMultiplier;
					((StatusEffect)item.Effect).m_ttl = Settings.CocktailCooldown;
				}
			}
		}

		private static void ApplyStaffSettings(Built built)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: 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_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			MageItemDef def = built.Def;
			SharedData shared = built.Item.m_itemData.m_shared;
			float scale = MageContent.TierDamage[def.Tier] / MageContent.TierDamage[built.NativeTier] * Settings.DamageMultiplier;
			shared.m_damages = Scaled(built.VanillaDamage, def.School, scale);
			shared.m_damagesPerLevel = Scaled(built.VanillaDamagePerLevel, def.School, scale);
			float num = MageContent.TierManaCost[def.Tier] / MageContent.TierManaCost[built.NativeTier] * Settings.ManaCostMultiplier;
			if (shared.m_attack != null)
			{
				shared.m_attack.m_attackEitr = built.VanillaAttackEitr * num;
				shared.m_attack.m_reloadEitrDrain = built.VanillaReloadEitr * num;
				shared.m_attack.m_drawEitrDrain = built.VanillaDrawEitr * num;
			}
			DamageTypes damages = shared.m_damages;
			float num2 = Mathf.Max(built.VanillaAttackEitr, built.VanillaReloadEitr) * num;
			MageModule.Log.LogInfo((object)$"{def.Name} ({MageContent.Biomes[def.Tier]}): {Describe(damages)} per hit, {num2:0.#} mana per cast.");
		}

		private static DamageTypes Scaled(DamageTypes vanilla, School school, float scale)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			DamageTypes result = ((school.Convert != null) ? school.Convert(vanilla) : ((DamageTypes)(ref vanilla)).Clone());
			((DamageTypes)(ref result)).Modify(scale);
			return result;
		}

		private static string Describe(DamageTypes d)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			List<string> parts = new List<string>();
			Add(d.m_frost, "frost");
			Add(d.m_fire, "fire");
			Add(d.m_lightning, "lightning");
			Add(d.m_spirit, "spirit");
			Add(d.m_blunt, "blunt");
			Add(d.m_pierce, "pierce");
			Add(d.m_poison, "poison");
			if (parts.Count <= 0)
			{
				return "no damage";
			}
			return string.Join(" + ", parts.ToArray());
			void Add(float value, string label)
			{
				if (value > 0.05f)
				{
					parts.Add($"{value:0.#} {label}");
				}
			}
		}

		private static void RebuildRecipes(ObjectDB db)
		{
			Recipe[] array = db.m_recipes.Where((Recipe r) => (Object)(object)r != (Object)null && ((Object)r).name.StartsWith("Recipe_FrostMage_")).ToArray();
			foreach (Recipe val in array)
			{
				db.m_recipes.Remove(val);
				Object.Destroy((Object)(object)val);
			}
			Dictionary<string, CraftingStation> dictionary = new Dictionary<string, CraftingStation>();
			foreach (Recipe recipe in db.m_recipes)
			{
				CraftingStation val2 = (((Object)(object)recipe != (Object)null) ? recipe.m_craftingStation : null);
				if ((Object)(object)val2 != (Object)null && !dictionary.ContainsKey(val2.m_name))
				{
					dictionary[val2.m_name] = val2;
				}
			}
			foreach (Built item in AllBuilt)
			{
				AddRecipe(db, item, dictionary);
			}
		}

		private static void AddRecipe(ObjectDB db, Built built, Dictionary<string, CraftingStation> stations)
		{
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Expected O, but got Unknown
			MageItemDef def = built.Def;
			if (!Settings.Recipes.TryGetValue(def.Id, out var value))
			{
				value = def.DefaultRecipe;
			}
			List<Requirement> list = new List<Requirement>(ParseRequirements(db, value, def.Name));
			if (list.Count == 0)
			{
				Warn("recipe " + def.Id, "The recipe for " + def.Name + " has no valid ingredients, so it cannot be crafted.");
				return;
			}
			if (def.Kind == ItemKind.Staff || def.Kind == ItemKind.Armor)
			{
				string text = string.Format("Upgrader{0}{1}", def.Tier, (def.Kind == ItemKind.Staff) ? "Weapon" : "Armor");
				GameObject itemPrefab = db.GetItemPrefab(text);
				if ((Object)(object)itemPrefab != (Object)null && (Object)(object)itemPrefab.GetComponent<ItemDrop>() != (Object)null)
				{
					list.Add(new Requirement
					{
						m_resItem = itemPrefab.GetComponent<ItemDrop>(),
						m_amount = 1,
						m_amountPerLevel = 0,
						m_upgraderResource = true,
						m_recover = false
					});
				}
			}
			stations.TryGetValue(def.Station ?? "", out var value2);
			if ((Object)(object)value2 == (Object)null && !string.IsNullOrEmpty(def.Station))
			{
				Warn("station " + def.Station, "Crafting station " + def.Station + " was not found; items that need it can be crafted without one.");
			}
			Recipe val = ScriptableObject.CreateInstance<Recipe>();
			((Object)val).name = "Recipe_" + def.Id;
			val.m_item = built.Item;
			val.m_amount = def.CraftAmount;
			val.m_enabled = true;
			val.m_craftingStation = value2;
			val.m_minStationLevel = def.StationLevel;
			val.m_resources = list.ToArray();
			db.m_recipes.Add(val);
		}

		private static IEnumerable<Requirement> ParseRequirements(ObjectDB db, string spec, string forItem)
		{
			string[] array = (spec ?? "").Split(new char[1] { ',' });
			foreach (string text in array)
			{
				string[] array2 = text.Split(new char[1] { ':' });
				string text2 = array2[0].Trim();
				if (text2.Length == 0)
				{
					continue;
				}
				int result = 1;
				int result2 = -1;
				if ((array2.Length > 1 && (!int.TryParse(array2[1].Trim(), out result) || result < 1)) || (array2.Length > 2 && (!int.TryParse(array2[2].Trim(), out result2) || result2 < 0)))
				{
					Warn(forItem + text, "Ingredient '" + text.Trim() + "' for " + forItem + " has an invalid amount; it is ignored.");
					continue;
				}
				GameObject itemPrefab = db.GetItemPrefab(text2);
				ItemDrop val = (((Object)(object)itemPrefab != (Object)null) ? itemPrefab.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val == (Object)null)
				{
					Warn(forItem + text2, "Ingredient '" + text2 + "' for " + forItem + " is not an item this game knows; it is ignored.");
					continue;
				}
				if (result2 < 0)
				{
					result2 = ((!text2.StartsWith("Trophy")) ? Mathf.Max(1, (result + 1) / 2) : 0);
				}
				yield return new Requirement
				{
					m_resItem = val,
					m_amount = result,
					m_amountPerLevel = result2,
					m_recover = true
				};
			}
		}

		private static void Warn(string key, string message)
		{
			if (s_warned.Add(key))
			{
				MageModule.Log.LogWarning((object)message);
			}
		}
	}
	internal sealed class MageModule : IModule
	{
		internal static readonly ManualLogSource Log = Logger.CreateLogSource("ElPopeAdventures.Mage");

		private ConfigEntry<float> _baseMana;

		private ConfigEntry<float> _manaPerBoss;

		private ConfigEntry<bool> _staffsRequireMage;

		private ConfigEntry<float> _damageMultiplier;

		private ConfigEntry<float> _manaCostMultiplier;

		private ConfigEntry<float> _cocktailManaMultiplier;

		private ConfigEntry<float> _cocktailCooldown;

		private readonly Dictionary<string, ConfigEntry<string>> _recipes = new Dictionary<string, ConfigEntry<string>>();

		public string Name => "Mage";

		public string Description => "The Mage class: mana, frost/fire/arcane staffs for every biome, mage armour, and mana cocktails.";

		public Type[] PatchClasses { get; } = new Type[8]
		{
			typeof(ElPopeAdventures.Mage.Patches.ObjectDBAwake),
			typeof(ElPopeAdventures.Mage.Patches.ObjectDBCopyOtherDB),
			typeof(ElPopeAdventures.Mage.Patches.ZNetSceneAwake),
			typeof(PlayerGetTotalFoodValue),
			typeof(PlayerConsumeItem),
			typeof(PlayerHaveRequirementsRecipe),
			typeof(PlayerGetAvailableRecipes),
			typeof(HumanoidStartAttack)
		};

		public void Initialize()
		{
		}

		public void Bind(ConfigFile config)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Expected O, but got Unknown
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Expected O, but got Unknown
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Expected O, but got Unknown
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Expected O, but got Unknown
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Expected O, but got Unknown
			_baseMana = config.Bind<float>("Mage - Class", "BaseMana", 50f, new ConfigDescription("Mana (eitr) a Mage always has, on top of any eitr from food.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 500f), Array.Empty<object>()));
			_manaPerBoss = config.Bind<float>("Mage - Class", "ManaPerBoss", 20f, new ConfigDescription("Extra mana for every boss defeated, so the pool keeps pace with each new biome.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 200f), Array.Empty<object>()));
			_staffsRequireMage = config.Bind<bool>("Mage - Class", "StaffsRequireMage", true, "If true, mage staff and mage armour recipes only appear after the character reads the Mage Tome, and only a Mage can cast with a mage staff. Vanilla staffs and armour are never affected.");
			_damageMultiplier = config.Bind<float>("Mage - Spells", "DamageMultiplier", 1f, new ConfigDescription("Multiplies the damage of every mage staff. Each staff's final numbers are written to the BepInEx log. Applies on the next world load.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>()));
			_manaCostMultiplier = config.Bind<float>("Mage - Spells", "ManaCostMultiplier", 1f, new ConfigDescription("Multiplies the mana cost of every mage staff. Applies on the next world load.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			_cocktailManaMultiplier = config.Bind<float>("Mage - Cocktails", "ManaMultiplier", 1f, new ConfigDescription("Multiplies the mana restored by cocktails (Meadow Berry 40, Blueberry 70, Cloudberry 110). Applies on the next world load.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), Array.Empty<object>()));
			_cocktailCooldown = config.Bind<float>("Mage - Cocktails", "CooldownSeconds", 60f, new ConfigDescription("Seconds before another cocktail can be drunk. Applies on the next world load.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 600f), Array.Empty<object>()));
			MageItemDef[] items = MageContent.Items;
			foreach (MageItemDef mageItemDef in items)
			{
				string text = ((mageItemDef.Kind == ItemKind.Staff) ? (mageItemDef.School.Name + " staff, " + MageContent.Biomes[mageItemDef.Tier] + ". ") : ((mageItemDef.Kind == ItemKind.Armor) ? ("Mage armour, " + MageContent.Biomes[mageItemDef.Tier] + ". ") : ""));
				_recipes[mageItemDef.Id] = config.Bind<string>("Mage - Recipes", mageItemDef.ConfigKey, mageItemDef.DefaultRecipe, mageItemDef.Name + ". " + text + "Ingredients as ItemName:Amount pairs (optionally ItemName:Amount:PerUpgradeLevel). Applies on the next world load.");
			}
			PullSettings();
		}

		public void OnSettingChanged()
		{
			PullSettings();
		}

		public void Update()
		{
		}

		private void PullSettings()
		{
			Settings.BaseMana = _baseMana.Value;
			Settings.ManaPerBoss = _manaPerBoss.Value;
			Settings.StaffsRequireMage = _staffsRequireMage.Value;
			Settings.DamageMultiplier = _damageMultiplier.Value;
			Settings.ManaCostMultiplier = _manaCostMultiplier.Value;
			Settings.CocktailManaMultiplier = _cocktailManaMultiplier.Value;
			Settings.CocktailCooldown = _cocktailCooldown.Value;
			foreach (KeyValuePair<string, ConfigEntry<string>> recipe in _recipes)
			{
				Settings.Recipes[recipe.Key] = recipe.Value.Value ?? "";
			}
		}
	}
	internal static class Settings
	{
		internal static float BaseMana = 50f;

		internal static float ManaPerBoss = 20f;

		internal static bool StaffsRequireMage = true;

		internal static float DamageMultiplier = 1f;

		internal static float ManaCostMultiplier = 1f;

		internal static float CocktailManaMultiplier = 1f;

		internal static float CocktailCooldown = 60f;

		internal static readonly Dictionary<string, string> Recipes = new Dictionary<string, string>();
	}
}
namespace ElPopeAdventures.Mage.Patches
{
	[HarmonyPatch(typeof(ObjectDB), "Awake")]
	internal static class ObjectDBAwake
	{
		private static void Postfix(ObjectDB __instance)
		{
			try
			{
				MageItems.RegisterInObjectDB(__instance);
			}
			catch (Exception ex)
			{
				MageModule.Log.LogError((object)("Adding the mage items failed; they are unavailable this session. " + ex));
			}
		}
	}
	[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
	internal static class ObjectDBCopyOtherDB
	{
		private static void Postfix(ObjectDB __instance)
		{
			try
			{
				MageItems.RegisterInObjectDB(__instance);
			}
			catch (Exception ex)
			{
				MageModule.Log.LogError((object)("Adding the mage items failed; they are unavailable this session. " + ex));
			}
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	internal static class ZNetSceneAwake
	{
		private static void Postfix(ZNetScene __instance)
		{
			try
			{
				MageItems.RegisterInScene(__instance);
			}
			catch (Exception ex)
			{
				MageModule.Log.LogError((object)("Registering the mage items with the world failed. " + ex));
			}
		}
	}
	[HarmonyPatch(typeof(Player), "GetTotalFoodValue")]
	internal static class PlayerGetTotalFoodValue
	{
		private static void Postfix(Player __instance, ref float eitr)
		{
			try
			{
				eitr += MageClass.BonusMana(__instance);
			}
			catch (Exception ex)
			{
				MageModule.Log.LogWarning((object)("Mage mana check failed: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(Player), "ConsumeItem")]
	internal static class PlayerConsumeItem
	{
		private static bool Prefix(Player __instance, ItemData item, ref bool __result)
		{
			try
			{
				if (MageClass.RefuseTome(__instance, item) || MageClass.RefuseCocktail(__instance, item))
				{
					__result = false;
					return false;
				}
			}
			catch (Exception ex)
			{
				MageModule.Log.LogWarning((object)("Tome check failed: " + ex.Message));
			}
			return true;
		}

		private static void Postfix(Player __instance, ItemData item, bool __result)
		{
			if (!__result)
			{
				return;
			}
			try
			{
				MageClass.OnConsumed(__instance, item);
			}
			catch (Exception ex)
			{
				MageModule.Log.LogError((object)("Becoming a Mage failed: " + ex));
			}
		}
	}
	[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
	{
		typeof(Recipe),
		typeof(bool),
		typeof(int),
		typeof(int)
	})]
	internal static class PlayerHaveRequirementsRecipe
	{
		private static bool Prefix(Player __instance, Recipe recipe, ref bool __result)
		{
			try
			{
				if (MageClass.StaffsLocked(__instance) && MageItems.IsMageOnlyRecipe(recipe))
				{
					__result = false;
					return false;
				}
			}
			catch (Exception ex)
			{
				MageModule.Log.LogWarning((object)("Staff recipe check failed: " + ex.Message));
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Player), "GetAvailableRecipes")]
	internal static class PlayerGetAvailableRecipes
	{
		private static void Postfix(Player __instance, ref List<Recipe> available)
		{
			try
			{
				if (available != null && MageClass.StaffsLocked(__instance))
				{
					available.RemoveAll(MageItems.IsMageOnlyRecipe);
				}
			}
			catch (Exception ex)
			{
				MageModule.Log.LogWarning((object)("Staff recipe list check failed: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "StartAttack")]
	internal static class HumanoidStartAttack
	{
		private static bool Prefix(Humanoid __instance, ref bool __result)
		{
			try
			{
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if (val != null && MageClass.RefuseStaff(val))
				{
					__result = false;
					return false;
				}
			}
			catch (Exception ex)
			{
				MageModule.Log.LogWarning((object)("Staff use check failed: " + ex.Message));
			}
			return true;
		}
	}
}
namespace ElPopeAdventures.InventoryQoL
{
	internal static class ChestStore
	{
		internal static void StoreMaterials(Player player)
		{
			Run(player, "materials", Settings.DumpSkipHotbar, (ItemData item) => (int)item.m_shared.m_itemType == 1 || (Settings.DumpIncludeTrophies && (int)item.m_shared.m_itemType == 13));
		}

		internal static void StoreConsumables(Player player)
		{
			Run(player, "consumables", Settings.ConsumablesSkipHotbar, (ItemData item) => (int)item.m_shared.m_itemType == 2);
		}

		private static void Run(Player player, string what, bool skipHotbar, Func<ItemData, bool> matches)
		{
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			if (InventoryGuiAccess.IsDragging())
			{
				return;
			}
			Container val = InventoryGuiAccess.OpenContainer();
			if ((Object)(object)val == (Object)null)
			{
				((Character)player).Message((MessageType)2, "Open a chest to store your " + what + " in it", 0, (Sprite)null, false);
				return;
			}
			Inventory inventory = val.GetInventory();
			Inventory inventory2 = ((Humanoid)player).GetInventory();
			int num = inventory.CountItems((string)null, -1, true);
			bool flag = false;
			foreach (ItemData item in new List<ItemData>(inventory2.GetAllItems()))
			{
				if (matches(item) && !((Humanoid)player).IsItemEquiped(item) && (!skipHotbar || item.m_gridPos.y != 0))
				{
					if (!inventory.CanAddItem(item, 1))
					{
						flag = true;
					}
					else if (inventory.AddItem(item))
					{
						inventory2.RemoveItem(item);
					}
					else
					{
						flag = true;
					}
				}
			}
			int num2 = inventory.CountItems((string)null, -1, true) - num;
			NearbyContainers.NotifyChanged(inventory);
			NearbyContainers.NotifyChanged(inventory2);
			if (num2 > 0)
			{
				InventoryGui instance = InventoryGui.instance;
				if (instance != null)
				{
					instance.m_moveItemEffects.Create(((Component)val).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
				}
				((Character)player).Message((MessageType)2, flag ? $"Stored {num2} {what} (chest is full)" : $"Stored {num2} {what}", 0, (Sprite)null, false);
			}
			else
			{
				((Character)player).Message((MessageType)2, flag ? "The chest is full" : ("No " + what + " to store"), 0, (Sprite)null, false);
			}
		}
	}
	internal static class CraftFromChests
	{
		private const float RefreshSeconds = 1f;

		private static readonly List<Container> s_containers = new List<Container>();

		private static readonly Dictionary<(string, int, bool), int> s_counts = new Dictionary<(string, int, bool), int>();

		private static float s_nextRefresh;

		private static int s_countsFrame = -1;

		private static int s_depth;

		private static int s_suspended;

		internal static void Enter()
		{
			s_depth++;
		}

		internal static void Exit()
		{
			if (s_depth > 0)
			{
				s_depth--;
			}
		}

		internal static void Invalidate()
		{
			s_nextRefresh = 0f;
			s_counts.Clear();
		}

		internal static bool Applies(Inventory inventory)
		{
			if (Settings.CraftFromChests && s_depth > 0 && s_suspended == 0 && inventory != null && (Object)(object)Player.m_localPlayer != (Object)null)
			{
				return inventory == ((Humanoid)Player.m_localPlayer).GetInventory();
			}
			return false;
		}

		internal static int CountInChests(string name, int quality, bool matchWorldLevel)
		{
			if (Time.frameCount != s_countsFrame)
			{
				s_counts.Clear();
				s_countsFrame = Time.frameCount;
			}
			(string, int, bool) key = (name, quality, matchWorldLevel);
			if (s_counts.TryGetValue(key, out var value))
			{
				return value;
			}
			foreach (Container item in Containers())
			{
				if (Usable(item))
				{
					value += item.GetInventory().CountItems(name, quality, matchWorldLevel);
				}
			}
			s_counts[key] = value;
			return value;
		}

		internal static void Remove(Inventory inventory, string name, int amount, int itemQuality, bool worldLevelBased)
		{
			s_suspended++;
			try
			{
				int num = Mathf.Min(inventory.CountItems(name, itemQuality, worldLevelBased), amount);
				if (num > 0)
				{
					inventory.RemoveItem(name, num, itemQuality, worldLevelBased);
				}
				int num2 = amount - num;
				foreach (Container item in Containers())
				{
					if (num2 <= 0)
					{
						break;
					}
					if (!Usable(item))
					{
						continue;
					}
					Inventory inventory2 = item.GetInventory();
					int num3 = Mathf.Min(inventory2.CountItems(name, itemQuality, worldLevelBased), num2);
					if (num3 > 0)
					{
						ZNetView val = NearbyContainers.View(item);
						if (!val.IsOwner())
						{
							val.ClaimOwnership();
						}
						inventory2.RemoveItem(name, num3, itemQuality, worldLevelBased);
						num2 -= num3;
					}
				}
				if (num2 > 0)
				{
					InventoryModule.Log.LogWarning((object)$"Crafting needed {amount} {name} but only found {amount - num2}.");
				}
			}
			finally
			{
				s_suspended--;
				s_counts.Clear();
			}
		}

		private static List<Container> Containers()
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				s_containers.Clear();
			}
			else if (Time.time >= s_nextRefresh)
			{
				NearbyContainers.Find(((Component)localPlayer).transform.position, Settings.CraftRange, s_containers);
				s_nextRefresh = Time.time + 1f;
			}
			return s_containers;
		}

		private static bool Usable(Container container)
		{
			if ((Object)(object)container != (Object)null && container.GetInventory() != null)
			{
				return !NearbyContainers.BusyElsewhere(container);
			}
			return false;
		}
	}
	internal sealed class GamepadCombo
	{
		private bool _wasHeld;

		internal bool Pressed(string modifier, string button)
		{
			bool flag = IsSet(button) && ZInput.GetButton(button) && (!IsSet(modifier) || modifier == button || ZInput.GetButton(modifier));
			bool result = flag && !_wasHeld;
			_wasHeld = flag;
			return result;
		}

		private static bool IsSet(string button)
		{
			if (!string.IsNullOrEmpty(button))
			{
				return button != "None";
			}
			return false;
		}
	}
	internal static class InventoryGuiAccess
	{
		private static readonly FieldRef<InventoryGui, Container> s_currentContainer = AccessTools.FieldRefAccess<InventoryGui, Container>("m_currentContainer");

		private static readonly FieldRef<InventoryGui, ItemData> s_dragItem = AccessTools.FieldRefAccess<InventoryGui, ItemData>("m_dragItem");

		internal static Container OpenContainer()
		{
			InventoryGui instance = InventoryGui.instance;
			Container val = (((Object)(object)instance != (Object)null && InventoryGui.IsVisible()) ? s_currentContainer.Invoke(instance) : null);
			if (!((Object)(object)val != (Object)null) || !val.IsOwner() || val.GetInventory() == null)
			{
				return null;
			}
			return val;
		}

		internal static bool IsDragging()
		{
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance != (Object)null)
			{
				return s_dragItem.Invoke(instance) != null;
			}
			return false;
		}
	}
	internal sealed class InventoryModule : IModule
	{
		internal static readonly ManualLogSource Log = Logger.CreateLogSource("ElPopeAdventures.Inventory");

		private ConfigEntry<float> _stackMultiplier;

		private ConfigEntry<KeyCode> _sortKey;

		private ConfigEntry<string> _sortModifier;

		private ConfigEntry<string> _sortButton;

		private ConfigEntry<bool> _sortSkipHotbar;

		private ConfigEntry<KeyCode> _dumpKey;

		private ConfigEntry<string> _dumpModifier;

		private ConfigEntry<string> _dumpButton;

		private ConfigEntry<bool> _dumpIncludeTrophies;

		private ConfigEntry<bool> _dumpSkipHotbar;

		private ConfigEntry<KeyCode> _consumablesKey;

		private ConfigEntry<string> _consumablesModifier;

		private ConfigEntry<string> _consumablesButton;

		private ConfigEntry<bool> _consumablesSkipHotbar;

		private ConfigEntry<KeyCode> _quickStackKey;

		private ConfigEntry<float> _quickStackRange;

		private ConfigEntry<bool> _quickStackSkipHotbar;

		private ConfigEntry<bool> _craftFromChests;

		private ConfigEntry<float> _craftRange;

		private const string ModifierNote = "Controller button to hold while pressing GamepadButton. JoyBack is the View/Select button (Share/Create on PlayStation), which does nothing in the inventory. None = GamepadButton works on its own.";

		private const string GamepadNote = "The right stick does nothing in the inventory, so View + right stick never clashes with the game. Buttons the inventory already uses (A, B, X, Y, bumpers, triggers, stick clicks, D-pad, left stick) would do both things at once. Set to None to turn the controller shortcut off.";

		private readonly GamepadCombo _sortCombo = new GamepadCombo();

		private readonly GamepadCombo _dumpCombo = new GamepadCombo();

		private readonly GamepadCombo _consumablesCombo = new GamepadCombo();

		private static readonly string[] GamepadButtons = new string[18]
		{
			"None", "JoyBack", "JoyRStickUp", "JoyRStickDown", "JoyRStickLeft", "JoyRStickRight", "JoyRStick", "JoyLStick", "JoyButtonX", "JoyButtonY",
			"JoyLBumper", "JoyRBumper", "JoyLTrigger", "JoyRTrigger", "JoyDPadUp", "JoyDPadDown", "JoyDPadLeft", "JoyDPadRight"
		};

		private int _hotkeyErrors;

		public string Name => "Inventory";

		public string Description => "Inventory quality of life: bigger stacks, sorting, storing materials or consumables in a chest, quick stack to nearby chests, crafting from nearby chests.";

		public Type[] PatchClasses { get; } = new Type[11]
		{
			typeof(ElPopeAdventures.InventoryQoL.Patches.ObjectDBAwake),
			typeof(ElPopeAdventures.InventoryQoL.Patches.ObjectDBCopyOtherDB),
			typeof(InventoryStackAll),
			typeof(ContainerStackResponse),
			typeof(PlayerHaveRequirementItems),
			typeof(PlayerHaveRequirementsPiece),
			typeof(PlayerConsumeResources),
			typeof(InventoryGuiSetupRequirement),
			typeof(InventoryCountItems),
			typeof(InventoryHaveItem),
			typeof(InventoryRemoveItemByName)
		};

		public void Initialize()
		{
		}

		public void Bind(ConfigFile config)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Expected O, but got Unknown
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Expected O, but got Unknown
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Expected O, but got Unknown
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Expected O, but got Unknown
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Expected O, but got Unknown
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Expected O, but got Unknown
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Expected O, but got Unknown
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0289: Expected O, but got Unknown
			//IL_02f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02fa: Expected O, but got Unknown
			_stackMultiplier = config.Bind<float>("Inventory - Stacks", "StackMultiplier", 4f, new ConfigDescription("Multiplies the stack size of every item that already stacks (4 = wood stacks to 200 instead of 50). 1 keeps vanilla stack sizes. Items that don't stack (weapons, armour) are never changed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 20f), Array.Empty<object>()));
			_sortKey = config.Bind<KeyCode>("Inventory - Sort", "SortKey", (KeyCode)107, "Press while the inventory is open to sort it, and the open chest if there is one. Stacks are merged and items grouped by type. Set to None to turn sorting off.");
			_sortModifier = config.Bind<string>("Inventory - Sort", "GamepadModifier", "JoyBack", new ConfigDescription("Controller button to hold while pressing GamepadButton. JoyBack is the View/Select button (Share/Create on PlayStation), which does nothing in the inventory. None = GamepadButton works on its own.", (AcceptableValueBase)(object)new AcceptableValueList<string>(GamepadButtons), Array.Empty<object>()));
			_sortButton = config.Bind<string>("Inventory - Sort", "GamepadButton", "JoyRStickDown", new ConfigDescription("Controller shortcut for sorting: hold GamepadModifier and press this. Default: hold View and push the right stick down. The right stick does nothing in the inventory, so View + right stick never clashes with the game. Buttons the inventory already uses (A, B, X, Y, bumpers, triggers, stick clicks, D-pad, left stick) would do both things at once. Set to None to turn the controller shortcut off.", (AcceptableValueBase)(object)new AcceptableValueList<string>(GamepadButtons), Array.Empty<object>()));
			_sortSkipHotbar = config.Bind<bool>("Inventory - Sort", "SkipHotbar", true, "Leave the hotbar (the top row of the inventory, keys 1-8) untouched when sorting.");
			_dumpKey = config.Bind<KeyCode>("Inventory - Store materials", "Key", (KeyCode)106, "Press while a chest is open to move all crafting materials from your inventory into it. Set to None to turn this off.");
			_dumpModifier = config.Bind<string>("Inventory - Store materials", "GamepadModifier", "JoyBack", new ConfigDescription("Controller button to hold while pressing GamepadButton. JoyBack is the View/Select button (Share/Create on PlayStation), which does nothing in the inventory. None = GamepadButton works on its own.", (AcceptableValueBase)(object)new AcceptableValueList<string>(GamepadButtons), Array.Empty<object>()));
			_dumpButton = config.Bind<string>("Inventory - Store materials", "GamepadButton", "JoyRStickUp", new ConfigDescription("Controller shortcut for storing materials: hold GamepadModifier and press this. Default: hold View and push the right stick up (towards the chest panel). The right stick does nothing in the inventory, so View + right stick never clashes with the game. Buttons the inventory already uses (A, B, X, Y, bumpers, triggers, stick clicks, D-pad, left stick) would do both things at once. Set to None to turn the controller shortcut off.", (AcceptableValueBase)(object)new AcceptableValueList<string>(GamepadButtons), Array.Empty<object>()));
			_dumpIncludeTrophies = config.Bind<bool>("Inventory - Store materials", "IncludeTrophies", false, "Also store trophies.");
			_dumpSkipHotbar = config.Bind<bool>("Inventory - Store materials", "SkipHotbar", true, "Keep materials that are on the hotbar (top row).");
			_consumablesKey = config.Bind<KeyCode>("Inventory - Store consumables", "Key", (KeyCode)104, "Press while a chest is open to move all consumables (food, meads, potions, cocktails) from your inventory into it. Set to None to turn this off.");
			_consumablesModifier = config.Bind<string>("Inventory - Store consumables", "GamepadModifier", "JoyBack", new ConfigDescription("Controller button to hold while pressing GamepadButton. JoyBack is the View/Select button (Share/Create on PlayStation), which does nothing in the inventory. None = GamepadButton works on its own.", (AcceptableValueBase)(object)new AcceptableValueList<string>(GamepadButtons), Array.Empty<object>()));
			_consumablesButton = config.Bind<string>("Inventory - Store consumables", "GamepadButton", "JoyRStickLeft", new ConfigDescription("Controller shortcut for storing consumables: hold GamepadModifier and press this. Default: hold View and push the right stick left. The right stick does nothing in the inventory, so View + right stick never clashes with the game. Buttons the inventory already uses (A, B, X, Y, bumpers, triggers, stick clicks, D-pad, left stick) would do both things at once. Set to None to turn the controller shortcut off.", (AcceptableValueBase)(object)new AcceptableValueList<string>(GamepadButtons), Array.Empty<object>()));
			_consumablesSkipHotbar = config.Bind<bool>("Inventory - Store consumables", "SkipHotbar", true, "Keep consumables that are on the hotbar (top row), so the food and meads you carry stay with you.");
			_quickStackKey = config.Bind<KeyCode>("Inventory - Quick stack", "QuickStackKey", (KeyCode)108, "Moves items from your inventory into nearby chests that already hold that item. Works with or without the inventory open. Set to None to turn quick stack off.");
			_quickStackRange = config.Bind<float>("Inventory - Quick stack", "Range", 10f, new ConfigDescription("How far (in metres) quick stack reaches.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 50f), Array.Empty<object>()));
			_quickStackSkipHotbar = config.Bind<bool>("Inventory - Quick stack", "SkipHotbar", true, "Never quick stack items on the hotbar (top row). Equipped items are never moved.");
			_craftFromChests = config.Bind<bool>("Inventory - Craft from chests", "Enabled", true, "Crafting, upgrading and building also use materials from nearby chests you can open. Your inventory is used first.");
			_craftRange = config.Bind<float>("Inventory - Craft from chests", "Range", 40f, new ConfigDescription("How far (in metres) from you chests are used for crafting and building.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>()));
			PullSettings();
		}

		public void OnSettingChanged()
		{
			float stackMultiplier = Settings.StackMultiplier;
			PullSettings();
			if (!Mathf.Approximately(stackMultiplier, Settings.StackMultiplier))
			{
				StackSizes.Apply(ObjectDB.instance);
			}
			CraftFromChests.Invalidate();
		}

		public void Update()
		{
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: 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_007a: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null || ((Character)localPlayer).IsDead() || IsTyping())
				{
					return;
				}
				if (InventoryGui.IsVisible())
				{
					if (KeyPressed(Settings.SortKey) | _sortCombo.Pressed(Settings.SortModifier, Settings.SortButton))
					{
						Sorting.SortOpenInventories(localPlayer);
					}
					if (KeyPressed(Settings.DumpKey) | _dumpCombo.Pressed(Settings.DumpModifier, Settings.DumpButton))
					{
						ChestStore.StoreMaterials(localPlayer);
					}
					if (KeyPressed(Settings.ConsumablesKey) | _consumablesCombo.Pressed(Settings.ConsumablesModifier, Settings.ConsumablesButton))
					{
						ChestStore.StoreConsumables(localPlayer);
					}
				}
				if ((int)Settings.QuickStackKey != 0 && ZInput.GetKeyDown(Settings.QuickStackKey, false))
				{
					QuickStack.Run(localPlayer);
				}
			}
			catch (Exception ex)
			{
				if (++_hotkeyErrors <= 3)
				{
					Log.LogError((object)ex);
				}
			}
		}

		private static bool KeyPressed(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			if ((int)key != 0)
			{
				return ZInput.GetKeyDown(key, false);
			}
			return false;
		}

		private static bool IsTyping()
		{
			if ((!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !Console.IsVisible() && !TextInput.IsVisible() && !Menu.IsVisible() && !StoreGui.IsVisible())
			{
				return Minimap.InTextInput();
			}
			return true;
		}

		private void PullSettings()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			Settings.StackMultiplier = _stackMultiplier.Value;
			Settings.SortKey = _sortKey.Value;
			Settings.SortModifier = _sortModifier.Value;
			Settings.SortButton = _sortButton.Value;
			Settings.SortSkipHotbar = _sortSkipHotbar.Value;
			Settings.DumpKey = _dumpKey.Value;
			Settings.DumpModifier = _dumpModifier.Value;
			Settings.DumpButton = _dumpButton.Value;
			Settings.DumpIncludeTrophies = _dumpIncludeTrophies.Value;
			Settings.DumpSkipHotbar = _dumpSkipHotbar.Value;
			Settings.ConsumablesKey = _consumablesKey.Value;
			Settings.ConsumablesModifier = _consumablesModifier.Value;
			Settings.ConsumablesButton = _consumablesButton.Value;
			Settings.ConsumablesSkipHotbar = _consumablesSkipHotbar.Value;
			Settings.QuickStackKey = _quickStackKey.Value;
			Settings.QuickStackRange = _quickStackRange.Value;
			Settings.QuickStackSkipHotbar = _quickStackSkipHotbar.Value;
			Settings.CraftFromChests = _craftFromChests.Value;
			Settings.CraftRange = _craftRange.Value;
		}
	}
	internal static class Settings
	{
		internal static float StackMultiplier = 4f;

		internal static KeyCode SortKey = (KeyCode)107;

		internal static string SortModifier = "JoyBack";

		internal static string SortButton = "JoyRStickDown";

		internal static bool SortSkipHotbar = true;

		internal static KeyCode DumpKey = (KeyCode)106;

		internal static string DumpModifier = "JoyBack";

		internal static string DumpButton = "JoyRStickUp";

		internal static bool DumpIncludeTrophies;

		internal static bool DumpSkipHotbar = true;

		internal static KeyCode ConsumablesKey = (KeyCode)104;

		internal static string ConsumablesModifier = "JoyBack";

		internal static string ConsumablesButton = "JoyRStickLeft";

		internal static bool ConsumablesSkipHotbar = true;

		internal static KeyCode QuickStackKey = (KeyCode)108;

		internal static float QuickStackRange = 10f;

		internal static bool QuickStackSkipHotbar = true;

		internal static bool CraftFromChests = true;

		internal static float CraftRange = 40f;
	}
	internal static class NearbyContainers
	{
		private static readonly FieldRef<Container, ZNetView> s_nview = AccessTools.FieldRefAccess<Container, ZNetView>("m_nview");

		private static readonly MethodInfo s_changed = AccessTools.Method(typeof(Inventory), "Changed", (Type[])null, (Type[])null);

		private static readonly List<Piece> s_pieces = new List<Piece>();

		internal static ZNetView View(Container container)
		{
			if (!((Object)(object)container != (Object)null))
			{
				return null;
			}
			return s_nview.Invoke(container);
		}

		internal static void NotifyChanged(Inventory inventory)
		{
			s_changed?.Invoke(inventory, new object[2] { false, false });
		}

		internal static void Find(Vector3 center, float radius, List<Container> result)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			result.Clear();
			s_pieces.Clear();
			if ((Object)(object)Game.instance == (Object)null)
			{
				return;
			}
			Piece.GetAllPiecesInRadius(center, radius, s_pieces);
			long playerID = Game.instance.GetPlayerProfile().GetPlayerID();
			foreach (Piece s_piece in s_pieces)
			{
				if (!((Object)(object)s_piece == (Object)null) && s_piece.IsPlacedByPlayer())
				{
					Container componentInChildren = ((Component)s_piece).GetComponentInChildren<Container>();
					if (!((Object)(object)componentInChildren == (Object)null) && !((Object)(object)((Component)componentInChildren).GetComponent<TombStone>() != (Object)null) && !result.Contains(componentInChildren) && MayOpen(componentInChildren, s_piece, playerID))
					{
						result.Add(componentInChildren);
					}
				}
			}
			s_pieces.Clear();
		}

		private static bool MayOpen(Container container, Piece piece, long playerId)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Invalid comparison between Unknown and I4
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			ZNetView val = View(container);
			if ((Object)(object)val == (Object)null || !val.IsValid() || container.GetInventory() == null)
			{
				return false;
			}
			PrivacySetting privacy = container.m_privacy;
			if ((int)privacy != 0)
			{
				if ((int)privacy != 2)
				{
					return false;
				}
			}
			else if (piece.GetCreator() != playerId)
			{
				return false;
			}
			if (container.m_checkGuardStone)
			{
				return PrivateArea.CheckAccess(((Component)container).transform.position, 0f, false, false);
			}
			return true;
		}

		internal static bool BusyElsewhere(Container container)
		{
			ZNetView val = View(container);
			if ((Object)(object)val == (Object)null || !val.IsValid())
			{
				return true;
			}
			if (val.IsOwner())
			{
				return false;
			}
			if (val.GetZDO().GetInt(ZDOVars.s_inUse, 0) != 1)
			{
				if ((Object)(object)container.m_wagon != (Object)null)
				{
					return container.m_wagon.InUse();
				}
				return false;
			}
			return true;
		}
	}
	internal static class QuickStack
	{
		private const float ReplyTimeout = 5f;

		private static readonly List<Container> s_found = new List<Container>();

		private static readonly HashSet<Inventory> s_pending = new HashSet<Inventory>();

		private static float s_expires;

		private static int s_moved;

		private static int s_chests;

		internal static void Run(Player player)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			NearbyContainers.Find(((Component)player).transform.position, Settings.QuickStackRange, s_found);
			if (s_found.Count == 0)
			{
				((Character)player).Message((MessageType)2, "No chests nearby to quick stack into", 0, (Sprite)null, false);
				return;
			}
			s_pending.Clear();
			s_moved = 0;
			s_chests = 0;
			s_expires = Time.time + 5f;
			foreach (Container item in s_found)
			{
				s_pending.Add(item.GetInventory());
			}
			Container[] array = s_found.ToArray();
			for (int i = 0; i < array.Length; i++)
			{
				array[i].StackAll();
			}
			s_found.Clear();
		}

		internal static bool IsPending(Inventory chest)
		{
			if (chest != null && Time.time < s_expires)
			{
				return s_pending.Contains(chest);
			}
			return false;
		}

		internal static void Granted(Inventory chest, int moved)
		{
			s_pending.Remove(chest);
			if (moved > 0)
			{
				s_moved += moved;
				s_chests++;
			}
			Report();
		}

		internal static void Refused(Inventory chest)
		{
			s_pending.Remove(chest);
			Report();
		}

		private static void Report()
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null))
			{
				if (s_moved > 0)
				{
					string arg = ((s_chests == 1) ? "1 chest" : $"{s_chests} chests");
					((Character)localPlayer).Message((MessageType)2, $"Quick stacked {s_moved} items into {arg}", 0, (Sprite)null, false);
				}
				else if (s_pending.Count == 0)
				{
					((Character)localPlayer).Message((MessageType)2, "$msg_stackall_none", 0, (Sprite)null, false);
				}
			}
		}

		internal static int StackInto(Inventory chest, Inventory from, bool skipHotbar)
		{
			Player localPlayer = Player.m_localPlayer;
			int num = chest.CountItems((string)null, -1, true);
			foreach (ItemData item in new List<ItemData>(from.GetAllItems()))
			{
				if ((!skipHotbar || item.m_gridPos.y != 0) && chest.ContainsItemByName(item.m_shared.m_name) && ((Object)(object)localPlayer == (Object)null || !((Humanoid)localPlayer).IsItemEquiped(item)) && chest.AddItem(item))
				{
					from.RemoveItem(item);
				}
			}
			int result = chest.CountItems((string)null, -1, true) - num;
			NearbyContainers.NotifyChanged(chest);
			NearbyContainers.NotifyChanged(from);
			Game instance = Game.instance;
			if (instance != null)
			{
				instance.IncrementPlayerStat((PlayerStatType)82, 1f, false);
				return result;
			}
			return result;
		}
	}
	internal static class Sorting
	{
		private static readonly Dictionary<ItemType, int> s_typeOrder = BuildTypeOrder();

		internal static void SortOpenInventories(Player player)
		{
			if (!InventoryGuiAccess.IsDragging())
			{
				Sort(((Humanoid)player).GetInventory(), Settings.SortSkipHotbar ? 1 : 0, player);
				Container val = InventoryGuiAccess.OpenContainer();
				if ((Object)(object)val != (Object)null)
				{
					Sort(val.GetInventory(), 0, player);
				}
			}
		}

		private static void Sort(Inventory inventory, int firstRow, Player player)
		{
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			if (inventory == null)
			{
				return;
			}
			List<ItemData> allItems = inventory.GetAllItems();
			List<ItemData> list = allItems.FindAll((ItemData i) => i.m_gridPos.y >= firstRow);
			if (list.Count == 0)
			{
				return;
			}
			MergeStacks(list, player);
			foreach (ItemData item in list.FindAll((ItemData i) => i.m_stack <= 0))
			{
				allItems.Remove(item);
				list.Remove(item);
			}
			list.Sort(Compare);
			int num = Math.Max(1, inventory.GetWidth());
			for (int num2 = 0; num2 < list.Count; num2++)
			{
				list[num2].m_gridPos = new Vector2i(num2 % num, firstRow + num2 / num);
			}
			NearbyContainers.NotifyChanged(inventory);
		}

		private static void MergeStacks(List<ItemData> items, Player player)
		{
			for (int i = 0; i < items.Count; i++)
			{
				ItemData val = items[i];
				int maxStackSize = val.m_shared.m_maxStackSize;
				if (maxStackSize <= 1 || val.m_stack <= 0 || ((Humanoid)player).IsItemEquiped(val))
				{
					continue;
				}
				for (int j = i + 1; j < items.Count; j++)
				{
					if (val.m_stack >= maxStackSize)
					{
						break;
					}
					ItemData val2 = items[j];
					if (val2.m_stack > 0 && !(val2.m_shared.m_name != val.m_shared.m_name) && val2.m_quality == val.m_quality && val2.m_worldLevel == val.m_worldLevel && !((Humanoid)player).IsItemEquiped(val2))
					{
						int num = Math.Min(val2.m_stack, maxStackSize - val.m_stack);
						val.m_stack += num;
						val2.m_stack -= num;
					}
				}
			}
		}

		private static int Compare(ItemData a, ItemData b)
		{
			int num = TypeOrder(a).CompareTo(TypeOrder(b));
			if (num != 0)
			{
				return num;
			}
			num = string.Compare(DisplayName(a), DisplayName(b), StringComparison.CurrentCultureIgnoreCase);
			if (num != 0)
			{
				return num;
			}
			num = b.m_quality.CompareTo(a.m_quality);
			if (num == 0)
			{
				return b.m_stack.CompareTo(a.m_stack);
			}
			return num;
		}

		private static string DisplayName(ItemData item)
		{
			string text = item.m_shared.m_name ?? "";
			if (Localization.instance == null)
			{
				return text;
			}
			return Localization.instance.Localize(text);
		}

		private static int TypeOrder(ItemData item)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			if (!s_typeOrder.TryGetValue(item.m_shared.m_itemType, out var value))
			{
				return s_typeOrder.Count;
			}
			return value;
		}

		private static Dictionary<ItemType, int> BuildTypeOrder()
		{
			ItemType[] array = new ItemType[23];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			ItemType[] array2 = (ItemType[])(object)array;
			Dictionary<ItemType, int> dictionary = new Dictionary<ItemType, int>();
			for (int i = 0; i < array2.Length; i++)
			{
				dictionary[array2[i]] = i;
			}
			return dictionary;
		}
	}
	internal static class StackSizes
	{
		private static readonly Dictionary<SharedData, int> s_vanilla = new Dictionary<SharedData, int>();

		internal static void Apply(ObjectDB db)
		{
			if ((Object)(object)db == (Object)null || db.m_items == null)
			{
				return;
			}
			int num = 0;
			foreach (GameObject item in db.m_items)
			{
				SharedData val = (((Object)(object)item != (Object)null) ? item.GetComponent<ItemDrop>() : null)?.m_itemData?.m_shared;
				if (val == null)
				{
					continue;
				}
				if (!s_vanilla.TryGetValue(val, out var value))
				{
					value = val.m_maxStackSize;
					s_vanilla[val] = value;
				}
				if (value > 1)
				{
					int num2 = Mathf.Max(value, Mathf.RoundToInt((float)value * Settings.StackMultiplier));
					if (val.m_maxStackSize != num2)
					{
						val.m_maxStackSize = num2;
						num++;
					}
				}
			}
			if (num > 0)
			{
				InventoryModule.Log.LogInfo((object)$"Stack sizes set to {Settings.StackMultiplier:0.##}x vanilla ({num} items).");
			}
		}
	}
}
namespace ElPopeAdventures.InventoryQoL.Patches
{
	[HarmonyPatch(typeof(ObjectDB), "Awake")]
	internal static class ObjectDBAwake
	{
		[HarmonyPriority(200)]
		private static void Postfix(ObjectDB __instance)
		{
			try
			{
				StackSizes.Apply(__instance);
			}
			catch (Exception ex)
			{
				InventoryModule.Log.LogError((object)ex);
			}
		}
	}
	[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
	internal static class ObjectDBCopyOtherDB
	{
		[HarmonyPriority(200)]
		private static void Postfix(ObjectDB __instance)
		{
			try
			{
				StackSizes.Apply(__instance);
			}
			catch (Exception ex)
			{
				InventoryModule.Log.LogError((object)ex);
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "StackAll")]
	internal static class InventoryStackAll
	{
		private static bool Prefix(Inventory __instance, Inventory fromInventory, ref int __result)
		{
			if (!QuickStack.IsPending(__instance) || (Object)(object)Player.m_localPlayer == (Object)null || fromInventory != ((Humanoid)Player.m_localPlayer).GetInventory())
			{
				return true;
			}
			try
			{
				__result = QuickStack.StackInto(__instance, fromInventory, Settings.QuickStackSkipHotbar);
				QuickStack.Granted(__instance, __result);
				return false;
			}
			catch (Exception ex)
			{
				InventoryModule.Log.LogError((object)ex);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(Container), "RPC_StackResponse")]
	internal static class ContainerStackResponse
	{
		private static bool Prefix(Container __instance, bool granted)
		{
			try
			{
				if (!granted && QuickStack.IsPending(__instance.GetInventory()))
				{
					QuickStack.Refused(__instance.GetInventory());
					return false;
				}
			}
			catch (Exception ex)
			{
				InventoryModule.Log.LogError((object)ex);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Player), "HaveRequirementItems", new Type[]
	{
		typeof(Recipe),
		typeof(bool),
		typeof(int),
		typeof(int)
	})]
	internal static class PlayerHaveRequirementItems
	{
		private static void Prefix()
		{
			CraftFromChests.Enter();
		}

		private static Exception Finalizer(Exception __exception)
		{
			CraftFromChests.Exit();
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
	{
		typeof(Piece),
		typeof(RequirementMode)
	})]
	internal static class PlayerHaveRequirementsPiece
	{
		private static void Prefix()
		{
			CraftFromChests.Enter();
		}

		private static Exception Finalizer(Exception __exception)
		{
			CraftFromChests.Exit();
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Player), "ConsumeResources")]
	internal static class PlayerConsumeResources
	{
		private static void Prefix()
		{
			CraftFromChests.Enter();
		}

		private static Exception Finalizer(Exception __exception)
		{
			CraftFromChests.Exit();
			return __exception;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "SetupRequirement")]
	internal static class InventoryGuiSetupRequirement
	{
		private static void Prefix()
		{
			CraftFromChests.Enter();
		}

		private static Exception Finalizer(Exception __exception)
		{
			CraftFromChests.Exit();
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Inventory), "CountItems", new Type[]
	{
		typeof(string),
		typeof(int),
		typeof(bool)
	})]
	internal static class InventoryCountItems
	{
		private static void Postfix(Inventory __instance, string name, int quality, bool matchWorldLevel, ref int __result)
		{
			if (name == null || !CraftFromChests.Applies(__instance))
			{
				return;
			}
			try
			{
				__result += CraftFromChests.CountInChests(name, quality, matchWorldLevel);
			}
			catch (Exception ex)
			{
				InventoryModule.Log.LogError((object)ex);
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "HaveItem", new Type[]
	{
		typeof(string),
		typeof(bool)
	})]
	internal static class InventoryHaveItem
	{
		private static void Postfix(Inventory __instance, string name, bool matchWorldLevel, ref bool __result)
		{
			if (__result || name == null || !CraftFromChests.Applies(__instance))
			{
				return;
			}
			try
			{
				__result = CraftFromChests.CountInChests(name, -1, matchWorldLevel) > 0;
			}
			catch (Exception ex)
			{
				InventoryModule.Log.LogError((object)ex);
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[]
	{
		typeof(string),
		typeof(int),
		typeof(int),
		typeof(bool)
	})]
	internal static class InventoryRemoveItemByName
	{
		private static bool Prefix(Inventory __instance, string name, int amount, int itemQuality, bool worldLevelBased)
		{
			if (name == null || amount <= 0 || !CraftFromChests.Applies(__instance))
			{
				return true;
			}
			try
			{
				CraftFromChests.Remove(__instance, name, amount, itemQuality, worldLevelBased);
				return false;
			}
			catch (Exception ex)
			{
				InventoryModule.Log.LogError((object)ex);
				return true;
			}
		}
	}
}
namespace ElPopeAdventures.Deer
{
	internal sealed class DeerModule : IModule
	{
		internal static readonly ManualLogSource Log = Logger.CreateLogSource("ElPopeAdventures.Deer");

		private ConfigEntry<bool> _allowTaming;

		private ConfigEntry<string> _tamingFoods;

		private ConfigEntry<float> _tamingTime;

		private ConfigEntry<float> _fedDuration;

		private ConfigEntry<float> _foodSearchRange;

		private ConfigEntry<bool> _requireSaddle;

		private ConfigEntry<string> _saddleItem;

		private ConfigEntry<float> _speedMultiplier;

		private ConfigEntry<float> _maxStamina;

		private ConfigEntry<bool> _commandable;

		private ConfigEntry<bool> _showSaddleModel;

		private ConfigEntry<float> _saddleHeight;

		private ConfigEntry<float> _saddleForward;

		private ConfigEntry<float> _saddleSize;

		public string Name => "Deer";

		public string Description => "Tame, saddle and ride deer.";

		public Type[] PatchClasses { get; } = new Type[5]
		{
			typeof(ElPopeAdventures.Deer.Patches.ZNetSceneAwake),
			typeof(MonsterAIUpdateAI),
			typeof(BaseAIIdleMovement),
			typeof(CharacterGetHoverText),
			typeof(SadleInteract)
		};

		public void Initialize()
		{
			try
			{
				GameAccess.Bind();
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Could not look up the game internals the deer need; they stay inactive. " + ex));
			}
		}

		public void Bind(ConfigFile config)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Expected O, but got Unknown
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Expected O, but got Unknown
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Expected O, but got Unknown
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Expected O, but got Unknown
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Expected O, but got Unknown
			//IL_01f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Expected O, but got Unknown
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Expected O, but got Unknown
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			//IL_0273: Expected O, but got Unknown
			_allowTaming = config.Bind<bool>("Deer - Taming", "AllowTaming", true, "Let wild deer be tamed. Turning this off never affects deer you have already tamed.");
			_tamingFoods = config.Bind<string>("Deer - Taming", "Foods", "Raspberry,Blueberries,Mushroom,Carrot", "Item names (comma separated) that a deer will eat. Drop them on the ground near a calm wild deer.");
			_tamingTime = config.Bind<float>("Deer - Taming", "TamingTimeSeconds", 600f, new ConfigDescription("Seconds a wild deer must spend fed and calm before it becomes tame.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 7200f), Array.Empty<object>()));
			_fedDuration = config.Bind<float>("Deer - Taming", "FedDurationSeconds", 300f, new ConfigDescription("How long one piece of food keeps a deer fed. Taming only progresses while it is fed. Tame deer that are fed regain riding stamina faster.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 3600f), Array.Empty<object>()));
			_foodSearchRange = config.Bind<float>("Deer - Taming", "FoodSearchRange", 10f, new ConfigDescription("How far (in metres) a deer will walk to reach food on the ground.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 30f), Array.Empty<object>()));
			_requireSaddle = config.Bind<bool>("Deer - Riding", "RequireSaddle", true, "If true, a tame deer needs a saddle item before it can be ridden. If false, tame deer always wear a saddle.");
			_saddleItem = config.Bind<string>("Deer - Riding", "SaddleItem", "SaddleLox", "Item that counts as a deer saddle (item prefab name). The default is the Lox saddle.");
			_speedMultiplier = config.Bind<float>("Deer - Riding", "SpeedMultiplier", 1f, new ConfigDescription("Movement speed of tame deer (1 = the speed of a normal deer). Wild deer are never affected.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 3f), Array.Empty<object>()));
			_maxStamina = config.Bind<float>("Deer - Riding", "MaxStamina", 200f, new ConfigDescription("Stamina a tame deer can spend galloping or swimming with a rider.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(20f, 1000f), Array.Empty<object>()));
			_commandable = config.Bind<bool>("Deer - Riding", "Commandable", true, "If true, using a tame deer toggles follow / stay, like a wolf. If false, using it pets it, like a lox.");
			_showSaddleModel = config.Bind<bool>("Deer - Saddle", "ShowSaddleModel", true, "Show a saddle model on saddled deer. Riding works either way.");
			_saddleHeight = config.Bind<float>("Deer - Saddle", "HeightAdjust", 0f, new ConfigDescription("Moves the saddle and rider up (+) or down (-), in metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), Array.Empty<object>()));
			_saddleForward = config.Bind<float>("Deer - Saddle", "ForwardAdjust", 0f, new ConfigDescription("Moves the saddle and rider forward (+) or back (-), in metres.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), Array.Empty<object>()));
			_saddleSize = config.Bind<float>("Deer - Saddle", "SizeMultiplier", 1f, new ConfigDescription("Size of the saddle model.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.3f, 3f), Array.Empty<object>()));
			PullSettings();
		}

		public void OnSettingChanged()
		{
			PullSettings();
			try
			{
				TameDeer.ApplyLiveSettings();
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Could not apply the changed settings right away (they apply on the next world load): " + ex.Message));
			}
		}

		public void Update()
		{
		}

		private void PullSettings()
		{
			Settings.AllowTaming = _allowTaming.Value;
			Settings.TamingFoods = (from s in (_tamingFoods.Value ?? "").Split(new char[1] { ',' })
				select s.Trim() into s
				where s.Length > 0
				select s).ToArray();
			Settings.TamingTime = _tamingTime.Value;
			Settings.FedDuration = _fedDuration.Value;
			Settings.FoodSearchRange = _foodSearchRange.Value;
			Settings.RequireSaddle = _requireSaddle.Value;
			Settings.SaddleItem = (_saddleItem.Value ?? "").Trim();
			Settings.SpeedMultiplier = _speedMultiplier.Value;
			Settings.MaxStamina = _maxStamina.Value;
			Settings.Commandable = _commandable.Value;
			Settings.ShowSaddleModel = _showSaddleModel.Value;
			Settings.SaddleHeightAdjust = _saddleHeight.Value;
			Settings.SaddleForwardAdjust = _saddleForward.Value;
			Settings.SaddleSize = _saddleSize.Value;
		}
	}
	internal static class Settings
	{
		internal static bool AllowTaming = true;

		internal static string[] TamingFoods = new string[4] { "Raspberry", "Blueberries", "Mushroom", "Carrot" };

		internal static float TamingTime = 600f;

		internal static float FedDuration = 300f;

		internal static float FoodSearchRange = 10f;

		internal static bool RequireSaddle = true;

		internal static string SaddleItem = "SaddleLox";

		internal static float SpeedMultiplier = 1f;

		internal static float MaxStamina = 200f;

		internal static bool Commandable = true;

		internal static bool ShowSaddleModel = true;

		internal static float SaddleHeightAdjust;

		internal static float SaddleForwardAdjust;

		internal static float SaddleSize = 1f;
	}
	internal sealed class FeatureGuard
	{
		private const int MaxErrors = 3;

		private readonly string _name;

		private int _errors;

		internal bool Enabled { get; private set; } = true;

		internal FeatureGuard(string name)
		{
			_name = name;
		}

		internal void Fail(Exception e)
		{
			_errors++;
			if (_errors <= 3)
			{
				DeerModule.Log.LogError((object)$"[{_name}] {e}");
			}
			if (_errors >= 3 && Enabled)
			{
				Enabled = false;
				DeerModule.Log.LogError((object)("[" + _name + "] switched off after repeated errors. The game uses its normal behaviour for this instead."));
			}
		}
	}
	internal static class Guards
	{
		internal static readonly FeatureGuard Brain = new FeatureGuard("Tame deer behaviour");

		internal static readonly FeatureGuard Taming = new FeatureGuard("Deer taming");

		internal static readonly FeatureGuard Hover = new FeatureGuard("Taming progress text");
	}
	internal static class DeerBrain
	{
		[MethodImpl(MethodImplOptions.NoInlining)]
		internal static bool IsOurs(MonsterAI ai)
		{
			return ai is DeerMountAI;
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		internal static bool TryUpdate(MonsterAI ai, float dt, out bool result)
		{
			result = false;
			if (!GameAccess.AIReady)
			{
				return false;
			}
			result = ((DeerMountAI)(object)ai).UpdateMountAI(dt);
			return true;
		}
	}
	public class DeerMountAI : MonsterAI
	{
		public float m_rdTimeToSafe = 6f;

		[NonSerialized]
		private float m_rdThreatCheckTimer;

		[NonSerialized]
		private float m_rdSinceSensed;

		[NonSerialized]
		private bool m_rdWasAlerted;

		[NonSerialized]
		private bool m_rdWasRidden;

		[NonSerialized]
		private bool m_rdLookedUp;

		[NonSerialized]
		private Tameable m_rdTameable;

		[NonSerialized]
		private Humanoid m_rdBody;

		internal bool UpdateMountAI(float dt)
		{
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			if (!GameAccess.BaseUpdateAI((BaseAI)(object)this, dt))
			{
				return false;
			}
			if (!m_rdLookedUp)
			{
				m_rdLookedUp = true;
				m_rdTameable = ((Component)this).GetComponent<Tameable>();
				m_rdBody = ((Component)this).GetComponent<Humanoid>();
			}
			Sadle val = (((Object)(object)m_rdTameable != (Object)null) ? m_rdTameable.m_saddle : null);
			if ((Object)(object)val != (Object)null)
			{
				if (val.UpdateRiding(dt))
				{
					m_rdWasRidden = true;
					return true;
				}
				if (GameAccess.HasRider != null && GameAccess.HasRider(val))
				{
					m_rdWasRidden = true;
					GameAccess.StopMoving((BaseAI)(object)this);
					return true;
				}
			}
			if (m_rdWasRidden)
			{
				m_rdWasRidden = false;
				OnDismounted();
			}
			if (((BaseAI)this).m_afraidOfFire && GameAccess.AvoidFire != null && GameAccess.AvoidFire((BaseAI)(object)this, dt, null, arg4: true))
			{
				return true;
			}
			ref Character reference = ref GameAccess.MonsterAITarget.Invoke((MonsterAI)(object)this);
			m_rdThreatCheckTimer -= dt;
			if (m_rdThreatCheckTimer <= 0f)
			{
				m_rdThreatCheckTimer = (Character.IsCharacterInRange(((Component)this).transform.position, 32f) ? 2f : 10f);
				Character val2 = GameAccess.FindEnemy((BaseAI)(object)this);
				if (IsThreat(val2))
				{
					if ((Object)(object)val2 != (Object)(object)reference)
					{
						m_rdSinceSensed = 0f;
					}
					reference = val2;
				}
			}
			if ((Object)(object)reference != (Object)null && !IsThreat(reference))
			{
				reference = null;
			}
			bool flag = ((BaseAI)this).IsAlerted();
			if (flag && !m_rdWasAlerted)
			{
				m_rdSinceSensed = 0f;
			}
			if ((Object)(object)reference != (Object)null)
			{
				if (GameAccess.CanSenseTarget((BaseAI)(object)this, reference))
				{
					m_rdSinceSensed = 0f;
					if (!flag)
					{
						GameAccess.SetAlerted((BaseAI)(object)this, arg2: true);
						flag = true;
					}
				}
				GameAccess.SetTargetInfo((BaseAI)(object)this, reference.GetZDOID());
			}
			else
			{
				GameAccess.SetTargetInfo((BaseAI)(object)this, ZDOID.None);
			}
			if ((Object)(object)reference != (O