Decompiled source of ZenPlayer v0.2.12

plugins\ZenPlayer.dll

Decompiled a week ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using JetBrains.Annotations;
using Jotunn.Configs;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;
using Zen.Config;
using Zen.Lib;
using Zen.Logging;
using ZenPlayer.Sections;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ZenPlayer")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ZenPlayer")]
[assembly: AssemblyCopyright("Copyright \ufffd  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("0.2.12")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.2.12.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ZenPlayer
{
	[BepInPlugin("ZenDragon.ZenPlayer", "ZenPlayer", "0.2.12")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[SynchronizationMode(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	internal class Plugin : ZenMod<Plugin>
	{
		public const string PluginName = "ZenPlayer";

		public const string PluginVersion = "0.2.12";

		public const string PluginGUID = "ZenDragon.ZenPlayer";

		protected override void Setup()
		{
			((ZenMod)this).ConfigSync += OnConfigSync;
		}

		private static void OnConfigSync()
		{
			Durability.SetItemDurabilities();
			SkillRules.RefreshLocks();
		}

		protected override void TitleScene(bool isFirstBoot)
		{
		}

		protected override void WorldStart()
		{
			Autopickup.Start();
			Knowledge.Start();
			SkillRules.RefreshLocks();
		}

		protected override void Shutdown()
		{
		}
	}
}
namespace ZenPlayer.Sections
{
	[HarmonyPatch]
	internal static class Autopickup
	{
		public static class Configs
		{
			public static readonly ConfigEntry<bool> AutopickupExtended;

			public static readonly ConfigEntry<PickupMode> AutopickupDefaultMode;

			static Configs()
			{
				AutopickupExtended = Config.Define<bool>(false, "Autopickup", "Have In Inventory - Enable", true, "If enabled then pressing the Toggle Autopickup button will cycle through 3 states:\n- Everything: Vanilla\n- Inventory: Only pickup items already in your inventory\n- Nothing: Off");
				AutopickupDefaultMode = Config.Define<PickupMode>(false, "Autopickup", "Autopickup - Default Mode", PickupMode.Everything, "Autopickup mode set on game start when \"" + ((ConfigEntryBase)AutopickupExtended).Definition.Key + "\" is on.");
			}
		}

		internal enum PickupMode
		{
			Everything,
			HaveInInventory,
			Nothing
		}

		private static PickupMode _pickupMode;

		internal static void Start()
		{
			if (Configs.AutopickupExtended.Value)
			{
				_pickupMode = Configs.AutopickupDefaultMode.Value;
				Log.Message((object)$"{((ConfigEntryBase)Configs.AutopickupDefaultMode).Definition.Key}: {_pickupMode}", (ushort)0);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Game), "SpawnPlayer")]
		private static void Game_SpawnPlayer(bool spawnValkyrie, Player __result)
		{
			if (!spawnValkyrie)
			{
				ShowMessage(__result, (MessageType)1);
			}
		}

		[HarmonyTranspiler]
		[HarmonyPatch(typeof(Player), "AutoPickup")]
		private static IEnumerable<CodeInstruction> Player_AutoPickup_Transpile(IEnumerable<CodeInstruction> codes)
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(Inventory), "CanAddItem", new Type[2]
			{
				typeof(ItemData),
				typeof(int)
			}, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(Autopickup), "CanAddItem_Intercept", (Type[])null, (Type[])null);
			return Transpilers.MethodReplacer(codes, (MethodBase)methodInfo, (MethodBase)methodInfo2);
		}

		private static bool CanAddItem_Intercept(Inventory inventory, ItemData item, int stack)
		{
			if (!Configs.AutopickupExtended.Value)
			{
				return inventory.CanAddItem(item, -1);
			}
			return _pickupMode switch
			{
				PickupMode.Everything => inventory.CanAddItem(item, -1), 
				PickupMode.HaveInInventory => inventory.HaveItem(ItemDataExt.GetName(item), true) && inventory.CanAddItem(item, -1), 
				PickupMode.Nothing => false, 
				_ => throw new ArgumentOutOfRangeException(), 
			};
		}

		[HarmonyTranspiler]
		[HarmonyPatch(typeof(Player), "Update")]
		private static IEnumerable<CodeInstruction> Player_Update_Transpile(IEnumerable<CodeInstruction> codes)
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(Character), "Message", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(Autopickup), "AutopickupMessage_Intercept", (Type[])null, (Type[])null);
			return Transpilers.MethodReplacer(codes, (MethodBase)methodInfo, (MethodBase)methodInfo2);
		}

		private static void AutopickupMessage_Intercept(Player player, MessageType type, string msg, int amount, Sprite icon)
		{
			if (!msg.StartsWith("$hud_autopickup"))
			{
				return;
			}
			if (Configs.AutopickupExtended.Value)
			{
				switch (_pickupMode)
				{
				case PickupMode.Everything:
					_pickupMode = PickupMode.HaveInInventory;
					Player.m_enableAutoPickup = true;
					break;
				case PickupMode.HaveInInventory:
					_pickupMode = PickupMode.Nothing;
					Player.m_enableAutoPickup = false;
					break;
				case PickupMode.Nothing:
					_pickupMode = PickupMode.Everything;
					Player.m_enableAutoPickup = true;
					break;
				default:
					throw new ArgumentOutOfRangeException();
				}
			}
			Log.Info((object)$"Switch Autopickup Mode to {_pickupMode}", (ushort)0);
			ShowMessage(player, (MessageType)2);
		}

		private static string GetMsg()
		{
			if (!Configs.AutopickupExtended.Value)
			{
				if (!Player.m_enableAutoPickup)
				{
					return "$hud_autopickup: $hud_off";
				}
				return "$hud_autopickup: $hud_on";
			}
			return _pickupMode switch
			{
				PickupMode.HaveInInventory => "$hud_autopickup: $settings_inventory", 
				PickupMode.Everything => "$hud_autopickup: $hud_on", 
				PickupMode.Nothing => "$hud_autopickup: $hud_off", 
				_ => throw new ArgumentOutOfRangeException(), 
			};
		}

		private static void ShowMessage(Player player, MessageType type)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			((Character)player).Message(type, GetMsg(), 0, (Sprite)null);
		}
	}
	[HarmonyPatch]
	public static class Death
	{
		private static class Configs
		{
			public static readonly ConfigEntry<bool> BuffNoSkillLoss;

			public static readonly ConfigEntry<bool> BuffCorpseRun;

			public static readonly ConfigEntry<int> KeepEquipMin;

			public static readonly ConfigEntry<bool> KeepEatenFood;

			public static readonly ConfigEntry<float> SkillReductionRate;

			public static readonly ConfigEntry<bool> SkillReduction0KeepProgress;

			static Configs()
			{
				KeepEquipMin = Config.Define<int>(true, "Death", "Keep Equipment Min", 1, Config.AcceptRange<int>(0, 8), "This many equipped items will be selected at random and kept on death. (Vanilla: 0)\r\nAny kept items will be removed from your corpse ragdoll for visuall accuracy.\r\nNOTE: Ammo is never protected, it will always be something more useful that you had equipped.\r\nThis is a kindness from the Gods.");
				KeepEatenFood = Config.Define<bool>(true, "Death", "Keep Eaten Food", true, "Keep all the food in your belly when you die. (Vanilla: false)");
				SkillReductionRate = Config.Define<float>(true, "Death", "Skill Reduction Rate", 0f, Config.AcceptRange<float>(0f, 20f), "Vanilla has a DeathFactor of 0.05.  \r\nThis option calculates the skill loss as: Skill Reduction Rate x DeathFactor\r\nSet to 1: 1 x DeathFactor = 5% skill loss (Vanilla default)\r\nSet to 0.5: 0.5 x DeathFactor = 2.5% skill loss. (Half Vanilla default)\r\nSet to 0: 0 x DeathFctor = 0% skill loss (No skill loss)\r\nSet to 2: 2 x DeathFactor = 10% skill loss. (Double vanilla default)\r\nSet to 10: 10 x DeathFactor = 50% skill loss. (Half your current skills)\r\nSet to 20: 20 x DeathFactor = 100% skill loss. (All your current skills)\r\nNOTE: This is the same as the vanilla global key: skillreductionrate X\r\nIf the global key is set, this config option will be ignored.");
				SkillReduction0KeepProgress = Config.Define<bool>(true, "Death", "Skill Reduction - Keep Progress", false, "Keep all progress towards the next level when you die. Only applies if " + ((ConfigEntryBase)SkillReductionRate).Definition.Key + " is 0.\r\nNormally the progress towards the next level will be wiped out when you die even if you have the skill reduction rate set to 0.\r\nNOTE: By default this mod has been configured for you to keep all your skills but lose your progress towards the next level.\r\nThis seems fair to me, but you can change it if you want.");
				BuffNoSkillLoss = Config.Define<bool>(true, "Death", "Enable Buff - No Skill Loss", false, "Enable the No Skill Loss buff. No reason to show the buff if you don't lose skills. (Vanilla: true)");
				BuffCorpseRun = Config.Define<bool>(true, "Death", "Enable Buff - Corpse Run", false, "Enable the Corpse Run buff. No need for this buff if you don't lose skills. (Vanilla: true)");
			}
		}

		[HarmonyPatch(typeof(Player), "OnDeath")]
		private static class Player_OnDeath_KeepFood
		{
			[UsedImplicitly]
			private static void Prefix(Player __instance, ref Food[] __state)
			{
				if (Configs.KeepEatenFood.Value)
				{
					__state = __instance.m_foods.ToArray();
				}
			}

			[UsedImplicitly]
			private static void Postfix(Player __instance, Food[] __state)
			{
				if (Configs.KeepEatenFood.Value)
				{
					foreach (Food item in __state)
					{
						__instance.m_foods.Add(item);
					}
				}
			}
		}

		[HarmonyPatch(typeof(Humanoid), "UnequipAllItems")]
		private static class PlayerUnequipAllItems
		{
			[HarmonyPatch(typeof(Player), "CreateTombStone")]
			private static class PlayerCreateTombStone
			{
				[UsedImplicitly]
				private static bool Prefix(Player __instance)
				{
					_isCreateTombStone = true;
					ItemData[] array = EquippedKeepable((Humanoid)(object)__instance);
					if (((Humanoid)__instance).GetInventory().NrOfItems() > array.Length)
					{
						return true;
					}
					bool flag = array.Length > Configs.KeepEquipMin.Value;
					if (!flag)
					{
						ItemData[] array2 = array;
						foreach (ItemData item in array2)
						{
							RemoveItemFromDyingCorpse((Humanoid)(object)__instance, item);
						}
						ResetRagdollVisual(__instance);
					}
					return flag;
				}

				[UsedImplicitly]
				private static void Postfix()
				{
					_isCreateTombStone = false;
				}
			}

			private static bool _isCreateTombStone;

			private static ItemData[] EquippedKeepable(Humanoid human)
			{
				return human.GetInventory().m_inventory.Where((ItemData item) => item.m_equipped && !ItemDataExt.IsItemType(item, (ItemType)9)).ToArray();
			}

			private static void RemoveItemFromDyingCorpse(Humanoid human, ItemData item)
			{
				string text = "$menu_manualsave " + ItemDataExt.GetName(item);
				((Character)human).Message((MessageType)1, text, 0, item.GetIcon());
				if (human.m_helmetItem == item)
				{
					human.m_helmetItem = null;
				}
				if (human.m_chestItem == item)
				{
					human.m_chestItem = null;
				}
				if (human.m_legItem == item)
				{
					human.m_legItem = null;
				}
				if (human.m_shoulderItem == item)
				{
					human.m_shoulderItem = null;
				}
				if (human.m_utilityItem == item)
				{
					human.m_utilityItem = null;
				}
			}

			private static void ResetRagdollVisual(Player human)
			{
				VisEquipment component = ((Component)human.GetRagdoll()).GetComponent<VisEquipment>();
				((Humanoid)human).SetupVisEquipment(component, true);
			}

			[UsedImplicitly]
			private static void Prefix(Player __instance, out List<Vector2i> __state)
			{
				//IL_007d: Unknown result type (might be due to invalid IL or missing references)
				__state = new List<Vector2i>();
				if (!_isCreateTombStone || Configs.KeepEquipMin.Value <= 0)
				{
					return;
				}
				ItemData[] source = EquippedKeepable((Humanoid)(object)__instance);
				Random rand = new Random();
				int num = 0;
				foreach (ItemData item in source.OrderBy((ItemData _) => rand.Next()))
				{
					if (num >= Configs.KeepEquipMin.Value)
					{
						break;
					}
					__state.Add(item.m_gridPos);
					RemoveItemFromDyingCorpse((Humanoid)(object)__instance, item);
					Log.Info((object)("Save from the grave: " + ItemDataExt.GetPrefabName(item)), (ushort)0);
					num++;
				}
				ResetRagdollVisual(__instance);
			}

			[UsedImplicitly]
			private static void Postfix(Player __instance, List<Vector2i> __state)
			{
				//IL_000b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0010: Unknown result type (might be due to invalid IL or missing references)
				//IL_0017: 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)
				foreach (Vector2i item in __state)
				{
					ItemData itemAt = ((Humanoid)__instance).GetInventory().GetItemAt(item.x, item.y);
					itemAt.m_equipped = true;
					Log.Info((object)("Restored from from the grave: " + ItemDataExt.GetPrefabName(itemAt)), (ushort)0);
				}
			}
		}

		private static readonly int StatusEffectNoSkillLoss = SEMan.s_statusEffectSoftDeath;

		private static void SetSkillReductionRate()
		{
			bool value = Configs.SkillReduction0KeepProgress.Value;
			float num = default(float);
			if (ZoneSystem.instance.GetGlobalKey((GlobalKeys)9, ref num))
			{
				Log.Message((object)string.Format("{0} global key: skillreductionrate {1} Keep progress: {2}", "Skill reduction rate set by", num, num <= 0f && value), (ushort)0);
				return;
			}
			num = Configs.SkillReductionRate.Value;
			Log.Message((object)string.Format("{0} config: {1:N2} Keep progress: {2}", "Skill reduction rate set by", num, num <= 0f && value), (ushort)0);
			Game.m_skillReductionRate = num;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Skills), "OnDeath")]
		private static void Skills_OnDeath()
		{
			SetSkillReductionRate();
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "Message")]
		private static void Player_Message(Player __instance, ref bool __runOriginal, MessageType type, string msg)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			if (!(Game.m_skillReductionRate > 0f) && (int)type == 1 && (msg == "$msg_softdeath" || msg == "$msg_skills_lowered"))
			{
				__runOriginal = false;
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "Awake")]
		private static void Player_Awake(Player __instance)
		{
			if (!Configs.BuffNoSkillLoss.Value || Game.m_skillReductionRate <= 0f)
			{
				__instance.m_hardDeathCooldown = 0f;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(TombStone), "GiveBoost")]
		private static void TombStone_GiveBoost(ref bool __runOriginal)
		{
			__runOriginal = Configs.BuffCorpseRun.Value;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(SEMan), "AddStatusEffect", new Type[]
		{
			typeof(int),
			typeof(bool),
			typeof(int),
			typeof(float)
		})]
		private static void SEMan_AddStatusEffect(ref bool __runOriginal, int nameHash)
		{
			if (!Configs.BuffNoSkillLoss.Value && nameHash == StatusEffectNoSkillLoss)
			{
				__runOriginal = false;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Skills), "LowerAllSkills")]
		private static void Skills_LowerAllSkills(ref bool __runOriginal, float factor)
		{
			if (factor <= 0f && Configs.SkillReduction0KeepProgress.Value)
			{
				__runOriginal = false;
			}
		}
	}
	[HarmonyPatch]
	public static class Durability
	{
		private static class Configs
		{
			internal static readonly ConfigEntry<float> TorchBurn;

			internal static readonly ConfigEntry<float> TorchAttack;

			internal static readonly ConfigEntry<float> Hammer;

			internal static readonly ConfigEntry<float> Hoe;

			internal static readonly ConfigEntry<float> Cultivator;

			static Configs()
			{
				TorchBurn = Config.Define<float>(true, "Durability", "Torch Burn Rate", 0.0333f, Config.AcceptRange<float>(0f, 2f), "Rate of torch burning consumption (Vanilla: 0.0333)\r\n[Logout required for changes to take effect]");
				TorchAttack = Config.Define<float>(true, "Durability", "Torch Attack", 1f, Config.AcceptRange<float>(0f, 2f), "When used as an attack (Vanilla: 1)\r\n[Logout required for changes to take effect]");
				Hammer = Config.Define<float>(true, "Durability", "Hammer", 1f, Config.AcceptRange<float>(0f, 2f), "Rate of use (Vanilla: 1)\r\n[Logout required for changes to take effect]");
				Hoe = Config.Define<float>(true, "Durability", "Hoe", 1f, Config.AcceptRange<float>(0f, 2f), "Rate of use (Vanilla: 1)\r\n[Logout required for changes to take effect]");
				Cultivator = Config.Define<float>(true, "Durability", "Cultivator", 1f, Config.AcceptRange<float>(0f, 2f), "Rate of use (Vanilla: 1)\r\n[Logout required for changes to take effect]");
			}
		}

		internal static void SetItemDurabilities()
		{
			SetItemDurability("Cultivator", Configs.Cultivator.Value);
			SetItemDurability("Hammer", Configs.Hammer.Value);
			SetItemDurability("Hoe", Configs.Hoe.Value);
			SetItemDurability("Torch", Configs.TorchAttack.Value, Configs.TorchBurn.Value);
		}

		private static void SetItemDurability(string prefabName, float useDrain, float drain = -1f)
		{
			Log.Info((object)$"{prefabName} Durability UseDrain: {useDrain}, Drain: {drain}", (ushort)0);
			ItemData itemData = ObjectDB.instance.GetItemPrefab(prefabName).GetComponent<ItemDrop>().m_itemData;
			itemData.m_shared.m_useDurabilityDrain = useDrain;
			if (drain >= 0f)
			{
				itemData.m_shared.m_durabilityDrain = drain;
			}
		}
	}
	[HarmonyPatch]
	public static class Food
	{
		public static class Configs
		{
			public static readonly ConfigEntry<bool> FoodDegradeHealth;

			public static readonly ConfigEntry<bool> FoodDegradeStamina;

			public static readonly ConfigEntry<bool> FoodDegradeEitr;

			static Configs()
			{
				FoodDegradeHealth = Config.Define<bool>(true, "Food", "Food Degrade Enabled - HP", false, "Food loses Health potency over time (Vanilla: True)");
				FoodDegradeStamina = Config.Define<bool>(true, "Food", "Food Degrade Enabled - Stamina", true, "Food loses Stamina potency over time (Vanilla: True)");
				FoodDegradeEitr = Config.Define<bool>(true, "Food", "Food Degrade Enabled - Eitr", false, "Food loses Eitr potency over time (Vanilla: True)");
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "GetTotalFoodValue")]
		private static void Player_GetTotalFoodValue(Player __instance, ref bool __runOriginal, out float hp, out float stamina, out float eitr)
		{
			hp = __instance.m_baseHP;
			stamina = __instance.m_baseStamina;
			eitr = 0f;
			__runOriginal = false;
			foreach (Food food in __instance.m_foods)
			{
				hp += (Configs.FoodDegradeHealth.Value ? food.m_health : (food.m_health = food.m_item.m_shared.m_food));
				stamina += (Configs.FoodDegradeStamina.Value ? food.m_stamina : (food.m_stamina = food.m_item.m_shared.m_foodStamina));
				eitr += (Configs.FoodDegradeEitr.Value ? food.m_eitr : (food.m_eitr = food.m_item.m_shared.m_foodEitr));
			}
		}
	}
	[HarmonyPatch]
	public static class Health
	{
		private static class Configs
		{
			public static readonly ConfigEntry<float> HealRestingComfortAmount;

			public static readonly ConfigEntry<float> HealRestingInterval;

			public static readonly ConfigEntry<float> HealRestingSeatedPercent;

			static Configs()
			{
				HealRestingComfortAmount = Config.Define<float>(true, "Health", "Heal Amount Per Comfort", 1f, Config.AcceptRange<float>(0f, 10f), "Regenerate health per comfort level while resting every interval");
				HealRestingInterval = Config.Define<float>(true, "Health", "Heal Interval When Resting", 2f, Config.AcceptRange<float>(1f, 60f), "Regenerate health every X seconds while resting");
				HealRestingSeatedPercent = Config.Define<float>(true, "Health", "Heal Seated Bonus Percent", 0.5f, Config.AcceptRange<float>(0f, 1f), "Bonus Percent extra to apply when seated and resting. Example: 0.5 = +50%");
			}
		}

		private static float _lastHealTime;

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "UpdateEnvStatusEffects")]
		private static void Player_UpdateEnvStatusEffects(Player __instance, float dt)
		{
			Player localPlayer = Player.m_localPlayer;
			if (PlayerExt.Is(localPlayer, (Character)(object)__instance))
			{
				HealWhenResting(localPlayer);
			}
		}

		private static void HealWhenResting(Player player)
		{
			if (!(Configs.HealRestingComfortAmount.Value <= 0f) && ((Character)player).GetSEMan().HaveStatusEffect(SEMan.s_statusEffectResting) && ((Character)player).GetHealth() < ((Character)player).GetMaxHealth() && Time.time > _lastHealTime + Configs.HealRestingInterval.Value)
			{
				float num = (((Character)player).IsSitting() ? (1f + Configs.HealRestingSeatedPercent.Value) : 1f);
				float num2 = (float)player.GetComfortLevel() * num * Configs.HealRestingComfortAmount.Value;
				((Character)player).Heal(num2, true);
				_lastHealTime = Time.time;
			}
		}
	}
	[HarmonyPatch]
	public static class InventoryRules
	{
		public static class Configs
		{
			public const string SECTION_INVENTORY = "Inventory";

			public static readonly ConfigEntry<DropStyle> DiscardStyle;

			public static readonly ConfigEntry<bool> RequipAfterSwim;

			public static readonly ConfigEntry<bool> RemoveItemFromSmallestStack;

			public static readonly ConfigEntry<bool> CombineExistingStacks;

			public static readonly ConfigEntry<bool> ExcludeHotbarWhenStacking;

			public static readonly ConfigEntry<int> MaxCarryWeight;

			static Configs()
			{
				DiscardStyle = Config.Define<DropStyle>(false, "Inventory", "Drop Style", DropStyle.CameraDirection, "When dropping items on the ground throw them towards the camera or\nthe vanilla method of chucking them in the direction the character is facing");
				RequipAfterSwim = Config.Define<bool>(true, "Inventory", "Re-equip Items After Swimming", true, "Restore equipped items after swimming");
				RemoveItemFromSmallestStack = Config.Define<bool>(true, "Inventory", "Use From Smallest Stack First", true, "When items are automatically removed from your inventory, such as when fueling a fireplace or crafting always draw from the smallest stacks first");
				CombineExistingStacks = Config.Define<bool>(true, "Inventory", "Stacking Combines Existing Stacks", true, "When stacking items in a container the existing items are combined into as few stacks as possible. (Vanilla: false)");
				ExcludeHotbarWhenStacking = Config.Define<bool>(true, "Inventory", "Exclude Hotbar When Stacking", true, "When stacking items into a container any items on the hotbar are skipped. (Vanilla: false)");
				MaxCarryWeight = Config.Define<int>(true, "Inventory", "Max Carry Weight", 300, Config.AcceptRange<int>(100, 600), "Max unmodified inventory carry weight (Vanilla: 300)\r\nWARNING: Not recommended to raise this value. Instead it is recommended to use in game items to adjust max carry weight.\r\nInventory management, logistics, and hauling cargo is a core part of the gameplay loop.\r\nThese restrictions lead to dynamic emergent gameplay situations.  Ex: Hauling a cart of ore and being attacked by a troll.\r\nIf you raise this value you are nullifying the usefulness of the cart and the requirement to make paths to haul stuff. \r\nThis will shorten your gameplay loop and make the experience less immersive, which is not the goal of this mod.\r\nZenWorldSettings: Adjust the cart to make the rope stronger and easier to connect/disconnect.\r\nZenPath: Make it easier to navigate the cart over paths without using stamina.");
				MaxCarryWeight.SettingChanged += delegate
				{
					SetMaxWeight(Player.m_localPlayer);
				};
			}
		}

		public enum DropStyle
		{
			Vanilla,
			CameraDirection
		}

		private static bool _requipPending;

		private static void SetMaxWeight(Player? player)
		{
			if (Object.op_Implicit((Object)(object)player))
			{
				player.m_maxCarryWeight = Configs.MaxCarryWeight.Value;
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "Awake")]
		private static void Player_Awake(Player __instance)
		{
			SetMaxWeight(__instance);
		}

		[HarmonyTranspiler]
		[HarmonyPatch(typeof(Inventory), "StackAll")]
		private static IEnumerable<CodeInstruction> Inventory_StackAll_Transiple(IEnumerable<CodeInstruction> codes)
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(Inventory), "GetAllItems", Array.Empty<Type>(), (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(InventoryRules), "StackAll_GetAllItems_Intercept", (Type[])null, (Type[])null);
			return Transpilers.MethodReplacer(codes, (MethodBase)methodInfo, (MethodBase)methodInfo2);
		}

		private static List<ItemData> StackAll_GetAllItems_Intercept(Inventory inventory)
		{
			int hotbarRow = ((!Configs.ExcludeHotbarWhenStacking.Value) ? (-1) : 0);
			return (from item in inventory.GetAllItems()
				where item.m_gridPos.y > hotbarRow
				select item).ToList();
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Inventory), "StackAll")]
		private static void Inventory_StackAll_Prefix(Inventory __instance)
		{
			if (Configs.CombineExistingStacks.Value)
			{
				CombineStacks(__instance);
			}
		}

		private static void CombineStacks(Inventory inventory)
		{
			List<ItemData> allItems = (from item in inventory.GetAllItems()
				orderby item.m_gridPos.y descending, item.m_gridPos.x
				select item).ToList();
			foreach (var (current2, startIndex2) in IEnumerableExt.WithIndex<ItemData>((IEnumerable<ItemData>)allItems))
			{
				FillItem(current2, startIndex2);
			}
			inventory.GetAllItems().RemoveAll((ItemData item) => item.m_shared.m_maxStackSize > 1 && item.m_stack == 0);
			void FillItem(ItemData current, int startIndex)
			{
				if (current.m_shared.m_maxStackSize > 1)
				{
					int num = startIndex + 1;
					while (current.m_stack < current.m_shared.m_maxStackSize)
					{
						ItemData val2 = FindNextStack(current, num);
						if (val2 == null)
						{
							break;
						}
						int num2 = Math.Min(current.m_shared.m_maxStackSize - current.m_stack, val2.m_stack);
						current.m_stack += num2;
						val2.m_stack -= num2;
						num++;
					}
				}
			}
			ItemData? FindNextStack(ItemData searchItem, int startIndex)
			{
				for (int i = startIndex; i < allItems.Count; i++)
				{
					ItemData val = allItems[i];
					if (val.m_shared.m_name == searchItem.m_shared.m_name && val.m_quality == searchItem.m_quality && val.m_worldLevel == searchItem.m_worldLevel && val.m_stack > 0)
					{
						return val;
					}
				}
				return null;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[]
		{
			typeof(string),
			typeof(int),
			typeof(int),
			typeof(bool)
		})]
		private static void Inventory_RemoveItem(Inventory __instance)
		{
			if (Configs.RemoveItemFromSmallestStack.Value)
			{
				__instance.m_inventory.Sort((ItemData item1, ItemData item2) => item1.m_stack - item2.m_stack);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Humanoid), "UpdateEquipment")]
		private static void Humanoid_UpdateEquipment(Humanoid __instance)
		{
			if (!Configs.RequipAfterSwim.Value)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			if (!PlayerExt.Is(localPlayer, (Character)(object)__instance))
			{
				return;
			}
			if (((Character)localPlayer).IsSwimming())
			{
				if (!((Character)localPlayer).IsOnGround() && (((Humanoid)localPlayer).LeftItem != null || ((Humanoid)localPlayer).RightItem != null))
				{
					_requipPending = true;
				}
			}
			else if (((Character)localPlayer).IsOnGround() && _requipPending)
			{
				((Humanoid)localPlayer).ShowHandItems(false, true);
				_requipPending = false;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Humanoid), "DropItem")]
		private static void Humanoid_DropItem(Humanoid __instance)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: 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_0053: Unknown result type (might be due to invalid IL or missing references)
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (!((Object)(object)val != (Object)(object)Player.m_localPlayer))
			{
				switch (Configs.DiscardStyle.Value)
				{
				case DropStyle.CameraDirection:
				{
					Transform transform = ((Component)val).transform;
					Vector3 forward = ((Component)GameCamera.instance).transform.forward;
					forward.y = 0f;
					transform.forward = forward;
					((Character)val).SetLookDir(transform.forward, 0f);
					val.FaceLookDirection();
					break;
				}
				default:
					throw new ArgumentOutOfRangeException();
				case DropStyle.Vanilla:
					break;
				}
			}
		}
	}
	[HarmonyPatch]
	public static class InventoryUseEquipRun
	{
		private static class Configs
		{
			private const string SECTION = "Inventory";

			public static readonly ConfigEntry<bool> EquipWhileMoving = Config.Define<bool>(true, "Inventory", "Change Gear While Moving", true, "Swap your weapons while in motion and prevent the movement slowdown when doing so (Vanilla : false)");
		}

		[HarmonyTranspiler]
		[HarmonyPatch(typeof(Character), "UpdateWalking")]
		private static IEnumerable<CodeInstruction> Character_UpdateWalking_Transpiler(IEnumerable<CodeInstruction> codes)
		{
			MethodInfo inActionSlowdown = AccessTools.Method(typeof(Character), "InMinorActionSlowdown", (Type[])null, (Type[])null);
			return Transpilers.Manipulator(codes, (Func<CodeInstruction, bool>)((CodeInstruction code) => CodeInstructionExtensions.OperandIs(code, (MemberInfo)inActionSlowdown)), (Action<CodeInstruction>)delegate(CodeInstruction code)
			{
				code.operand = OpCodes.Call;
				code.operand = AccessTools.Method(typeof(InventoryUseEquipRun), "InMinorActionSlowdownIntercept", (Type[])null, (Type[])null);
			});
		}

		private static bool InMinorActionSlowdownIntercept(Character character)
		{
			if (!Configs.EquipWhileMoving.Value)
			{
				return character.InMinorActionSlowdown();
			}
			if ((Object)(object)Player.m_localPlayer != (Object)(object)character)
			{
				return character.InMinorActionSlowdown();
			}
			return false;
		}

		[HarmonyTranspiler]
		[HarmonyPatch(typeof(Player), "CheckRun")]
		private static IEnumerable<CodeInstruction> Player_CheckRun_Transpiler(IEnumerable<CodeInstruction> codes)
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(Humanoid), "ClearActionQueue", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(InventoryUseEquipRun), "ClearActionQueueIntercept", (Type[])null, (Type[])null);
			return Transpilers.MethodReplacer(codes, (MethodBase)methodInfo, (MethodBase)methodInfo2);
		}

		private static void ClearActionQueueIntercept(Humanoid human)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Invalid comparison between Unknown and I4
			if (Configs.EquipWhileMoving.Value)
			{
				Player val = (Player)(object)((human is Player) ? human : null);
				if (val != null)
				{
					List<MinorActionData> actionQueue = val.m_actionQueue;
					for (int num = actionQueue.Count - 1; num >= 0; num--)
					{
						ActionType type = actionQueue[num].m_type;
						if ((int)type != 0 && (int)type != 1)
						{
							actionQueue.RemoveAt(num);
						}
					}
					return;
				}
			}
			human.ClearActionQueue();
		}
	}
	[HarmonyPatch]
	public static class Knowledge
	{
		private static class Configs
		{
			public static readonly ConfigEntry<float> StationDiscoveryRadius;

			static Configs()
			{
				StationDiscoveryRadius = Config.Define<float>(true, "Knowledge", "Station Discovery Radius", 0f, Config.AcceptRange<float>(0f, 4f), "How far away the player needs to be from a crafting station in order to 'discover' it.\r\nSet to 0 to disable ranged discovery and force direct interaction instead. (Vanilla: 4)\r\nThis is very useful in multiplayer, as it prevents players from accidentally discovering recipes \r\nby simply walking around their friend's base.");
			}
		}

		public static void Start()
		{
			CollectionExtensions.Do<CraftingStation>((IEnumerable<CraftingStation>)CraftingStation.m_allStations, (Action<CraftingStation>)delegate(CraftingStation station)
			{
				station.m_discoverRange = Configs.StationDiscoveryRadius.Value;
			});
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(CraftingStation), "Start")]
		private static void CraftingStation_Start(CraftingStation __instance)
		{
			__instance.m_discoverRange = Configs.StationDiscoveryRadius.Value;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(CraftingStation), "UpdateKnownStationsInRange")]
		private static void CraftingStation_UpdateKnownStationsInRange(ref bool __runOriginal)
		{
			if (Configs.StationDiscoveryRadius.Value <= 0f)
			{
				__runOriginal = false;
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(CraftingStation), "Interact")]
		private static void CraftingStation_Interact(CraftingStation __instance)
		{
			if (Object.op_Implicit((Object)(object)__instance.m_nview) && Configs.StationDiscoveryRadius.Value <= 0f && __instance.m_nview.GetPrefabName() == CraftingStations.Workbench)
			{
				Player.m_localPlayer.AddKnownStation(__instance);
			}
		}
	}
	[HarmonyPatch]
	internal static class SkillRules
	{
		[Flags]
		private enum SkillCategory
		{
			None = 0,
			Athletics = 1,
			Combat = 2,
			Magic = 4,
			Survival = 8,
			All = -1
		}

		private static class Configs
		{
			private const string SECTION_SKILLS = "Skills";

			public static readonly ConfigEntry<bool> DisplaySkillNotifications;

			public static readonly ConfigEntry<SkillCategory> LockSkillCategories;

			public static readonly Dictionary<SkillType, ConfigEntry<int>> SkillConfigs;

			static Configs()
			{
				SkillConfigs = new Dictionary<SkillType, ConfigEntry<int>>();
				DisplaySkillNotifications = Config.Define<bool>(true, "Skills", "Display Skill Notifications", false, "Show the notifications when skills are gained");
				LockSkillCategories = Config.Define<SkillCategory>(true, "Skills", "Lock Skill Categories", SkillCategory.None, $"For the selected categories: Disable normal skill progression and lock skills to the values defined in this config instead.\r\nNo gain or loss of the skills in the given category unless you set their value to {-1}\r\nThis is useful for players who want to have a fixed skill level for certain skills or groups of skills.\r\nGear, magical items, environmental effects, and any status effects will still apply to the skills.\r\nThis only works on vanilla skills, not custom skills from other mods.\r\nWARNING: Changes will be applied to your skills immediately!\r\nIf installed on the server then changes will be applied to all logged in player's skills simultaneously.\r\nMake backups of all player files if you are not sure.");
				LockSkillCategories.SettingChanged += delegate
				{
					RefreshLocks();
				};
				GenerateSkillConfigs();
			}

			private static void GenerateSkillConfigs()
			{
				//IL_0027: 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_0032: Unknown result type (might be due to invalid IL or missing references)
				//IL_0037: 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_003d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0044: Invalid comparison between Unknown and I4
				//IL_0056: Unknown result type (might be due to invalid IL or missing references)
				//IL_0097: 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)
				foreach (SkillType value in Enum.GetValues(typeof(SkillType)))
				{
					SkillType skillType = value;
					SkillType val = skillType;
					if (((int)val == 0 || (int)val == 999) ? true : false)
					{
						continue;
					}
					SkillCategory skillCategory = GetSkillCategory(skillType);
					ConfigEntry<int> val2 = Config.Define<int>(true, string.Format("{0} - {1}", "Skills", skillCategory), ((object)(SkillType)(ref skillType)).ToString(), 0, Config.AcceptRange<int>(-1, 100), $"The locked skill value for {skillType}.\r\nIf the category is locked then this value will overwrite the skill's level and block all progression or loss.\r\nHowever, gear and other buffs will still apply to the skill's overall level.\r\nSet to {-1} to bypass the category lock and use vanilla progression instead for this individual skill.\r\nWARNING: once this is set it will overwrite any existing value for this skill!");
					val2.SettingChanged += delegate
					{
						//IL_0010: Unknown result type (might be due to invalid IL or missing references)
						Player localPlayer = Player.m_localPlayer;
						if (localPlayer != null)
						{
							((Character)localPlayer).GetSkills().TryLockSkill(skillType);
						}
					};
					SkillConfigs.Add(skillType, val2);
				}
			}
		}

		[HarmonyPatch]
		private static class SkillLock
		{
			public static bool IsUpdatingDialog;

			[HarmonyPostfix]
			[HarmonyPatch(typeof(Skills), "Load")]
			private static void Load(Skills __instance)
			{
				RefreshLocks(__instance);
			}

			[HarmonyPostfix]
			[HarmonyPatch(typeof(Skills), "RebalanceSkills")]
			private static void Skills_RebalanceSkills(Skills __instance)
			{
				RefreshLocks(__instance);
			}

			[HarmonyPostfix]
			[HarmonyPatch(typeof(Skills), "LowerAllSkills")]
			private static void Skills_LowerAllSkills(Skills __instance)
			{
				RefreshLocks(__instance);
			}

			[HarmonyPostfix]
			[HarmonyPatch(typeof(Skills), "RaiseSkill")]
			private static void Skills_RaiseSkill(Skills __instance, SkillType skillType)
			{
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)__instance.m_player != (Object)(object)Player.m_localPlayer))
				{
					__instance.TryLockSkill(skillType);
				}
			}

			[HarmonyPostfix]
			[HarmonyPatch(typeof(Skill), "Raise")]
			private static void Skill_Raise(Skill __instance, ref bool __result)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				if (TryGetLockedValue(__instance.m_info.m_skill, out var _))
				{
					__result = false;
				}
			}

			[HarmonyPostfix]
			[HarmonyPatch(typeof(Skills), "CheatRaiseSkill")]
			private static void Skills_CheatRaiseSkill(Skills __instance)
			{
				RefreshLocks(__instance);
			}

			[HarmonyPostfix]
			[HarmonyPatch(typeof(Skills), "CheatResetSkill")]
			private static void Skills_CheatResetSkill(Skills __instance)
			{
				RefreshLocks(__instance);
			}

			[HarmonyPrefix]
			[HarmonyPatch(typeof(Skills), "GetSkillLevel")]
			private static void Skills_GetSkillLevel(Skills __instance, SkillType skillType, ref float __result, ref bool __runOriginal)
			{
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)__instance.m_player != (Object)(object)Player.m_localPlayer) && TryGetLockedValue(skillType, out var lockedValue))
				{
					__result = lockedValue;
					((Character)__instance.m_player).GetSEMan().ModifySkillLevel(skillType, ref __result);
					__result = Mathf.Floor(__result);
					__runOriginal = false;
				}
			}

			[HarmonyPrefix]
			[HarmonyPatch(typeof(SkillsDialog), "Setup")]
			private static void SkillsDialog_Setup(SkillsDialog __instance, Player player)
			{
				//IL_001f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0032: 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)
				Skills skills = ((Character)player).GetSkills();
				foreach (SkillDef skill in skills.m_skills)
				{
					if (skills.GetSkillLevel(skill.m_skill) == 0f && skills.GetSkill(skill.m_skill).m_accumulator == 0f)
					{
						player.m_skills.m_skillData.Remove(skill.m_skill);
					}
				}
			}

			[HarmonyTranspiler]
			[HarmonyPatch(typeof(SkillsDialog), "Setup")]
			private static IEnumerable<CodeInstruction> SkillsDialog_Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(MonoBehaviour), "StartCoroutine", new Type[1] { typeof(IEnumerator) }, (Type[])null);
				MethodInfo methodInfo2 = AccessTools.Method(typeof(SkillLock), "StartCoroutine_Intercept", (Type[])null, (Type[])null);
				return Transpilers.MethodReplacer(instructions, (MethodBase)methodInfo, (MethodBase)methodInfo2);
			}

			private static Coroutine? StartCoroutine_Intercept(MonoBehaviour __instance, IEnumerator coroutine)
			{
				if (!IsUpdatingDialog)
				{
					return __instance.StartCoroutine(coroutine);
				}
				return null;
			}
		}

		private const int Bypass = -1;

		private static readonly Dictionary<SkillCategory, SkillType[]> SkillCategories;

		private static SkillCategory GetSkillCategory(SkillType skillType)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			SkillCategory key = SkillCategories.FirstOrDefault<KeyValuePair<SkillCategory, SkillType[]>>((KeyValuePair<SkillCategory, SkillType[]> kvp) => kvp.Value.Contains(skillType)).Key;
			if (key != 0)
			{
				return key;
			}
			return SkillCategory.Combat;
		}

		private static void UpdateSkillsDialog()
		{
			SkillsDialog val = InventoryGui.m_instance?.m_skillsDialog;
			if (!((Object)(object)val == (Object)null))
			{
				SkillLock.IsUpdatingDialog = true;
				if (((Behaviour)val).isActiveAndEnabled)
				{
					val.Setup(Player.m_localPlayer);
				}
				SkillLock.IsUpdatingDialog = false;
			}
		}

		internal static void RefreshLocks()
		{
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				RefreshLocks(((Character)Player.m_localPlayer).GetSkills());
			}
		}

		private static void RefreshLocks(Skills skills)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)skills.m_player != (Object)(object)Player.m_localPlayer || Configs.LockSkillCategories.Value == SkillCategory.None)
			{
				return;
			}
			foreach (SkillDef skill in skills.m_skills)
			{
				skills.TryLockSkill(skill.m_skill, updateDialog: false);
			}
			UpdateSkillsDialog();
		}

		private static bool TryGetLockedValue(SkillType skillType, out int lockedValue)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			lockedValue = -1;
			if (!Configs.SkillConfigs.TryGetValue(skillType, out ConfigEntry<int> value))
			{
				return false;
			}
			SkillCategory skillCategory = GetSkillCategory(skillType);
			lockedValue = (Configs.LockSkillCategories.Value.HasFlag(skillCategory) ? value.Value : (-1));
			return lockedValue != -1;
		}

		private static bool TryLockSkill(this Skills skills, SkillType skillType, bool updateDialog = true)
		{
			//IL_0014: 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)
			if ((Object)(object)skills.m_player != (Object)(object)Player.m_localPlayer)
			{
				return false;
			}
			if (!TryGetLockedValue(skillType, out var lockedValue))
			{
				return false;
			}
			Skill skill = skills.GetSkill(skillType);
			if (skill == null)
			{
				return false;
			}
			skill.m_level = lockedValue;
			skill.m_accumulator = 0f;
			if (updateDialog)
			{
				UpdateSkillsDialog();
			}
			return true;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "OnSkillLevelup")]
		private static void Player_OnSkillLevelUp(ref bool __runOriginal)
		{
			if (!Configs.DisplaySkillNotifications.Value)
			{
				__runOriginal = false;
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Player), "Message")]
		private static void MessageHud_ShowMessage(ref bool __runOriginal, string msg)
		{
			if (!Configs.DisplaySkillNotifications.Value && msg.StartsWith("$msg_skillup"))
			{
				__runOriginal = false;
			}
		}

		static SkillRules()
		{
			Dictionary<SkillCategory, SkillType[]> dictionary = new Dictionary<SkillCategory, SkillType[]>();
			SkillType[] array = new SkillType[6];
			RuntimeHelpers.InitializeArray(array, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			dictionary.Add(SkillCategory.Survival, (SkillType[])(object)array);
			SkillType[] array2 = new SkillType[5];
			RuntimeHelpers.InitializeArray(array2, (RuntimeFieldHandle)/*OpCode not supported: LdMemberToken*/);
			dictionary.Add(SkillCategory.Athletics, (SkillType[])(object)array2);
			dictionary.Add(SkillCategory.Magic, (SkillType[])(object)new SkillType[2]
			{
				(SkillType)10,
				(SkillType)9
			});
			SkillCategories = dictionary;
		}
	}
	[HarmonyPatch]
	public static class Sleep
	{
		public static class Configs
		{
			public static readonly ConfigEntry<bool> SleepAnyBed;

			static Configs()
			{
				SleepAnyBed = Config.Define<bool>(true, "Sleep", "Sleep In Any Bed", true, "When true you can sleep in any bed, not just owned beds. (Vanilla: false)");
			}
		}

		[HarmonyPatch(typeof(Bed), "Interact")]
		private static class BedPatches
		{
			[UsedImplicitly]
			private static void Prefix(Bed __instance, bool alt, ref bool __runOriginal)
			{
				_interactAltBed = ((Configs.SleepAnyBed.Value && alt) ? __instance : null);
				__runOriginal = !__instance.IsInUse();
			}

			[UsedImplicitly]
			private static void Postfix()
			{
				_interactAltBed = null;
			}
		}

		private static Bed? _interactAltBed;

		private static bool IsInUse(this Bed bed)
		{
			Bed bed2 = bed;
			return Player.GetAllPlayers().Any((Player player) => ((Character)player).InBed() && MathExt.Approximately(((Component)player).transform.position, bed2.GetSpawnPoint()));
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Bed), "GetHoverText")]
		private static void GetHoverText_Prefix(Bed __instance, ref string __result, ref bool __runOriginal)
		{
			if (Configs.SleepAnyBed.Value && __instance.IsInUse())
			{
				__runOriginal = false;
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Bed), "GetHoverText")]
		private static void GetHoverText_Postfix(Bed __instance, ref string __result, bool __runOriginal)
		{
			if (Configs.SleepAnyBed.Value)
			{
				if (!__runOriginal)
				{
					string ownerName = __instance.GetOwnerName();
					__result = StringExt.Localize(Utility.IsNullOrWhiteSpace(ownerName) ? "$piece_bed_unclaimed" : (ownerName + "'s $piece_bed"));
				}
				else if (!__result.Contains(Localization.instance.Localize("$piece_bed_sleep")) && (!__instance.IsMine() || !__instance.IsCurrent()))
				{
					__result += Localization.instance.Localize("\n" + UI.PromptInteractAlt + " $piece_bed_sleep");
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Bed), "GetOwner")]
		private static void GetOwner(Bed __instance, ref long __result)
		{
			if ((Object)(object)__instance == (Object)(object)_interactAltBed)
			{
				__result = Game.instance.GetPlayerProfile().GetPlayerID();
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Bed), "IsCurrent")]
		private static void IsCurrent(Bed __instance, ref bool __result)
		{
			__result = __result || (Object)(object)__instance == (Object)(object)_interactAltBed;
		}
	}
	[HarmonyPatch]
	public static class Stamina
	{
		private static class Configs
		{
			public static readonly ConfigEntry<int> DrainEncumbered;

			public static readonly ConfigEntry<int> DrainSneak;

			public static readonly ConfigEntry<int> DrainBlock;

			public static readonly ConfigEntry<int> UsageJump;

			public static readonly ConfigEntry<int> UsageDodge;

			public static readonly ConfigEntry<bool> RegenEncumbered;

			public static readonly ConfigEntry<bool> RegenSwimming;

			static Configs()
			{
				DrainEncumbered = Config.Define<int>(true, "Stamina", "Drain Encumbered", -1, Config.AcceptRange<int>(-1, 100), "Stamina drain when encumbered. [-1 do not use config] (Vanilla: 10)");
				DrainEncumbered.SettingChanged += delegate
				{
					SetDefaults(Player.m_localPlayer);
				};
				DrainSneak = Config.Define<int>(true, "Stamina", "Drain Sneak", 0, Config.AcceptRange<int>(-1, 100), "Stamina drain when sneaking. [-1 = do not use config] (Vanilla: 5)");
				DrainSneak.SettingChanged += delegate
				{
					SetDefaults(Player.m_localPlayer);
				};
				DrainBlock = Config.Define<int>(true, "Stamina", "Drain Block", -1, Config.AcceptRange<int>(-1, 100), "Stamina drain when blocking. [-1 = do not use config] (Vanilla: 25)");
				DrainBlock.SettingChanged += delegate
				{
					SetDefaults(Player.m_localPlayer);
				};
				UsageJump = Config.Define<int>(true, "Stamina", "Usage Jump", -1, Config.AcceptRange<int>(-1, 100), "Stamina usage when jumping. [-1 = do not use config] (Vanilla: 10)");
				UsageJump.SettingChanged += delegate
				{
					SetDefaults(Player.m_localPlayer);
				};
				UsageDodge = Config.Define<int>(true, "Stamina", "Usage Dodge", -1, Config.AcceptRange<int>(-1, 100), "Stamina usage when dodging. [-1 = do not use config] (Vanilla: 10)");
				UsageDodge.SettingChanged += delegate
				{
					SetDefaults(Player.m_localPlayer);
				};
				RegenEncumbered = Config.Define<bool>(true, "Stamina - Regen", "Regen Encumbered", true, "Allow stamina to regen when you stop moving while encumbered (Vanilla: false)");
				RegenSwimming = Config.Define<bool>(true, "Stamina - Regen", "Regen Swimming", true, "Allow stamina to regen when you stop moving while swimming (Vanilla: false)\r\nNOTE: If you are encumbered while swimming, you will not regen stamina. Drop stuff or drown.");
			}
		}

		private static void SetDefaults(Player? player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				if (Configs.DrainEncumbered.Value >= 0)
				{
					player.m_encumberedStaminaDrain = Configs.DrainEncumbered.Value;
				}
				if (Configs.DrainSneak.Value >= 0)
				{
					player.m_sneakStaminaDrain = Configs.DrainSneak.Value;
				}
				if (Configs.DrainBlock.Value >= 0)
				{
					((Humanoid)player).m_blockStaminaDrain = Configs.DrainBlock.Value;
				}
				if (Configs.UsageJump.Value >= 0)
				{
					((Character)player).m_jumpStaminaUsage = Configs.UsageJump.Value;
				}
				if (Configs.UsageDodge.Value >= 0)
				{
					player.m_dodgeStaminaUsage = Configs.UsageDodge.Value;
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "Awake")]
		private static void Player_Awake(Player __instance)
		{
			SetDefaults(__instance);
		}

		private static bool IsEncumberedMoving(Player player)
		{
			//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)
			if (!PlayerExt.IsReady(player))
			{
				return false;
			}
			if (!((Character)player).IsEncumbered())
			{
				return false;
			}
			if (!Configs.RegenEncumbered.Value)
			{
				return true;
			}
			Vector3 velocity = ((Character)player).GetVelocity();
			return ((Vector3)(ref velocity)).magnitude > 0.1f;
		}

		private static bool IsSwimmingMoving(Player player)
		{
			//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)
			if (!PlayerExt.IsReady(player))
			{
				return false;
			}
			if (!((Character)player).IsSwimming())
			{
				return false;
			}
			if (!Configs.RegenSwimming.Value)
			{
				return true;
			}
			Vector3 velocity = ((Character)player).GetVelocity();
			velocity.y = 0f;
			return ((Vector3)(ref velocity)).magnitude > 0.1f;
		}

		[HarmonyTranspiler]
		[HarmonyPatch(typeof(Player), "UpdateStats", new Type[] { typeof(float) })]
		private static IEnumerable<CodeInstruction> Player_UpdateStats(IEnumerable<CodeInstruction> codes)
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(Character), "IsEncumbered", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(Stamina), "IsEncumberedMoving", (Type[])null, (Type[])null);
			MethodInfo methodInfo3 = AccessTools.Method(typeof(Character), "IsSwimming", (Type[])null, (Type[])null);
			MethodInfo methodInfo4 = AccessTools.Method(typeof(Stamina), "IsSwimmingMoving", (Type[])null, (Type[])null);
			return Transpilers.MethodReplacer(Transpilers.MethodReplacer(codes, (MethodBase)methodInfo, (MethodBase)methodInfo2), (MethodBase)methodInfo3, (MethodBase)methodInfo4);
		}
	}
}