Decompiled source of ComputerModpack v0.0.2

plugins/Computer-classes/Computer-classes.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ComputerMod.Core;
using ComputerMod.Foundation.Classes;
using ComputerMod.Foundation.Compatibility;
using ComputerMod.Foundation.Items;
using ComputerMod.Foundation.UI;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Computer-classes")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("0.3.1.0")]
[assembly: AssemblyInformationalVersion("0.3.1")]
[assembly: AssemblyProduct("Computer-classes")]
[assembly: AssemblyTitle("Computer-classes")]
[assembly: AssemblyVersion("0.3.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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]
	[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]
	[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 ComputerMod.Foundation
{
	[BepInPlugin("com.froze.computermod.foundation", "Computer-classes", "0.3.1")]
	[BepInDependency("com.jotunn.jotunn", "2.30.1")]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public sealed class ComputerModPlugin : BaseUnityPlugin
	{
		private Harmony? _harmony;

		internal static ManualLogSource Logger { get; private set; }

		private void Awake()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			Logger = ((BaseUnityPlugin)this).Logger;
			ClassService.Initialize(((BaseUnityPlugin)this).Config, Logger);
			try
			{
				_harmony = new Harmony("com.froze.computermod.foundation");
				_harmony.PatchAll(typeof(ComputerModPlugin).Assembly);
				ClassStone.Install(Logger);
				if (!Application.isBatchMode)
				{
					ClassMenu.Install();
				}
				Logger.LogInfo((object)"Computer-classes 0.3.1 loaded; patches active. Class Stone registration queued.");
			}
			catch (Exception arg)
			{
				Harmony? harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
				ClassStone.Uninstall();
				Logger.LogError((object)string.Format("{0} initialization failed; patches removed: {1}", "Computer-classes", arg));
			}
		}

		private void OnDestroy()
		{
			ClassService.Save(Player.m_localPlayer);
			ClassStone.Uninstall();
			ClassMenu.Uninstall();
			Harmony? harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
}
namespace ComputerMod.Foundation.UI
{
	internal sealed class ClassMenu : MonoBehaviour
	{
		private static ClassMenu? _instance;

		private bool _visible;

		private bool _confirm;

		private PlayerClass _selected = PlayerClass.Tank;

		private int _tab;

		private string? _backup;

		private string[] _backups = Array.Empty<string>();

		private Vector2 _scroll;

		private Texture2D? _pixel;

		private GUIStyle? _title;

		private GUIStyle? _body;

		private GUIStyle? _section;

		private GUIStyle? _button;

		private CursorLockMode _oldLock;

		private bool _oldCursor;

		private Player? _openedFor;

		internal static void Install()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			if (!((Object)(object)_instance != (Object)null))
			{
				GameObject val = new GameObject("ComputerMod.ClassMenu");
				Object.DontDestroyOnLoad((Object)(object)val);
				_instance = val.AddComponent<ClassMenu>();
			}
		}

		internal static void Uninstall()
		{
			if (!((Object)(object)_instance == (Object)null))
			{
				_instance.Close();
				Object.Destroy((Object)(object)((Component)_instance).gameObject);
				_instance = null;
			}
		}

		internal static void Toggle()
		{
			//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_009b: 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)
			if (!((Object)(object)_instance == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null))
			{
				if (_instance._visible)
				{
					_instance.Close();
					return;
				}
				ClassMenu instance = _instance;
				instance._openedFor = Player.m_localPlayer;
				PlayerClass current = ClassService.Current;
				instance._selected = ((current == PlayerClass.None) ? PlayerClass.Tank : current);
				instance._confirm = false;
				instance._tab = ((!ClassService.TutorialSeen) ? 3 : 0);
				instance._backups = ClassService.Backups();
				instance._backup = null;
				instance._scroll = Vector2.zero;
				instance._oldLock = Cursor.lockState;
				instance._oldCursor = Cursor.visible;
				instance._visible = true;
				GUIManager.BlockInput(true);
			}
		}

		private void Close()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			if (_visible)
			{
				_visible = false;
				GUIManager.BlockInput(false);
				Cursor.lockState = _oldLock;
				Cursor.visible = _oldCursor;
			}
		}

		private void Update()
		{
			if (_visible && ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)Player.m_localPlayer != (Object)(object)_openedFor || ((Character)Player.m_localPlayer).IsDead()))
			{
				Close();
			}
		}

		private void LateUpdate()
		{
			if (_visible)
			{
				Cursor.lockState = (CursorLockMode)0;
				Cursor.visible = true;
			}
		}

		private void OnDestroy()
		{
			Close();
			if ((Object)(object)_pixel != (Object)null)
			{
				Object.Destroy((Object)(object)_pixel);
			}
		}

		private void Styles()
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: 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_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Expected O, but got Unknown
			//IL_004c: 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: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: 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_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected O, but got Unknown
			//IL_009c: 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_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Expected O, but got Unknown
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Expected O, but got Unknown
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			if (_title == null)
			{
				_title = new GUIStyle(GUI.skin.label)
				{
					fontSize = 25,
					fontStyle = (FontStyle)1,
					wordWrap = true
				};
				_body = new GUIStyle(GUI.skin.label)
				{
					fontSize = 15,
					wordWrap = true,
					richText = false
				};
				_section = new GUIStyle(_body)
				{
					fontSize = 17,
					fontStyle = (FontStyle)1
				};
				_button = new GUIStyle(GUI.skin.button)
				{
					fontSize = 16,
					wordWrap = true
				};
				_pixel = new Texture2D(1, 1);
				_pixel.SetPixel(0, 0, Color.white);
				_pixel.Apply();
			}
		}

		private void Fill(Rect rect, Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			Color color2 = GUI.color;
			GUI.color = color;
			GUI.DrawTexture(rect, (Texture)(object)_pixel);
			GUI.color = color2;
		}

		private void OnGUI()
		{
			//IL_0143: 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_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Invalid comparison between Unknown and I4
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Invalid comparison between Unknown and I4
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			if (!_visible)
			{
				return;
			}
			if ((int)Event.current.type == 4 && (int)Event.current.keyCode == 27)
			{
				if (_confirm)
				{
					_confirm = false;
				}
				else
				{
					Close();
				}
				Event.current.Use();
				return;
			}
			Styles();
			Matrix4x4 matrix = GUI.matrix;
			Color color = GUI.color;
			bool enabled = GUI.enabled;
			try
			{
				GUI.color = Color.white;
				Fill(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), new Color(0f, 0f, 0f, 0.82f));
				float num = Mathf.Min(1.2f, Mathf.Min((float)Screen.width / 960f, (float)Screen.height / 730f));
				GUI.matrix = Matrix4x4.TRS(new Vector3(((float)Screen.width - 920f * num) / 2f, ((float)Screen.height - 690f * num) / 2f), Quaternion.identity, Vector3.one * num);
				DrawPanel();
			}
			finally
			{
				GUI.matrix = matrix;
				GUI.color = color;
				GUI.enabled = enabled;
			}
		}

		private void DrawPanel()
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: 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_00b1: 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_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0313: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0318: Unknown result type (might be due to invalid IL or missing references)
			//IL_0319: Unknown result type (might be due to invalid IL or missing references)
			//IL_0322: Unknown result type (might be due to invalid IL or missing references)
			//IL_032c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0331: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_05b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_027b: Unknown result type (might be due to invalid IL or missing references)
			//IL_05fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_04cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_0628: Unknown result type (might be due to invalid IL or missing references)
			//IL_062d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0476: Unknown result type (might be due to invalid IL or missing references)
			//IL_0460: Unknown result type (might be due to invalid IL or missing references)
			//IL_07ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_06ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_06f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0774: Unknown result type (might be due to invalid IL or missing references)
			Fill(new Rect(0f, 0f, 920f, 690f), new Color(0.07f, 0.085f, 0.105f, 1f));
			Fill(new Rect(0f, 0f, 920f, 4f), new Color(0.8f, 0.61f, 0.3f, 1f));
			GUI.Label(new Rect(24f, 20f, 760f, 40f), "Computer-classes  v0.3.1", _title);
			GUI.Label(new Rect(24f, 67f, 865f, 44f), $"Active: {ClassService.Current}   |   {ClassService.SaveStatus}", _body);
			if (GUI.Button(new Rect(828f, 20f, 68f, 34f), "Close", _button))
			{
				Close();
			}
			string[] array = new string[4] { "Choose class", "Saved skills", "Backups", "How it works" };
			for (int i = 0; i < array.Length; i++)
			{
				GUI.color = (Color)((i == _tab) ? new Color(1f, 0.82f, 0.48f) : Color.white);
				if (GUI.Button(new Rect((float)(24 + i * 221), 117f, 214f, 36f), array[i], _button))
				{
					_tab = i;
					_confirm = false;
					_scroll = Vector2.zero;
				}
			}
			GUI.color = Color.white;
			if (_tab <= 1)
			{
				for (int j = 0; j < ClassDefinition.All.Length; j++)
				{
					ClassDefinition classDefinition = ClassDefinition.All[j];
					GUI.color = (Color)((classDefinition.Id == _selected) ? new Color(1f, 0.82f, 0.48f) : Color.white);
					string text = classDefinition.Title + ((classDefinition.Id == ClassService.Current) ? "  [active]" : "");
					if (GUI.Button(new Rect(24f, (float)(173 + j * 49), 218f, 41f), text, _button))
					{
						_selected = classDefinition.Id;
						_confirm = false;
						_scroll = Vector2.zero;
					}
				}
			}
			GUI.color = Color.white;
			Rect val = ((_tab <= 1) ? new Rect(263f, 174f, 630f, 380f) : new Rect(24f, 174f, 870f, 380f));
			GUILayout.BeginArea(val);
			_scroll = GUILayout.BeginScrollView(_scroll, Array.Empty<GUILayoutOption>());
			if (_tab == 0)
			{
				Details();
			}
			else if (_tab == 1)
			{
				GUILayout.Label($"{_selected} — saved progress", _section, Array.Empty<GUILayoutOption>());
				foreach (string item in ClassService.SkillSummary(_selected))
				{
					GUILayout.Label(item, _body, Array.Empty<GUILayoutOption>());
				}
			}
			else if (_tab == 2)
			{
				GUILayout.Label("Restore a previous class snapshot", _section, Array.Empty<GUILayoutOption>());
				GUILayout.Label("Backups belong to this character only. Restoring replaces all saved class progress with that snapshot. A backup of your current progress is made first.", _body, Array.Empty<GUILayoutOption>());
				if (_backups.Length == 0)
				{
					GUILayout.Label("No backups yet. One is created before your first class choice.", _body, Array.Empty<GUILayoutOption>());
				}
				string[] backups = _backups;
				foreach (string text2 in backups)
				{
					GUI.color = (Color)((_backup == text2) ? new Color(1f, 0.82f, 0.48f) : Color.white);
					if (GUILayout.Button(Path.GetFileNameWithoutExtension(text2), _button, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(34f) }))
					{
						_backup = text2;
						_confirm = false;
					}
				}
				GUI.color = Color.white;
			}
			else
			{
				GUILayout.Label("Your class can change. Your progress stays.", _title, Array.Empty<GUILayoutOption>());
				GUILayout.Label("1. Craft a Class Stone at a workbench using 1 Stone. Use it from your inventory or hotbar; it is reusable.", _body, Array.Empty<GUILayoutOption>());
				GUILayout.Space(10f);
				GUILayout.Label("2. Each class has separate skills, including modded skills and partial XP. A new class starts at zero. Returning restores its previous progress. Your pre-class skills are backed up on your first choice.", _body, Array.Empty<GUILayoutOption>());
				GUILayout.Space(10f);
				GUILayout.Label("3. The class preview lists XP bonuses and restrictions. Gathering remains available to everyone. Shields require Tank; bombs require Thrower. Other weapons remain usable, with class XP bonuses only for matching weapons.", _body, Array.Empty<GUILayoutOption>());
				GUILayout.Space(10f);
				GUILayout.Label("4. Restricted equipped items move into your normal inventory. If there is insufficient space, the change is refused. Attacking or taking damage delays switching; changes also have a 10-second cooldown.", _body, Array.Empty<GUILayoutOption>());
				GUILayout.Space(10f);
				GUILayout.Label("5. Multiplayer requires this exact mod version on every player and the server. Server-controlled balance settings synchronize through Jotunn. Character skill saves are client-owned, not an anti-cheat system.", _body, Array.Empty<GUILayoutOption>());
			}
			GUILayout.EndScrollView();
			GUILayout.EndArea();
			string text3 = ClassService.ChangeBlockReason();
			GUI.Label(new Rect(24f, 565f, 870f, 48f), (text3.Length > 0) ? text3 : ClassService.LastMessage, _body);
			if (_tab == 3)
			{
				if (GUI.Button(new Rect(24f, 626f, 870f, 42f), "Understood — choose a class", _button))
				{
					ClassService.DismissTutorial();
					_tab = 0;
					_scroll = Vector2.zero;
				}
			}
			else
			{
				if (_tab == 1)
				{
					return;
				}
				bool enabled = text3.Length == 0 && ((_tab == 2) ? (_backup != null) : (_selected != ClassService.Current));
				GUI.enabled = enabled;
				string text4 = ((_tab == 2) ? "Restore selected backup" : $"Change to {_selected}");
				if (_confirm)
				{
					GUI.Label(new Rect(24f, 607f, 870f, 22f), "Confirm: current skills will be saved, then selected progress will replace active skills.", _body);
					if (GUI.Button(new Rect(24f, 635f, 620f, 36f), "Confirm — " + text4, _button))
					{
						bool flag = ((_tab == 2) ? ClassService.RestoreBackup(_backup) : ClassService.Select(_selected));
						_confirm = false;
						if (flag)
						{
							_backups = ClassService.Backups();
						}
					}
					GUI.enabled = true;
					if (GUI.Button(new Rect(657f, 635f, 237f, 36f), "Cancel", _button))
					{
						_confirm = false;
					}
				}
				else if (GUI.Button(new Rect(24f, 626f, 870f, 42f), text4, _button))
				{
					_confirm = true;
				}
				GUI.enabled = true;
			}
		}

		private void Details()
		{
			ClassDefinition classDefinition = ClassDefinition.All.First((ClassDefinition c) => c.Id == _selected);
			GUILayout.Label(classDefinition.Title, _title, Array.Empty<GUILayoutOption>());
			GUILayout.Label(classDefinition.Summary, _body, Array.Empty<GUILayoutOption>());
			GUILayout.Space(10f);
			GUILayout.Label($"+{(ClassService.ExperienceMultiplier - 1f) * 100f:0.#}% experience", _section, Array.Empty<GUILayoutOption>());
			GUILayout.Label(classDefinition.BonusSkills, _body, Array.Empty<GUILayoutOption>());
			GUILayout.Space(10f);
			GUILayout.Label("Active effects", _section, Array.Empty<GUILayoutOption>());
			GUILayout.Label((_selected == PlayerClass.Tank) ? $"{ClassService.TankReduction * 100f:0.#}% less stamina for blocks and parries." : ((_selected == PlayerClass.Berserker) ? $"+{ClassService.RadiusBonus:0.#}m two-handed melee hit radius, including area attacks. This widens the existing attack; it does not create a separate explosion." : "No additional class combat effect."), _body, Array.Empty<GUILayoutOption>());
			GUILayout.Label("Direct damage bonus: 0%. Direct damage penalty: 0%. Normal skill-based damage still applies.", _body, Array.Empty<GUILayoutOption>());
			GUILayout.Space(10f);
			GUILayout.Label("Equipment and skill rules", _section, Array.Empty<GUILayoutOption>());
			GUILayout.Label("Shields: Tank only. Bombs: Thrower only. All classes may gather, craft, farm, cook and fish.", _body, Array.Empty<GUILayoutOption>());
			GUILayout.Label("Dual weapons, lances and tridents use Warrior bonuses. Other two-handed melee weapons use Berserker bonuses. Blood Magic receives no class XP bonus.", _body, Array.Empty<GUILayoutOption>());
			if (_selected == PlayerClass.Support)
			{
				GUILayout.Label("Also supports Mining, Lumberjacking, Blacksmithing, Sailing and PackHorse, plus Run, Jump, Swim, Sneak, Dodge and Ride. Unknown custom skills are not automatically treated as gathering.", _body, Array.Empty<GUILayoutOption>());
			}
		}
	}
}
namespace ComputerMod.Foundation.Patches
{
	[HarmonyPatch]
	internal static class BerserkerMeleeRadiusPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(Attack), "DoMeleeAttack", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Attack), "DoAreaAttack", (Type[])null, (Type[])null);
		}

		private static void Prefix(Attack __instance, Humanoid ___m_character, ItemData ___m_weapon, out float? __state)
		{
			__state = null;
			if (!((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)___m_character != (Object)(object)Player.m_localPlayer) && ClassService.Current == PlayerClass.Berserker && ___m_weapon != null && WeaponRules.IsBerserker(___m_weapon))
			{
				__state = __instance.m_attackRayWidth;
				__instance.m_attackRayWidth += ClassService.RadiusBonus;
			}
		}

		private static void Finalizer(Attack __instance, float? __state)
		{
			if (__state.HasValue)
			{
				__instance.m_attackRayWidth = __state.Value;
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "EquipItem", new Type[]
	{
		typeof(ItemData),
		typeof(bool)
	})]
	internal static class ClassEquipmentRestrictionPatch
	{
		private static bool Prefix(Humanoid __instance, ItemData item, ref bool __result)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Invalid comparison between Unknown and I4
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || item == null || !WeaponRules.Locked(item, ClassService.Current))
			{
				return true;
			}
			__result = false;
			((Character)__instance).Message((MessageType)2, ((int)item.m_shared.m_itemType == 5) ? "Only Tanks can equip shields." : "Only Throwers can equip bombs.", 0, (Sprite)null, false);
			return false;
		}
	}
	[HarmonyPatch(typeof(Skills), "RaiseSkill")]
	internal static class ClassExperiencePatch
	{
		private static void Prefix(Skills __instance, SkillType skillType, ref float factor)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			factor *= ClassService.GetExperienceMultiplier(__instance, skillType);
		}
	}
	[HarmonyPatch(typeof(Player), "Save", new Type[] { typeof(ZPackage) })]
	internal static class ClassSavePatch
	{
		[HarmonyPriority(800)]
		private static void Prefix(Player __instance)
		{
			ClassService.Save(__instance);
		}
	}
	[HarmonyPatch(typeof(Player), "Load", new Type[] { typeof(ZPackage) })]
	internal static class ClassLoadPatch
	{
		private static void Postfix(Player __instance)
		{
			ClassService.OnLoaded(__instance);
		}
	}
	[HarmonyPatch(typeof(Player), "OnDamaged", new Type[] { typeof(HitData) })]
	internal static class ClassDamageLockPatch
	{
		private static void Postfix(Player __instance, HitData hit)
		{
			if (hit.GetTotalDamage() > 0f)
			{
				ClassService.MarkCombat(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "StartAttack", new Type[]
	{
		typeof(Character),
		typeof(bool)
	})]
	internal static class ClassAttackLockPatch
	{
		private static void Postfix(Humanoid __instance, bool __result)
		{
			if (__result)
			{
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if (val != null)
				{
					ClassService.MarkCombat(val);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "UseItem", new Type[]
	{
		typeof(Inventory),
		typeof(ItemData),
		typeof(bool)
	})]
	internal static class ClassStoneUsePatch
	{
		private static bool Prefix(Humanoid __instance, Inventory inventory, ItemData item)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				object obj;
				if (item == null)
				{
					obj = null;
				}
				else
				{
					GameObject dropPrefab = item.m_dropPrefab;
					obj = ((dropPrefab != null) ? ((Object)dropPrefab).name : null);
				}
				if (!((string?)obj != "ComputerMod_ClassStone"))
				{
					if ((inventory ?? __instance.GetInventory()).ContainsItem(item))
					{
						ClassMenu.Toggle();
					}
					return false;
				}
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Player), "GetEquipmentBlockStaminaModifier")]
	internal static class TankBlockStaminaPatch
	{
		private static void Postfix(Player __instance, ref float __result)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer && ClassService.Current == PlayerClass.Tank)
			{
				__result = (1f + __result) * (1f - ClassService.TankReduction) - 1f;
			}
		}
	}
}
namespace ComputerMod.Foundation.Items
{
	internal static class ClassStone
	{
		internal const string PrefabName = "ComputerMod_ClassStone";

		private static bool _registered;

		private static ManualLogSource _logger;

		internal static void Install(ManualLogSource logger)
		{
			_logger = logger;
			PrefabManager.OnVanillaPrefabsAvailable -= Register;
			PrefabManager.OnVanillaPrefabsAvailable += Register;
		}

		internal static void Uninstall()
		{
			PrefabManager.OnVanillaPrefabsAvailable -= Register;
		}

		private static void Register()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Expected O, but got Unknown
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Expected O, but got Unknown
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			if (_registered)
			{
				return;
			}
			try
			{
				ItemConfig val = new ItemConfig();
				val.Name = "Class Stone";
				val.Description = "Use to choose a class, inspect bonuses and saved skills, or restore a class backup. Reusable.";
				val.CraftingStation = "piece_workbench";
				val.StackSize = 1;
				val.Requirements = (RequirementConfig[])(object)new RequirementConfig[1]
				{
					new RequirementConfig
					{
						Item = "Stone",
						Amount = 1
					}
				};
				ItemConfig val2 = val;
				CustomItem val3 = new CustomItem("ComputerMod_ClassStone", "Stone", val2);
				if ((Object)(object)val3.ItemDrop == (Object)null)
				{
					throw new InvalidOperationException("The Stone prefab is not available.");
				}
				val3.ItemDrop.m_itemData.m_shared.m_itemType = (ItemType)2;
				if (!ItemManager.Instance.AddItem(val3))
				{
					throw new InvalidOperationException("Jotunn rejected the Class Stone.");
				}
				_registered = true;
				Uninstall();
				_logger.LogInfo((object)"Registered Class Stone: craft from 1 Stone at a workbench; use to open the class menu.");
			}
			catch (Exception arg)
			{
				_logger.LogError((object)$"Class Stone registration failed: {arg}");
			}
		}
	}
}
namespace ComputerMod.Foundation.Compatibility
{
	internal sealed class EquipmentAdapter
	{
		private readonly Player _player;

		private readonly List<(ItemData Item, Vector2i Destination)> _moves = new List<(ItemData, Vector2i)>();

		private EquipmentAdapter(Player player)
		{
			_player = player;
		}

		internal static bool TryPlan(Player player, PlayerClass target, out EquipmentAdapter plan, out string error)
		{
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			plan = new EquipmentAdapter(player);
			error = "";
			Inventory inventory = ((Humanoid)player).GetInventory();
			int width = inventory.GetWidth();
			int num = inventory.GetHeight();
			Type type = AccessTools.TypeByName("AzuEPI.Core.InventoryHandlers.Layout");
			if (type != null)
			{
				try
				{
					num = Math.Min(num, (int)AccessTools.Property(type, "NormalInventoryRows").GetValue(null, null));
				}
				catch
				{
					error = "Cannot read equipment slots. Unequip restricted equipment manually before switching.";
					return false;
				}
			}
			List<ItemData> allItems = inventory.GetAllItems();
			HashSet<(int, int)> hashSet = new HashSet<(int, int)>(allItems.Select((ItemData i) => (x: i.m_gridPos.x, y: i.m_gridPos.y)));
			ItemData[] array = allItems.Where((ItemData i) => i.m_equipped && WeaponRules.Locked(i, target)).ToArray();
			foreach (ItemData val in array)
			{
				if (val.m_gridPos.y >= 0 && val.m_gridPos.y < num && val.m_gridPos.x >= 0 && val.m_gridPos.x < width)
				{
					plan._moves.Add((val, val.m_gridPos));
					continue;
				}
				Vector2i? val2 = null;
				for (int num3 = 0; num3 < num; num3++)
				{
					if (val2.HasValue)
					{
						break;
					}
					for (int num4 = 0; num4 < width; num4++)
					{
						if (hashSet.Add((num4, num3)))
						{
							val2 = new Vector2i(num4, num3);
							break;
						}
					}
				}
				if (!val2.HasValue)
				{
					error = "Cannot change class: clear space in your normal inventory for restricted equipment.";
					return false;
				}
				plan._moves.Add((val, val2.Value));
			}
			return true;
		}

		internal void Apply()
		{
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			foreach (var move in _moves)
			{
				((Humanoid)_player).UnequipItem(move.Item, false);
				if (move.Item.m_equipped)
				{
					throw new InvalidOperationException("Another mod prevented unequipping.");
				}
				if (!((Humanoid)_player).GetInventory().ContainsItem(move.Item) || ((Humanoid)_player).GetInventory().GetAllItems().Any((ItemData i) => i != move.Item && i.m_gridPos.x == move.Destination.x && i.m_gridPos.y == move.Destination.y))
				{
					throw new InvalidOperationException("Inventory changed while unequipping. No occupied slot was overwritten.");
				}
				move.Item.m_gridPos = move.Destination;
			}
			if (_moves.Count > 0)
			{
				AccessTools.Method(typeof(Inventory), "Changed", new Type[2]
				{
					typeof(bool),
					typeof(bool)
				}, (Type[])null).Invoke(((Humanoid)_player).GetInventory(), new object[2] { false, false });
			}
		}
	}
	internal static class WeaponRules
	{
		private static readonly string[] LifeNames = new string[9] { "Mining", "Lumberjacking", "Blacksmithing", "Cooking", "Farming", "Sailing", "PackHorse", "Fishing", "Crafting" };

		private static readonly Dictionary<SkillType, string> CustomLife = LifeNames.ToDictionary((string n) => (SkillType)StableId(n), (string n) => n);

		internal static readonly SkillType Throwing = (SkillType)StableId("Throwing");

		private static int StableId(string text)
		{
			int num = 5381;
			int num2 = num;
			for (int i = 0; i < text.Length; i += 2)
			{
				num = ((num << 5) + num) ^ text[i];
				if (i == text.Length - 1)
				{
					break;
				}
				num2 = ((num2 << 5) + num2) ^ text[i + 1];
			}
			return Math.Abs(num + num2 * 1566083941);
		}

		private static bool Has(ItemData item, params string[] names)
		{
			string name = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : item.m_shared.m_name);
			return names.Any((string n) => name.IndexOf(n, StringComparison.OrdinalIgnoreCase) >= 0);
		}

		internal static bool IsBomb(ItemData item)
		{
			return Has(item, "Bomb", "Grenade");
		}

		internal static bool IsWarrior(ItemData item)
		{
			return Has(item, "Dual", "Lance", "Trident") || ((object)Unsafe.As<ItemType, ItemType>(ref item.m_shared.m_itemType)/*cast due to .constrained prefix*/).ToString().Equals("TwoHandedWeaponLeft");
		}

		internal static bool IsBerserker(ItemData item)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Invalid comparison between Unknown and I4
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Invalid comparison between Unknown and I4
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Invalid comparison between Unknown and I4
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Invalid comparison between Unknown and I4
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Invalid comparison between Unknown and I4
			return (int)item.m_shared.m_itemType == 14 && !IsWarrior(item) && (int)item.m_shared.m_skillType != 9 && (int)item.m_shared.m_skillType != 10 && (int)item.m_shared.m_skillType != 12 && (int)item.m_shared.m_skillType != 8 && (int)item.m_shared.m_skillType != 14;
		}

		internal static bool Locked(ItemData item, PlayerClass target)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			return target != PlayerClass.None && (((int)item.m_shared.m_itemType == 5 && target != PlayerClass.Tank) || (IsBomb(item) && target != PlayerClass.Thrower));
		}

		internal static bool IsLife(SkillType skill)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: 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)
			//IL_0016: Invalid comparison between Unknown and I4
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Invalid comparison between Unknown and I4
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Invalid comparison between Unknown and I4
			bool flag = CustomLife.ContainsKey(skill);
			bool flag2 = flag;
			if (!flag2)
			{
				bool flag3 = ((skill - 12 <= 1 || skill - 100 <= 8 || (int)skill == 110) ? true : false);
				flag2 = flag3;
			}
			return flag2;
		}

		internal static bool Boosts(PlayerClass cls, SkillType skill, ItemData? weapon)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Invalid comparison between Unknown and I4
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Invalid comparison between Unknown and I4
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Invalid comparison between Unknown and I4
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Invalid comparison between Unknown and I4
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Invalid comparison between Unknown and I4
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Invalid comparison between Unknown and I4
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Invalid comparison between Unknown and I4
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Invalid comparison between Unknown and I4
			switch (cls)
			{
			case PlayerClass.Support:
				return IsLife(skill);
			case PlayerClass.Tank:
				return (int)skill == 6;
			case PlayerClass.Ranger:
				return ((int)skill == 8 || (int)skill == 14) ? true : false;
			case PlayerClass.Magician:
				return (int)skill == 9;
			case PlayerClass.Thrower:
				if (skill == Throwing)
				{
					return true;
				}
				break;
			}
			bool flag = cls == PlayerClass.Rogue;
			bool flag2 = flag;
			bool flag3;
			if (flag2)
			{
				flag3 = (((int)skill == 11 || (int)skill == 101) ? true : false);
				flag2 = flag3;
			}
			if (flag2)
			{
				return true;
			}
			if (weapon == null || weapon.m_shared.m_skillType != skill || IsLife(skill))
			{
				return false;
			}
			if (1 == 0)
			{
			}
			flag3 = cls switch
			{
				PlayerClass.Rogue => !IsWarrior(weapon) && !IsBomb(weapon) && ((int)weapon.m_shared.m_itemType == 3 || (int)skill == 5), 
				PlayerClass.Berserker => IsBerserker(weapon), 
				PlayerClass.Warrior => IsWarrior(weapon), 
				PlayerClass.Thrower => IsBomb(weapon) || Has(weapon, "Throw"), 
				_ => false, 
			};
			if (1 == 0)
			{
			}
			return flag3;
		}

		internal unsafe static string SkillName(SkillType skill)
		{
			//IL_0006: 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_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Expected I4, but got Unknown
			if (CustomLife.TryGetValue(skill, out string value))
			{
				return value;
			}
			if (skill == Throwing)
			{
				return "Throwing";
			}
			string text = "$skill_" + (Enum.IsDefined(typeof(SkillType), skill) ? ((object)(*(SkillType*)(&skill))/*cast due to .constrained prefix*/).ToString().ToLowerInvariant() : ((int)skill).ToString());
			Localization instance = Localization.instance;
			string text2 = ((instance != null) ? instance.Localize(text) : null);
			return (string.IsNullOrEmpty(text2) || text2.Contains("skill_")) ? ((object)(*(SkillType*)(&skill))/*cast due to .constrained prefix*/).ToString() : text2;
		}
	}
}
namespace ComputerMod.Foundation.Classes
{
	public sealed class ClassDefinition
	{
		public static readonly ClassDefinition[] All = new ClassDefinition[8]
		{
			new ClassDefinition(PlayerClass.Tank, "Tank", "Defensive shield specialist.", "Blocking", "Only Tanks can equip shields. Blocking costs 15% less stamina."),
			new ClassDefinition(PlayerClass.Rogue, "Rogue", "Fast one-handed fighter and stealth user.", "One-handed weapons, Spears, Unarmed, Sneak", "Cannot equip shields."),
			new ClassDefinition(PlayerClass.Ranger, "Ranger", "Dedicated ranged weapon user.", "Bows, Crossbows", "Shields require Tank; bombs require Thrower."),
			new ClassDefinition(PlayerClass.Thrower, "Thrower", "Thrown-weapon and explosive specialist.", "Throwing weapons", "Only Throwers can equip bombs and thrown explosives."),
			new ClassDefinition(PlayerClass.Support, "Support", "Gathering, crafting, and life-skill specialist.", "Fishing, Cooking, Farming, Pickaxes, WoodCutting, Mining, Lumberjacking, Blacksmithing, Crafting, Sailing, PackHorse, Run, Jump, Swim, Sneak, Dodge, Ride", "Shields require Tank; bombs require Thrower."),
			new ClassDefinition(PlayerClass.Magician, "Magician", "Elemental spellcaster.", "Elemental Magic", "Shields require Tank; bombs require Thrower."),
			new ClassDefinition(PlayerClass.Berserker, "Berserker", "Two-handed melee area-damage specialist.", "Two-handed melee weapons, excluding Warrior weapons, magic, bows and gathering tools", "Shields require Tank; bombs require Thrower. Radius bonus is configurable."),
			new ClassDefinition(PlayerClass.Warrior, "Warrior", "Dual-wield, lance, and trident specialist.", "Dual-wield weapons, Lances, Tridents", "Shields require Tank; bombs require Thrower.")
		};

		public PlayerClass Id { get; }

		public string Title { get; }

		public string Summary { get; }

		public string BonusSkills { get; }

		public string Restrictions { get; }

		public ClassDefinition(PlayerClass id, string title, string summary, string bonusSkills, string restrictions)
		{
			Id = id;
			Title = title;
			Summary = summary;
			BonusSkills = bonusSkills;
			Restrictions = restrictions;
		}
	}
	public static class ClassService
	{
		private const string SaveKey = "ComputerMod.Classes.v1";

		private static readonly MethodInfo GetSkill = AccessTools.Method(typeof(Skills), "GetSkill", (Type[])null, (Type[])null);

		private static readonly MethodInfo GetSkillDef = AccessTools.Method(typeof(Skills), "GetSkillDef", (Type[])null, (Type[])null);

		private static ConfigFile _config = null;

		private static ManualLogSource _logger = null;

		private static Player? _owner;

		private static ClassProgress? _progress;

		private static string? _loadError;

		private static float _nextChange;

		private static float _combatUntil;

		private static ConfigEntry<float> _xp = null;

		private static ConfigEntry<float> _tank = null;

		private static ConfigEntry<float> _radius = null;

		private static ConfigEntry<float> _combatLock = null;

		public static string LastMessage { get; private set; } = "";

		public static PlayerClass Current
		{
			get
			{
				EnsurePlayer();
				ClassProgress? progress = _progress;
				return (progress != null) ? ((PlayerClass)progress.CurrentClass) : PlayerClass.None;
			}
		}

		public static float ExperienceMultiplier => _xp.Value;

		public static float TankReduction => _tank.Value;

		public static float RadiusBonus => _radius.Value;

		public static bool TutorialSeen
		{
			get
			{
				ClassProgress? progress = _progress;
				return progress != null && progress.TutorialSeen;
			}
		}

		public static string SaveStatus => _loadError ?? "Class levels and partial XP are saved with this character.";

		private static string BackupDirectory => Path.Combine(Path.GetDirectoryName(_config.ConfigFilePath), "ComputerModBackups");

		public static void Initialize(ConfigFile config, ManualLogSource logger)
		{
			_config = config;
			_logger = logger;
			_xp = Setting("Class XP multiplier", 1.5f, 1f, 5f, "XP multiplier for skills supported by your class.");
			_tank = Setting("Tank block stamina reduction", 0.15f, 0f, 0.9f, "Reduces blocking and parrying stamina cost. 0.15 = 15%.");
			_radius = Setting("Berserker melee radius bonus", 1f, 0f, 5f, "Extra metres of hit radius for two-handed melee and area attacks.");
			_combatLock = Setting("Combat class change lock", 30f, 0f, 300f, "Seconds after attacking or taking damage before switching or restoring.");
		}

		private static ConfigEntry<float> Setting(string key, float value, float min, float max, string description)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			return _config.Bind<float>("Class Bonuses", key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
		}

		private static void EnsurePlayer()
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)(object)_owner)
			{
				return;
			}
			_owner = localPlayer;
			_progress = null;
			_loadError = null;
			_nextChange = (_combatUntil = 0f);
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			try
			{
				_progress = (ClassProgress?)(localPlayer.m_customData.TryGetValue("ComputerMod.Classes.v1", out var value) ? ((object)ClassProgress.Parse(value, localPlayer.GetPlayerID())) : ((object)new ClassProgress
				{
					PlayerId = localPlayer.GetPlayerID()
				}));
			}
			catch (Exception arg)
			{
				_loadError = "Class save could not be read. Switching is disabled to preserve your data.";
				_logger.LogError((object)$"Class data for character {localPlayer.GetPlayerID()} was preserved: {arg}");
			}
		}

		public static void OnLoaded(Player player)
		{
			if ((Object)(object)player == (Object)(object)_owner)
			{
				_owner = null;
				_progress = null;
			}
		}

		public static void MarkCombat(Player player)
		{
			if (!((Object)(object)player == (Object)null) && !((Object)(object)player != (Object)(object)Player.m_localPlayer))
			{
				EnsurePlayer();
				_combatUntil = Time.unscaledTime + _combatLock.Value;
			}
		}

		public static string ChangeBlockReason()
		{
			EnsurePlayer();
			if ((Object)(object)_owner == (Object)null)
			{
				return "Enter a world to choose a class.";
			}
			if (_loadError != null)
			{
				return _loadError;
			}
			if (((Character)_owner).IsDead() || ((Character)_owner).IsTeleporting() || ((Character)_owner).InAttack())
			{
				return "Finish your current action before changing class.";
			}
			int num = Mathf.CeilToInt(Mathf.Max(_combatUntil, _nextChange) - Time.unscaledTime);
			return (num > 0) ? $"Class changes available in {num}s." : "";
		}

		public static bool Select(PlayerClass target)
		{
			if (!Enum.IsDefined(typeof(PlayerClass), target) || target == PlayerClass.None)
			{
				return false;
			}
			string text = ChangeBlockReason();
			if (text.Length > 0)
			{
				return Message(text);
			}
			if (Current == target)
			{
				return Message("This class is already active.");
			}
			return ApplyChange(null, target);
		}

		private static bool ApplyChange(ClassProgress? restore, PlayerClass target)
		{
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			Player owner = _owner;
			ClassProgress progress = _progress;
			if (!EquipmentAdapter.TryPlan(owner, target, out EquipmentAdapter plan, out string error))
			{
				return Message(error);
			}
			Capture(owner, progress);
			string text = progress.Serialize();
			Dictionary<Skill, SkillProgress> dictionary = ((IEnumerable<Skill>)((Character)owner).GetSkills().GetSkillList()).ToDictionary((Func<Skill, Skill>)((Skill s) => s), (Func<Skill, SkillProgress>)((Skill s) => new SkillProgress(s.m_level, s.m_accumulator)));
			try
			{
				Directory.CreateDirectory(BackupDirectory);
				File.WriteAllText(Path.Combine(BackupDirectory, $"{owner.GetPlayerID()}-{DateTime.UtcNow:yyyyMMdd-HHmmss-fffffff}.xml"), text);
				ClassProgress val = restore ?? ClassProgress.Parse(text, owner.GetPlayerID());
				val.CurrentClass = (int)target;
				Dictionary<Skill, SkillProgress> dictionary2 = BuildTargets(owner, val, target);
				plan.Apply();
				foreach (KeyValuePair<Skill, SkillProgress> item in dictionary2)
				{
					Skill key = item.Key;
					SkillProgress value = item.Value;
					key.m_level = ((SkillProgress)(ref value)).Level;
					Skill key2 = item.Key;
					value = item.Value;
					key2.m_accumulator = ((SkillProgress)(ref value)).Experience;
				}
				_progress = val;
				owner.m_customData["ComputerMod.Classes.v1"] = val.Serialize();
				_nextChange = Time.unscaledTime + 10f;
				_logger.LogInfo((object)$"Character {owner.GetPlayerID()} selected {target}; skill levels and partial XP restored.");
				LastMessage = $"{target} active. Your previous class skills are saved.";
				return true;
			}
			catch (Exception arg)
			{
				_progress = progress;
				foreach (Skill skill in ((Character)owner).GetSkills().GetSkillList())
				{
					SkillProgress value2;
					SkillProgress val2 = (SkillProgress)(dictionary.TryGetValue(skill, out value2) ? value2 : new SkillProgress(0f, 0f));
					skill.m_level = ((SkillProgress)(ref val2)).Level;
					skill.m_accumulator = ((SkillProgress)(ref val2)).Experience;
				}
				owner.m_customData["ComputerMod.Classes.v1"] = text;
				_logger.LogError((object)$"Class switch failed: {arg}");
				return Message("Could not finish changing class. Your previous skills were restored; see the log.");
			}
		}

		private static Dictionary<Skill, SkillProgress> BuildTargets(Player player, ClassProgress data, PlayerClass target)
		{
			//IL_00a8: 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_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Expected O, but got Unknown
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			Skills skills = ((Character)player).GetSkills();
			Dictionary<Skill, SkillProgress> dictionary = (from s in skills.GetSkillList()
				where s.m_info != null
				select s).ToDictionary((Func<Skill, Skill>)((Skill s) => s), (Func<Skill, SkillProgress>)((Skill _) => new SkillProgress(0f, 0f)));
			if (data.Classes.TryGetValue((int)target, out var value))
			{
				foreach (KeyValuePair<int, SkillProgress> item in value)
				{
					SkillType val = (SkillType)item.Key;
					if (GetSkillDef.Invoke(skills, new object[1] { val }) != null)
					{
						Skill key = (Skill)GetSkill.Invoke(skills, new object[1] { val });
						dictionary[key] = item.Value;
					}
				}
			}
			return dictionary;
		}

		private static void Capture(Player player, ClassProgress data)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Expected I4, but got Unknown
			if (!data.Classes.TryGetValue(data.CurrentClass, out var value))
			{
				value = (data.Classes[data.CurrentClass] = new Dictionary<int, SkillProgress>());
			}
			foreach (Skill item in from s in ((Character)player).GetSkills().GetSkillList()
				where s.m_info != null
				select s)
			{
				value[(int)item.m_info.m_skill] = new SkillProgress(item.m_level, item.m_accumulator);
			}
		}

		public static void Save(Player player)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			EnsurePlayer();
			if (_progress == null)
			{
				return;
			}
			try
			{
				Capture(player, _progress);
				player.m_customData["ComputerMod.Classes.v1"] = _progress.Serialize();
			}
			catch (Exception arg)
			{
				_logger.LogError((object)$"Could not update class save: {arg}");
			}
		}

		public static void DismissTutorial()
		{
			EnsurePlayer();
			if (_progress != null)
			{
				_progress.TutorialSeen = true;
				Save(_owner);
			}
		}

		public static string[] Backups()
		{
			EnsurePlayer();
			if ((Object)(object)_owner == (Object)null || !Directory.Exists(BackupDirectory))
			{
				return Array.Empty<string>();
			}
			return Directory.GetFiles(BackupDirectory, $"{_owner.GetPlayerID()}-*.xml").OrderByDescending(Path.GetFileName).Take(20)
				.ToArray();
		}

		public static bool RestoreBackup(string path)
		{
			string text = ChangeBlockReason();
			if (text.Length > 0)
			{
				return Message(text);
			}
			if (!Backups().Contains(path))
			{
				return Message("Choose a backup for this character.");
			}
			try
			{
				ClassProgress val = ClassProgress.Parse(File.ReadAllText(path), _owner.GetPlayerID());
				return ApplyChange(val, (PlayerClass)val.CurrentClass);
			}
			catch (Exception ex)
			{
				_logger.LogError((object)ex);
				return Message("Backup is invalid; nothing was restored.");
			}
		}

		public static IEnumerable<string> SkillSummary(PlayerClass target)
		{
			EnsurePlayer();
			if ((Object)(object)_owner == (Object)null || _progress == null)
			{
				return Array.Empty<string>();
			}
			if (target == Current)
			{
				Capture(_owner, _progress);
			}
			if (!_progress.Classes.TryGetValue((int)target, out var value) || value.Count == 0)
			{
				return new string[1] { "New class: all skills start at level 0." };
			}
			return value.OrderBy((KeyValuePair<int, SkillProgress> p) => WeaponRules.SkillName((SkillType)p.Key)).Select(delegate(KeyValuePair<int, SkillProgress> p)
			{
				//IL_0013: Unknown result type (might be due to invalid IL or missing references)
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				//IL_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)
				string arg = WeaponRules.SkillName((SkillType)p.Key);
				SkillProgress value2 = p.Value;
				object arg2 = ((SkillProgress)(ref value2)).Level;
				value2 = p.Value;
				return $"{arg}: {arg2:0.#} / 100  |  XP progress: {((SkillProgress)(ref value2)).Experience:0.##}";
			}).ToArray();
		}

		public static float GetExperienceMultiplier(Skills skills, SkillType type)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			return ((Object)(object)Player.m_localPlayer != (Object)null && (Object)(object)((Character)Player.m_localPlayer).GetSkills() == (Object)(object)skills && WeaponRules.Boosts(Current, type, ((Humanoid)Player.m_localPlayer).GetCurrentWeapon())) ? ExperienceMultiplier : 1f;
		}

		private static bool Message(string text)
		{
			LastMessage = text;
			Player localPlayer = Player.m_localPlayer;
			if (localPlayer != null)
			{
				((Character)localPlayer).Message((MessageType)2, text, 0, (Sprite)null, false);
			}
			return false;
		}
	}
	public enum PlayerClass
	{
		None,
		Tank,
		Rogue,
		Ranger,
		Thrower,
		Support,
		Magician,
		Berserker,
		Warrior
	}
}

plugins/Computer-classes/Computer-classes.Core.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Xml;
using System.Xml.Linq;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Computer-classes.Core")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("0.3.1.0")]
[assembly: AssemblyInformationalVersion("0.3.1")]
[assembly: AssemblyProduct("Computer-classes.Core")]
[assembly: AssemblyTitle("Computer-classes.Core")]
[assembly: AssemblyVersion("0.3.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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]
	[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]
	[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 ComputerMod.Core
{
	public sealed class ClassProgress
	{
		public long PlayerId { get; set; }

		public int CurrentClass { get; set; }

		public bool TutorialSeen { get; set; }

		public Dictionary<int, Dictionary<int, SkillProgress>> Classes { get; } = new Dictionary<int, Dictionary<int, SkillProgress>>();

		public string Serialize()
		{
			XElement xElement = new XElement("ComputerMod", new XAttribute("version", 1), new XAttribute("player", PlayerId), new XAttribute("class", CurrentClass), new XAttribute("tutorial", TutorialSeen));
			foreach (KeyValuePair<int, Dictionary<int, SkillProgress>> item in Classes.OrderBy<KeyValuePair<int, Dictionary<int, SkillProgress>>, int>((KeyValuePair<int, Dictionary<int, SkillProgress>> pair) => pair.Key))
			{
				xElement.Add(new XElement("Class", new XAttribute("id", item.Key), from skill in item.Value
					orderby skill.Key
					select new XElement("Skill", new XAttribute("id", skill.Key), new XAttribute("level", skill.Value.Level), new XAttribute("xp", skill.Value.Experience))));
			}
			return xElement.ToString(SaveOptions.DisableFormatting);
		}

		public static ClassProgress Parse(string text, long expectedPlayerId)
		{
			using XmlReader reader = XmlReader.Create(new StringReader(text), new XmlReaderSettings
			{
				DtdProcessing = DtdProcessing.Prohibit,
				XmlResolver = null,
				MaxCharactersInDocument = 4000000L
			});
			XElement xElement = XElement.Load(reader);
			if (xElement.Name != "ComputerMod" || (int?)xElement.Attribute("version") != 1)
			{
				throw new FormatException("Unsupported class save format.");
			}
			ClassProgress classProgress = new ClassProgress
			{
				PlayerId = (long)xElement.Attribute("player"),
				CurrentClass = (int)xElement.Attribute("class"),
				TutorialSeen = ((bool?)xElement.Attribute("tutorial") == true)
			};
			if (classProgress.PlayerId != expectedPlayerId || classProgress.CurrentClass < 0 || classProgress.CurrentClass > 8)
			{
				throw new FormatException("Class save belongs to another character or has an invalid class.");
			}
			foreach (XElement item in xElement.Elements("Class"))
			{
				int num = (int)item.Attribute("id");
				if (num < 0 || num > 8)
				{
					throw new FormatException("Invalid saved class.");
				}
				Dictionary<int, SkillProgress> dictionary = new Dictionary<int, SkillProgress>();
				foreach (XElement item2 in item.Elements("Skill"))
				{
					float num2 = float.Parse((string?)item2.Attribute("level"), CultureInfo.InvariantCulture);
					float num3 = float.Parse((string?)item2.Attribute("xp"), CultureInfo.InvariantCulture);
					if (!ValidNumber(num2) || !ValidNumber(num3) || num2 > 100f)
					{
						throw new FormatException("Invalid saved skill progress.");
					}
					dictionary.Add((int)item2.Attribute("id"), new SkillProgress(num2, num3));
				}
				classProgress.Classes.Add(num, dictionary);
			}
			return classProgress;
		}

		private static bool ValidNumber(float value)
		{
			return !float.IsNaN(value) && !float.IsInfinity(value) && value >= 0f;
		}
	}
	public readonly struct SkillProgress
	{
		public float Level { get; }

		public float Experience { get; }

		public SkillProgress(float level, float experience)
		{
			Level = level;
			Experience = experience;
		}
	}
	public static class PluginMetadata
	{
		public const string Guid = "com.froze.computermod.foundation";

		public const string Name = "Computer-classes";

		public const string Version = "0.3.1";
	}
}

plugins/Computer-TamedAnimals/Computer-TamedAnimals.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Computer-TamedAnimals")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Expanded taming, riding, and animal cart hauling for Valheim.")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0")]
[assembly: AssemblyProduct("Computer-TamedAnimals")]
[assembly: AssemblyTitle("Computer-TamedAnimals")]
[assembly: AssemblyVersion("0.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Computer.TamedAnimals
{
	public sealed class AnimalCompanion : MonoBehaviour
	{
		internal const string CartKey = "CTA.Cart";

		internal const string RequestRpc = "CTA.RequestCart";

		internal const string ReleaseRpc = "CTA.ReleaseCart";

		internal const string FollowRpc = "CTA.Follow";

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

		internal Character Character;

		internal MonsterAI Ai;

		internal Tameable Tame;

		internal ZNetView View;

		internal string Prefab;

		internal bool OwnSaddle;

		private float _reservationUntil;

		private float _nextCheck;

		internal bool CanPull
		{
			get
			{
				if (Plugin.Settings.Carts && Object.op_Implicit((Object)(object)Character) && !Character.IsDead() && Character.IsTamed() && Plugin.Settings.Pullers.Contains(Prefab) && !Character.IsFlying())
				{
					return !Ai.m_avoidLand;
				}
				return false;
			}
		}

		internal bool CanRide
		{
			get
			{
				if (Plugin.Settings.Riding && Object.op_Implicit((Object)(object)Character) && !Character.IsDead() && Character.IsTamed() && Plugin.Settings.Riders.Contains(Prefab) && Object.op_Implicit((Object)(object)Tame) && Object.op_Implicit((Object)(object)Tame.m_saddle))
				{
					return ((Behaviour)Tame.m_saddle).isActiveAndEnabled;
				}
				return false;
			}
		}

		internal bool IsHauling
		{
			get
			{
				//IL_0020: Unknown result type (might be due to invalid IL or missing references)
				if (Object.op_Implicit((Object)(object)View) && View.IsValid())
				{
					return (Object)(object)CartHarness.FindFor(Character.GetZDOID()) != (Object)null;
				}
				return false;
			}
		}

		internal Vector3 HitchPosition => ((Component)this).transform.TransformPoint(new Vector3(0f, Mathf.Clamp(Character.GetHeight() * 0.35f, 0.45f, 1.6f), 0f - Mathf.Max(0.6f, Character.GetRadius())));

		internal static void AddTo(MonsterAI ai)
		{
			if (!Plugin.Settings.Enabled || Object.op_Implicit((Object)(object)((Component)ai).GetComponent<AnimalCompanion>()))
			{
				return;
			}
			Humanoid component = ((Component)ai).GetComponent<Humanoid>();
			ZNetView component2 = ((Component)ai).GetComponent<ZNetView>();
			if (!Object.op_Implicit((Object)(object)component) || ((Character)component).IsPlayer() || ((Character)component).IsBoss() || ((Character)component).m_flying || ((BaseAI)ai).m_randomFly || ai.m_avoidLand || !Object.op_Implicit((Object)(object)component2) || !component2.IsValid())
			{
				return;
			}
			string prefabName = Utils.GetPrefabName(((Component)ai).gameObject);
			Settings settings = Plugin.Settings;
			if (!settings.Extra.Contains(prefabName) && !settings.Riders.Contains(prefabName) && !settings.Pullers.Contains(prefabName))
			{
				return;
			}
			Tameable val = ((Component)ai).GetComponent<Tameable>();
			if (!Object.op_Implicit((Object)(object)val))
			{
				if (!settings.Extra.Contains(prefabName))
				{
					return;
				}
				List<ItemDrop> list = new List<ItemDrop>();
				if (settings.Foods.TryGetValue(prefabName, out var value))
				{
					string[] array = value;
					foreach (string text in array)
					{
						GameObject val2 = (Object.op_Implicit((Object)(object)ObjectDB.instance) ? ObjectDB.instance.GetItemPrefab(text) : null);
						if (Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)val2.GetComponent<ItemDrop>()))
						{
							list.Add(val2.GetComponent<ItemDrop>());
						}
						else if (Reported.Add("food:" + text))
						{
							Plugin.Warn("Food prefab not found: " + text);
						}
					}
				}
				if (list.Count == 0)
				{
					if (Reported.Add("diet:" + prefabName))
					{
						Plugin.Warn("No valid food configured for " + prefabName + "; taming was not added.");
					}
					return;
				}
				ai.m_consumeItems = list;
				ai.m_consumeSearchRange = Mathf.Max(10f, ai.m_consumeSearchRange);
				val = ((Component)ai).gameObject.AddComponent<Tameable>();
				val.m_tamingTime = settings.TamingTime;
				val.m_fedDuration = settings.FedTime;
				GameObject prefab = ZNetScene.instance.GetPrefab("Boar");
				Tameable val3 = (Object.op_Implicit((Object)(object)prefab) ? prefab.GetComponent<Tameable>() : null);
				if (Object.op_Implicit((Object)(object)val3))
				{
					val.m_tamedEffect = val3.m_tamedEffect;
					val.m_sootheEffect = val3.m_sootheEffect;
					val.m_petEffect = val3.m_petEffect;
				}
			}
			GameApi.AiTame.Invoke((BaseAI)(object)ai) = val;
			val.m_commandable = true;
			AnimalCompanion animalCompanion = ((Component)ai).gameObject.AddComponent<AnimalCompanion>();
			animalCompanion.Character = (Character)(object)component;
			animalCompanion.Ai = ai;
			animalCompanion.Tame = val;
			animalCompanion.View = component2;
			animalCompanion.Prefab = prefabName;
			animalCompanion.Initialize();
		}

		private void Initialize()
		{
			View.Register<ZDOID, ZDOID>("CTA.RequestCart", (Action<long, ZDOID, ZDOID>)RequestCartRpc);
			View.Register<ZDOID>("CTA.ReleaseCart", (Action<long, ZDOID>)ReleaseCartRpc);
			View.Register<ZDOID, ZDOID>("CTA.Follow", (Action<long, ZDOID, ZDOID>)FollowCartRpc);
			if (Plugin.Settings.Riding && Plugin.Settings.Riders.Contains(Prefab) && !Object.op_Implicit((Object)(object)Tame.m_saddle))
			{
				CreateSeat();
			}
			if (Reported.Add("ready:" + Prefab))
			{
				Plugin.Log.LogInfo((object)("Enabled creature: " + Prefab));
			}
		}

		private void CreateSeat()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("CTA_BarebackSeat");
			val.SetActive(false);
			val.transform.SetParent(((Component)this).transform, false);
			Transform transform = new GameObject("AttachPoint").transform;
			transform.SetParent(val.transform, false);
			if (Plugin.Settings.Offsets.TryGetValue(Prefab, out var value))
			{
				transform.localPosition = new Vector3(value[0], value[1], value[2]);
			}
			else
			{
				CapsuleCollider component = ((Component)this).GetComponent<CapsuleCollider>();
				transform.localPosition = (Object.op_Implicit((Object)(object)component) ? (component.center + Vector3.up * component.height * 0.35f) : Vector3.up);
			}
			Sadle val2 = val.AddComponent<Sadle>();
			val2.m_attachPoint = transform;
			val2.m_hoverText = Character.m_name;
			val2.m_maxUseRange = 10f;
			val2.m_detachOffset = new Vector3(Character.GetRadius() + 1f, 0.5f, 0f);
			GameObject prefab = ZNetScene.instance.GetPrefab("Lox");
			Sadle val3 = ((!Object.op_Implicit((Object)(object)prefab)) ? null : prefab.GetComponent<Tameable>()?.m_saddle);
			if (Object.op_Implicit((Object)(object)val3))
			{
				val2.m_attachAnimation = val3.m_attachAnimation;
				val2.m_mountIcon = val3.m_mountIcon;
			}
			Tame.m_saddle = val2;
			Tame.m_dropSaddleOnDeath = false;
			OwnSaddle = true;
			val.SetActive(true);
		}

		private void Update()
		{
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)View) || !View.IsValid() || !View.IsOwner() || Time.time < _nextCheck)
			{
				return;
			}
			_nextCheck = Time.time + 1f;
			if (Character.IsTamed())
			{
				((BaseAI)Ai).SetHuntPlayer(false);
				Ai.SetDespawnInDay(false);
				Ai.SetEventCreature(false);
			}
			ZDOID zDOID = View.GetZDO().GetZDOID("CTA.Cart");
			if (!((ZDOID)(ref zDOID)).IsNone() && !(Time.time < _reservationUntil))
			{
				ZDO zDO = ZDOMan.instance.GetZDO(zDOID);
				if (zDO == null || zDO.GetZDOID("CTA.Animal") != Character.GetZDOID())
				{
					View.GetZDO().Set("CTA.Cart", ZDOID.None);
				}
			}
		}

		internal void RequestCart(CartHarness cart, Player player)
		{
			//IL_001e: 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)
			View.InvokeRPC("CTA.RequestCart", new object[2]
			{
				cart.View.GetZDO().m_uid,
				((Character)player).GetZDOID()
			});
		}

		private void RequestCartRpc(long sender, ZDOID cartId, ZDOID playerId)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: 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_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0188: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			if (!View.IsValid() || !View.IsOwner() || !CanPull)
			{
				return;
			}
			Player val = GameApi.Requester(sender, playerId, ((Component)this).transform.position);
			GameObject val2 = ZNetScene.instance.FindInstance(cartId);
			CartHarness cartHarness = (Object.op_Implicit((Object)(object)val2) ? val2.GetComponent<CartHarness>() : null);
			if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)cartHarness) && cartHarness.View.IsValid())
			{
				ZDOID zDOID = View.GetZDO().GetZDOID("CTA.Cart");
				ZDO val3 = (((ZDOID)(ref zDOID)).IsNone() ? null : ZDOMan.instance.GetZDO(zDOID));
				bool num = Time.time < _reservationUntil;
				bool flag = val3 != null && val3.GetZDOID("CTA.Animal") == Character.GetZDOID();
				if (num || flag || cartHarness.HasLink || cartHarness.Cart.InUse() || Vector3.Distance(((Component)val).transform.position, ((Component)cartHarness).transform.position) > 10f || Vector3.Distance(cartHarness.Cart.m_attachPoint.position, HitchPosition) > Plugin.Settings.HitchRange)
				{
					cartHarness.Reply(sender, "Cannot hitch: move the cart shafts closer, and detach any existing hitch first.");
					return;
				}
				View.GetZDO().Set("CTA.Cart", cartId);
				_reservationUntil = Time.time + 8f;
				cartHarness.View.InvokeRPC("CTA.Connect", new object[2]
				{
					Character.GetZDOID(),
					playerId
				});
			}
		}

		private void ReleaseCartRpc(long sender, ZDOID cartId)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			if (View.IsValid() && View.IsOwner() && !(View.GetZDO().GetZDOID("CTA.Cart") != cartId))
			{
				ZDO zDO = ZDOMan.instance.GetZDO(cartId);
				if (zDO == null || zDO.GetOwner() == sender)
				{
					View.GetZDO().Set("CTA.Cart", ZDOID.None);
					_reservationUntil = 0f;
				}
			}
		}

		private void FollowCartRpc(long sender, ZDOID cartId, ZDOID playerId)
		{
			//IL_0028: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			if (!View.IsValid() || !View.IsOwner() || !CanPull)
			{
				return;
			}
			ZDO zDO = ZDOMan.instance.GetZDO(cartId);
			if (zDO != null && zDO.GetOwner() == sender && !(View.GetZDO().GetZDOID("CTA.Cart") != cartId))
			{
				GameObject val = ZNetScene.instance.FindInstance(playerId);
				Player val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<Player>() : null);
				if (Object.op_Implicit((Object)(object)val2) && !((Character)val2).IsDead() && !(Vector3.Distance(((Component)val2).transform.position, ((Component)this).transform.position) > 12f))
				{
					((BaseAI)Ai).ResetPatrolPoint();
					Ai.SetFollowTarget(((Component)val2).gameObject);
					View.GetZDO().Set(ZDOVars.s_follow, val2.GetPlayerName());
				}
			}
		}

		private void OnDestroy()
		{
			Player localPlayer = Player.m_localPlayer;
			if (Object.op_Implicit((Object)(object)localPlayer))
			{
				IDoodadController doodadController = localPlayer.GetDoodadController();
				Sadle val = (Sadle)(object)((doodadController is Sadle) ? doodadController : null);
				if (val != null && (Object)(object)val == (Object)(object)Tame?.m_saddle)
				{
					localPlayer.StopDoodadControl();
				}
			}
		}
	}
	public sealed class CartHarness : MonoBehaviour
	{
		internal const string AnimalKey = "CTA.Animal";

		internal const string DriverKey = "CTA.Driver";

		internal const string ConnectRpc = "CTA.Connect";

		private const string DetachRpc = "CTA.Detach";

		private const string MessageRpc = "CTA.Message";

		private static readonly HashSet<CartHarness> Instances = new HashSet<CartHarness>();

		internal Vagon Cart;

		internal ZNetView View;

		private AnimalCompanion _animal;

		private Rigidbody _anchor;

		private ConfigurableJoint _joint;

		private float _missingSince;

		private readonly List<(Collider cart, Collider animal)> _ignored = new List<(Collider, Collider)>();

		internal bool HasLink
		{
			get
			{
				//IL_002a: Unknown result type (might be due to invalid IL or missing references)
				//IL_002f: Unknown result type (might be due to invalid IL or missing references)
				if (Object.op_Implicit((Object)(object)View) && View.IsValid())
				{
					ZDOID zDOID = View.GetZDO().GetZDOID("CTA.Animal");
					return !((ZDOID)(ref zDOID)).IsNone();
				}
				return false;
			}
		}

		internal bool IsConnected
		{
			get
			{
				//IL_005f: Unknown result type (might be due to invalid IL or missing references)
				//IL_006a: Unknown result type (might be due to invalid IL or missing references)
				if (HasLink && Object.op_Implicit((Object)(object)_joint) && Object.op_Implicit((Object)(object)_anchor) && (Object)(object)((Joint)_joint).connectedBody == (Object)(object)_anchor && Object.op_Implicit((Object)(object)_animal) && _animal.CanPull)
				{
					return Vector3.Distance(Cart.m_attachPoint.position, _animal.HitchPosition) < Plugin.Settings.BreakDistance;
				}
				return false;
			}
		}

		internal bool Protects
		{
			get
			{
				if (Plugin.Settings.Protection)
				{
					return IsConnected;
				}
				return false;
			}
		}

		internal bool IsOurAnchor(GameObject go)
		{
			if (Object.op_Implicit((Object)(object)_anchor))
			{
				return (Object)(object)go == (Object)(object)((Component)_anchor).gameObject;
			}
			return false;
		}

		private void Awake()
		{
			Cart = ((Component)this).GetComponent<Vagon>();
			View = ((Component)this).GetComponent<ZNetView>();
			if (!Object.op_Implicit((Object)(object)Cart) || !Object.op_Implicit((Object)(object)View) || !View.IsValid())
			{
				((Behaviour)this).enabled = false;
				return;
			}
			Instances.Add(this);
			View.Register<ZDOID, ZDOID>("CTA.Connect", (Action<long, ZDOID, ZDOID>)Connect);
			View.Register<ZDOID>("CTA.Detach", (Action<long, ZDOID>)DetachRequest);
			View.Register<string>("CTA.Message", (Action<long, string>)ReceiveMessage);
		}

		internal static CartHarness FindFor(ZDOID animalId)
		{
			//IL_0035: 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)
			foreach (CartHarness instance in Instances)
			{
				if (Object.op_Implicit((Object)(object)instance) && instance.HasLink && instance.View.GetZDO().GetZDOID("CTA.Animal") == animalId)
				{
					return instance;
				}
			}
			return null;
		}

		internal void RequestDetach(Player player)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			View.InvokeRPC("CTA.Detach", new object[1] { ((Character)player).GetZDOID() });
		}

		internal void Reply(long peer, string message)
		{
			View.InvokeRPC(peer, "CTA.Message", new object[1] { message });
		}

		private void ReceiveMessage(long sender, string message)
		{
			if (message.Length <= 240)
			{
				Plugin.Say(message);
			}
		}

		private void Connect(long sender, ZDOID animalId, ZDOID playerId)
		{
			//IL_0020: 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_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			if (!View.IsValid() || !View.IsOwner())
			{
				return;
			}
			GameObject val = ZNetScene.instance.FindInstance(animalId);
			AnimalCompanion animalCompanion = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<AnimalCompanion>() : null);
			GameObject val2 = ZNetScene.instance.FindInstance(playerId);
			Player val3 = (Object.op_Implicit((Object)(object)val2) ? val2.GetComponent<Player>() : null);
			if (!Object.op_Implicit((Object)(object)animalCompanion) || !animalCompanion.View.IsValid() || animalCompanion.Character.GetOwner() != sender)
			{
				return;
			}
			if (!Object.op_Implicit((Object)(object)val3) || ((Character)val3).IsDead() || ((Character)val3).IsTeleporting() || !animalCompanion.CanPull || HasLink || Cart.InUse() || (Object)(object)FindFor(animalId) != (Object)null || Vector3.Distance(((Component)val3).transform.position, ((Component)this).transform.position) > 10f || Vector3.Distance(((Component)val3).transform.position, ((Component)animalCompanion).transform.position) > 10f || Vector3.Distance(Cart.m_attachPoint.position, animalCompanion.HitchPosition) > Plugin.Settings.HitchRange)
			{
				animalCompanion.View.InvokeRPC("CTA.ReleaseCart", new object[1] { View.GetZDO().m_uid });
				if (Object.op_Implicit((Object)(object)val3))
				{
					Reply(((Character)val3).GetOwner(), "The cart could not connect. Move closer or detach its existing user.");
				}
			}
			else
			{
				View.GetZDO().Set("CTA.Animal", animalId);
				View.GetZDO().Set("CTA.Driver", playerId);
				animalCompanion.View.InvokeRPC("CTA.Follow", new object[2]
				{
					View.GetZDO().m_uid,
					playerId
				});
				Reply(((Character)val3).GetOwner(), "Cart hitched. Your animal will follow; pet it to toggle follow/stay.");
			}
		}

		private void DetachRequest(long sender, ZDOID playerId)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			if (View.IsValid() && View.IsOwner() && Object.op_Implicit((Object)(object)GameApi.Requester(sender, playerId, ((Component)this).transform.position)))
			{
				Disconnect();
				Reply(sender, "Cart detached. Normal cart damage is restored.");
			}
		}

		private void FixedUpdate()
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_023e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)View) || !View.IsValid())
			{
				ClearLocal();
				return;
			}
			if (!HasLink)
			{
				ClearLocal();
				return;
			}
			ZDOID zDOID = View.GetZDO().GetZDOID("CTA.Animal");
			if (!Object.op_Implicit((Object)(object)_animal) || _animal.Character.GetZDOID() != zDOID)
			{
				GameObject val = ZNetScene.instance.FindInstance(zDOID);
				_animal = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<AnimalCompanion>() : null);
			}
			if (!View.IsOwner())
			{
				ClearLocal();
				return;
			}
			if (!Object.op_Implicit((Object)(object)_animal))
			{
				ClearLocal();
				if (_missingSince == 0f)
				{
					_missingSince = Time.time;
				}
				if (Time.time - _missingSince > 5f)
				{
					Disconnect();
				}
				return;
			}
			_missingSince = 0f;
			GameObject val2 = ZNetScene.instance.FindInstance(View.GetZDO().GetZDOID("CTA.Driver"));
			Player val3 = (Object.op_Implicit((Object)(object)val2) ? val2.GetComponent<Player>() : null);
			if (!_animal.CanPull || (Object.op_Implicit((Object)(object)val3) && (((Character)val3).IsDead() || ((Character)val3).IsTeleporting() || Vector3.Distance(((Component)val3).transform.position, ((Component)_animal).transform.position) > 64f)) || (!Object.op_Implicit((Object)(object)val3) && !TameHasRider()) || Vector3.Distance(Cart.m_attachPoint.position, _animal.HitchPosition) > Plugin.Settings.BreakDistance || ((Component)this).transform.up.y < 0.1f)
			{
				Disconnect();
				return;
			}
			long owner = _animal.Character.GetOwner();
			if (owner != 0L && owner != View.GetZDO().GetOwner())
			{
				ClearLocal();
				View.GetZDO().SetOwner(owner);
				return;
			}
			if (!Object.op_Implicit((Object)(object)_joint))
			{
				CreateJoint();
			}
			if (Object.op_Implicit((Object)(object)_anchor))
			{
				_anchor.MovePosition(_animal.HitchPosition);
				_anchor.MoveRotation(((Component)_animal).transform.rotation);
			}
		}

		private bool TameHasRider()
		{
			if (Object.op_Implicit((Object)(object)_animal) && Object.op_Implicit((Object)(object)_animal.Tame))
			{
				return _animal.Tame.HaveRider();
			}
			return false;
		}

		private void CreateJoint()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			//IL_002b: 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_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: 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_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)_animal))
			{
				return;
			}
			ClearLocal();
			GameObject val = new GameObject("CTA_CartHitch");
			val.transform.SetPositionAndRotation(_animal.HitchPosition, ((Component)_animal).transform.rotation);
			_anchor = val.AddComponent<Rigidbody>();
			_anchor.isKinematic = true;
			_anchor.useGravity = false;
			_joint = ((Component)this).gameObject.AddComponent<ConfigurableJoint>();
			((Joint)_joint).autoConfigureConnectedAnchor = false;
			((Joint)_joint).anchor = ((Component)this).transform.InverseTransformPoint(Cart.m_attachPoint.position);
			((Joint)_joint).connectedBody = _anchor;
			((Joint)_joint).connectedAnchor = Vector3.zero;
			_joint.xMotion = (ConfigurableJointMotion)1;
			_joint.yMotion = (ConfigurableJointMotion)1;
			_joint.zMotion = (ConfigurableJointMotion)1;
			ConfigurableJoint joint = _joint;
			SoftJointLimit linearLimit = default(SoftJointLimit);
			((SoftJointLimit)(ref linearLimit)).limit = 0.15f;
			joint.linearLimit = linearLimit;
			ConfigurableJoint joint2 = _joint;
			SoftJointLimitSpring linearLimitSpring = default(SoftJointLimitSpring);
			((SoftJointLimitSpring)(ref linearLimitSpring)).spring = Cart.m_spring;
			((SoftJointLimitSpring)(ref linearLimitSpring)).damper = Cart.m_springDamping;
			joint2.linearLimitSpring = linearLimitSpring;
			((Joint)_joint).breakForce = float.PositiveInfinity;
			GameApi.CartJoint.Invoke(Cart) = _joint;
			GameApi.CartObject.Invoke(Cart) = ((Component)_animal).gameObject;
			View.GetZDO().Set(ZDOVars.s_attachJointHash, true);
			Collider[] componentsInChildren = ((Component)this).GetComponentsInChildren<Collider>();
			foreach (Collider val2 in componentsInChildren)
			{
				Collider[] componentsInChildren2 = ((Component)_animal).GetComponentsInChildren<Collider>();
				foreach (Collider val3 in componentsInChildren2)
				{
					if (!val2.isTrigger && !val3.isTrigger && !Physics.GetIgnoreCollision(val2, val3))
					{
						Physics.IgnoreCollision(val2, val3, true);
						_ignored.Add((val2, val3));
					}
				}
			}
		}

		internal void Disconnect()
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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_0070: 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_00e6: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)View) && View.IsValid() && View.IsOwner())
			{
				ZDOID zDOID = View.GetZDO().GetZDOID("CTA.Animal");
				View.GetZDO().Set("CTA.Animal", ZDOID.None);
				View.GetZDO().Set("CTA.Driver", ZDOID.None);
				View.GetZDO().Set(ZDOVars.s_attachJointHash, false);
				GameObject val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.FindInstance(zDOID) : null);
				ZNetView val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<ZNetView>() : null);
				if (Object.op_Implicit((Object)(object)val2) && val2.IsValid())
				{
					val2.InvokeRPC("CTA.ReleaseCart", new object[1] { View.GetZDO().m_uid });
				}
			}
			ClearLocal();
		}

		private void ClearLocal()
		{
			if (Object.op_Implicit((Object)(object)_joint))
			{
				if (Object.op_Implicit((Object)(object)Cart) && (Object)(object)GameApi.CartJoint.Invoke(Cart) == (Object)(object)_joint)
				{
					GameApi.CartJoint.Invoke(Cart) = null;
					GameApi.CartObject.Invoke(Cart) = null;
				}
				Object.Destroy((Object)(object)_joint);
			}
			_joint = null;
			if (Object.op_Implicit((Object)(object)_anchor))
			{
				Object.Destroy((Object)(object)((Component)_anchor).gameObject);
			}
			_anchor = null;
			foreach (var item in _ignored)
			{
				if (Object.op_Implicit((Object)(object)item.cart) && Object.op_Implicit((Object)(object)item.animal))
				{
					Physics.IgnoreCollision(item.cart, item.animal, false);
				}
			}
			_ignored.Clear();
		}

		private void OnDestroy()
		{
			ClearLocal();
			Instances.Remove(this);
		}
	}
	internal static class CreatureRules
	{
		internal const string ExtraDefaults = "Neck,Greyling,Greydwarf,Greydwarf_Elite,Troll,Ulv,Fenring";

		internal const string WorkingDefaults = "Boar,Wolf,Lox,Neck,Greyling,Greydwarf,Greydwarf_Elite,Troll,Ulv,Fenring";

		internal static HashSet<string> Names(string value)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			string[] array = (value ?? "").Split(',');
			foreach (string text in array)
			{
				if (text.Trim().Length > 0)
				{
					hashSet.Add(text.Trim());
				}
			}
			return hashSet;
		}

		internal static Dictionary<string, string[]> Foods(string value, Action<string> warn)
		{
			Dictionary<string, string[]> dictionary = new Dictionary<string, string[]>(StringComparer.Ordinal);
			string[] array = (value ?? "").Split(';');
			foreach (string text in array)
			{
				if (!string.IsNullOrWhiteSpace(text))
				{
					string[] array2 = text.Split('=');
					if (array2.Length != 2 || array2[0].Trim().Length == 0 || Names(array2[1]).Count == 0)
					{
						warn("Invalid food rule: " + text);
					}
					else
					{
						dictionary[array2[0].Trim()] = new List<string>(Names(array2[1])).ToArray();
					}
				}
			}
			return dictionary;
		}

		internal static Dictionary<string, float[]> Offsets(string value, Action<string> warn)
		{
			Dictionary<string, float[]> dictionary = new Dictionary<string, float[]>(StringComparer.Ordinal);
			string[] array = (value ?? "").Split(';');
			foreach (string text in array)
			{
				if (!string.IsNullOrWhiteSpace(text))
				{
					string[] array2 = text.Split('=');
					string[] array3 = ((array2.Length == 2) ? array2[1].Split(',') : Array.Empty<string>());
					float[] array4 = new float[3];
					bool flag = array2.Length == 2 && array2[0].Trim().Length > 0 && array3.Length == 3;
					int num = 0;
					while (flag && num < 3)
					{
						flag = float.TryParse(array3[num], NumberStyles.Float, CultureInfo.InvariantCulture, out array4[num]) && !float.IsNaN(array4[num]) && !float.IsInfinity(array4[num]) && Math.Abs(array4[num]) <= 15f;
						num++;
					}
					if (flag)
					{
						dictionary[array2[0].Trim()] = array4;
					}
					else
					{
						warn("Invalid riding offset: " + text);
					}
				}
			}
			return dictionary;
		}
	}
	[HarmonyPatch(typeof(MonsterAI), "Start")]
	internal static class CreatureStartPatch
	{
		[HarmonyPostfix]
		private static void Postfix(MonsterAI __instance)
		{
			AnimalCompanion.AddTo(__instance);
		}
	}
	[HarmonyPatch(typeof(MonsterAI), "MakeTame")]
	internal static class TamedPersistencePatch
	{
		[HarmonyPostfix]
		private static void Postfix(MonsterAI __instance)
		{
			AnimalCompanion component = ((Component)__instance).GetComponent<AnimalCompanion>();
			if (Object.op_Implicit((Object)(object)component) && component.View.IsOwner())
			{
				((BaseAI)__instance).SetHuntPlayer(false);
				__instance.SetDespawnInDay(false);
				__instance.SetEventCreature(false);
			}
		}
	}
	[HarmonyPatch(typeof(MonsterAI), "UpdateTarget")]
	internal static class HaulingTargetPatch
	{
		[HarmonyPostfix]
		private static void Postfix(MonsterAI __instance)
		{
			AnimalCompanion component = ((Component)__instance).GetComponent<AnimalCompanion>();
			if (Object.op_Implicit((Object)(object)component) && component.Character.IsTamed() && (component.IsHauling || component.Tame.HaveRider()))
			{
				GameApi.TargetCreature.Invoke(__instance) = null;
				GameApi.TargetStatic.Invoke(__instance) = null;
			}
		}
	}
	[HarmonyPatch(typeof(Tameable), "GetHoverText")]
	internal static class CreatureHoverPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Tameable __instance, ref string __result)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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_0078: Unknown result type (might be due to invalid IL or missing references)
			AnimalCompanion component = ((Component)__instance).GetComponent<AnimalCompanion>();
			if (Object.op_Implicit((Object)(object)component) && component.Character.IsTamed())
			{
				__result += "\nPet to toggle follow / stay";
				if (component.CanRide)
				{
					__result = __result + "\n[<color=yellow>" + ((object)Plugin.Settings.RideKey/*cast due to .constrained prefix*/).ToString() + "</color>] Ride";
				}
				if (component.CanPull)
				{
					__result = __result + "\n[<color=yellow>" + ((object)Plugin.Settings.HitchKey/*cast due to .constrained prefix*/).ToString() + "</color>] Select for cart";
				}
			}
		}
	}
	[HarmonyPatch(typeof(Sadle), "UpdateRiding")]
	internal static class BarebackStopPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Sadle __instance, ref bool __result)
		{
			AnimalCompanion componentInParent = ((Component)__instance).GetComponentInParent<AnimalCompanion>();
			if (!__result && Object.op_Implicit((Object)(object)componentInParent) && componentInParent.OwnSaddle && componentInParent.Character.IsTamed() && __instance.HaveValidUser())
			{
				((BaseAI)componentInParent.Ai).StopMoving();
				__result = true;
			}
		}
	}
	[HarmonyPatch(typeof(Tameable), "DropSaddle")]
	internal static class BarebackDropPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(Tameable __instance, ref bool __result)
		{
			AnimalCompanion component = ((Component)__instance).GetComponent<AnimalCompanion>();
			if (!Object.op_Implicit((Object)(object)component) || !component.OwnSaddle)
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(Vagon), "Awake")]
	internal static class CartAwakePatch
	{
		[HarmonyPostfix]
		private static void Postfix(Vagon __instance)
		{
			if (Plugin.Settings.Carts && !Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<CartHarness>()))
			{
				ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
				if (Object.op_Implicit((Object)(object)component) && component.IsValid())
				{
					((Component)__instance).gameObject.AddComponent<CartHarness>();
				}
			}
		}
	}
	[HarmonyPatch(typeof(Vagon), "GetHoverText")]
	internal static class CartHoverPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Vagon __instance, ref string __result)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			CartHarness component = ((Component)__instance).GetComponent<CartHarness>();
			if (Object.op_Implicit((Object)(object)component))
			{
				__result = __result + "\n[<color=yellow>" + ((object)Plugin.Settings.HitchKey/*cast due to .constrained prefix*/).ToString() + "</color>] " + (component.HasLink ? "Detach animal" : "Hitch selected animal");
			}
		}
	}
	[HarmonyPatch(typeof(Vagon), "Interact")]
	internal static class CartInteractPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(Vagon __instance, Humanoid character, bool hold, ref bool __result)
		{
			CartHarness component = ((Component)__instance).GetComponent<CartHarness>();
			if (!Object.op_Implicit((Object)(object)component) || !component.HasLink)
			{
				return true;
			}
			if (!hold)
			{
				Player val = (Player)(object)((character is Player) ? character : null);
				if (val != null)
				{
					component.RequestDetach(val);
				}
			}
			__result = !hold;
			return false;
		}
	}
	[HarmonyPatch(typeof(Vagon), "InUse")]
	internal static class CartInUsePatch
	{
		[HarmonyPostfix]
		private static void Postfix(Vagon __instance, ref bool __result)
		{
			CartHarness component = ((Component)__instance).GetComponent<CartHarness>();
			__result |= Object.op_Implicit((Object)(object)component) && component.HasLink;
		}
	}
	[HarmonyPatch(typeof(Vagon), "CanAttach")]
	internal static class CartCanAttachPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(Vagon __instance, GameObject go, ref bool __result)
		{
			CartHarness component = ((Component)__instance).GetComponent<CartHarness>();
			if (!Object.op_Implicit((Object)(object)component) || !component.IsOurAnchor(go))
			{
				return true;
			}
			__result = component.IsConnected;
			return false;
		}
	}
	[HarmonyPatch(typeof(Vagon), "Detach")]
	internal static class CartDetachPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(Vagon __instance)
		{
			CartHarness component = ((Component)__instance).GetComponent<CartHarness>();
			if (Object.op_Implicit((Object)(object)component))
			{
				return !component.HasLink;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Vagon), "LateUpdate")]
	internal static class CartRopePatch
	{
		[HarmonyPostfix]
		private static void Postfix(Vagon __instance, LineRenderer ___m_lineRenderer)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			CartHarness component = ((Component)__instance).GetComponent<CartHarness>();
			if (Object.op_Implicit((Object)(object)component) && component.HasLink && Object.op_Implicit((Object)(object)___m_lineRenderer))
			{
				GameObject val = ZNetScene.instance.FindInstance(component.View.GetZDO().GetZDOID("CTA.Animal"));
				AnimalCompanion animalCompanion = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<AnimalCompanion>() : null);
				if (Object.op_Implicit((Object)(object)animalCompanion) && animalCompanion.CanPull && Object.op_Implicit((Object)(object)__instance.m_lineAttachPoints0) && Object.op_Implicit((Object)(object)__instance.m_lineAttachPoints1))
				{
					((Renderer)___m_lineRenderer).enabled = true;
					___m_lineRenderer.SetPosition(0, __instance.m_lineAttachPoints0.position);
					___m_lineRenderer.SetPosition(1, animalCompanion.HitchPosition);
					___m_lineRenderer.SetPosition(2, __instance.m_lineAttachPoints1.position);
				}
			}
		}
	}
	[HarmonyPatch(typeof(WearNTear), "ApplyDamage")]
	internal static class AttachedCartDamagePatch
	{
		[HarmonyPrefix]
		private static bool Prefix(WearNTear __instance, ref bool __result)
		{
			CartHarness component = ((Component)__instance).GetComponent<CartHarness>();
			if (!Object.op_Implicit((Object)(object)component) || !component.Protects)
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[BepInPlugin("com.computer.tamedanimals", "Computer-TamedAnimals", "0.1.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Id = "com.computer.tamedanimals";

		public const string Name = "Computer-TamedAnimals";

		public const string Version = "0.1.0";

		internal static Settings Settings;

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private AnimalCompanion _selected;

		private float _selectionExpires;

		private void Awake()
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: 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_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Settings = new Settings(((BaseUnityPlugin)this).Config);
			if (!Settings.Enabled)
			{
				return;
			}
			try
			{
				RuntimeHelpers.RunClassConstructor(typeof(GameApi).TypeHandle);
				_harmony = new Harmony("com.computer.tamedanimals");
				_harmony.PatchAll(typeof(Plugin).Assembly);
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Computer-TamedAnimals 0.1.0 loaded. Ride: " + ((object)Settings.RideKey/*cast due to .constrained prefix*/).ToString() + "; hitch: " + ((object)Settings.HitchKey/*cast due to .constrained prefix*/).ToString()));
			}
			catch (Exception ex)
			{
				Harmony harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
				((BaseUnityPlugin)this).Logger.LogError((object)("Could not patch this Valheim version; mod disabled. " + ex));
				((Behaviour)this).enabled = false;
			}
		}

		private unsafe void Update()
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (!Settings.Enabled || !Object.op_Implicit((Object)(object)localPlayer) || !GameApi.TakeInput(localPlayer))
			{
				return;
			}
			if (Object.op_Implicit((Object)(object)_selected) && Time.time > _selectionExpires)
			{
				_selected = null;
			}
			KeyboardShortcut val;
			if (Settings.Riding)
			{
				val = Settings.RideKey;
				if (((KeyboardShortcut)(ref val)).IsDown())
				{
					if (localPlayer.GetDoodadController() is Sadle)
					{
						localPlayer.StopDoodadControl();
						return;
					}
					if (((Character)localPlayer).IsAttached() || localPlayer.GetDoodadController() != null)
					{
						return;
					}
					GameObject hoverObject = ((Humanoid)localPlayer).GetHoverObject();
					AnimalCompanion animalCompanion = (Object.op_Implicit((Object)(object)hoverObject) ? hoverObject.GetComponentInParent<AnimalCompanion>() : null);
					if (Object.op_Implicit((Object)(object)animalCompanion) && animalCompanion.CanRide && Vector3.Distance(((Component)localPlayer).transform.position, ((Component)animalCompanion).transform.position) <= 8f)
					{
						animalCompanion.Tame.m_saddle.Interact((Humanoid)(object)localPlayer, false, false);
					}
					else
					{
						Say("Look at a nearby tamed, rideable creature.");
					}
				}
			}
			if (!Settings.Carts)
			{
				return;
			}
			val = Settings.HitchKey;
			if (!((KeyboardShortcut)(ref val)).IsDown() || ((Character)localPlayer).IsAttached())
			{
				return;
			}
			GameObject hoverObject2 = ((Humanoid)localPlayer).GetHoverObject();
			if (!Object.op_Implicit((Object)(object)hoverObject2))
			{
				_selected = null;
				Say("Look at a tame, then the cart shafts.");
				return;
			}
			CartHarness componentInParent = hoverObject2.GetComponentInParent<CartHarness>();
			if (Object.op_Implicit((Object)(object)componentInParent))
			{
				if (componentInParent.HasLink)
				{
					componentInParent.RequestDetach(localPlayer);
				}
				else if (Object.op_Implicit((Object)(object)_selected) && _selected.CanPull)
				{
					_selected.RequestCart(componentInParent, localPlayer);
				}
				else
				{
					val = Settings.HitchKey;
					Say("First select a tamed creature with " + ((object)(*(KeyboardShortcut*)(&val))/*cast due to .constrained prefix*/).ToString() + ".");
				}
				_selected = null;
				return;
			}
			AnimalCompanion componentInParent2 = hoverObject2.GetComponentInParent<AnimalCompanion>();
			if (!Object.op_Implicit((Object)(object)componentInParent2) || !componentInParent2.CanPull)
			{
				Say("This creature cannot pull a cart.");
				return;
			}
			_selected = componentInParent2;
			_selectionExpires = Time.time + 30f;
			string hoverName = componentInParent2.Tame.GetHoverName();
			val = Settings.HitchKey;
			Say(hoverName + " selected. Look at nearby cart shafts and press " + ((object)(*(KeyboardShortcut*)(&val))/*cast due to .constrained prefix*/).ToString() + ".");
		}

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

		internal static void Warn(string text)
		{
			Log.LogWarning((object)text);
		}

		internal static void Say(string text)
		{
			if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
			{
				((Character)Player.m_localPlayer).Message((MessageType)2, text, 0, (Sprite)null, false);
			}
		}
	}
	internal static class GameApi
	{
		internal static readonly FieldRef<BaseAI, Tameable> AiTame = AccessTools.FieldRefAccess<BaseAI, Tameable>("m_tamable");

		internal static readonly FieldRef<Vagon, ConfigurableJoint> CartJoint = AccessTools.FieldRefAccess<Vagon, ConfigurableJoint>("m_attachJoin");

		internal static readonly FieldRef<Vagon, GameObject> CartObject = AccessTools.FieldRefAccess<Vagon, GameObject>("m_attachedObject");

		internal static readonly FieldRef<MonsterAI, Character> TargetCreature = AccessTools.FieldRefAccess<MonsterAI, Character>("m_targetCreature");

		internal static readonly FieldRef<MonsterAI, StaticTarget> TargetStatic = AccessTools.FieldRefAccess<MonsterAI, StaticTarget>("m_targetStatic");

		internal static readonly Func<Player, bool> TakeInput = AccessTools.MethodDelegate<Func<Player, bool>>(AccessTools.DeclaredMethod(typeof(Player), "TakeInput", (Type[])null, (Type[])null), (object)null, true);

		internal static Player Requester(long sender, ZDOID playerId, Vector3 position, float range = 10f)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = (Object.op_Implicit((Object)(object)ZNetScene.instance) ? ZNetScene.instance.FindInstance(playerId) : null);
			Player val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<Player>() : null);
			if (!Object.op_Implicit((Object)(object)val2) || ((Character)val2).GetOwner() != sender || ((Character)val2).IsDead() || ((Character)val2).IsTeleporting() || !(Vector3.Distance(((Component)val2).transform.position, position) <= range))
			{
				return null;
			}
			return val2;
		}
	}
	internal sealed class Settings
	{
		internal readonly HashSet<string> Extra;

		internal readonly HashSet<string> Riders;

		internal readonly HashSet<string> Pullers;

		internal readonly Dictionary<string, string[]> Foods;

		internal readonly Dictionary<string, float[]> Offsets;

		internal readonly bool Enabled;

		internal readonly bool Riding;

		internal readonly bool Carts;

		internal readonly bool Protection;

		internal readonly float TamingTime;

		internal readonly float FedTime;

		internal readonly float HitchRange;

		internal readonly float BreakDistance;

		internal readonly KeyboardShortcut RideKey;

		internal readonly KeyboardShortcut HitchKey;

		internal Settings(ConfigFile config)
		{
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Expected O, but got Unknown
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Expected O, but got Unknown
			//IL_022b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Expected O, but got Unknown
			//IL_026e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0278: Expected O, but got Unknown
			//IL_029d: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_02da: Unknown result type (might be due to invalid IL or missing references)
			//IL_02df: Unknown result type (might be due to invalid IL or missing references)
			Enabled = Bind<bool>("General", "Enabled", value: true, "Enable the mod. All peers need the same mod and settings.");
			Extra = CreatureRules.Names(Bind<string>("Taming", "AdditionalCreatures", "Neck,Greyling,Greydwarf,Greydwarf_Elite,Troll,Ulv,Fenring", "Comma-separated prefab names. Requires a ground Humanoid with MonsterAI; bosses, flying and aquatic-only creatures are excluded."));
			Foods = CreatureRules.Foods(Bind<string>("Taming", "FoodRules", "Neck=RawMeat,FishRaw;Greyling=Raspberry,Blueberries,Mushroom;Greydwarf=Raspberry,Blueberries,Mushroom;Greydwarf_Elite=Raspberry,Blueberries,Mushroom;Troll=RawMeat,DeerMeat;Ulv=RawMeat,WolfMeat;Fenring=RawMeat,WolfMeat", "Prefab=foodPrefab,foodPrefab;Prefab=... . Used only for newly tameable creatures; existing taming and diets are preserved."), Plugin.Warn);
			Offsets = CreatureRules.Offsets(Bind<string>("Riding", "SeatOffsets", "Boar=0,0.85,-0.1;Wolf=0,1.05,-0.15;Neck=0,0.45,0;Greyling=0,1.2,-0.15;Greydwarf=0,1.5,-0.2;Greydwarf_Elite=0,2,-0.25;Troll=0,4.2,-0.3;Ulv=0,1,-0.15;Fenring=0,1.8,-0.2", "Local seat coordinates (x,y,z), using decimal dots. Unlisted creatures use their capsule collider. Existing saddles keep their own seats."), Plugin.Warn);
			TamingTime = config.Bind<float>("Taming", "TamingSeconds", 1800f, new ConfigDescription("Calm, fed time for added tameables. Restart required.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 86400f), Array.Empty<object>())).Value;
			FedTime = config.Bind<float>("Taming", "FedSeconds", 600f, new ConfigDescription("How long food lasts for added tameables. Restart required.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 86400f), Array.Empty<object>())).Value;
			Riding = Bind<bool>("Riding", "Enabled", value: true, "Enable bareback riding for configured creatures without existing saddles.");
			Riders = CreatureRules.Names(Bind<string>("Riding", "Creatures", "Boar,Wolf,Lox,Neck,Greyling,Greydwarf,Greydwarf_Elite,Troll,Ulv,Fenring", "Rideable prefab names. Existing saddles still require their normal saddle item."));
			Carts = Bind<bool>("Carts", "Enabled", value: true, "Enable attaching carts to tamed creatures.");
			Pullers = CreatureRules.Names(Bind<string>("Carts", "Creatures", "Boar,Wolf,Lox,Neck,Greyling,Greydwarf,Greydwarf_Elite,Troll,Ulv,Fenring", "Prefab names allowed to pull carts."));
			Protection = Bind<bool>("Carts", "PreventAttachedDamage", value: true, "Block cart damage only while its live animal hitch is intact. Does not protect the animal or prevent hammer removal.");
			HitchRange = config.Bind<float>("Carts", "HitchRange", 6f, new ConfigDescription("Maximum distance from cart shafts to animal hitch when connecting. Restart required.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 10f), Array.Empty<object>())).Value;
			BreakDistance = config.Bind<float>("Carts", "BreakDistance", 10f, new ConfigDescription("Disconnect if the cart gets stuck this far behind its hitch. Restart required.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(4f, 20f), Array.Empty<object>())).Value;
			RideKey = Bind<KeyboardShortcut>("Controls", "RideKey", new KeyboardShortcut((KeyCode)114, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), "Look at a tame to mount; press again to dismount. Native riding controls apply.");
			HitchKey = Bind<KeyboardShortcut>("Controls", "HitchKey", new KeyboardShortcut((KeyCode)104, (KeyCode[])(object)new KeyCode[1] { (KeyCode)304 }), "Look at a tame to select it, then at cart shafts to hitch. On a hitched cart, detach instead.");
			T Bind<T>(string section, string key, T value, string description)
			{
				return config.Bind<T>(section, key, value, description + " Restart Valheim after changing this setting.").Value;
			}
		}
	}
}