Decompiled source of MidgardPlus v1.1.5

plugins\MidgardPlus.dll

Decompiled 4 minutes 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 HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using MidgardPlus.Content;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("MidgardPlus")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.5.0")]
[assembly: AssemblyInformationalVersion("1.1.5")]
[assembly: AssemblyProduct("MidgardPlus")]
[assembly: AssemblyTitle("MidgardPlus")]
[assembly: AssemblyVersion("1.1.5.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 MidgardPlus
{
	public sealed class Synced<T> : ConfigSync.ISyncedEntry
	{
		private readonly ConfigEntry<T> _entry;

		private T _serverValue;

		private bool _hasServerValue;

		public T Value
		{
			get
			{
				if (!_hasServerValue)
				{
					return _entry.Value;
				}
				return _serverValue;
			}
		}

		public T LocalValue => _entry.Value;

		public bool IsServerControlled => _hasServerValue;

		string ConfigSync.ISyncedEntry.Key => ((ConfigEntryBase)_entry).Definition.Section + "/" + ((ConfigEntryBase)_entry).Definition.Key;

		internal Synced(ConfigEntry<T> entry)
		{
			_entry = entry;
		}

		public static implicit operator T(Synced<T> s)
		{
			return s.Value;
		}

		string ConfigSync.ISyncedEntry.SerializeLocal()
		{
			return TomlTypeConverter.ConvertToString((object)_entry.Value, typeof(T));
		}

		void ConfigSync.ISyncedEntry.ApplyServerValue(string raw)
		{
			try
			{
				_serverValue = (T)TomlTypeConverter.ConvertToValue(raw, typeof(T));
				_hasServerValue = true;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not read server value '" + raw + "' for " + ((ConfigSync.ISyncedEntry)this).Key + ": " + ex.Message));
			}
		}

		void ConfigSync.ISyncedEntry.ClearServerValue()
		{
			_hasServerValue = false;
			_serverValue = default(T);
		}
	}
	public static class ConfigSync
	{
		internal interface ISyncedEntry
		{
			string Key { get; }

			string SerializeLocal();

			void ApplyServerValue(string raw);

			void ClearServerValue();
		}

		internal const string RpcName = "MidgardPlus_ConfigSync";

		private static readonly List<ISyncedEntry> Entries = new List<ISyncedEntry>();

		private static readonly Dictionary<string, ISyncedEntry> ByKey = new Dictionary<string, ISyncedEntry>(StringComparer.Ordinal);

		public static int Count => Entries.Count;

		public static bool IsAuthority
		{
			get
			{
				if (!((Object)(object)ZNet.instance == (Object)null))
				{
					return ZNet.instance.IsServer();
				}
				return true;
			}
		}

		public static event Action Changed;

		internal static Synced<T> Register<T>(ConfigEntry<T> entry)
		{
			Synced<T> synced = new Synced<T>(entry);
			string key = ((ISyncedEntry)synced).Key;
			Entries.Add(synced);
			ByKey[key] = synced;
			return synced;
		}

		internal static ZPackage BuildPackage()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write("1.1.5");
			val.Write(Entries.Count);
			foreach (ISyncedEntry entry in Entries)
			{
				val.Write(entry.Key);
				val.Write(entry.SerializeLocal());
			}
			return val;
		}

		internal static void ApplyPackage(ZPackage pkg)
		{
			if (pkg == null)
			{
				return;
			}
			pkg.SetPos(0);
			string text = pkg.ReadString();
			if (text != "1.1.5")
			{
				Plugin.Log.LogWarning((object)("Server runs MidgardPlus " + text + " but this client runs 1.1.5. Applying the settings it sent; update if anything looks wrong."));
			}
			int num = pkg.ReadInt();
			int num2 = 0;
			int num3 = 0;
			for (int i = 0; i < num; i++)
			{
				string key = pkg.ReadString();
				string raw = pkg.ReadString();
				if (ByKey.TryGetValue(key, out var value))
				{
					value.ApplyServerValue(raw);
					num2++;
				}
				else
				{
					num3++;
				}
			}
			NotifyChanged();
			Plugin.Log.LogInfo((object)("Applied " + num2 + " settings from the server" + ((num3 > 0) ? (" (" + num3 + " unrecognised, ignored)") : "") + "."));
		}

		internal static void ClearServerValues()
		{
			bool flag = false;
			foreach (ISyncedEntry entry in Entries)
			{
				entry.ClearServerValue();
				flag = true;
			}
			if (flag)
			{
				Plugin.Log.LogInfo((object)"Reverted to local MidgardPlus settings.");
			}
			if (flag)
			{
				NotifyChanged();
			}
		}

		internal static void RaiseLocalChange()
		{
			NotifyChanged();
		}

		private static void NotifyChanged()
		{
			Action changed = ConfigSync.Changed;
			if (changed == null)
			{
				return;
			}
			try
			{
				changed();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Error re-applying settings: " + ex));
			}
		}
	}
	internal static class ItemTuning
	{
		private struct Baseline
		{
			public int MaxStackSize;

			public float Weight;
		}

		private static readonly Dictionary<SharedData, Baseline> Baselines = new Dictionary<SharedData, Baseline>();

		internal static void ApplyToLoadedItems()
		{
			ObjectDB instance = ObjectDB.instance;
			if ((Object)(object)instance == (Object)null || instance.m_items == null)
			{
				return;
			}
			int num = 0;
			foreach (GameObject item in instance.m_items)
			{
				if (!((Object)(object)item == (Object)null))
				{
					ItemDrop component = item.GetComponent<ItemDrop>();
					if (!((Object)(object)component == (Object)null) && component.m_itemData != null && component.m_itemData.m_shared != null)
					{
						Apply(component.m_itemData.m_shared);
						num++;
					}
				}
			}
			Plugin.Log.LogInfo((object)("Applied item tuning to " + num + " item definitions."));
		}

		private static void Apply(SharedData shared)
		{
			if (!Baselines.TryGetValue(shared, out var value))
			{
				value = new Baseline
				{
					MaxStackSize = shared.m_maxStackSize,
					Weight = shared.m_weight
				};
				Baselines[shared] = value;
			}
			if (value.MaxStackSize > 1)
			{
				float num = (float)value.MaxStackSize * Plugin.StackSizeMultiplier.Value;
				shared.m_maxStackSize = Mathf.Max(1, Mathf.RoundToInt(num));
			}
			shared.m_weight = Mathf.Max(0f, value.Weight * Plugin.ItemWeightMultiplier.Value);
		}
	}
	internal static class Nearby
	{
		private static readonly List<Container> Cache = new List<Container>();

		private static float _nextScan;

		private static Vector3 _lastScanAt;

		internal static List<Container> Containers(Vector3 position, float range)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			if (!(Time.time >= _nextScan) && !(Vector3.Distance(position, _lastScanAt) > 2f))
			{
				return Cache;
			}
			_nextScan = Time.time + 0.5f;
			_lastScanAt = position;
			Cache.Clear();
			Collider[] array = Physics.OverlapSphere(position, range);
			for (int i = 0; i < array.Length; i++)
			{
				Container componentInParent = ((Component)array[i]).GetComponentInParent<Container>();
				if (!((Object)(object)componentInParent == (Object)null) && !Cache.Contains(componentInParent))
				{
					ZNetView component = ((Component)componentInParent).GetComponent<ZNetView>();
					if (!((Object)(object)component == (Object)null) && component.IsValid() && !componentInParent.IsInUse() && !((Object)(object)((Component)componentInParent).GetComponent<Character>() != (Object)null) && componentInParent.GetInventory() != null)
					{
						Cache.Add(componentInParent);
					}
				}
			}
			return Cache;
		}

		internal static int Count(Vector3 position, float range, string sharedName)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			foreach (Container item in Containers(position, range))
			{
				Inventory inventory = item.GetInventory();
				if (inventory != null)
				{
					num += inventory.CountItems(sharedName, -1, true);
				}
			}
			return num;
		}

		internal static int Pull(Vector3 position, float range, string sharedName, int wanted, Inventory into)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			if (into == null || wanted <= 0)
			{
				return 0;
			}
			int num = 0;
			foreach (Container item in Containers(position, range))
			{
				if (num >= wanted)
				{
					break;
				}
				Inventory inventory = item.GetInventory();
				if (inventory == null)
				{
					continue;
				}
				foreach (ItemData item2 in inventory.GetAllItems().ToList())
				{
					if (num >= wanted)
					{
						break;
					}
					if (item2?.m_shared == null || item2.m_shared.m_name != sharedName)
					{
						continue;
					}
					int num2 = Mathf.Min(wanted - num, item2.m_stack);
					if (num2 > 0)
					{
						ItemData val = item2.Clone();
						val.m_stack = num2;
						if (!into.AddItem(val))
						{
							break;
						}
						inventory.RemoveItem(item2, num2);
						num += num2;
					}
				}
			}
			return num;
		}

		internal static void TopUp(Player player, string sharedName, int need, float range)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory != null)
			{
				int num = inventory.CountItems(sharedName, -1, true);
				if (num < need)
				{
					Pull(((Component)player).transform.position, range, sharedName, need - num, inventory);
				}
			}
		}
	}
	[BepInPlugin("midgardplus", "MidgardPlus", "1.1.5")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInProcess("valheim.exe")]
	[BepInProcess("valheim_server.exe")]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "midgardplus";

		public const string DisplayName = "MidgardPlus";

		public const string Version = "1.1.5";

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private const string MinimumJotunn = "2.30.0";

		internal static string DependencyWarning;

		private const string SecQoL = "1 - Quality of Life";

		private const string SecPlayer = "2 - Player";

		private const string SecBuild = "3 - Building";

		private const string SecCombat = "4 - Combat";

		private const string SecCraft = "5 - Production";

		private const string SecWorld = "6 - World";

		private const string SecContent = "7 - Companions";

		private const string SecChests = "8 - Containers";

		public static Synced<float> StackSizeMultiplier;

		public static Synced<float> ItemWeightMultiplier;

		public static Synced<float> AutoPickupRangeMultiplier;

		public static Synced<bool> PortalsCarryEverything;

		public static Synced<float> SkillLossOnDeathMultiplier;

		public static Synced<float> CarryWeightBonus;

		public static Synced<float> StaminaRegenMultiplier;

		public static Synced<float> StaminaCostMultiplier;

		public static Synced<float> RunSpeedMultiplier;

		public static Synced<bool> FreeBuilding;

		public static Synced<bool> IgnoreStability;

		public static Synced<float> BuildRangeMultiplier;

		public static Synced<float> EnemyHealthMultiplier;

		public static Synced<float> EnemyDamageMultiplier;

		public static Synced<float> PlayerDamageMultiplier;

		public static Synced<float> SmelterSpeedMultiplier;

		public static Synced<float> FermenterSpeedMultiplier;

		public static Synced<float> FireplaceFuelMultiplier;

		public static Synced<bool> InfiniteFireplaceFuel;

		public static Synced<int> DayLengthSeconds;

		public static Synced<int> CompanionLimit;

		public static Synced<float> CompanionHealthMultiplier;

		public static Synced<int> CompanionLevel;

		public static Synced<string> CompanionGear;

		public static Synced<float> CompanionRecallDistance;

		public static Synced<bool> CompanionScaleToSkill;

		public static Synced<float> CompanionMinimumStrength;

		public static Synced<bool> CraftFromNearbyChests;

		public static Synced<float> ChestSearchRange;

		private void Awake()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			BindSettings();
			_harmony = new Harmony("midgardplus");
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
			CheckDependencies();
			Tuning.Init();
			CustomContent.Init();
			((BaseUnityPlugin)this).Config.SettingChanged += delegate
			{
				ConfigSync.RaiseLocalChange();
			};
			Log.LogInfo((object)("MidgardPlus v1.1.5 loaded - " + ConfigSync.Count + " settings, host-authoritative in multiplayer."));
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		private void CheckDependencies()
		{
			string text;
			try
			{
				text = "2.30.0";
			}
			catch (Exception)
			{
				return;
			}
			if (IsOlderThan(text, "2.30.0"))
			{
				DependencyWarning = "MidgardPlus needs Jotunn 2.30.0 or newer. You have " + text + ". Update Jotunn, or characters will not finish loading.";
				Log.LogError((object)DependencyWarning);
				Log.LogError((object)"Get it from https://thunderstore.io/c/valheim/p/ValheimModding/Jotunn/");
			}
		}

		private static bool IsOlderThan(string found, string minimum)
		{
			string[] array = found.Split(new char[1] { '.' });
			string[] array2 = minimum.Split(new char[1] { '.' });
			for (int i = 0; i < Math.Max(array.Length, array2.Length); i++)
			{
				int result = 0;
				int result2 = 0;
				if (i < array.Length && !int.TryParse(array[i], out result))
				{
					return false;
				}
				if (i < array2.Length)
				{
					int.TryParse(array2[i], out result2);
				}
				if (result != result2)
				{
					return result < result2;
				}
			}
			return false;
		}

		private void BindSettings()
		{
			StackSizeMultiplier = Range("1 - Quality of Life", "Stack size multiplier", 1f, 1f, 100f, "Multiplies the maximum stack size of stackable items. Items that do not stack in vanilla (weapons, armour, tools) are never made stackable.");
			ItemWeightMultiplier = Range("1 - Quality of Life", "Item weight multiplier", 1f, 0f, 10f, "Multiplies the weight of every item. 0.5 = everything weighs half as much.");
			AutoPickupRangeMultiplier = Range("1 - Quality of Life", "Auto pickup range multiplier", 1f, 0f, 20f, "Multiplies how far away you automatically pick items up.");
			PortalsCarryEverything = Bind("1 - Quality of Life", "Portals carry everything", defaultValue: false, "Allow ores, metal bars and other normally teleport-blocked items through portals.");
			SkillLossOnDeathMultiplier = Range("1 - Quality of Life", "Skill loss on death multiplier", 1f, 0f, 2f, "Scales the skill penalty when you die. 0 = lose no skill at all, 1 = vanilla.");
			CarryWeightBonus = Range("2 - Player", "Carry weight bonus", 0f, 0f, 5000f, "Extra carry capacity added on top of your normal maximum.");
			StaminaRegenMultiplier = Range("2 - Player", "Stamina regen multiplier", 1f, 0.1f, 10f, "How fast stamina comes back. Higher regenerates faster.");
			StaminaCostMultiplier = Range("2 - Player", "Stamina cost multiplier", 1f, 0f, 5f, "Scales stamina spent on running, jumping, attacking and dodging. 0 = free stamina.");
			RunSpeedMultiplier = Range("2 - Player", "Run speed multiplier", 1f, 0.5f, 5f, "Multiplies your running speed.");
			FreeBuilding = Bind("3 - Building", "Free building", defaultValue: false, "Place building pieces without consuming materials. Crafting at a workbench or forge still costs resources as normal.");
			IgnoreStability = Bind("3 - Building", "Ignore structural stability", defaultValue: false, "Pieces no longer need support to stay up, letting you build freely into the air. Structures still take damage normally.");
			BuildRangeMultiplier = Range("3 - Building", "Build range multiplier", 1f, 1f, 10f, "Multiplies how far away you can place and repair pieces.");
			EnemyHealthMultiplier = Range("4 - Combat", "Enemy health multiplier", 1f, 0.1f, 20f, "Scales the health of every non-player creature. Above 1 makes the world tougher.");
			EnemyDamageMultiplier = Range("4 - Combat", "Enemy damage multiplier", 1f, 0f, 20f, "Scales damage dealt to players by anything that is not a player.");
			PlayerDamageMultiplier = Range("4 - Combat", "Player damage multiplier", 1f, 0f, 20f, "Scales damage players deal to everything else.");
			SmelterSpeedMultiplier = Range("5 - Production", "Smelter speed multiplier", 1f, 0.1f, 50f, "Speeds up smelters, kilns, blast furnaces and similar. 2 = twice as fast.");
			FermenterSpeedMultiplier = Range("5 - Production", "Fermenter speed multiplier", 1f, 0.1f, 50f, "Speeds up mead fermenting. 2 = twice as fast.");
			FireplaceFuelMultiplier = Range("5 - Production", "Fireplace fuel duration multiplier", 1f, 0.1f, 50f, "How long each piece of fuel lasts in fires, torches and braziers.");
			InfiniteFireplaceFuel = Bind("5 - Production", "Infinite fireplace fuel", defaultValue: false, "Fires, torches and braziers never burn out and never need fuel.");
			DayLengthSeconds = Range("6 - World", "Day length in seconds", 0, 0, 86400, "Length of a full day/night cycle in seconds. 0 keeps the vanilla length (1800).");
			CompanionLimit = Range("7 - Companions", "Companions per player", 1, 1, 10, "How many companions one player may have summoned at once.");
			CompanionHealthMultiplier = Range("7 - Companions", "Companion health multiplier", 1f, 0.1f, 20f, "Scales a summoned companion's health. Raise it if they die too easily.");
			CompanionLevel = Range("7 - Companions", "Companion star level", 1, 1, 3, "Star level of summoned companions. Higher is tougher and hits harder.");
			CompanionScaleToSkill = Bind("7 - Companions", "Scale companion to player skill", defaultValue: true, "Scale a companion's strength to your best weapon skill, so an early character gets help rather than something that clears the Black Forest without them. Turn this off for a companion that is always at full strength.");
			CompanionMinimumStrength = Range("7 - Companions", "Companion minimum strength", 0.35f, 0.1f, 1f, "How strong a companion is at weapon skill 0, as a fraction of full strength. Only used when scaling is on.");
			CompanionGear = Choice("7 - Companions", "Companion gear", "Starter", Companion.GearTiers.Keys.ToArray(), "What a companion arrives carrying. Starter is deliberately poor so you have something to upgrade: open their bag and give them better gear, and they will wear it. Auto instead picks a tier from your own weapon skill. Default keeps the Dverger loadout. Applies to the next companion you summon.");
			CraftFromNearbyChests = Bind("8 - Containers", "Craft from nearby chests", defaultValue: false, "Build, craft and repair using materials sitting in chests around you, instead of carrying everything to the workbench. Off by default, like everything here.");
			ChestSearchRange = Range("8 - Containers", "Chest search range", 20f, 5f, 100f, "How far the search for chests reaches, in metres. Larger is not always better, since it scans more of the world every time a recipe list refreshes.");
			CompanionRecallDistance = Range("7 - Companions", "Companion recall distance", 40f, 0f, 200f, "If a following companion falls further behind than this, it is pulled back to you. This is what keeps them from being lost when you take a portal. 0 disables recall.");
		}

		private Synced<T> Bind<T>(string section, string key, T defaultValue, string description)
		{
			return ConfigSync.Register<T>(((BaseUnityPlugin)this).Config.Bind<T>(section, key, defaultValue, description));
		}

		private Synced<string> Choice(string section, string key, string defaultValue, string[] options, string description)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			return ConfigSync.Register<string>(((BaseUnityPlugin)this).Config.Bind<string>(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueList<string>(options), Array.Empty<object>())));
		}

		private Synced<T> Range<T>(string section, string key, T defaultValue, T min, T max, string description) where T : IComparable
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			return ConfigSync.Register<T>(((BaseUnityPlugin)this).Config.Bind<T>(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<T>(min, max), Array.Empty<object>())));
		}
	}
	internal static class Tuning
	{
		private static bool _playerBaselineCaptured;

		private static float _baseAutoPickupRange;

		private static float _baseStaminaRegen;

		private static float _baseMaxPlaceDistance;

		internal static void Init()
		{
			ConfigSync.Changed += ApplyAll;
		}

		internal static void ApplyAll()
		{
			ApplyToPlayer(Player.m_localPlayer);
			ItemTuning.ApplyToLoadedItems();
		}

		internal static void ApplyToPlayer(Player player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				if (!_playerBaselineCaptured)
				{
					_baseAutoPickupRange = player.m_autoPickupRange;
					_baseStaminaRegen = player.m_staminaRegen;
					_baseMaxPlaceDistance = player.m_maxPlaceDistance;
					_playerBaselineCaptured = true;
				}
				player.m_autoPickupRange = _baseAutoPickupRange * Plugin.AutoPickupRangeMultiplier.Value;
				player.m_staminaRegen = _baseStaminaRegen * Plugin.StaminaRegenMultiplier.Value;
				player.m_maxPlaceDistance = _baseMaxPlaceDistance * Plugin.BuildRangeMultiplier.Value;
			}
		}
	}
}
namespace MidgardPlus.Patches
{
	[HarmonyPatch]
	internal static class BuildPatches
	{
		private static int _placementDepth;

		private static bool PlacingPiece => _placementDepth > 0;

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "TryPlacePiece")]
		private static void BeforeTryPlacePiece()
		{
			_placementDepth++;
		}

		[HarmonyFinalizer]
		[HarmonyPatch(typeof(Player), "TryPlacePiece")]
		private static void AfterTryPlacePiece()
		{
			_placementDepth--;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "PlacePiece")]
		private static void BeforePlacePiece()
		{
			_placementDepth++;
		}

		[HarmonyFinalizer]
		[HarmonyPatch(typeof(Player), "PlacePiece")]
		private static void AfterPlacePiece()
		{
			_placementDepth--;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
		{
			typeof(Piece),
			typeof(RequirementMode)
		})]
		private static void AlwaysAffordable(ref bool __result)
		{
			if (Plugin.FreeBuilding.Value)
			{
				__result = true;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "ConsumeResources")]
		private static bool SkipResourceCost()
		{
			if (Plugin.FreeBuilding.Value)
			{
				return !PlacingPiece;
			}
			return true;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(WearNTear), "HaveSupport")]
		private static void AlwaysSupported(ref bool __result)
		{
			if (Plugin.IgnoreStability.Value)
			{
				__result = true;
			}
		}
	}
	[HarmonyPatch]
	internal static class CombatPatches
	{
		[HarmonyPrefix]
		[HarmonyPatch(typeof(Character), "SetMaxHealth")]
		private static void ScaleCreatureHealth(Character __instance, ref float health)
		{
			if (!((Object)(object)__instance == (Object)null) && !__instance.IsPlayer())
			{
				float value = Plugin.EnemyHealthMultiplier.Value;
				if (!Mathf.Approximately(value, 1f))
				{
					health *= value;
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Character), "Damage")]
		private static void ScaleDamage(Character __instance, HitData hit)
		{
			if ((Object)(object)__instance == (Object)null || hit == null)
			{
				return;
			}
			bool flag = __instance.IsPlayer();
			Character attacker = hit.GetAttacker();
			bool flag2 = (Object)(object)attacker != (Object)null && attacker.IsPlayer();
			float value;
			if (flag && !flag2)
			{
				value = Plugin.EnemyDamageMultiplier.Value;
			}
			else
			{
				if (!(!flag && flag2))
				{
					return;
				}
				value = Plugin.PlayerDamageMultiplier.Value;
			}
			if (!Mathf.Approximately(value, 1f))
			{
				hit.ApplyModifier(value);
			}
		}
	}
	[HarmonyPatch]
	internal static class CompanionPatches
	{
		private static float _lastBagOpen;

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Humanoid), "UseItem")]
		private static bool OnUseItem(Humanoid __instance, ItemData item)
		{
			if (item == null || (Object)(object)item.m_dropPrefab == (Object)null)
			{
				return true;
			}
			if (((Object)item.m_dropPrefab).name != "MP_CompanionHorn")
			{
				return true;
			}
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if ((Object)(object)val == (Object)null)
			{
				return true;
			}
			Companion.TrySummon(val);
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Tameable), "Interact")]
		private static bool OpenCompanionBag(Tameable __instance, Humanoid user, bool hold, bool alt, ref bool __result)
		{
			if (!hold || alt)
			{
				return true;
			}
			Character component = ((Component)__instance).GetComponent<Character>();
			if ((Object)(object)component == (Object)null || component.m_group != "MidgardPlusCompanion")
			{
				return true;
			}
			Container component2 = ((Component)__instance).GetComponent<Container>();
			if ((Object)(object)component2 == (Object)null)
			{
				return true;
			}
			if ((Object)(object)InventoryGui.instance != (Object)null && InventoryGui.instance.IsContainerOpen())
			{
				__result = true;
				return false;
			}
			if (Time.time - _lastBagOpen < 0.7f)
			{
				__result = true;
				return false;
			}
			_lastBagOpen = Time.time;
			CompanionGear.ValidateGrid(component2);
			__result = component2.Interact(user, false, false);
			return false;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Tameable), "GetHoverText")]
		private static void CompanionHoverText(Tameable __instance, ref string __result)
		{
			Character component = ((Component)__instance).GetComponent<Character>();
			if (!((Object)(object)component == (Object)null) && !(component.m_group != "MidgardPlusCompanion"))
			{
				__result += "\n[<color=yellow><b>Hold E</b></color>] Open bag";
			}
		}
	}
	[HarmonyPatch]
	internal static class CraftingPatches
	{
		private static bool Enabled => Plugin.CraftFromNearbyChests.Value;

		private static float Range => Plugin.ChestSearchRange.Value;

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "HaveRequirementItems")]
		private static void RecipeAffordableFromChests(Player __instance, Recipe piece, int qualityLevel, int amount, ref bool __result)
		{
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			if (__result || !Enabled || (Object)(object)piece == (Object)null || piece.m_resources == null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			Inventory inventory = ((Humanoid)__instance).GetInventory();
			if (inventory == null)
			{
				return;
			}
			Requirement[] resources = piece.m_resources;
			foreach (Requirement val in resources)
			{
				if ((Object)(object)val?.m_resItem == (Object)null)
				{
					continue;
				}
				int num = val.GetAmount(qualityLevel) * Mathf.Max(1, amount);
				if (num > 0)
				{
					string name = val.m_resItem.m_itemData.m_shared.m_name;
					if (inventory.CountItems(name, -1, true) + Nearby.Count(((Component)__instance).transform.position, Range, name) < num)
					{
						return;
					}
				}
			}
			__result = true;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
		{
			typeof(Piece),
			typeof(RequirementMode)
		})]
		private static void PieceAffordableFromChests(Player __instance, Piece piece, ref bool __result)
		{
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			if (__result || !Enabled || (Object)(object)piece == (Object)null || piece.m_resources == null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			Inventory inventory = ((Humanoid)__instance).GetInventory();
			if (inventory == null)
			{
				return;
			}
			Requirement[] resources = piece.m_resources;
			foreach (Requirement val in resources)
			{
				if ((Object)(object)val?.m_resItem == (Object)null)
				{
					continue;
				}
				int amount = val.GetAmount(0);
				if (amount > 0)
				{
					string name = val.m_resItem.m_itemData.m_shared.m_name;
					if (inventory.CountItems(name, -1, true) + Nearby.Count(((Component)__instance).transform.position, Range, name) < amount)
					{
						return;
					}
				}
			}
			__result = true;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "ConsumeResources")]
		private static void PullBeforeConsuming(Player __instance, Requirement[] requirements, int qualityLevel, int multiplier)
		{
			if (!Enabled || requirements == null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			foreach (Requirement val in requirements)
			{
				if (!((Object)(object)val?.m_resItem == (Object)null))
				{
					int num = val.GetAmount(qualityLevel) * Mathf.Max(1, multiplier);
					if (num > 0)
					{
						Nearby.TopUp(__instance, val.m_resItem.m_itemData.m_shared.m_name, num, Range);
					}
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class ItemPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(ObjectDB), "Awake")]
		private static void AfterObjectDbAwake()
		{
			ItemTuning.ApplyToLoadedItems();
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ObjectDB), "CopyOtherDB")]
		private static void AfterObjectDbCopy()
		{
			ItemTuning.ApplyToLoadedItems();
		}
	}
	[HarmonyPatch]
	internal static class NetworkPatches
	{
		private const string RequestRpc = "MidgardPlus_ConfigRequest";

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Game), "Start")]
		private static void RegisterRpcs()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance == null)
			{
				Plugin.Log.LogWarning((object)"No ZRoutedRpc at Game.Start; config sync unavailable this session.");
				return;
			}
			instance.Register<ZPackage>("MidgardPlus_ConfigSync", (Action<long, ZPackage>)OnConfigReceived);
			instance.Register("MidgardPlus_ConfigRequest", (Action<long>)OnConfigRequested);
		}

		private static void OnConfigRequested(long sender)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && sender != 0L)
			{
				Plugin.Log.LogInfo((object)("Sending MidgardPlus settings to peer " + sender + "."));
				ZRoutedRpc.instance.InvokeRoutedRPC(sender, "MidgardPlus_ConfigSync", new object[1] { ConfigSync.BuildPackage() });
			}
		}

		private static void OnConfigReceived(long sender, ZPackage pkg)
		{
			if (!((Object)(object)ZNet.instance != (Object)null) || !ZNet.instance.IsServer())
			{
				ConfigSync.ApplyPackage(pkg);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
		private static void OnPeerInfo()
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer() && ZRoutedRpc.instance != null)
			{
				Plugin.Log.LogInfo((object)"Connected as client; requesting MidgardPlus settings from server.");
				ZRoutedRpc.instance.InvokeRoutedRPC("MidgardPlus_ConfigRequest", Array.Empty<object>());
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(ZNet), "Shutdown")]
		private static void OnShutdown()
		{
			ConfigSync.ClearServerValues();
		}
	}
	[HarmonyPatch]
	internal static class PlayerPatches
	{
		private static bool _deathFactorCaptured;

		private static float _baseDeathLowerFactor;

		private static bool _warnedAboutDependencies;

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "Awake")]
		private static void OnPlayerAwake(Player __instance)
		{
			Tuning.ApplyToPlayer(__instance);
			if (!_warnedAboutDependencies && Plugin.DependencyWarning != null && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				_warnedAboutDependencies = true;
				((Character)__instance).Message((MessageType)2, Plugin.DependencyWarning, 0, (Sprite)null, false);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "GetMaxCarryWeight")]
		private static void ExtraCarryWeight(ref float __result)
		{
			__result += Plugin.CarryWeightBonus.Value;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "UseStamina")]
		private static bool ScaleStaminaCost(ref float v)
		{
			float value = Plugin.StaminaCostMultiplier.Value;
			if (value <= 0f)
			{
				return false;
			}
			v *= value;
			return true;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "GetRunSpeedFactor")]
		private static void FasterRunning(ref float __result)
		{
			__result *= Plugin.RunSpeedMultiplier.Value;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Inventory), "IsTeleportable")]
		private static void PortalsCarryEverything(ref bool __result)
		{
			if (Plugin.PortalsCarryEverything.Value)
			{
				__result = true;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Skills), "OnDeath")]
		private static bool ScaleSkillLoss(Skills __instance)
		{
			float value = Plugin.SkillLossOnDeathMultiplier.Value;
			if (value <= 0f)
			{
				return false;
			}
			if (!_deathFactorCaptured)
			{
				_baseDeathLowerFactor = __instance.m_DeathLowerFactor;
				_deathFactorCaptured = true;
			}
			__instance.m_DeathLowerFactor = _baseDeathLowerFactor * value;
			return true;
		}
	}
	[HarmonyPatch]
	internal static class ProductionPatches
	{
		private sealed class Original
		{
			public float Value;
		}

		private static readonly ConditionalWeakTable<Smelter, Original> SmelterTimes = new ConditionalWeakTable<Smelter, Original>();

		private static readonly ConditionalWeakTable<Fermenter, Original> FermenterTimes = new ConditionalWeakTable<Fermenter, Original>();

		private static readonly ConditionalWeakTable<Fireplace, Original> FireplaceFuelTimes = new ConditionalWeakTable<Fireplace, Original>();

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Smelter), "UpdateSmelter")]
		private static void TuneSmelter(Smelter __instance)
		{
			Original value = SmelterTimes.GetValue(__instance, (Smelter s) => new Original
			{
				Value = s.m_secPerProduct
			});
			float num = Mathf.Max(0.01f, Plugin.SmelterSpeedMultiplier.Value);
			__instance.m_secPerProduct = value.Value / num;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Fermenter), "SlowUpdate")]
		private static void TuneFermenter(Fermenter __instance)
		{
			Original value = FermenterTimes.GetValue(__instance, (Fermenter f) => new Original
			{
				Value = f.m_fermentationDuration
			});
			float num = Mathf.Max(0.01f, Plugin.FermenterSpeedMultiplier.Value);
			__instance.m_fermentationDuration = value.Value / num;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Fireplace), "UpdateFireplace")]
		private static void TuneFireplace(Fireplace __instance)
		{
			Original value = FireplaceFuelTimes.GetValue(__instance, (Fireplace f) => new Original
			{
				Value = f.m_secPerFuel
			});
			float num = Mathf.Max(0.01f, Plugin.FireplaceFuelMultiplier.Value);
			__instance.m_secPerFuel = value.Value * num;
			__instance.m_infiniteFuel = Plugin.InfiniteFireplaceFuel.Value;
		}
	}
	[HarmonyPatch]
	internal static class WorldPatches
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(EnvMan), "Awake")]
		private static void SetDayLength(EnvMan __instance)
		{
			int value = Plugin.DayLengthSeconds.Value;
			if (value > 0)
			{
				__instance.m_dayLengthSec = value;
				Plugin.Log.LogInfo((object)("Day length set to " + value + "s."));
			}
		}
	}
}
namespace MidgardPlus.Content
{
	internal static class Companion
	{
		internal const string CreaturePrefabName = "MP_Companion";

		internal const string HornPrefabName = "MP_CompanionHorn";

		internal const string CompanionGroup = "MidgardPlusCompanion";

		private const string BaseCreature = "Dverger";

		private const string BaseHornItem = "Wishbone";

		internal static readonly Dictionary<string, string[]> GearTiers = new Dictionary<string, string[]>
		{
			{ "Auto", null },
			{ "Default", null },
			{
				"Starter",
				new string[4] { "Club", "ShieldWood", "ArmorRagsChest", "ArmorRagsLegs" }
			},
			{
				"Flint",
				new string[4] { "AxeFlint", "ShieldWood", "ArmorLeatherChest", "ArmorLeatherLegs" }
			},
			{
				"Bronze",
				new string[4] { "SwordBronze", "ShieldBronzeBuckler", "ArmorBronzeChest", "ArmorBronzeLegs" }
			},
			{
				"Iron",
				new string[4] { "SwordIron", "ShieldIronSquare", "ArmorIronChest", "ArmorIronLegs" }
			},
			{
				"Silver",
				new string[4] { "SwordSilver", "ShieldSilver", "ArmorWolfChest", "ArmorWolfLegs" }
			},
			{
				"BlackMetal",
				new string[4] { "SwordBlackmetal", "ShieldBlackmetal", "ArmorPaddedCuirass", "ArmorPaddedGreaves" }
			}
		};

		private static readonly SkillType[] WeaponSkills;

		private static readonly string[] Names;

		internal static void Register()
		{
			RegisterCreature();
			RegisterHorn();
		}

		private static void RegisterCreature()
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Expected O, but got Unknown
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Expected O, but got Unknown
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Expected O, but got Unknown
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Expected O, but got Unknown
			GameObject val = PrefabManager.Instance.CreateClonedPrefab("MP_Companion", "Dverger");
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogWarning((object)"Could not clone 'Dverger'; the companion will be unavailable. This usually means the game renamed the prefab in an update.");
				return;
			}
			Character component = val.GetComponent<Character>();
			if ((Object)(object)component != (Object)null)
			{
				component.m_name = "Companion";
				component.m_faction = (Faction)0;
				component.m_group = "MidgardPlusCompanion";
			}
			Tameable val2 = val.GetComponent<Tameable>();
			if ((Object)(object)val2 == (Object)null)
			{
				val2 = val.AddComponent<Tameable>();
				val2.m_tamedEffect = new EffectList();
				val2.m_sootheEffect = new EffectList();
				val2.m_petEffect = new EffectList();
				val2.m_unSummonEffect = new EffectList();
				val2.m_randomStartingName = new List<string>();
				val2.m_tameText = "Companion";
			}
			val2.m_startsTamed = true;
			val2.m_commandable = true;
			val2.m_unsummonOnOwnerLogoutSeconds = 0f;
			CompanionGear.AddBag(val);
			if ((Object)(object)val.GetComponent<CompanionBehaviour>() == (Object)null)
			{
				val.AddComponent<CompanionBehaviour>();
			}
			CustomCreature val3 = new CustomCreature(val, false, new CreatureConfig
			{
				Name = "MP_Companion",
				Faction = (Faction)0
			});
			if (CreatureManager.Instance.AddCreature(val3))
			{
				Plugin.Log.LogInfo((object)"Registered companion creature.");
			}
		}

		private static void RegisterHorn()
		{
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_0041: 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_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Expected O, but got Unknown
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: 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_0074: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Expected O, but got Unknown
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Expected O, but got Unknown
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Expected O, but got Unknown
			ItemConfig val = new ItemConfig();
			val.Name = "Companion Horn";
			val.Description = "Sound it to call a Dverger companion to your side.";
			val.CraftingStation = CraftingStations.Workbench;
			val.MinStationLevel = 2;
			val.Requirements = (RequirementConfig[])(object)new RequirementConfig[4]
			{
				new RequirementConfig
				{
					Item = "FineWood",
					Amount = 10
				},
				new RequirementConfig
				{
					Item = "Bronze",
					Amount = 2
				},
				new RequirementConfig
				{
					Item = "DeerHide",
					Amount = 4
				},
				new RequirementConfig
				{
					Item = "GreydwarfEye",
					Amount = 10
				}
			};
			CustomItem val2 = new CustomItem("MP_CompanionHorn", "Wishbone", val);
			if (ItemManager.Instance.AddItem(val2))
			{
				Plugin.Log.LogInfo((object)"Registered Companion Horn.");
			}
		}

		internal static bool TrySummon(Player player)
		{
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: 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_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: 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)
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			int num = Mathf.Max(1, Plugin.CompanionLimit.Value);
			int num2 = CountCompanionsNear(player);
			if (num2 >= num)
			{
				((Character)player).Message((MessageType)2, "Your companions are already at your side (" + num2 + "/" + num + ")", 0, (Sprite)null, false);
				return false;
			}
			GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab("MP_Companion") : null);
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogWarning((object)"Companion prefab is not in the scene; cannot summon.");
				return false;
			}
			string text = Plugin.CompanionGear.Value;
			if (text == "Auto")
			{
				text = AutoGearFor(player);
			}
			ApplyGear(val, text);
			float num3 = StrengthFor(player);
			Vector3 val2 = ((Component)player).transform.position + ((Component)player).transform.forward * 2f + Vector3.up * 0.5f;
			GameObject val3 = Object.Instantiate<GameObject>(val, val2, Quaternion.identity);
			if ((Object)(object)val3 == (Object)null)
			{
				return false;
			}
			string text2 = Names[Random.Range(0, Names.Length)];
			ZNetView component = val3.GetComponent<ZNetView>();
			if ((Object)(object)component != (Object)null && component.IsValid())
			{
				ZDO zDO = component.GetZDO();
				if (zDO != null)
				{
					zDO.Set("MP_CompanionName", text2);
					zDO.Set("MP_CompanionStrength", num3);
				}
			}
			Character component2 = val3.GetComponent<Character>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.m_name = text2;
				component2.SetTamed(true);
				int num4 = Mathf.Clamp(Plugin.CompanionLevel.Value, 1, 3);
				if (Plugin.CompanionScaleToSkill.Value)
				{
					num4 = Mathf.Clamp(Mathf.FloorToInt(1f + (float)(num4 - 1) * num3), 1, num4);
				}
				if (num4 > 1)
				{
					component2.SetLevel(num4);
				}
				float num5 = Mathf.Max(0.1f, Plugin.CompanionHealthMultiplier.Value);
				float num6 = component2.GetMaxHealth() * num5 * num3;
				component2.SetMaxHealth(num6);
				component2.SetHealth(num6);
			}
			MonsterAI component3 = val3.GetComponent<MonsterAI>();
			if ((Object)(object)component3 != (Object)null)
			{
				component3.SetFollowTarget(((Component)player).gameObject);
			}
			if (Plugin.CompanionScaleToSkill.Value)
			{
				Plugin.Log.LogInfo((object)$"Summoned {text2}: weapon skill {HighestWeaponSkill(player):0}, strength {num3:P0}, gear {text}, level {((!((Object)(object)component2 != (Object)null)) ? 1 : component2.GetLevel())}");
			}
			((Character)player).Message((MessageType)2, text2 + " answers your horn", 0, (Sprite)null, false);
			return true;
		}

		private static float HighestWeaponSkill(Player player)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			Skills val = (((Object)(object)player != (Object)null) ? ((Character)player).GetSkills() : null);
			if ((Object)(object)val == (Object)null)
			{
				return 0f;
			}
			float num = 0f;
			SkillType[] weaponSkills = WeaponSkills;
			foreach (SkillType val2 in weaponSkills)
			{
				num = Mathf.Max(num, val.GetSkillLevel(val2));
			}
			return num;
		}

		private static float StrengthFor(Player player)
		{
			if (!Plugin.CompanionScaleToSkill.Value)
			{
				return 1f;
			}
			float num = Mathf.Clamp01(Plugin.CompanionMinimumStrength.Value);
			float num2 = Mathf.Clamp01(HighestWeaponSkill(player) / 100f);
			return Mathf.Lerp(num, 1f, num2);
		}

		private static string AutoGearFor(Player player)
		{
			float num = HighestWeaponSkill(player);
			if (num < 15f)
			{
				return "Starter";
			}
			if (num < 30f)
			{
				return "Flint";
			}
			if (num < 50f)
			{
				return "Bronze";
			}
			if (num < 70f)
			{
				return "Iron";
			}
			if (num < 85f)
			{
				return "Silver";
			}
			return "BlackMetal";
		}

		private static void ApplyGear(GameObject prefab, string tier)
		{
			Humanoid component = prefab.GetComponent<Humanoid>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			CompanionCombat.RememberNativeAttacks(component);
			if (GearTiers.TryGetValue(tier, out var value) && value != null)
			{
				GameObject[] array = (from name in value
					select PrefabManager.Instance.GetPrefab(name) into go
					where (Object)(object)go != (Object)null
					select go).ToArray();
				if (array.Length == 0)
				{
					Plugin.Log.LogWarning((object)("None of the '" + tier + "' companion gear prefabs were found; keeping default gear."));
					return;
				}
				component.m_defaultItems = array;
				component.m_randomWeapon = (GameObject[])(object)new GameObject[0];
				component.m_randomSets = (ItemSet[])(object)new ItemSet[0];
				component.m_randomItems = (RandomItem[])(object)new RandomItem[0];
				component.m_randomArmor = (GameObject[])(object)new GameObject[0];
				component.m_randomShield = (GameObject[])(object)new GameObject[0];
			}
		}

		private static int CountCompanionsNear(Player player)
		{
			//IL_0040: 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)
			int num = 0;
			foreach (Character allCharacter in Character.GetAllCharacters())
			{
				if (!((Object)(object)allCharacter == (Object)null) && allCharacter.IsTamed() && !(allCharacter.m_group != "MidgardPlusCompanion") && !(Vector3.Distance(((Component)allCharacter).transform.position, ((Component)player).transform.position) > 200f))
				{
					num++;
				}
			}
			return num;
		}

		static Companion()
		{
			SkillType[] array = new SkillType[11];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			WeaponSkills = (SkillType[])(object)array;
			Names = new string[16]
			{
				"Bjorn", "Sigrid", "Halvar", "Astrid", "Ragnar", "Solveig", "Torvald", "Ingrid", "Leif", "Runa",
				"Hakon", "Freydis", "Sten", "Yrsa", "Ulf", "Dagny"
			};
		}
	}
	internal class CompanionBehaviour : MonoBehaviour
	{
		internal const string NameKey = "MP_CompanionName";

		internal const string StrengthKey = "MP_CompanionStrength";

		private const float CheckIntervalSeconds = 2f;

		private Character _character;

		private MonsterAI _ai;

		private ZNetView _nview;

		private Humanoid _humanoid;

		private Tameable _tameable;

		private Container _bag;

		private float _nextCheck;

		private float _nextGearCheck;

		private float _strength = 1f;

		private void Awake()
		{
			_character = ((Component)this).GetComponent<Character>();
			_ai = ((Component)this).GetComponent<MonsterAI>();
			_nview = ((Component)this).GetComponent<ZNetView>();
			_humanoid = ((Component)this).GetComponent<Humanoid>();
			_tameable = ((Component)this).GetComponent<Tameable>();
			_bag = ((Component)this).GetComponent<Container>();
			CompanionGear.ValidateGrid(_bag);
			if ((Object)(object)_character != (Object)null)
			{
				Character character = _character;
				character.m_onDeath = (Action)Delegate.Combine(character.m_onDeath, new Action(DropBag));
			}
			ApplyStoredName();
		}

		private void DropBag()
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_nview == (Object)null) && _nview.IsValid() && _nview.IsOwner())
			{
				CompanionGear.DropAll(_bag, _humanoid, ((Component)this).transform.position);
			}
		}

		private void ApplyStoredName()
		{
			if ((Object)(object)_character == (Object)null || (Object)(object)_nview == (Object)null || !_nview.IsValid())
			{
				return;
			}
			ZDO zDO = _nview.GetZDO();
			if (zDO != null)
			{
				string text = zDO.GetString("MP_CompanionName", string.Empty);
				if (!string.IsNullOrEmpty(text))
				{
					_character.m_name = text;
				}
				_strength = Mathf.Clamp(zDO.GetFloat("MP_CompanionStrength", 1f), 0.05f, 1f);
			}
		}

		private void Update()
		{
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			if (Time.time < _nextCheck)
			{
				return;
			}
			_nextCheck = Time.time + 2f;
			if ((Object)(object)_character == (Object)null || (Object)(object)_nview == (Object)null || !_nview.IsValid() || !_nview.IsOwner())
			{
				return;
			}
			if (Time.time >= _nextGearCheck)
			{
				_nextGearCheck = Time.time + 4f;
				if ((Object)(object)_bag != (Object)null && !_bag.IsInUse())
				{
					CompanionGear.SyncGear(_bag, _humanoid);
					CompanionGear.TryEat(_character, _bag, _tameable);
				}
				CompanionCombat.EnsureUsable(_character, _humanoid, _strength);
				if ((Object)(object)_bag != (Object)null && !_bag.IsInUse())
				{
					CompanionCombat.StashNativeWeapons(_bag, _humanoid);
				}
			}
			GameObject val = (((Object)(object)_ai != (Object)null) ? _ai.GetFollowTarget() : null);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			float value = Plugin.CompanionRecallDistance.Value;
			if (!(value <= 0f))
			{
				float num = Vector3.Distance(((Component)this).transform.position, val.transform.position);
				if (!(num < value))
				{
					Vector3 val2 = val.transform.position + val.transform.forward * 1.5f + Vector3.up * 0.5f;
					_character.TeleportTo(val2, val.transform.rotation, num > 100f);
				}
			}
		}
	}
	internal static class CompanionCombat
	{
		private static readonly MethodInfo ShallowCopy = typeof(object).GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic);

		private static readonly HashSet<SharedData> Prepared = new HashSet<SharedData>();

		private static readonly HashSet<int> Reported = new HashSet<int>();

		private static readonly HashSet<SharedData> Scaled = new HashSet<SharedData>();

		private static readonly List<ItemData> Donors = new List<ItemData>();

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

		internal static bool MakeUsable(Character companion, ItemData weapon, IEnumerable<ItemData> nativeWeapons)
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Invalid comparison between Unknown and I4
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Invalid comparison between Unknown and I4
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)companion == (Object)null || weapon == null || weapon.m_shared == null)
			{
				return false;
			}
			if (weapon.m_shared.m_attack == null)
			{
				return false;
			}
			if (Prepared.Contains(weapon.m_shared))
			{
				return true;
			}
			ZSyncAnimation zanim = companion.GetZAnim();
			if ((Object)(object)zanim == (Object)null)
			{
				return false;
			}
			object? obj = ShallowCopy?.Invoke(weapon.m_shared, null);
			SharedData val = (SharedData)((obj is SharedData) ? obj : null);
			if (val == null)
			{
				return false;
			}
			Attack val2 = weapon.m_shared.m_attack.Clone();
			bool ranged = (int)val2.m_attackType == 2 || (int)val2.m_attackType == 5;
			if (!Supported(zanim, val2.m_attackAnimation))
			{
				ItemData val3 = nativeWeapons?.FirstOrDefault((Func<ItemData, bool>)((ItemData n) => n?.m_shared?.m_attack != null && IsRanged(n.m_shared.m_attack) == ranged && Supported(zanim, n.m_shared.m_attack.m_attackAnimation)));
				if (val3 == null)
				{
					Report(companion, zanim, weapon);
					return false;
				}
				val2.m_attackAnimation = val3.m_shared.m_attack.m_attackAnimation;
				val2.m_attackRandomAnimations = val3.m_shared.m_attack.m_attackRandomAnimations;
			}
			val.m_attack = val2;
			float num = ((val2.m_attackRange > 0f) ? val2.m_attackRange : 2f);
			val.m_aiAttackRange = (ranged ? Mathf.Max(num, 25f) : Mathf.Max(num, 2f));
			val.m_aiAttackRangeMin = (ranged ? 6f : 0f);
			val.m_aiAttackInterval = (ranged ? 3f : 2f);
			val.m_aiAttackMaxAngle = (ranged ? 15f : 30f);
			val.m_aiTargetType = (AiTarget)0;
			val.m_aiWhenWalking = true;
			val.m_aiWhenSwiming = false;
			val.m_aiWhenFlying = false;
			val.m_aiInDungeonOnly = false;
			val.m_aiInMistOnly = false;
			val.m_aiMinHealthPercentage = 0f;
			val.m_aiMaxHealthPercentage = 1f;
			val.m_aiPrioritized = true;
			weapon.m_shared = val;
			Prepared.Add(val);
			Plugin.Log.LogInfo((object)string.Format("Companion weapon ready: {0} ({1}, reach {2:0.#}, animation '{3}')", val.m_name, ranged ? "ranged" : "melee", val.m_aiAttackRange, val2.m_attackAnimation));
			return true;
		}

		private static bool IsRanged(Attack attack)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Invalid comparison between Unknown and I4
			if ((int)attack.m_attackType != 2)
			{
				return (int)attack.m_attackType == 5;
			}
			return true;
		}

		private static bool Supported(ZSyncAnimation zanim, string animation)
		{
			if (!string.IsNullOrEmpty(animation))
			{
				return zanim.HasParameter(animation, (AnimatorControllerParameterType)9);
			}
			return false;
		}

		private static void Report(Character companion, ZSyncAnimation zanim, ItemData weapon)
		{
			int instanceID = ((Object)companion).GetInstanceID();
			if (!Reported.Add(instanceID))
			{
				return;
			}
			Plugin.Log.LogInfo((object)$"Companion cannot swing {weapon.m_shared.m_name}: its rig has no '{weapon.m_shared.m_attack.m_attackAnimation}' attack, so it keeps its own weapon.");
			Animator componentInChildren = ((Component)companion).GetComponentInChildren<Animator>();
			if (!((Object)(object)(((Object)(object)componentInChildren != (Object)null) ? componentInChildren.runtimeAnimatorController : null) == (Object)null))
			{
				string[] array = (from p in componentInChildren.parameters
					where (int)p.type == 9
					select p.name into n
					orderby n
					select n).ToArray();
				Plugin.Log.LogInfo((object)("Attacks this companion's rig does support: " + ((array.Length != 0) ? string.Join(", ", array) : "none found")));
			}
		}

		internal static void ScaleDamage(ItemData weapon, float strength)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: 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)
			if (weapon?.m_shared != null && !(strength >= 0.999f) && !Scaled.Contains(weapon.m_shared))
			{
				object? obj = ShallowCopy?.Invoke(weapon.m_shared, null);
				SharedData val = (SharedData)((obj is SharedData) ? obj : null);
				if (val != null)
				{
					val.m_damages = Scale(val.m_damages, strength);
					val.m_damagesPerLevel = Scale(val.m_damagesPerLevel, strength);
					weapon.m_shared = val;
					Scaled.Add(val);
					Plugin.Log.LogInfo((object)$"Companion weapon scaled to {strength:P0} of full strength: {val.m_name}");
				}
			}
		}

		private static DamageTypes Scale(DamageTypes d, float f)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			d.m_damage *= f;
			d.m_blunt *= f;
			d.m_slash *= f;
			d.m_pierce *= f;
			d.m_chop *= f;
			d.m_pickaxe *= f;
			d.m_fire *= f;
			d.m_frost *= f;
			d.m_lightning *= f;
			d.m_poison *= f;
			d.m_spirit *= f;
			return d;
		}

		internal static bool IsWeapon(ItemType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Invalid comparison between Unknown and I4
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Invalid comparison between Unknown and I4
			if ((int)type != 3 && (int)type != 14 && (int)type != 22)
			{
				return (int)type == 4;
			}
			return true;
		}

		internal static void EnsureUsable(Character companion, Humanoid humanoid, float strength)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			Inventory val = (((Object)(object)humanoid != (Object)null) ? humanoid.GetInventory() : null);
			if (val == null)
			{
				return;
			}
			List<ItemData> list = null;
			foreach (ItemData item in val.GetAllItems().ToList())
			{
				if (item?.m_shared == null || !item.m_equipped || !IsWeapon(item.m_shared.m_itemType))
				{
					continue;
				}
				if (item.m_shared.m_aiAttackRange <= 0f)
				{
					if (list == null)
					{
						list = NativeWeapons(humanoid);
					}
					MakeUsable(companion, item, list);
				}
				ScaleDamage(item, strength);
			}
		}

		internal static bool IsNative(ItemData item)
		{
			if (item?.m_shared != null)
			{
				return NativeNames.Contains(item.m_shared.m_name);
			}
			return false;
		}

		internal static bool PlayerArmed(Humanoid humanoid)
		{
			Inventory val = (((Object)(object)humanoid != (Object)null) ? humanoid.GetInventory() : null);
			if (val == null)
			{
				return false;
			}
			return val.GetAllItems().Any((ItemData i) => i?.m_shared != null && i.m_equipped && IsWeapon(i.m_shared.m_itemType) && !IsNative(i));
		}

		internal static void StashNativeWeapons(Container bag, Humanoid humanoid)
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			Inventory val = (((Object)(object)humanoid != (Object)null) ? humanoid.GetInventory() : null);
			Inventory val2 = (((Object)(object)bag != (Object)null) ? bag.GetInventory() : null);
			if (val == null || val2 == null || !PlayerArmed(humanoid))
			{
				return;
			}
			foreach (ItemData item in val.GetAllItems().ToList())
			{
				if (item?.m_shared != null && IsWeapon(item.m_shared.m_itemType) && IsNative(item))
				{
					humanoid.UnequipItem(item, false);
					ItemData val3 = item.Clone();
					if (val2.AddItem(val3))
					{
						val.RemoveItem(item);
					}
					else
					{
						humanoid.EquipItem(item, false);
					}
					Plugin.Log.LogInfo((object)("Companion put away its own " + item.m_shared.m_name + "; using what you gave it instead."));
				}
			}
		}

		internal static void RememberNativeAttacks(Humanoid humanoid)
		{
			if ((Object)(object)humanoid == (Object)null || Donors.Count > 0)
			{
				return;
			}
			List<GameObject> list = new List<GameObject>();
			if (humanoid.m_defaultItems != null)
			{
				list.AddRange(humanoid.m_defaultItems);
			}
			if (humanoid.m_randomWeapon != null)
			{
				list.AddRange(humanoid.m_randomWeapon);
			}
			if (humanoid.m_randomItems != null)
			{
				list.AddRange(from r in humanoid.m_randomItems
					where r != null
					select r.m_prefab);
			}
			if (humanoid.m_randomSets != null)
			{
				foreach (ItemSet item in humanoid.m_randomSets.Where((ItemSet s) => s?.m_items != null))
				{
					list.AddRange(item.m_items);
				}
			}
			foreach (GameObject item2 in list)
			{
				ItemDrop val = (((Object)(object)item2 != (Object)null) ? item2.GetComponent<ItemDrop>() : null);
				ItemData val2 = (((Object)(object)val != (Object)null) ? val.m_itemData : null);
				if (val2?.m_shared != null)
				{
					NativeNames.Add(val2.m_shared.m_name);
					if (val2.m_shared.m_attack != null && !(val2.m_shared.m_aiAttackRange <= 0f))
					{
						Donors.Add(val2);
					}
				}
			}
			if (Donors.Count > 0)
			{
				Plugin.Log.LogInfo((object)("Companion attack animations available from base creature: " + string.Join(", ", Donors.Select((ItemData d) => d.m_shared.m_name + " (" + d.m_shared.m_attack.m_attackAnimation + ")"))));
			}
		}

		internal static List<ItemData> NativeWeapons(Humanoid humanoid)
		{
			List<ItemData> list = new List<ItemData>(Donors);
			Inventory val = (((Object)(object)humanoid != (Object)null) ? humanoid.GetInventory() : null);
			if (val != null)
			{
				list.AddRange(from i in val.GetAllItems()
					where i?.m_shared?.m_attack != null && i.m_shared.m_aiAttackRange > 0f
					select i);
			}
			return list;
		}
	}
	internal static class CompanionGear
	{
		internal const int BagWidth = 5;

		internal const int BagHeight = 3;

		private static readonly ItemType[] Wearable;

		internal static void AddBag(GameObject prefab)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Expected O, but got Unknown
			Container val = prefab.GetComponent<Container>();
			if ((Object)(object)val == (Object)null)
			{
				val = prefab.AddComponent<Container>();
			}
			val.m_name = "Companion";
			val.m_width = 5;
			val.m_height = 3;
			val.m_checkGuardStone = false;
			val.m_autoDestroyEmpty = false;
			val.m_closeEffects = new EffectList();
		}

		internal static bool ValidateGrid(Container bag)
		{
			Inventory val = (((Object)(object)bag != (Object)null) ? bag.GetInventory() : null);
			if (val == null)
			{
				return true;
			}
			int width = val.GetWidth();
			int height = val.GetHeight();
			List<ItemData> allItems = val.GetAllItems();
			if (allItems.All((ItemData i) => i != null && i.m_gridPos.x >= 0 && i.m_gridPos.x < width && i.m_gridPos.y >= 0 && i.m_gridPos.y < height))
			{
				return true;
			}
			List<ItemData> list = new List<ItemData>(allItems.Where((ItemData i) => i != null));
			val.RemoveAll();
			int num = 0;
			int num2 = 0;
			foreach (ItemData item in list)
			{
				if (val.AddItem(item))
				{
					num++;
				}
				else
				{
					num2++;
				}
			}
			Plugin.Log.LogWarning((object)$"Repaired companion bag: {num} items placed back in the grid, {num2} did not fit");
			return false;
		}

		internal static void SyncGear(Container bag, Humanoid humanoid)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)bag == (Object)null || (Object)(object)humanoid == (Object)null)
			{
				return;
			}
			Inventory inventory = bag.GetInventory();
			Inventory inventory2 = humanoid.GetInventory();
			if (inventory == null || inventory2 == null)
			{
				return;
			}
			List<ItemData> nativeWeapons = CompanionCombat.NativeWeapons(humanoid);
			bool ignoreNative = CompanionCombat.PlayerArmed(humanoid);
			ItemType[] wearable = Wearable;
			foreach (ItemType type in wearable)
			{
				ItemData val = (from item in inventory.GetAllItems()
					where item != null && item.m_shared != null && item.m_shared.m_itemType == type
					where !ignoreNative || !CompanionCombat.IsWeapon(type) || !CompanionCombat.IsNative(item)
					select item).OrderByDescending(Rank).FirstOrDefault();
				if (val == null)
				{
					continue;
				}
				ItemData val2 = ((IEnumerable<ItemData>)inventory2.GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData val5) => val5 != null && val5.m_shared != null && val5.m_shared.m_itemType == type && val5.m_equipped));
				if (val2 != null && Rank(val2) >= Rank(val))
				{
					continue;
				}
				if (val2 != null)
				{
					humanoid.UnequipItem(val2, false);
					ItemData val3 = val2.Clone();
					if (inventory.AddItem(val3))
					{
						inventory2.RemoveItem(val2);
					}
					else
					{
						humanoid.EquipItem(val2, false);
					}
				}
				if (inventory2.GetAllItems().Any((ItemData val5) => val5 != null && val5.m_shared != null && val5.m_shared.m_itemType == type && val5.m_equipped))
				{
					continue;
				}
				ItemData val4 = val.Clone();
				val4.m_stack = 1;
				if (inventory2.AddItem(val4))
				{
					if (CompanionCombat.IsWeapon(type))
					{
						CompanionCombat.MakeUsable((Character)(object)humanoid, val4, nativeWeapons);
					}
					inventory.RemoveItem(val, 1);
					humanoid.EquipItem(val4, false);
				}
			}
		}

		internal static void DropAll(Container bag, Humanoid humanoid, Vector3 position)
		{
			//IL_006b: 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_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			Inventory[] array = (Inventory[])(object)new Inventory[2]
			{
				((Object)(object)bag != (Object)null) ? bag.GetInventory() : null,
				((Object)(object)humanoid != (Object)null) ? humanoid.GetInventory() : null
			};
			foreach (Inventory val in array)
			{
				if (val == null)
				{
					continue;
				}
				foreach (ItemData item in val.GetAllItems().ToList())
				{
					if (item != null && item.m_shared != null)
					{
						Vector3 val2 = position + Vector3.up * 0.6f + new Vector3(Random.Range(-0.6f, 0.6f), 0f, Random.Range(-0.6f, 0.6f));
						ItemDrop.DropItem(item, item.m_stack, val2, Quaternion.identity);
					}
				}
				val.RemoveAll();
			}
		}

		private static float Rank(ItemData item)
		{
			if (item == null || item.m_shared == null)
			{
				return -1f;
			}
			return ((DamageTypes)(ref item.m_shared.m_damages)).GetTotalDamage() * 10f + item.m_shared.m_armor + (float)item.m_quality;
		}

		internal static bool TryEat(Character character, Container bag, Tameable tameable)
		{
			if ((Object)(object)character == (Object)null || (Object)(object)bag == (Object)null)
			{
				return false;
			}
			if (character.GetHealthPercentage() >= 0.999f)
			{
				return false;
			}
			Inventory inventory = bag.GetInventory();
			if (inventory == null)
			{
				return false;
			}
			ItemData val = ((IEnumerable<ItemData>)inventory.GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => i != null && i.m_shared != null && (int)i.m_shared.m_itemType == 2 && i.m_shared.m_food > 0f));
			if (val == null)
			{
				return false;
			}
			float num = Mathf.Max(1f, val.m_shared.m_food);
			inventory.RemoveItem(val, 1);
			character.Heal(num, true);
			return true;
		}

		static CompanionGear()
		{
			ItemType[] array = new ItemType[12];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			Wearable = (ItemType[])(object)array;
		}
	}
	internal static class CustomContent
	{
		private static bool _registered;

		internal static void Init()
		{
			PrefabManager.OnVanillaPrefabsAvailable += RegisterAll;
		}

		private static void RegisterAll()
		{
			if (!_registered)
			{
				_registered = true;
				Companion.Register();
				Structures.Register();
				Recipes.Register();
				Plugin.Log.LogInfo((object)"Custom content registered.");
			}
		}
	}
	internal static class Recipes
	{
		internal static void Register()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			//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_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Expected O, but got Unknown
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Expected O, but got Unknown
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Expected O, but got Unknown
			Add("Chain", 1, CraftingStations.Forge, 2, new RequirementConfig
			{
				Item = "Iron",
				Amount = 4
			});
			Add("IronNails", 20, CraftingStations.Forge, 1, new RequirementConfig
			{
				Item = "Iron",
				Amount = 1
			});
			Add("Tar", 4, CraftingStations.Workbench, 3, new RequirementConfig
			{
				Item = "Resin",
				Amount = 8
			}, new RequirementConfig
			{
				Item = "Coal",
				Amount = 4
			}, new RequirementConfig
			{
				Item = "Bloodbag",
				Amount = 2
			});
		}

		private static void Add(string item, int amount, string station, int minStationLevel, params RequirementConfig[] requirements)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			CustomRecipe val = new CustomRecipe(new RecipeConfig
			{
				Item = item,
				Amount = amount,
				CraftingStation = station,
				MinStationLevel = minStationLevel,
				Requirements = requirements
			});
			if (ItemManager.Instance.AddRecipe(val))
			{
				Plugin.Log.LogInfo((object)("Registered recipe for " + item + "."));
			}
		}
	}
	internal static class Structures
	{
		private const string ReinforcedChestName = "MP_ChestReinforced";

		private const string BaseChest = "piece_chest_wood";

		internal static void Register()
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Expected O, but got Unknown
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Expected O, but got Unknown
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Expected O, but got Unknown
			GameObject val = PrefabManager.Instance.CreateClonedPrefab("MP_ChestReinforced", "piece_chest_wood");
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogWarning((object)"Could not clone 'piece_chest_wood'; the reinforced chest will be unavailable.");
				return;
			}
			Container component = val.GetComponent<Container>();
			if ((Object)(object)component != (Object)null)
			{
				component.m_name = "Reinforced Chest";
				component.m_width = 8;
				component.m_height = 4;
			}
			PieceConfig val2 = new PieceConfig();
			val2.Name = "Reinforced Chest";
			val2.Description = "Iron-banded storage. Holds far more than a plain chest.";
			val2.PieceTable = PieceTables.Hammer;
			val2.Category = PieceCategories.Furniture;
			val2.CraftingStation = CraftingStations.Workbench;
			val2.Requirements = (RequirementConfig[])(object)new RequirementConfig[2]
			{
				new RequirementConfig
				{
					Item = "FineWood",
					Amount = 10,
					Recover = true
				},
				new RequirementConfig
				{
					Item = "Iron",
					Amount = 4,
					Recover = true
				}
			};
			CustomPiece val3 = new CustomPiece(val, false, val2);
			if (PieceManager.Instance.AddPiece(val3))
			{
				Plugin.Log.LogInfo((object)"Registered Reinforced Chest.");
			}
		}
	}
}