Decompiled source of BetrTame v0.1.1

plugins/BetrTame.dll

Decompiled 5 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("DezzyCode")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("DezzyCode")]
[assembly: AssemblyDescription("Tame more creatures, let your tames grow in level, stars above two, and tames that are knocked out instead of dying.")]
[assembly: AssemblyFileVersion("0.1.1.0")]
[assembly: AssemblyInformationalVersion("0.1.1+085c0f1cee5fe3a31f48c04175c5dd7e756a38f3")]
[assembly: AssemblyProduct("BetrTame")]
[assembly: AssemblyTitle("BetrTame")]
[assembly: AssemblyVersion("0.1.1.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 BetrTame
{
	internal static class Animals
	{
		private sealed class State
		{
			public Character Character;

			public Tameable Tame;

			public ZNetView View;

			public int Prefab;

			public ItemDrop Food;

			public float SearchTimer;

			public GameObject Follow;

			public float FollowCheck;

			public float Danger;
		}

		[HarmonyPatch(typeof(AnimalAI), "UpdateAI")]
		private static class Think
		{
			private static bool Prefix(AnimalAI __instance, float dt, ref bool __result)
			{
				if (!Active((BaseAI)(object)__instance, out var state, out var food))
				{
					return true;
				}
				if (!state.Character.IsTamed())
				{
					if (!state.View.IsOwner() || ((BaseAI)__instance).IsAlerted() || !state.Tame.IsHungry())
					{
						return true;
					}
					if ((Object)(object)state.Food == (Object)null && !Search((BaseAI)(object)__instance, state, food, dt))
					{
						return true;
					}
					__result = BaseUpdate((BaseAI)(object)__instance, dt);
					if (__result)
					{
						Eat((BaseAI)(object)__instance, state, dt);
					}
					return false;
				}
				__result = BaseUpdate((BaseAI)(object)__instance, dt);
				if (!__result)
				{
					return false;
				}
				CalmDown(__instance, state, dt);
				if (Knockout.IsDown(state.Character))
				{
					((BaseAI)__instance).StopMoving();
					return false;
				}
				if (state.Tame.IsHungry() && ((Object)(object)state.Food != (Object)null || Search((BaseAI)(object)__instance, state, food, dt)))
				{
					Eat((BaseAI)(object)__instance, state, dt);
					return false;
				}
				KeepFollowing(state);
				if ((Object)(object)state.Follow != (Object)null)
				{
					FollowTo((BaseAI)(object)__instance, state.Follow, dt);
				}
				else
				{
					Idle((BaseAI)(object)__instance, dt);
				}
				return false;
			}
		}

		[HarmonyPatch(typeof(Tameable), "TamingUpdate")]
		private static class Count
		{
			private static void Postfix(Tameable __instance)
			{
				//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
				//IL_0105: Unknown result type (might be due to invalid IL or missing references)
				//IL_0113: 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_0127: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b3: 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_00cc: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
				if (Live.Count == 0 || (Object)(object)MonsterAiOf.Invoke(__instance) != (Object)null)
				{
					return;
				}
				Character component = ((Component)__instance).GetComponent<Character>();
				BaseAI val = (((Object)(object)component != (Object)null) ? component.GetBaseAI() : null);
				if ((Object)(object)val == (Object)null || !Active(val, out var state, out var _) || !state.View.IsValid() || !state.View.IsOwner() || component.IsTamed() || __instance.IsHungry() || val.IsAlerted())
				{
					return;
				}
				DecreaseRemainingTime(__instance, 3f);
				if (RemainingTime(__instance) > 0f)
				{
					__instance.m_sootheEffect.Create(((Component)__instance).transform.position, ((Component)__instance).transform.rotation, (Transform)null, 1f, -1, default(ZDOID));
					return;
				}
				component.SetTamed(true);
				SetAlerted(val, arg2: false);
				__instance.m_tamedEffect.Create(((Component)__instance).transform.position, ((Component)__instance).transform.rotation, (Transform)null, 1f, -1, default(ZDOID));
				Player closestPlayer = Player.GetClosestPlayer(((Component)__instance).transform.position, 30f);
				if ((Object)(object)closestPlayer != (Object)null)
				{
					((Character)closestPlayer).Message((MessageType)2, component.m_name + " $hud_tamedone", 0, (Sprite)null, false);
				}
			}
		}

		[HarmonyPatch(typeof(Tameable), "RPC_Command")]
		private static class Command
		{
			private static bool Prefix(Tameable __instance, ZDOID characterID, bool message)
			{
				//IL_005b: Unknown result type (might be due to invalid IL or missing references)
				if (Live.Count == 0 || (Object)(object)MonsterAiOf.Invoke(__instance) != (Object)null)
				{
					return true;
				}
				Character component = ((Component)__instance).GetComponent<Character>();
				BaseAI val = (((Object)(object)component != (Object)null) ? component.GetBaseAI() : null);
				if ((Object)(object)val == (Object)null || !Live.TryGetValue(val, out var value))
				{
					return true;
				}
				GameObject val2 = ZNetScene.instance.FindInstance(characterID);
				Player val3 = (((Object)(object)val2 != (Object)null) ? val2.GetComponent<Player>() : null);
				if ((Object)(object)val3 == (Object)null)
				{
					return false;
				}
				bool flag = value.View.IsOwner();
				if ((Object)(object)value.Follow != (Object)null)
				{
					value.Follow = null;
					if (flag)
					{
						value.View.GetZDO().Set(ZDOVars.s_follow, "");
					}
					if (message)
					{
						((Character)val3).Message((MessageType)2, __instance.GetHoverName() + " $hud_tamestay", 0, (Sprite)null, false);
					}
				}
				else
				{
					value.Follow = ((Component)val3).gameObject;
					if (flag)
					{
						value.View.GetZDO().Set(ZDOVars.s_follow, val3.GetPlayerName());
					}
					if (message)
					{
						((Character)val3).Message((MessageType)2, __instance.GetHoverName() + " $hud_tamefollow", 0, (Sprite)null, false);
					}
				}
				return false;
			}
		}

		private static readonly Dictionary<int, List<ItemDrop>> FoodByPrefab = new Dictionary<int, List<ItemDrop>>();

		private static readonly Dictionary<BaseAI, State> Live = new Dictionary<BaseAI, State>();

		private const float SearchInterval = 10f;

		private const float SearchRange = 10f;

		private const float EatRange = 1.5f;

		private static readonly Collider[] Hits = (Collider[])(object)new Collider[64];

		private static int _itemMask;

		private static readonly Func<BaseAI, float, bool> BaseUpdate = AccessTools.MethodDelegate<Func<BaseAI, float, bool>>(AccessTools.Method(typeof(BaseAI), "UpdateAI", (Type[])null, (Type[])null), (object)null, false);

		private static readonly Action<BaseAI, GameObject, float> FollowTo = AccessTools.MethodDelegate<Action<BaseAI, GameObject, float>>(AccessTools.Method(typeof(BaseAI), "Follow", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Action<BaseAI, float> Idle = AccessTools.MethodDelegate<Action<BaseAI, float>>(AccessTools.Method(typeof(BaseAI), "IdleMovement", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Func<BaseAI, float, Vector3, float, bool, bool> MoveTo = AccessTools.MethodDelegate<Func<BaseAI, float, Vector3, float, bool, bool>>(AccessTools.Method(typeof(BaseAI), "MoveTo", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Action<BaseAI, Vector3> LookAt = AccessTools.MethodDelegate<Action<BaseAI, Vector3>>(AccessTools.Method(typeof(BaseAI), "LookAt", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Func<BaseAI, Vector3, bool> HavePath = AccessTools.MethodDelegate<Func<BaseAI, Vector3, bool>>(AccessTools.Method(typeof(BaseAI), "HavePath", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Action<BaseAI, bool> SetAlerted = AccessTools.MethodDelegate<Action<BaseAI, bool>>(AccessTools.Method(typeof(BaseAI), "SetAlerted", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Action<Tameable, float> DecreaseRemainingTime = AccessTools.MethodDelegate<Action<Tameable, float>>(AccessTools.Method(typeof(Tameable), "DecreaseRemainingTime", (Type[])null, (Type[])null), (object)null, true);

		private static readonly Func<Tameable, float> RemainingTime = AccessTools.MethodDelegate<Func<Tameable, float>>(AccessTools.Method(typeof(Tameable), "GetRemainingTime", (Type[])null, (Type[])null), (object)null, true);

		private static readonly FieldRef<Tameable, MonsterAI> MonsterAiOf = AccessTools.FieldRefAccess<Tameable, MonsterAI>("m_monsterAI");

		public static void ClearFood()
		{
			FoodByPrefab.Clear();
		}

		public static void SetFood(string prefab, List<ItemDrop> food)
		{
			FoodByPrefab[StringExtensionMethods.GetStableHashCode(prefab)] = food;
		}

		public static void Register(Character character, ZNetView view)
		{
			BaseAI baseAI = character.GetBaseAI();
			AnimalAI val = (AnimalAI)(object)((baseAI is AnimalAI) ? baseAI : null);
			if (val != null)
			{
				Live[(BaseAI)(object)val] = new State
				{
					Character = character,
					Tame = ((Component)character).GetComponent<Tameable>(),
					View = view,
					Prefab = view.GetZDO().GetPrefab(),
					SearchTimer = Random.Range(0f, 10f)
				};
			}
		}

		public static void Forget(Character character)
		{
			BaseAI baseAI = character.GetBaseAI();
			if ((Object)(object)baseAI != (Object)null)
			{
				Live.Remove(baseAI);
			}
		}

		public static GameObject FollowTarget(BaseAI ai)
		{
			if (Live.Count <= 0 || !Live.TryGetValue(ai, out var value))
			{
				return null;
			}
			return value.Follow;
		}

		private static bool Active(BaseAI ai, out State state, out List<ItemDrop> food)
		{
			food = null;
			state = null;
			if (Live.Count > 0 && Live.TryGetValue(ai, out state) && (Object)(object)state.Tame != (Object)null && FoodByPrefab.TryGetValue(state.Prefab, out food))
			{
				return food != null;
			}
			return false;
		}

		private static void CalmDown(AnimalAI ai, State state, float dt)
		{
			if (!((BaseAI)ai).IsAlerted())
			{
				state.Danger = 0f;
				return;
			}
			state.Danger += dt;
			if (!(state.Danger <= ai.m_timeToSafe))
			{
				state.Danger = 0f;
				SetAlerted((BaseAI)(object)ai, arg2: false);
			}
		}

		private static bool Search(BaseAI ai, State state, List<ItemDrop> food, float dt)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: 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_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: 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)
			state.SearchTimer += dt;
			if (state.SearchTimer < 10f)
			{
				return false;
			}
			state.SearchTimer = 0f;
			if (_itemMask == 0)
			{
				_itemMask = LayerMask.GetMask(new string[1] { "item" });
			}
			Vector3 position = ((Component)ai).transform.position;
			int num = Physics.OverlapSphereNonAlloc(position, 10f, Hits, _itemMask);
			ItemDrop val = null;
			float num2 = float.MaxValue;
			for (int i = 0; i < num; i++)
			{
				Rigidbody attachedRigidbody = Hits[i].attachedRigidbody;
				ItemDrop val2 = (((Object)(object)attachedRigidbody != (Object)null) ? ((Component)attachedRigidbody).GetComponent<ItemDrop>() : null);
				if ((Object)(object)val2 == (Object)null || !Eats(food, val2))
				{
					continue;
				}
				ZNetView component = ((Component)val2).GetComponent<ZNetView>();
				if (!((Object)(object)component == (Object)null) && component.IsValid())
				{
					Vector3 val3 = ((Component)val2).transform.position - position;
					float sqrMagnitude = ((Vector3)(ref val3)).sqrMagnitude;
					if (sqrMagnitude < num2)
					{
						val = val2;
						num2 = sqrMagnitude;
					}
				}
			}
			if ((Object)(object)val != (Object)null && HavePath(ai, ((Component)val).transform.position))
			{
				state.Food = val;
			}
			return (Object)(object)state.Food != (Object)null;
		}

		private static bool Eats(List<ItemDrop> food, ItemDrop item)
		{
			string name = item.m_itemData.m_shared.m_name;
			foreach (ItemDrop item2 in food)
			{
				if ((Object)(object)item2 != (Object)null && item2.m_itemData.m_shared.m_name == name)
				{
					return true;
				}
			}
			return false;
		}

		private static void Eat(BaseAI ai, State state, float dt)
		{
			//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_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_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: 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_00af: Unknown result type (might be due to invalid IL or missing references)
			ItemDrop food = state.Food;
			if ((Object)(object)food == (Object)null)
			{
				state.Food = null;
				return;
			}
			Vector3 position = ((Component)food).transform.position;
			if (!MoveTo(ai, dt, position, 1.5f, arg5: false))
			{
				return;
			}
			LookAt(ai, position);
			if (ai.IsLookingAt(position, 20f, false))
			{
				if (food.RemoveOne())
				{
					state.View.GetZDO().Set(ZDOVars.s_tameLastFeeding, ZNet.instance.GetTime().Ticks);
					state.Tame.m_sootheEffect.Create(state.Character.GetCenterPoint(), Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
				}
				state.Food = null;
			}
		}

		private static void KeepFollowing(State state)
		{
			if ((Object)(object)state.Follow != (Object)null || Time.time < state.FollowCheck)
			{
				return;
			}
			state.FollowCheck = Time.time + 1f;
			string text = state.View.GetZDO().GetString(ZDOVars.s_follow, "");
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if (!(allPlayer.GetPlayerName() != text))
				{
					state.Follow = ((Component)allPlayer).gameObject;
					break;
				}
			}
		}
	}
	internal static class Breeding
	{
		[HarmonyPatch(typeof(Procreation), "Procreate")]
		private static class Count
		{
			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				MethodInfo original = AccessTools.Method(typeof(SpawnSystem), "GetNrOfInstances", new Type[5]
				{
					typeof(GameObject),
					typeof(Vector3),
					typeof(float),
					typeof(bool),
					typeof(bool)
				}, (Type[])null);
				MethodInfo own = AccessTools.Method(typeof(Breeding), "Instances", (Type[])null, (Type[])null);
				int replaced = 0;
				foreach (CodeInstruction code in instructions)
				{
					if (CodeInstructionExtensions.Calls(code, original))
					{
						CodeInstruction val = new CodeInstruction(OpCodes.Ldarg_0, (object)null);
						val.labels.AddRange(code.labels);
						code.labels.Clear();
						yield return val;
						yield return new CodeInstruction(OpCodes.Call, (object)own)
						{
							blocks = code.blocks
						};
						replaced++;
					}
					else
					{
						yield return code;
					}
				}
				if (replaced == 0)
				{
					Log.Warning("Breeding count could not be set up; wild creatures nearby count as in the game.");
				}
			}
		}

		private static int Instances(GameObject prefab, Vector3 center, float range, bool eventOnly, bool procreationOnly, Procreation self)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			if (procreationOnly || eventOnly || (Object)(object)prefab == (Object)null || (Object)(object)self.m_offspring == (Object)null || ((Object)self.m_offspring).name != ((Object)prefab).name || Utils.GetPrefabName(((Component)self).gameObject) != ((Object)prefab).name)
			{
				return SpawnSystem.GetNrOfInstances(prefab, center, range, eventOnly, procreationOnly);
			}
			string text = ((Object)prefab).name + "(Clone)";
			int num = 0;
			foreach (BaseAI baseAIInstance in BaseAI.BaseAIInstances)
			{
				if (!(((Object)((Component)baseAIInstance).gameObject).name != text) && (!(range > 0f) || !(Vector3.Distance(center, ((Component)baseAIInstance).transform.position) > range)))
				{
					Character component = ((Component)baseAIInstance).GetComponent<Character>();
					if ((Object)(object)component != (Object)null && component.IsTamed())
					{
						num++;
					}
				}
			}
			return num;
		}
	}
	internal static class CatchUp
	{
		private const float Interval = 2f;

		private static float _next;

		private static readonly List<Character> Scratch = new List<Character>();

		public static void Tick()
		{
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			if (Time.time < _next || (Object)(object)ZNet.instance == (Object)null || !Settings.CatchUp.Value)
			{
				return;
			}
			_next = Time.time + 2f;
			float value = Settings.CatchUpDistance.Value;
			float num = value * value;
			Scratch.Clear();
			Scratch.AddRange(Character.GetAllCharacters());
			foreach (Character item in Scratch)
			{
				if ((Object)(object)item == (Object)null || item.IsPlayer() || !item.IsTamed() || item.IsDead())
				{
					continue;
				}
				ZNetView component = ((Component)item).GetComponent<ZNetView>();
				if ((Object)(object)component == (Object)null || !component.IsValid() || !component.IsOwner())
				{
					continue;
				}
				GameObject val = Leader(item);
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				Player component2 = val.GetComponent<Player>();
				if ((Object)(object)component2 == (Object)null || ((Character)component2).IsDead() || ((Character)component2).InInterior())
				{
					continue;
				}
				Vector3 position = ((Component)component2).transform.position;
				Vector3 val2 = ((Component)item).transform.position - position;
				if (!(((Vector3)(ref val2)).sqrMagnitude < num) && !Knockout.IsDown(item))
				{
					Tameable component3 = ((Component)item).GetComponent<Tameable>();
					if (!((Object)(object)component3 != (Object)null) || !((Object)(object)component3.m_saddle != (Object)null) || !component3.m_saddle.HaveValidUser())
					{
						Bring(item, component, component2);
					}
				}
			}
			Scratch.Clear();
		}

		private static GameObject Leader(Character tame)
		{
			BaseAI baseAI = tame.GetBaseAI();
			MonsterAI val = (MonsterAI)(object)((baseAI is MonsterAI) ? baseAI : null);
			if (val != null)
			{
				return val.GetFollowTarget();
			}
			if (!((Object)(object)baseAI != (Object)null))
			{
				return null;
			}
			return Animals.FollowTarget(baseAI);
		}

		private static void Bring(Character tame, ZNetView view, Player player)
		{
			//IL_0008: 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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: 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_00bd: 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_00ad: Unknown result type (might be due to invalid IL or missing references)
			Transform transform = ((Component)player).transform;
			Vector3 val = transform.position - transform.forward * (3f + tame.GetRadius());
			float num = default(float);
			if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetSolidHeight(val, ref num, 1000))
			{
				val.y = Mathf.Max(num, transform.position.y - 1f) + 0.2f;
			}
			else
			{
				val.y = transform.position.y + 0.2f;
			}
			((Component)tame).transform.position = val;
			Rigidbody component = ((Component)tame).GetComponent<Rigidbody>();
			if ((Object)(object)component != (Object)null)
			{
				component.position = val;
				component.linearVelocity = Vector3.zero;
			}
			view.GetZDO().SetPosition(val);
		}
	}
	internal static class ComeBack
	{
		[HarmonyPatch(typeof(Game), "Start")]
		private static class Register
		{
			private static void Postfix()
			{
				ZRoutedRpc instance = ZRoutedRpc.instance;
				if (instance != null)
				{
					instance.Register<Vector3>("BetrTame_ComeBack", (Action<long, Vector3>)delegate(long sender, Vector3 at)
					{
						//IL_0001: Unknown result type (might be due to invalid IL or missing references)
						Requested(sender, at);
					});
				}
			}
		}

		[HarmonyPatch(typeof(Player), "OnDeath")]
		private static class Died
		{
			private static void Postfix(Player __instance)
			{
				if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
				{
					_died = true;
				}
			}
		}

		[HarmonyPatch(typeof(Player), "OnSpawned")]
		private static class Respawned
		{
			private static void Postfix(Player __instance)
			{
				//IL_0047: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && _died)
				{
					_died = false;
					if (Settings.ComeBack.Value && ZRoutedRpc.instance != null)
					{
						ZRoutedRpc.instance.InvokeRoutedRPC("BetrTame_ComeBack", new object[1] { ((Component)__instance).transform.position });
					}
				}
			}
		}

		private const string Rpc = "BetrTame_ComeBack";

		private static readonly int DownKey = StringExtensionMethods.GetStableHashCode("BetrTame_down");

		private static readonly int SleepingKey = 438569 + ZSyncAnimation.GetHash("sleeping");

		private const double BudgetMs = 1.0;

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

		private const float Trust = 128f;

		private static bool _died;

		private static readonly FieldRef<ZDOMan, List<ZDO>[]> SectorsOf = AccessTools.FieldRefAccess<ZDOMan, List<ZDO>[]>("m_objectsBySector");

		private static void Requested(long sender, Vector3 at)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: 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_004f: 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_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !Settings.ComeBack.Value)
			{
				return;
			}
			ZNetPeer peer = ZNet.instance.GetPeer(sender);
			string playerName;
			if (peer != null)
			{
				playerName = peer.m_playerName;
				if (peer.m_refPos != Vector3.zero)
				{
					Vector3 val = peer.m_refPos - at;
					if (((Vector3)(ref val)).sqrMagnitude > 16384f)
					{
						at = peer.m_refPos;
					}
				}
			}
			else
			{
				if ((Object)(object)Player.m_localPlayer == (Object)null)
				{
					return;
				}
				playerName = Player.m_localPlayer.GetPlayerName();
				at = ((Component)Player.m_localPlayer).transform.position;
			}
			if (!string.IsNullOrEmpty(playerName))
			{
				((MonoBehaviour)ZNet.instance).StartCoroutine(Gather(playerName, at));
			}
		}

		private static IEnumerator Gather(string name, Vector3 at)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			ZDOMan instance = ZDOMan.instance;
			if (instance == null)
			{
				yield break;
			}
			List<ZDO>[] sectors = SectorsOf.Invoke(instance);
			if (sectors == null)
			{
				yield break;
			}
			Tameable.Clear();
			List<ZDO> found = new List<ZDO>();
			Stopwatch clock = Stopwatch.StartNew();
			foreach (List<ZDO> list in sectors)
			{
				if (list != null)
				{
					foreach (ZDO item in list)
					{
						if (CanBeTamed(item.GetPrefab()) && item.GetBool(ZDOVars.s_tamed, false) && item.GetString(ZDOVars.s_follow, "") == name)
						{
							found.Add(item);
						}
					}
				}
				if (clock.Elapsed.TotalMilliseconds >= 1.0)
				{
					yield return null;
					clock.Restart();
				}
			}
			int num = 0;
			foreach (ZDO item2 in found)
			{
				if (item2.IsValid() && item2.Persistent && !(item2.GetString(ZDOVars.s_follow, "") != name))
				{
					Move(item2, at + Ring(num++));
				}
			}
			if (num > 0)
			{
				Log.Info($"{num} tame(s) of {name} came back with them.");
			}
		}

		private static bool CanBeTamed(int prefab)
		{
			if (Tameable.TryGetValue(prefab, out var value))
			{
				return value;
			}
			GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefab) : null);
			value = (Object)(object)val != (Object)null && (Object)(object)val.GetComponent<Tameable>() != (Object)null;
			Tameable[prefab] = value;
			return value;
		}

		private static void Move(ZDO zdo, Vector3 to)
		{
			//IL_003b: 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)
			zdo.SetOwner(ZDOMan.GetSessionID());
			float num = default(float);
			if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetSolidHeight(to, ref num, 1000))
			{
				to.y = num + 0.2f;
			}
			zdo.SetPosition(to);
			if (zdo.GetLong(DownKey, 0L) != 0L)
			{
				zdo.Set(DownKey, 0L);
				zdo.Set(SleepingKey, 0, false);
				float num2 = zdo.GetFloat(ZDOVars.s_maxHealth, PrefabHealth(zdo));
				if (num2 > 0f)
				{
					zdo.Set(ZDOVars.s_health, num2 * Settings.RecoveryHealth.Value);
				}
			}
		}

		private static float PrefabHealth(ZDO zdo)
		{
			GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(zdo.GetPrefab()) : null);
			Character val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Character>() : null);
			if (!((Object)(object)val2 != (Object)null))
			{
				return 0f;
			}
			return val2.m_health;
		}

		private static Vector3 Ring(int n)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			float num = (float)(n % 8) * (float)Math.PI / 4f;
			float num2 = 2.5f + (float)(n / 8) * 1.5f;
			return new Vector3(Mathf.Cos(num) * num2, 0f, Mathf.Sin(num) * num2);
		}
	}
	internal static class Creatures
	{
		public sealed class Spec
		{
			public readonly string Key;

			public readonly string[] Prefabs;

			public readonly string Food;

			public readonly float TamingTime;

			public readonly bool Breeds;

			public string Offspring;

			public readonly string English;

			public readonly string German;

			public Spec(string key, string english, string german, string food, float tamingTime, bool breeds, params string[] variants)
			{
				Key = key;
				English = english;
				German = german;
				Food = food;
				TamingTime = tamingTime;
				Breeds = breeds;
				Prefabs = new string[variants.Length + 1];
				Prefabs[0] = key;
				variants.CopyTo(Prefabs, 1);
				Offspring = key;
			}
		}

		private const float Meadows = 600f;

		private const float BlackForest = 1200f;

		private const float Swamp = 1800f;

		private const float Mountain = 1800f;

		private const float Plains = 2400f;

		private const float Mistlands = 2400f;

		private const float Ashlands = 3000f;

		private const float DeepNorth = 3000f;

		public static readonly Spec[] All = new Spec[39]
		{
			new Spec("Deer", "Deer", "Hirsch", "Raspberry, Blueberries, Dandelion", 600f, true),
			new Spec("Neck", "Neck", "Nixe", "FishRaw, Raspberry", 600f, true),
			new Spec("Greyling", "Greyling", "Gräuling", "Resin, Raspberry", 600f, false),
			new Spec("Greydwarf", "Greydwarf", "Grauzwerg", "Resin, Blueberries", 1200f, false),
			new Spec("Greydwarf_Elite", "Greydwarf Brute", "Grauzwergen Berserker", "Resin, MushroomYellow", 1200f, false),
			new Spec("Greydwarf_Shaman", "Greydwarf Shaman", "Grauzwergen Schamane", "Resin, Thistle", 1200f, false, "Greydwarf_Shaman_Frozen"),
			new Spec("Troll", "Troll", "Troll", "Raspberry, Blueberries, Mushroom", 1200f, true, "Troll_sleeping"),
			new Spec("Skeleton", "Skeleton", "Skelett", "BoneFragments", 1200f, false, "Skeleton_NoArcher", "Skeleton_Meadows", "Skeleton_Meadows_noarcher", "Skeleton_Swamps", "Skeleton_Swamps_noarcher", "Skeleton_Mountains", "Skeleton_Mountains_noarcher", "Skeleton_DeepNorth"),
			new Spec("Draugr", "Draugr", "Draugr", "Entrails, RottenMeat", 1800f, false, "Draugr_sleeping", "Draugr_Ranged", "Draugr_Ranged_sleeping"),
			new Spec("Draugr_Elite", "Draugr Elite", "Elite Draugr", "Entrails, RottenMeat", 1800f, false, "Draugr_Elite_sleeping"),
			new Spec("Skeleton_Poison", "Rancid Remains", "Ranzige Überreste", "BoneFragments, Entrails", 1800f, false),
			new Spec("Blob", "Blob", "Schleimling", "Entrails", 1800f, false),
			new Spec("BlobElite", "Oozer", "Schleimmasse", "Entrails, Bloodbag", 1800f, false),
			new Spec("Wraith", "Wraith", "Zorngeist", "Ectoplasm", 1800f, false),
			new Spec("Abomination", "Abomination", "Monstrosität", "Root, ElderBark", 1800f, false),
			new Spec("Surtling", "Surtling", "Surtling", "Coal", 1800f, false),
			new Spec("Bjorn", "Bear", "Bär", "Blueberries, Raspberry, Honey", 1800f, true, "Bjorn_sleeping"),
			new Spec("Ulv", "Ulv", "Ulv", "WolfMeat, RawMeat", 1800f, true),
			new Spec("Fenring", "Fenring", "Fenring", "WolfMeat, RawMeat", 1800f, true),
			new Spec("Fenring_Cultist", "Cultist", "Kultist", "WolfMeat", 1800f, false),
			new Spec("Hatchling", "Drake", "Drache", "FreezeGland", 1800f, true),
			new Spec("StoneGolem", "Stone Golem", "Steingolem", "Crystal", 1800f, false),
			new Spec("Goblin", "Fuling", "Fuling", "Barley, Bread, LoxMeat", 2400f, false, "GoblinArcher"),
			new Spec("GoblinBrute", "Fuling Berserker", "Fuling Berserker", "LoxMeat, CookedLoxMeat", 2400f, false),
			new Spec("GoblinShaman", "Fuling Shaman", "Fuling Schamane", "Barley, Flax", 2400f, false),
			new Spec("Unbjorn", "Vile", "Aasbär", "Blueberries, Cloudberry", 2400f, true),
			new Spec("BlobTar", "Growth", "Plagewuchs", "Tar", 2400f, false),
			new Spec("Hare", "Hare", "Hase", "Carrot, Turnip, Dandelion", 2400f, true),
			new Spec("Seeker", "Seeker", "Sucher", "RoyalJelly, BugMeat", 2400f, true),
			new Spec("SeekerBrute", "Seeker Soldier", "Suchersoldat", "RoyalJelly", 2400f, false),
			new Spec("Tick", "Tick", "Zecke", "Bloodbag", 2400f, true),
			new Spec("Dverger", "Dvergr", "Dvergr", "Sausages, Bread", 2400f, false, "DvergerMage", "DvergerMageFire", "DvergerMageIce", "DvergerMageSupport", "DvergerAshlands", "DvergerDeepNorth"),
			new Spec("Charred_Melee", "Charred", "Verkohlte", "CharredBone", 3000f, false, "Charred_Archer", "Charred_Mage", "Charred_Twitcher"),
			new Spec("Volture", "Volture", "Vulkangeier", "AsksvinMeat", 3000f, true),
			new Spec("Morgen", "Morgen", "Morgen", "AsksvinMeat, BoneMawSerpentMeat", 3000f, true, "Morgen_NonSleeping")
			{
				Offspring = "Morgen_NonSleeping"
			},
			new Spec("BlobLava", "Lava Blob", "Lavaklumpen", "SulfurStone", 3000f, false),
			new Spec("TrollFrost", "Gammeltroll", "Gammeltroll", "Lingonberry, MooseMeat", 3000f, true),
			new Spec("Greydwarf_Frozen", "Greydwarf (Deep North)", "Grauzwerg (Tiefer Norden)", "Resin, Lingonberry", 3000f, false),
			new Spec("GoblinDeepNorth", "Captive Fuling", "Gefangener Fuling", "MooseMeat, SealBlubber", 3000f, false)
		};

		public static readonly Spec[] GameTames = new Spec[6]
		{
			new Spec("Wolf", "Wolf", "Wolf", "NeckTail, RawMeat, LoxMeat, Sausages, FishRaw, DeerMeat, ChickenMeat", 0f, false),
			new Spec("Boar", "Boar", "Wildschwein", "Carrot, Turnip, Onion, Mushroom, Raspberry, Blueberries", 0f, false),
			new Spec("Lox", "Lox", "Lox", "Cloudberry, Barley, Flax", 0f, false),
			new Spec("Hen", "Hen", "Henne", "Dandelion, Barley, BeechSeeds, BirchSeeds, CarrotSeeds, OnionSeeds, TurnipSeeds", 0f, false),
			new Spec("Asksvin", "Asksvin", "Asksvin", "Vineberry, Fiddleheadfern, MushroomSmokePuff", 0f, false),
			new Spec("Moose", "Moose", "Elch", "Lingonberry", 0f, false)
		};

		public static readonly Dictionary<string, string> Brazilian = new Dictionary<string, string>
		{
			{ "Deer", "Cervo" },
			{ "Neck", "Lagarto" },
			{ "Greyling", "Cinzento" },
			{ "Greydwarf", "Anão cinzento" },
			{ "Greydwarf_Elite", "Anão cinzento bruto" },
			{ "Greydwarf_Shaman", "Anão cinzento xamã" },
			{ "Troll", "Troll" },
			{ "Skeleton", "Esqueleto" },
			{ "Draugr", "Draugr" },
			{ "Draugr_Elite", "Draugr de elite" },
			{ "Skeleton_Poison", "Carcaça rançosa" },
			{ "Blob", "Bolha" },
			{ "BlobElite", "Bolha Elite" },
			{ "Wraith", "Espectro" },
			{ "Abomination", "Abominação" },
			{ "Surtling", "Surtling" },
			{ "Bjorn", "Urso" },
			{ "Ulv", "Ulv" },
			{ "Fenring", "Fenring" },
			{ "Fenring_Cultist", "Cultista" },
			{ "Hatchling", "Dragão" },
			{ "StoneGolem", "Golem de pedra" },
			{ "Goblin", "Fuling" },
			{ "GoblinBrute", "Fuling furioso" },
			{ "GoblinShaman", "Xamã Fuling" },
			{ "Unbjorn", "Zomburso" },
			{ "BlobTar", "Betumor" },
			{ "Hare", "Lebre" },
			{ "Seeker", "Buscador" },
			{ "SeekerBrute", "Soldado buscador" },
			{ "Tick", "Carrapato" },
			{ "Dverger", "Anão dvergr" },
			{ "Charred_Melee", "Carbonizado" },
			{ "Volture", "Uruvul" },
			{ "Morgen", "Morgen" },
			{ "BlobLava", "Bolha de lava" },
			{ "TrollFrost", "Gammeltroll" },
			{ "Greydwarf_Frozen", "Anão cinzento (Norte profundo)" },
			{ "GoblinDeepNorth", "Fuling cativo" },
			{ "Wolf", "Lobo" },
			{ "Boar", "Javali" },
			{ "Lox", "Lox" },
			{ "Hen", "Galinha" },
			{ "Asksvin", "Asksvin" },
			{ "Moose", "Alce" }
		};
	}
	internal static class Knockout
	{
		[HarmonyPatch(typeof(Character), "CheckDeath")]
		private static class InsteadOfDeath
		{
			private static bool Prefix(Character __instance)
			{
				if (!Settings.Knockout.Value || !__instance.IsTamed() || __instance.GetHealth() > 0f)
				{
					return true;
				}
				if (__instance.IsPlayer() || __instance.IsDead())
				{
					return true;
				}
				HitData obj = LastHitOf.Invoke(__instance);
				if (((obj != null) ? obj.GetAttacker() : null) is Player)
				{
					return true;
				}
				Tameable component = ((Component)__instance).GetComponent<Tameable>();
				if ((Object)(object)component == (Object)null || Tames.IsSummon(component))
				{
					return true;
				}
				ZNetView val = ViewOf.Invoke(__instance);
				if ((Object)(object)val == (Object)null || !val.IsValid() || !val.IsOwner())
				{
					return true;
				}
				long ticks = ZNet.instance.GetTime().AddSeconds(Settings.RecoveryTime.Value).Ticks;
				val.GetZDO().Set(UntilKey, ticks);
				__instance.SetHealth(1f);
				Down.Add(__instance);
				val.InvokeRPC(ZNetView.Everybody, "BetrTame_Down", new object[1] { true });
				return false;
			}
		}

		[HarmonyPatch(typeof(Character), "ApplyDamage")]
		private static class NoHarm
		{
			private static bool Prefix(Character __instance, HitData hit)
			{
				if (IsDown(__instance))
				{
					return ((hit != null) ? hit.GetAttacker() : null) is Player;
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(MonsterAI), "UpdateAI")]
		private static class LieStill
		{
			private static bool Prefix(MonsterAI __instance, ref bool __result)
			{
				if (Down.Count == 0 || !Down.Contains(CharacterOf.Invoke((BaseAI)(object)__instance)))
				{
					return true;
				}
				((BaseAI)__instance).StopMoving();
				__result = true;
				return false;
			}
		}

		[HarmonyPatch(typeof(Procreation), "Procreate")]
		private static class NoBreeding
		{
			private static bool Prefix(Procreation __instance)
			{
				if (Down.Count != 0)
				{
					return !Down.Contains(((Component)__instance).GetComponent<Character>());
				}
				return true;
			}
		}

		[HarmonyPatch(typeof(BaseAI), "IsEnemy", new Type[]
		{
			typeof(Character),
			typeof(Character)
		})]
		private static class NobodysEnemy
		{
			[HarmonyPriority(0)]
			private static void Postfix(Character a, Character b, ref bool __result)
			{
				if (__result && Down.Count > 0 && (Down.Contains(a) || Down.Contains(b)))
				{
					__result = false;
				}
			}
		}

		private const string Rpc = "BetrTame_Down";

		private static readonly int UntilKey = StringExtensionMethods.GetStableHashCode("BetrTame_down");

		private static readonly int SleepingHash = ZSyncAnimation.GetHash("sleeping");

		private static readonly HashSet<Character> Down = new HashSet<Character>();

		private static readonly List<Character> Scratch = new List<Character>();

		private static float _nextCheck;

		private static readonly FieldRef<Character, HitData> LastHitOf = AccessTools.FieldRefAccess<Character, HitData>("m_lastHit");

		private static readonly FieldRef<Character, ZNetView> ViewOf = AccessTools.FieldRefAccess<Character, ZNetView>("m_nview");

		private static readonly FieldRef<BaseAI, Character> CharacterOf = AccessTools.FieldRefAccess<BaseAI, Character>("m_character");

		private static readonly FieldRef<BaseAI, ZSyncAnimation> AnimatorOf = AccessTools.FieldRefAccess<BaseAI, ZSyncAnimation>("m_animator");

		public static bool IsDown(Character character)
		{
			if (Down.Count > 0)
			{
				return Down.Contains(character);
			}
			return false;
		}

		public static void Register(Character character, ZNetView view)
		{
			view.Register<bool>("BetrTame_Down", (Action<long, bool>)delegate(long sender, bool down)
			{
				SetDown(character, down);
			});
			if (view.GetZDO().GetLong(UntilKey, 0L) != 0L)
			{
				Down.Add(character);
			}
		}

		public static void Forget(Character character)
		{
			Down.Remove(character);
		}

		public static void Tick()
		{
			if (Down.Count == 0 || Time.time < _nextCheck || (Object)(object)ZNet.instance == (Object)null)
			{
				return;
			}
			_nextCheck = Time.time + 1f;
			long ticks = ZNet.instance.GetTime().Ticks;
			Scratch.Clear();
			Scratch.AddRange(Down);
			foreach (Character item in Scratch)
			{
				if ((Object)(object)item == (Object)null)
				{
					Down.Remove(item);
					continue;
				}
				ZNetView val = ViewOf.Invoke(item);
				if (!((Object)(object)val == (Object)null) && val.IsValid() && val.IsOwner())
				{
					long num = val.GetZDO().GetLong(UntilKey, 0L);
					if (num == 0L || ticks >= num)
					{
						val.GetZDO().Set(UntilKey, 0L);
						item.SetHealth(item.GetMaxHealth() * Settings.RecoveryHealth.Value);
						val.InvokeRPC(ZNetView.Everybody, "BetrTame_Down", new object[1] { false });
					}
				}
			}
		}

		public static int SecondsLeft(Character tame)
		{
			ZNetView val = ViewOf.Invoke(tame);
			if ((Object)(object)val == (Object)null || !val.IsValid() || (Object)(object)ZNet.instance == (Object)null)
			{
				return 0;
			}
			long num = val.GetZDO().GetLong(UntilKey, 0L);
			if (num == 0L)
			{
				return 0;
			}
			return Mathf.Max(0, Mathf.CeilToInt((float)new TimeSpan(num - ZNet.instance.GetTime().Ticks).TotalSeconds));
		}

		private static void SetDown(Character tame, bool down)
		{
			if ((Object)(object)tame == (Object)null)
			{
				return;
			}
			if (down)
			{
				Down.Add(tame);
			}
			else
			{
				Down.Remove(tame);
			}
			BaseAI baseAI = tame.GetBaseAI();
			if ((Object)(object)baseAI == (Object)null)
			{
				return;
			}
			baseAI.StopMoving();
			ZNetView val = ViewOf.Invoke(tame);
			if ((Object)(object)val != (Object)null && val.IsValid() && val.IsOwner())
			{
				ZSyncAnimation obj = AnimatorOf.Invoke(baseAI);
				if (obj != null)
				{
					obj.SetBool(SleepingHash, down);
				}
			}
		}
	}
	internal static class Levels
	{
		[HarmonyPatch(typeof(Character), "OnDeath")]
		private static class Kill
		{
			private static void Prefix(Character __instance)
			{
				//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
				//IL_0109: Unknown result type (might be due to invalid IL or missing references)
				//IL_010e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0110: Unknown result type (might be due to invalid IL or missing references)
				//IL_0115: Unknown result type (might be due to invalid IL or missing references)
				if (!Settings.Levels.Value || __instance.IsPlayer() || __instance.IsTamed())
				{
					return;
				}
				ZNetView val = ViewOf.Invoke(__instance);
				if ((Object)(object)val == (Object)null || !val.IsValid() || !val.IsOwner())
				{
					return;
				}
				HitData obj = LastHitOf.Invoke(__instance);
				Character val2 = ((obj != null) ? obj.GetAttacker() : null);
				if ((Object)(object)val2 == (Object)null)
				{
					return;
				}
				float num = __instance.GetMaxHealth() * Settings.Experience.Value;
				if (num <= 0f)
				{
					return;
				}
				if (val2.IsTamed())
				{
					Give(val2, num);
					return;
				}
				float value = Settings.FollowerShare.Value;
				Player val3 = (Player)(object)((val2 is Player) ? val2 : null);
				if (val3 == null || value <= 0f)
				{
					return;
				}
				string playerName = val3.GetPlayerName();
				Vector3 position = ((Component)val3).transform.position;
				foreach (Character allCharacter in Character.GetAllCharacters())
				{
					if ((Object)(object)allCharacter == (Object)null || !allCharacter.IsTamed() || allCharacter.IsPlayer())
					{
						continue;
					}
					Vector3 val4 = ((Component)allCharacter).transform.position - position;
					if (!(((Vector3)(ref val4)).sqrMagnitude > 900f))
					{
						ZNetView val5 = ViewOf.Invoke(allCharacter);
						if (!((Object)(object)val5 == (Object)null) && val5.IsValid() && !(val5.GetZDO().GetString(ZDOVars.s_follow, "") != playerName))
						{
							Give(allCharacter, num * value);
						}
					}
				}
			}
		}

		[HarmonyPatch(typeof(Procreation), "Procreate")]
		private static class Breeding
		{
			private static void Prefix()
			{
				_breeding++;
			}

			private static Exception Finalizer(Exception __exception)
			{
				_breeding--;
				return __exception;
			}
		}

		[HarmonyPatch(typeof(Character), "SetLevel")]
		private static class YoungLevel
		{
			private static void Prefix(ref int level)
			{
				if (_breeding > 0)
				{
					level = Mathf.Min(level, Settings.OffspringStars.Value + 1);
					if (level < 1)
					{
						level = 1;
					}
				}
			}
		}

		private const string XpRpc = "BetrTame_Xp";

		private const string LevelRpc = "BetrTame_Level";

		private static readonly int XpKey = StringExtensionMethods.GetStableHashCode("BetrTame_xp");

		private const float ShareRange = 30f;

		private static readonly FieldRef<Character, int> LevelOf = AccessTools.FieldRefAccess<Character, int>("m_level");

		private static readonly FieldRef<Character, HitData> LastHitOf = AccessTools.FieldRefAccess<Character, HitData>("m_lastHit");

		private static readonly FieldRef<Character, ZNetView> ViewOf = AccessTools.FieldRefAccess<Character, ZNetView>("m_nview");

		private static int _breeding;

		public static int MaxLevel => Settings.MaxTameStars.Value + 1;

		public static void Register(Character character, ZNetView view)
		{
			view.Register<float>("BetrTame_Xp", (Action<long, float>)delegate(long sender, float xp)
			{
				RpcXp(character, view, xp);
			});
			view.Register<int>("BetrTame_Level", (Action<long, int>)delegate(long sender, int level)
			{
				RpcLevel(character, level);
			});
		}

		public static float Needed(int level)
		{
			return 100f * (float)level * (float)level;
		}

		public static float Experience(Character character)
		{
			ZNetView val = ViewOf.Invoke(character);
			if (!((Object)(object)val != (Object)null) || !val.IsValid())
			{
				return 0f;
			}
			return val.GetZDO().GetFloat(XpKey, 0f);
		}

		private static void Give(Character tame, float xp)
		{
			ZNetView val = ViewOf.Invoke(tame);
			if (!((Object)(object)val == (Object)null) && val.IsValid() && !((Object)(object)((Component)tame).GetComponent<Tameable>() == (Object)null))
			{
				val.InvokeRPC("BetrTame_Xp", new object[1] { xp });
			}
		}

		private static void RpcXp(Character tame, ZNetView view, float xp)
		{
			if ((Object)(object)tame == (Object)null || !view.IsValid() || !view.IsOwner() || !tame.IsTamed() || tame.IsDead() || !Settings.Levels.Value)
			{
				return;
			}
			int level = tame.GetLevel();
			int maxLevel = MaxLevel;
			if (level >= maxLevel)
			{
				return;
			}
			xp = Mathf.Min(xp, Needed(level));
			ZDO zDO = view.GetZDO();
			float num = zDO.GetFloat(XpKey, 0f) + xp;
			int i;
			for (i = level; i < maxLevel && num >= Needed(i); i++)
			{
				num -= Needed(i);
			}
			zDO.Set(XpKey, (i >= maxLevel) ? 0f : num);
			if (i != level)
			{
				tame.SetLevel(i);
				if (Settings.HealOnLevelUp.Value)
				{
					tame.SetHealth(tame.GetMaxHealth());
				}
				view.InvokeRPC(ZNetView.Everybody, "BetrTame_Level", new object[1] { i });
			}
		}

		private static void RpcLevel(Character tame, int level)
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)tame == (Object)null)
			{
				return;
			}
			if (tame.GetLevel() != level)
			{
				LevelOf.Invoke(tame) = level;
				tame.m_onLevelSet?.Invoke(level);
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			ZNetView val = ViewOf.Invoke(tame);
			if (!((Object)(object)val != (Object)null) || !val.IsValid() || !(val.GetZDO().GetString(ZDOVars.s_follow, "") == localPlayer.GetPlayerName()))
			{
				Vector3 val2 = ((Component)tame).transform.position - ((Component)localPlayer).transform.position;
				if (((Vector3)(ref val2)).sqrMagnitude > 900f)
				{
					return;
				}
			}
			((Character)localPlayer).Message((MessageType)2, Localization.instance.Localize("$bt_new_star", new string[2]
			{
				tame.GetHoverName(),
				(level - 1).ToString()
			}), 0, (Sprite)null, false);
		}

		public static string HoverLine(Character tame)
		{
			if (!Settings.Levels.Value || !Settings.LevelInHover.Value)
			{
				return null;
			}
			int level = tame.GetLevel();
			int num = level - 1;
			if (level >= MaxLevel)
			{
				return Localization.instance.Localize("$bt_hover_stars_max", new string[1] { num.ToString() });
			}
			int num2 = Mathf.Clamp(Mathf.FloorToInt(Experience(tame) / Needed(level) * 100f), 0, 99);
			return Localization.instance.Localize("$bt_hover_stars", new string[2]
			{
				num.ToString(),
				num2.ToString()
			});
		}
	}
	internal static class Log
	{
		private static ManualLogSource _source;

		public static void Init(ManualLogSource source)
		{
			_source = source;
		}

		public static void Info(string message)
		{
			ManualLogSource source = _source;
			if (source != null)
			{
				source.LogInfo((object)message);
			}
		}

		public static void Warning(string message)
		{
			ManualLogSource source = _source;
			if (source != null)
			{
				source.LogWarning((object)message);
			}
		}
	}
	internal static class Mornings
	{
		private struct Kept
		{
			public int Key;

			public long Ticks;
		}

		[HarmonyPatch(typeof(SpawnSystem), "UpdateSpawnList")]
		private static class KeepPatch
		{
			private static void Prefix(SpawnSystem __instance, List<SpawnData> spawners, bool eventSpawners, string groupSalt)
			{
				KeptNotes.Clear();
				if (eventSpawners || spawners == null || !Settings.KeepWaiting.Value)
				{
					return;
				}
				ZNetView val = View.Invoke(__instance);
				ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null);
				if (val2 == null)
				{
					return;
				}
				bool flag = EnvMan.IsDay();
				bool flag2 = EnvMan.IsNight();
				if (!flag && !flag2)
				{
					return;
				}
				int[] array = null;
				for (int i = 0; i < spawners.Count; i++)
				{
					SpawnData val3 = spawners[i];
					if (val3 != null && val3.m_enabled && !((Object)(object)val3.m_prefab == (Object)null) && ((flag && !val3.m_spawnAtDay) || (flag2 && !val3.m_spawnAtNight)))
					{
						if (array == null)
						{
							array = KeysOf(spawners, groupSalt);
						}
						KeptNotes.Add(new Kept
						{
							Key = array[i],
							Ticks = val2.GetLong(array[i], 0L)
						});
					}
				}
			}

			private static void Postfix(SpawnSystem __instance)
			{
				if (KeptNotes.Count == 0)
				{
					return;
				}
				ZNetView val = View.Invoke(__instance);
				ZDO val2 = (((Object)(object)val != (Object)null && val.IsValid()) ? val.GetZDO() : null);
				if (val2 != null)
				{
					foreach (Kept keptNote in KeptNotes)
					{
						if (val2.GetLong(keptNote.Key, 0L) != keptNote.Ticks)
						{
							val2.Set(keptNote.Key, keptNote.Ticks);
						}
					}
				}
				KeptNotes.Clear();
			}
		}

		private static readonly FieldRef<SpawnSystem, ZNetView> View = AccessTools.FieldRefAccess<SpawnSystem, ZNetView>("m_nview");

		private static readonly List<Kept> KeptNotes = new List<Kept>(16);

		private static readonly Dictionary<(List<SpawnData>, string), int[]> Keys = new Dictionary<(List<SpawnData>, string), int[]>();

		private static int[] KeysOf(List<SpawnData> spawners, string salt)
		{
			if (Keys.TryGetValue((spawners, salt), out var value) && value.Length == spawners.Count)
			{
				return value;
			}
			value = new int[spawners.Count];
			for (int i = 0; i < spawners.Count; i++)
			{
				SpawnData val = spawners[i];
				value[i] = (((Object)(object)val?.m_prefab != (Object)null) ? StringExtensionMethods.GetStableHashCode(salt + ((Object)val.m_prefab).name + (i + 1)) : 0);
			}
			Keys[(spawners, salt)] = value;
			return value;
		}
	}
	[BepInPlugin("dezzycode.valheim.betrtame", "BetrTame", "0.1.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "dezzycode.valheim.betrtame";

		public const string Name = "BetrTame";

		public const string Version = "0.1.1";

		private const string BetrValheimGuid = "dezzycode.valheim.betrvalheim";

		private Harmony _harmony;

		private static Plugin _instance;

		public static IDictionary<string, string> SettingTexts(string language)
		{
			if (!((Object)(object)_instance != (Object)null))
			{
				return new Dictionary<string, string>();
			}
			return BetrTame.SettingTexts.For(((BaseUnityPlugin)_instance).Config, language);
		}

		private void Awake()
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			_instance = this;
			Log.Init(((BaseUnityPlugin)this).Logger);
			Settings.Bind(((BaseUnityPlugin)this).Config);
			((BaseUnityPlugin)this).Config.SettingChanged += delegate(object _, SettingChangedEventArgs e)
			{
				Taming.MarkDirty();
				Spawns.Refresh(e.ChangedSetting.Definition.Section);
			};
			Texts.Register();
			_harmony = new Harmony("dezzycode.valheim.betrtame");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			Log.Info("BetrTame 0.1.1 loaded.");
		}

		private void Update()
		{
			Taming.Tick();
			Knockout.Tick();
			CatchUp.Tick();
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class Settings
	{
		private const string TamingSection = "1 - Taming";

		private const string LevelSection = "2 - Tame levels";

		private const string StarsSection = "3 - Stars above two";

		private const string WildSection = "4 - Wild stars";

		private const string KnockoutSection = "5 - Knocked out";

		private const string CreatureSection = "6 - Tameable creatures";

		public const string CreatureSectionName = "6 - Tameable creatures";

		private const string DisplaySection = "7 - Display";

		private const string FollowSection = "8 - Following";

		private const string GameFoodSection = "9 - Game tames food";

		public const string GameFoodSectionName = "9 - Game tames food";

		private const string MonstersSection = "10 - More monsters";

		public const string MonstersSectionName = "10 - More monsters";

		public static ConfigEntry<bool> TameMore;

		public static ConfigEntry<float> TamingTime;

		public static ConfigEntry<float> FedTime;

		public static ConfigEntry<bool> CommandAll;

		public static ConfigEntry<bool> Breeding;

		public static ConfigEntry<bool> FightOwnKind;

		public static ConfigEntry<bool> Levels;

		public static ConfigEntry<int> MaxTameStars;

		public static ConfigEntry<float> Experience;

		public static ConfigEntry<float> FollowerShare;

		public static ConfigEntry<bool> HealOnLevelUp;

		public static ConfigEntry<int> OffspringStars;

		public static ConfigEntry<float> HealthPerStar;

		public static ConfigEntry<float> DamagePerStar;

		public static ConfigEntry<float> SizePerStar;

		public static ConfigEntry<float> MaxSize;

		public static ConfigEntry<float> LootPerStar;

		public static ConfigEntry<bool> WildStars;

		public static ConfigEntry<float> WildStarChance;

		public static readonly Dictionary<Biome, ConfigEntry<int>> BiomeStars = new Dictionary<Biome, ConfigEntry<int>>();

		public static ConfigEntry<bool> Knockout;

		public static ConfigEntry<float> RecoveryTime;

		public static ConfigEntry<float> RecoveryHealth;

		public static readonly Dictionary<string, ConfigEntry<string>> Food = new Dictionary<string, ConfigEntry<string>>();

		public static ConfigEntry<string> OtherCreatures;

		public static readonly Dictionary<string, ConfigEntry<string>> GameFood = new Dictionary<string, ConfigEntry<string>>();

		public static ConfigEntry<bool> CatchUp;

		public static ConfigEntry<float> CatchUpDistance;

		public static ConfigEntry<bool> ComeBack;

		public static ConfigEntry<float> Monsters;

		public static ConfigEntry<bool> KeepWaiting;

		public static readonly Dictionary<Biome, ConfigEntry<float>> BiomeMonsters = new Dictionary<Biome, ConfigEntry<float>>();

		public static ConfigEntry<bool> StarNumber;

		public static ConfigEntry<bool> LevelInHover;

		public static int Version { get; private set; }

		public static void Bind(ConfigFile file)
		{
			//IL_03c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_05fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0601: Unknown result type (might be due to invalid IL or missing references)
			//IL_060f: Unknown result type (might be due to invalid IL or missing references)
			file.SettingChanged += delegate
			{
				Version++;
			};
			TameMore = Rule(file, "1 - Taming", "Tame more creatures", value: true, "Make the creatures in \"6 - Tameable creatures\" tameable. Off, only the creatures the game tames itself can be tamed.");
			TamingTime = Rule(file, "1 - Taming", "Taming time", 1f, "How long taming takes, times the time each creature needs. 0.5 is half as long. Counts for the game's own tames too.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 10f));
			FedTime = Rule(file, "1 - Taming", "Fed time", 1f, "How long a tame stays fed after eating, times the game's own 10 minutes. A creature still being tamed stays fed at least 10 minutes, or taming would get slower.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 20f));
			CommandAll = Rule(file, "1 - Taming", "Command every tame", value: true, "Every tame follows you or stays when you use it, not only wolves.");
			Breeding = Rule(file, "1 - Taming", "Breeding", value: true, "Creatures tamed through this mod that breed have young, born tame, when two fed ones stand together.");
			FightOwnKind = Rule(file, "1 - Taming", "Tames fight their own kind", value: true, "A tame fights wild creatures of its own kind, except one that is fed and being tamed. Off, a tame wolf and a wild wolf leave each other alone, as in the game.");
			Levels = Rule(file, "2 - Tame levels", "Enabled", value: true, "Tames gain experience from what they kill and earn stars with it, up to the stars below.");
			MaxTameStars = Rule(file, "2 - Tame levels", "Most stars", 30, "The most stars a tame can earn.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 100));
			Experience = Rule(file, "2 - Tame levels", "Experience", 1f, "Experience per kill, times the health of what was killed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 20f));
			FollowerShare = Rule(file, "2 - Tame levels", "Share with followers", 0.5f, "When you kill something, every tame following you within 30 m gets this part of the experience. 0 is off.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f));
			HealOnLevelUp = Rule(file, "2 - Tame levels", "Heal on new star", value: true, "A tame that earns a star is healed in full.");
			OffspringStars = Rule(file, "2 - Tame levels", "Stars passed to young", 2, "Young are born with their parent's stars, but at most this many. 2 is the game's own; the rest a tame earns itself.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100));
			HealthPerStar = Rule(file, "3 - Stars above two", "Health per star", 0.5f, "Above two stars, each star adds this much of the creature's base health. Up to two stars the game's own counts: each star adds its full base health.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f));
			DamagePerStar = Rule(file, "3 - Stars above two", "Damage per star", 0.15f, "Above two stars, each star adds this much of the creature's base damage. Up to two stars the game's own counts: each star adds half.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f));
			SizePerStar = Rule(file, "3 - Stars above two", "Size per star", 0.02f, "Above two stars, each star makes the creature this much bigger.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.2f));
			MaxSize = Rule(file, "3 - Stars above two", "Largest size", 1.4f, "The most stars can grow a creature, times its two-star size.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 3f));
			LootPerStar = Rule(file, "3 - Stars above two", "Loot per star", 1f, "Above two stars, each star adds this many times the base loot. The game's own doubles it with every star, which would drop hundreds of items from a ten-star creature.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 4f));
			WildStars = Rule(file, "4 - Wild stars", "Enabled", value: true, "A wild creature that spawns with two stars can roll for more, up to its biome's most below.");
			WildStarChance = Rule(file, "4 - Wild stars", "Chance per star", 15f, "Chance in percent for each star above two.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f));
			(Biome, string, int)[] array = new(Biome, string, int)[9]
			{
				((Biome)1, "Meadows", 2),
				((Biome)8, "Black Forest", 3),
				((Biome)2, "Swamp", 4),
				((Biome)4, "Mountain", 5),
				((Biome)16, "Plains", 6),
				((Biome)256, "Ocean", 6),
				((Biome)512, "Mistlands", 7),
				((Biome)32, "Ashlands", 8),
				((Biome)64, "Deep North", 8)
			};
			(Biome, string, int)[] array2 = array;
			for (int num = 0; num < array2.Length; num++)
			{
				var (key, key2, value) = array2[num];
				BiomeStars[key] = Rule(file, "4 - Wild stars", key2, value, "Most stars of a wild creature here. 2 is the game's own.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 50));
			}
			Knockout = Rule(file, "5 - Knocked out", "Enabled", value: true, "A tame that would die is knocked out instead, stays where it is unharmed and gets up again. Only a player's blow - the butcher knife - kills it.");
			RecoveryTime = Rule(file, "5 - Knocked out", "Recovery time", 60f, "Seconds until a knocked-out tame gets up. The time runs on while nobody is near.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 3600f));
			RecoveryHealth = Rule(file, "5 - Knocked out", "Health when up", 1f, "Part of its health a tame has when it gets up.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f));
			Creatures.Spec[] all = Creatures.All;
			foreach (Creatures.Spec spec in all)
			{
				Food[spec.Key] = Rule(file, "6 - Tameable creatures", spec.Key, spec.Food, "What it eats, as item prefab names, separated by commas. Empty: not tameable.");
			}
			OtherCreatures = Rule(file, "6 - Tameable creatures", "Other creatures", string.Empty, "More creatures, also from other mods: \"Prefab: Food, Food; Prefab: Food\". A creature needs the game's monster or animal AI to be tamed.");
			CatchUp = Rule(file, "8 - Following", "Catch up", value: true, "A tame that follows you and falls far behind is brought to you. Not into dungeons and not while someone rides it.");
			CatchUpDistance = Rule(file, "8 - Following", "Catch-up distance", 50f, "From how many metres behind a following tame is brought to you.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(20f, 120f));
			ComeBack = Rule(file, "8 - Following", "Come back after death", value: true, "When you die, the tames that follow you come back with you: they wait around the place you respawn, and a knocked-out one gets up there.");
			all = Creatures.GameTames;
			foreach (Creatures.Spec spec2 in all)
			{
				GameFood[spec2.Key] = Rule(file, "9 - Game tames food", spec2.Key, spec2.Food, "What it eats, as item prefab names, separated by commas. Empty: the game's own food. Taming time and young stay the game's.");
			}
			Monsters = Rule(file, "10 - More monsters", "Everywhere", 1f, "How many creatures the open world spawns, times the game's own: how many of a kind may be around at once, and how often new ones come. 1 is the game's, 2 twice as many. Monsters and animals alike; raids, camps and dungeons stay the game's.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.25f, 5f));
			KeepWaiting = Rule(file, "10 - More monsters", "Full mornings", value: true, "A creature of the day waits through the night for its turn instead of starting to wait again at dawn, so mornings are not empty; likewise a creature of the night waits through the day. At most as many come at once as the game allows of that kind.");
			array2 = array;
			for (int num = 0; num < array2.Length; num++)
			{
				var (key3, key4, _) = array2[num];
				BiomeMonsters[key3] = Rule(file, "10 - More monsters", key4, 0f, "Its own factor for this biome, instead of \"Everywhere\". 0 takes \"Everywhere\".", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f));
			}
			StarNumber = file.Bind<bool>("7 - Display", "Star count above two", true, "Above two stars, the bar over a creature shows one star and the number of stars next to it. With BetrValheim's \"All stars\" on, BetrValheim shows them instead.");
			LevelInHover = file.Bind<bool>("7 - Display", "Experience on tames", true, "Show a tame's stars and its experience towards the next one when you look at it.");
		}

		private static ConfigEntry<T> Rule<T>(ConfigFile file, string section, string key, T value, string description, AcceptableValueBase range = null)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Expected O, but got Unknown
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			ConfigurationManagerAttributes val = new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			};
			return file.Bind<T>(section, key, value, new ConfigDescription(description + " Synced from the server.", range, new object[1] { val }));
		}
	}
	internal static class SettingTexts
	{
		private sealed class Language
		{
			public Dictionary<string, string> Texts;

			public Func<Creatures.Spec, string> Name;

			public string Food;

			public string GameFood;
		}

		private const string Synced = " Synced from the server.";

		private static readonly Dictionary<string, string> German = new Dictionary<string, string>(StringComparer.Ordinal)
		{
			{ "1_taming", "Zähmen" },
			{ "2_tame_levels", "Stufen der Gezähmten" },
			{ "3_stars_above_two", "Sterne über zwei" },
			{ "4_wild_stars", "Wilde Sterne" },
			{ "5_knocked_out", "K. o." },
			{ "6_tameable_creatures", "Zähmbare Kreaturen" },
			{ "7_display", "Anzeige" },
			{ "1_taming.tame_more_creatures", "Mehr Kreaturen zähmen" },
			{ "1_taming.tame_more_creatures.d", "Macht die Kreaturen unter „Zähmbare Kreaturen“ zähmbar. Aus: nur was das Spiel selbst zähmen lässt." },
			{ "1_taming.taming_time", "Zähmdauer" },
			{ "1_taming.taming_time.d", "Wie lange Zähmen dauert, mal der Zeit, die jede Kreatur braucht. 0,5 ist halb so lang. Gilt auch für die Gezähmten des Spiels." },
			{ "1_taming.fed_time", "Satt-Dauer" },
			{ "1_taming.fed_time.d", "Wie lange ein gezähmtes Tier nach dem Fressen satt bleibt, mal den 10 Minuten des Spiels. Eine Kreatur, die noch gezähmt wird, bleibt mindestens 10 Minuten satt, sonst würde das Zähmen langsamer." },
			{ "1_taming.command_every_tame", "Jedes Tier befehligen" },
			{ "1_taming.command_every_tame.d", "Jedes gezähmte Tier folgt dir oder bleibt, wenn du es benutzt – nicht nur Wölfe." },
			{ "1_taming.breeding", "Nachwuchs" },
			{ "1_taming.breeding.d", "Durch diese Mod gezähmte Kreaturen, die sich vermehren, bekommen gezähmten Nachwuchs, wenn zwei satte zusammenstehen." },
			{ "1_taming.tames_fight_their_own_kind", "Gezähmte kämpfen gegen ihre Art" },
			{ "1_taming.tames_fight_their_own_kind.d", "Ein gezähmtes Tier kämpft gegen wilde seiner Art, außer gegen eines, das satt ist und gerade gezähmt wird. Aus: Ein zahmer und ein wilder Wolf lassen sich in Ruhe, wie im Spiel." },
			{ "2_tame_levels.enabled", "An" },
			{ "2_tame_levels.enabled.d", "Gezähmte sammeln Erfahrung mit dem, was sie töten, und verdienen damit Sterne – bis zur Höchstzahl unten." },
			{ "2_tame_levels.most_stars", "Höchstens Sterne" },
			{ "2_tame_levels.most_stars.d", "So viele Sterne kann ein gezähmtes Tier höchstens verdienen." },
			{ "2_tame_levels.experience", "Erfahrung" },
			{ "2_tame_levels.experience.d", "Erfahrung pro Tötung, mal der Lebenspunkte des Getöteten." },
			{ "2_tame_levels.share_with_followers", "Anteil für Begleiter" },
			{ "2_tame_levels.share_with_followers.d", "Tötest du etwas, bekommt jedes Tier, das dir im Umkreis von 30 m folgt, diesen Anteil der Erfahrung. 0 ist aus." },
			{ "2_tame_levels.heal_on_new_star", "Heilen bei neuem Stern" },
			{ "2_tame_levels.heal_on_new_star.d", "Ein Tier, das einen Stern verdient, wird ganz geheilt." },
			{ "2_tame_levels.stars_passed_to_young", "Sterne für den Nachwuchs" },
			{ "2_tame_levels.stars_passed_to_young.d", "Nachwuchs kommt mit den Sternen der Eltern zur Welt, aber höchstens so vielen. 2 ist das des Spiels; den Rest verdient sich ein Tier selbst." },
			{ "3_stars_above_two.health_per_star", "Leben pro Stern" },
			{ "3_stars_above_two.health_per_star.d", "Über zwei Sternen gibt jeder Stern so viel vom Grundleben dazu. Bis zwei Sterne gilt das Spiel: jeder Stern das volle Grundleben." },
			{ "3_stars_above_two.damage_per_star", "Schaden pro Stern" },
			{ "3_stars_above_two.damage_per_star.d", "Über zwei Sternen gibt jeder Stern so viel vom Grundschaden dazu. Bis zwei Sterne gilt das Spiel: jeder Stern die Hälfte." },
			{ "3_stars_above_two.size_per_star", "Größe pro Stern" },
			{ "3_stars_above_two.size_per_star.d", "Über zwei Sternen macht jeder Stern die Kreatur um so viel größer." },
			{ "3_stars_above_two.largest_size", "Größte Größe" },
			{ "3_stars_above_two.largest_size.d", "So groß können Sterne eine Kreatur höchstens machen, mal ihrer Größe mit zwei Sternen." },
			{ "3_stars_above_two.loot_per_star", "Beute pro Stern" },
			{ "3_stars_above_two.loot_per_star.d", "Über zwei Sternen gibt jeder Stern so viel mal die Grundbeute dazu. Das Spiel verdoppelt sie mit jedem Stern – bei zehn Sternen wären das Hunderte Gegenstände." },
			{ "4_wild_stars.enabled", "An" },
			{ "4_wild_stars.enabled.d", "Eine wilde Kreatur, die mit zwei Sternen erscheint, kann weitere würfeln – bis zur Höchstzahl ihres Bioms unten." },
			{ "4_wild_stars.chance_per_star", "Chance pro Stern" },
			{ "4_wild_stars.chance_per_star.d", "Chance in Prozent für jeden Stern über zwei." },
			{ "4_wild_stars.meadows", "Wiesen" },
			{ "4_wild_stars.meadows.d", "Höchstens so viele Sterne hat eine wilde Kreatur hier. 2 ist das des Spiels." },
			{ "4_wild_stars.black_forest", "Schwarzwald" },
			{ "4_wild_stars.black_forest.d", "Höchstens so viele Sterne hat eine wilde Kreatur hier. 2 ist das des Spiels." },
			{ "4_wild_stars.swamp", "Sumpf" },
			{ "4_wild_stars.swamp.d", "Höchstens so viele Sterne hat eine wilde Kreatur hier. 2 ist das des Spiels." },
			{ "4_wild_stars.mountain", "Berge" },
			{ "4_wild_stars.mountain.d", "Höchstens so viele Sterne hat eine wilde Kreatur hier. 2 ist das des Spiels." },
			{ "4_wild_stars.plains", "Ebene" },
			{ "4_wild_stars.plains.d", "Höchstens so viele Sterne hat eine wilde Kreatur hier. 2 ist das des Spiels." },
			{ "4_wild_stars.ocean", "Ozean" },
			{ "4_wild_stars.ocean.d", "Höchstens so viele Sterne hat eine wilde Kreatur hier. 2 ist das des Spiels." },
			{ "4_wild_stars.mistlands", "Nebelland" },
			{ "4_wild_stars.mistlands.d", "Höchstens so viele Sterne hat eine wilde Kreatur hier. 2 ist das des Spiels." },
			{ "4_wild_stars.ashlands", "Aschland" },
			{ "4_wild_stars.ashlands.d", "Höchstens so viele Sterne hat eine wilde Kreatur hier. 2 ist das des Spiels." },
			{ "4_wild_stars.deep_north", "Tiefer Norden" },
			{ "4_wild_stars.deep_north.d", "Höchstens so viele Sterne hat eine wilde Kreatur hier. 2 ist das des Spiels." },
			{ "5_knocked_out.enabled", "An" },
			{ "5_knocked_out.enabled.d", "Ein gezähmtes Tier, das sterben würde, geht k. o., bleibt unverletzt liegen und steht wieder auf. Nur der Schlag eines Spielers – das Schlachtermesser – tötet es." },
			{ "5_knocked_out.recovery_time", "Erholungszeit" },
			{ "5_knocked_out.recovery_time.d", "Sekunden, bis ein Tier wieder aufsteht. Die Zeit läuft weiter, auch wenn niemand in der Nähe ist." },
			{ "5_knocked_out.health_when_up", "Leben beim Aufstehen" },
			{ "5_knocked_out.health_when_up.d", "Dieser Anteil seiner Lebenspunkte, wenn ein Tier wieder aufsteht." },
			{ "6_tameable_creatures.other_creatures", "Weitere Kreaturen" },
			{ "6_tameable_creatures.other_creatures.d", "Weitere Kreaturen, auch aus anderen Mods: „Prefab: Futter, Futter; Prefab: Futter“. Eine Kreatur braucht die Monster- oder Tier-KI des Spiels, um gezähmt zu werden." },
			{ "7_display.star_count_above_two", "Sternzahl über zwei" },
			{ "7_display.star_count_above_two.d", "Über zwei Sternen zeigt die Leiste über einer Kreatur einen Stern und daneben die Zahl der Sterne. Ist in BetrValheim „Alle Sterne“ an, zeigt BetrValheim sie stattdessen." },
			{ "7_display.experience_on_tames", "Erfahrung an Gezähmten" },
			{ "7_display.experience_on_tames.d", "Zeigt die Sterne eines Tiers und seine Erfahrung zum nächsten, wenn du es ansiehst." },
			{ "8_following", "Folgen" },
			{ "9_game_tames_food", "Futter der Spiel-Tiere" },
			{ "10_more_monsters", "Mehr Monster" },
			{ "10_more_monsters.everywhere", "Überall" },
			{ "10_more_monsters.everywhere.d", "Wie viele Kreaturen die offene Welt erscheinen lässt, mal der Menge des Spiels: wie viele einer Art gleichzeitig da sein dürfen und wie oft neue kommen. 1 ist das des Spiels, 2 doppelt so viele. Monster und Tiere gleichermaßen; Raids, Lager und Dungeons bleiben wie im Spiel." },
			{ "10_more_monsters.full_mornings", "Volle Morgen" },
			{ "10_more_monsters.full_mornings.d", "Ein Tagwesen wartet die Nacht über auf seinen Zug, statt erst im Morgengrauen neu zu warten, damit der Morgen nicht leer ist; ebenso ein Nachtwesen über den Tag. Höchstens so viele auf einmal, wie das Spiel von dieser Art zulässt." },
			{ "10_more_monsters.meadows", "Wiesen" },
			{ "10_more_monsters.meadows.d", "Eigener Faktor für dieses Biom statt „Überall“. 0 nimmt „Überall“." },
			{ "10_more_monsters.black_forest", "Schwarzwald" },
			{ "10_more_monsters.black_forest.d", "Eigener Faktor für dieses Biom statt „Überall“. 0 nimmt „Überall“." },
			{ "10_more_monsters.swamp", "Sumpf" },
			{ "10_more_monsters.swamp.d", "Eigener Faktor für dieses Biom statt „Überall“. 0 nimmt „Überall“." },
			{ "10_more_monsters.mountain", "Berge" },
			{ "10_more_monsters.mountain.d", "Eigener Faktor für dieses Biom statt „Überall“. 0 nimmt „Überall“." },
			{ "10_more_monsters.plains", "Ebene" },
			{ "10_more_monsters.plains.d", "Eigener Faktor für dieses Biom statt „Überall“. 0 nimmt „Überall“." },
			{ "10_more_monsters.ocean", "Ozean" },
			{ "10_more_monsters.ocean.d", "Eigener Faktor für dieses Biom statt „Überall“. 0 nimmt „Überall“." },
			{ "10_more_monsters.mistlands", "Nebelland" },
			{ "10_more_monsters.mistlands.d", "Eigener Faktor für dieses Biom statt „Überall“. 0 nimmt „Überall“." },
			{ "10_more_monsters.ashlands", "Aschland" },
			{ "10_more_monsters.ashlands.d", "Eigener Faktor für dieses Biom statt „Überall“. 0 nimmt „Überall“." },
			{ "10_more_monsters.deep_north", "Tiefer Norden" },
			{ "10_more_monsters.deep_north.d", "Eigener Faktor für dieses Biom statt „Überall“. 0 nimmt „Überall“." },
			{ "8_following.come_back_after_death", "Nach dem Tod mitkommen" },
			{ "8_following.come_back_after_death.d", "Stirbst du, kommen die Tiere, die dir folgen, mit dir zurück: Sie warten dort, wo du wieder erscheinst, und ein Tier, das k. o. ist, steht dort wieder auf." },
			{ "8_following.catch_up", "Nachholen" },
			{ "8_following.catch_up.d", "Ein Tier, das dir folgt und weit zurückfällt, wird zu dir gebracht. Nicht in Dungeons und nicht, während jemand darauf reitet." },
			{ "8_following.catch_up_distance", "Abstand zum Nachholen" },
			{ "8_following.catch_up_distance.d", "Ab so vielen Metern Rückstand wird ein folgendes Tier zu dir gebracht." }
		};

		private static readonly Dictionary<string, string> Brazilian = new Dictionary<string, string>(StringComparer.Ordinal)
		{
			{ "1_taming", "Domar" },
			{ "2_tame_levels", "Níveis das criaturas domadas" },
			{ "3_stars_above_two", "Estrelas acima de duas" },
			{ "4_wild_stars", "Estrelas selvagens" },
			{ "5_knocked_out", "Nocaute" },
			{ "6_tameable_creatures", "Criaturas domáveis" },
			{ "7_display", "Exibição" },
			{ "8_following", "Seguir" },
			{ "9_game_tames_food", "Comida das criaturas domáveis do jogo" },
			{ "10_more_monsters", "Mais monstros" },
			{ "1_taming.tame_more_creatures", "Domar mais criaturas" },
			{ "1_taming.tame_more_creatures.d", "Torna domáveis as criaturas de “Criaturas domáveis“. Desligado: só o que o próprio jogo deixa domar." },
			{ "1_taming.taming_time", "Tempo de domar" },
			{ "1_taming.taming_time.d", "Quanto tempo leva para domar, vezes o tempo que cada criatura precisa. 0,5 é metade do tempo. Vale também para as criaturas domáveis do jogo." },
			{ "1_taming.fed_time", "Tempo alimentado" },
			{ "1_taming.fed_time.d", "Quanto tempo uma criatura domada fica alimentada depois de comer, vezes os 10 minutos do jogo. Uma criatura que ainda está sendo domada fica alimentada pelo menos 10 minutos; senão, domar ficaria mais lento." },
			{ "1_taming.command_every_tame", "Comandar toda criatura domada" },
			{ "1_taming.command_every_tame.d", "Toda criatura domada segue você ou fica parada quando você interage com ela – não só os lobos." },
			{ "1_taming.breeding", "Filhotes" },
			{ "1_taming.breeding.d", "Criaturas domadas por este mod que se reproduzem têm filhotes já domados quando duas alimentadas ficam juntas." },
			{ "1_taming.tames_fight_their_own_kind", "Domadas lutam contra a própria espécie" },
			{ "1_taming.tames_fight_their_own_kind.d", "Uma criatura domada luta contra as selvagens da mesma espécie, menos contra uma que está alimentada e sendo domada. Desligado: um lobo domado e um selvagem se ignoram, como no jogo." },
			{ "2_tame_levels.enabled", "Ligado" },
			{ "2_tame_levels.enabled.d", "Criaturas domadas ganham experiência com o que matam e conquistam estrelas com ela – até o máximo abaixo." },
			{ "2_tame_levels.most_stars", "Máximo de estrelas" },
			{ "2_tame_levels.most_stars.d", "O máximo de estrelas que uma criatura domada pode conquistar." },
			{ "2_tame_levels.experience", "Experiência" },
			{ "2_tame_levels.experience.d", "Experiência por abate, vezes a vida do que foi morto." },
			{ "2_tame_levels.share_with_followers", "Parte para quem segue" },
			{ "2_tame_levels.share_with_followers.d", "Quando você mata algo, cada criatura domada que segue você num raio de 30 m recebe esta parte da experiência. 0 é desligado." },
			{ "2_tame_levels.heal_on_new_star", "Curar com nova estrela" },
			{ "2_tame_levels.heal_on_new_star.d", "Uma criatura que conquista uma estrela é curada por completo." },
			{ "2_tame_levels.stars_passed_to_young", "Estrelas passadas aos filhotes" },
			{ "2_tame_levels.stars_passed_to_young.d", "Filhotes nascem com as estrelas dos pais, mas no máximo esta quantidade. 2 é o do jogo; o resto a criatura conquista sozinha." },
			{ "3_stars_above_two.health_per_star", "Vida por estrela" },
			{ "3_stars_above_two.health_per_star.d", "Acima de duas estrelas, cada estrela soma esta parte da vida base. Até duas estrelas vale o jogo: cada estrela soma a vida base inteira." },
			{ "3_stars_above_two.damage_per_star", "Dano por estrela" },
			{ "3_stars_above_two.damage_per_star.d", "Acima de duas estrelas, cada estrela soma esta parte do dano base. Até duas estrelas vale o jogo: cada estrela soma metade." },
			{ "3_stars_above_two.size_per_star", "Tamanho por estrela" },
			{ "3_stars_above_two.size_per_star.d", "Acima de duas estrelas, cada estrela deixa a criatura este tanto maior." },
			{ "3_stars_above_two.largest_size", "Tamanho máximo" },
			{ "3_stars_above_two.largest_size.d", "O máximo que as estrelas podem aumentar uma criatura, vezes o tamanho dela com duas estrelas." },
			{ "3_stars_above_two.loot_per_star", "Saque por estrela" },
			{ "3_stars_above_two.loot_per_star.d", "Acima de duas estrelas, cada estrela soma esta quantidade de vezes o saque base. O jogo dobra o saque a cada estrela – com dez estrelas seriam centenas de itens." },
			{ "4_wild_stars.enabled", "Ligado" },
			{ "4_wild_stars.enabled.d", "Uma criatura selvagem que surge com duas estrelas pode sortear mais – até o máximo do seu bioma abaixo." },
			{ "4_wild_stars.chance_per_star", "Chance por estrela" },
			{ "4_wild_stars.chance_per_star.d", "Chance em porcentagem para cada estrela acima de duas." },
			{ "4_wild_stars.meadows", "Prado" },
			{ "4_wild_stars.meadows.d", "O máximo de estrelas de uma criatura selvagem aqui. 2 é o do jogo." },
			{ "4_wild_stars.black_forest", "Floresta negra" },
			{ "4_wild_stars.black_forest.d", "O máximo de estrelas de uma criatura selvagem aqui. 2 é o do jogo." },
			{ "4_wild_stars.swamp", "Pântano" },
			{ "4_wild_stars.swamp.d", "O máximo de estrelas de uma criatura selvagem aqui. 2 é o do jogo." },
			{ "4_wild_stars.mountain", "Montanha" },
			{ "4_wild_stars.mountain.d", "O máximo de estrelas de uma criatura selvagem aqui. 2 é o do jogo." },
			{ "4_wild_stars.plains", "Planície" },
			{ "4_wild_stars.plains.d", "O máximo de estrelas de uma criatura selvagem aqui. 2 é o do jogo." },
			{ "4_wild_stars.ocean", "Oceano" },
			{ "4_wild_stars.ocean.d", "O máximo de estrelas de uma criatura selvagem aqui. 2 é o do jogo." },
			{ "4_wild_stars.mistlands", "Terras nevoadas" },
			{ "4_wild_stars.mistlands.d", "O máximo de estrelas de uma criatura selvagem aqui. 2 é o do jogo." },
			{ "4_wild_stars.ashlands", "Terras Cinzentas" },
			{ "4_wild_stars.ashlands.d", "O máximo de estrelas de uma criatura selvagem aqui. 2 é o do jogo." },
			{ "4_wild_stars.deep_north", "Norte profundo" },
			{ "4_wild_stars.deep_north.d", "O máximo de estrelas de uma criatura selvagem aqui. 2 é o do jogo." },
			{ "5_knocked_out.enabled", "Ligado" },
			{ "5_knocked_out.enabled.d", "Uma criatura domada que morreria fica nocauteada: deita sem sofrer dano e depois levanta de novo. Só o golpe de um jogador – a Faca de açougueiro – a mata." },
			{ "5_knocked_out.recovery_time", "Tempo de recuperação" },
			{ "5_knocked_out.recovery_time.d", "Segundos até a criatura levantar. O tempo continua correndo mesmo sem ninguém por perto." },
			{ "5_knocked_out.health_when_up", "Vida ao levantar" },
			{ "5_knocked_out.health_when_up.d", "Esta parte da vida quando a criatura levanta." },
			{ "6_tameable_creatures.other_creatures", "Outras criaturas" },
			{ "6_tameable_creatures.other_creatures.d", "Mais criaturas, também de outros mods: “Prefab: Comida, Comida; Prefab: Comida“. A criatura precisa da IA de monstro ou de animal do jogo para ser domada." },
			{ "7_display.star_count_above_two", "Número de estrelas acima de duas" },
			{ "7_display.star_count_above_two.d", "Acima de duas estrelas, a barra sobre a criatura mostra uma estrela e o número de estrelas ao lado. Com “Todas as estrelas“ ligado no BetrValheim, quem mostra é o BetrValheim." },
			{ "7_display.experience_on_tames", "Experiência nas domadas" },
			{ "7_display.experience_on_tames.d", "Mostra as estrelas de uma criatura domada e a experiência até a próxima quando você olha para ela." },
			{ "8_following.come_back_after_death", "Voltar com você após a morte" },
			{ "8_following.come_back_after_death.d", "Quando você morre, as criaturas que seguem você voltam junto: esperam onde você renasce, e uma nocauteada levanta lá." },
			{ "8_following.catch_up", "Alcançar" },
			{ "8_following.catch_up.d", "Uma criatura que segue você e fica muito para trás é trazida até você. Não para dentro de masmorras e não enquanto alguém a monta." },
			{ "8_following.catch_up_distance", "Distância para alcançar" },
			{ "8_following.catch_up_distance.d", "A partir de quantos metros de distância uma criatura que segue você é trazida até você." },
			{ "10_more_monsters.everywhere", "Em todo lugar" },
			{ "10_more_monsters.everywhere.d", "Quantas criaturas o mundo aberto faz surgir, vezes a quantidade do jogo: quantas de uma espécie podem estar por perto ao mesmo tempo e com que frequência surgem novas. 1 é o do jogo, 2 o dobro. Monstros e animais igualmente; invasões, acampamentos e masmorras ficam como no jogo." },
			{ "10_more_monsters.full_mornings", "Manhãs cheias" },
			{ "10_more_monsters.full_mornings.d", "Uma criatura do dia espera a noite toda pela sua vez em vez de começar a esperar de novo ao amanhecer, para que as manhãs não fiquem vazias; da mesma forma uma criatura da noite espera o dia todo. No máximo tantas de uma vez quanto o jogo permite dessa espécie." },
			{ "10_more_monsters.meadows", "Prado" },
			{ "10_more_monsters.meadows.d", "Fator próprio para este bioma, no lugar de “Em todo lugar“. 0 usa “Em todo lugar“." },
			{ "10_more_monsters.black_forest", "Floresta negra" },
			{ "10_more_monsters.black_forest.d", "Fator próprio para este bioma, no lugar de “Em todo lugar“. 0 usa “Em todo lugar“." },
			{ "10_more_monsters.swamp", "Pântano" },
			{ "10_more_monsters.swamp.d", "Fator próprio para este bioma, no lugar de “Em todo lugar“. 0 usa “Em todo lugar“." },
			{ "10_more_monsters.mountain", "Montanha" },
			{ "10_more_monsters.mountain.d", "Fator próprio para este bioma, no lugar de “Em todo lugar“. 0 usa “Em todo lugar“." },
			{ "10_more_monsters.plains", "Planície" },
			{ "10_more_monsters.plains.d", "Fator próprio para este bioma, no lugar de “Em todo lugar“. 0 usa “Em todo lugar“." },
			{ "10_more_monsters.ocean", "Oceano" },
			{ "10_more_monsters.ocean.d", "Fator próprio para este bioma, no lugar de “Em todo lugar“. 0 usa “Em todo lugar“." },
			{ "10_more_monsters.mistlands", "Terras nevoadas" },
			{ "10_more_monsters.mistlands.d", "Fator próprio para este bioma, no lugar de “Em todo lugar“. 0 usa “Em todo lugar“." },
			{ "10_more_monsters.ashlands", "Terras Cinzentas" },
			{ "10_more_monsters.ashlands.d", "Fator próprio para este bioma, no lugar de “Em todo lugar“. 0 usa “Em todo lugar“." },
			{ "10_more_monsters.deep_north", "Norte profundo" },
			{ "10_more_monsters.deep_north.d", "Fator próprio para este bioma, no lugar de “Em todo lugar“. 0 usa “Em todo lugar“." }
		};

		private static readonly Language GermanTexts = new Language
		{
			Texts = German,
			Name = (Creatures.Spec spec) => spec.German,
			Food = "Was es frisst: Prefab-Namen von Gegenständen, durch Kommas getrennt. Leer: nicht zähmbar.",
			GameFood = "Was es frisst: Prefab-Namen von Gegenständen, durch Kommas getrennt. Leer: das Futter des Spiels. Zähmdauer und Nachwuchs bleiben die des Spiels."
		};

		private static readonly Language BrazilianTexts = new Language
		{
			Texts = Brazilian,
			Name = (Creatures.Spec spec) => (!Creatures.Brazilian.TryGetValue(spec.Key, out var value)) ? spec.English : value,
			Food = "O que ela come: nomes de prefab de itens, separados por vírgulas. Vazio: não domável.",
			GameFood = "O que ela come: nomes de prefab de itens, separados por vírgulas. Vazio: a comida do jogo. Tempo de domar e filhotes ficam como no jogo."
		};

		private static Language Of(string language)
		{
			if (string.Equals(language, "German", StringComparison.OrdinalIgnoreCase))
			{
				return GermanTexts;
			}
			if (string.Equals(language, "Portuguese_Brazilian", StringComparison.OrdinalIgnoreCase))
			{
				return BrazilianTexts;
			}
			return null;
		}

		public static Dictionary<string, string> For(ConfigFile config, string language)
		{
			Language language2 = Of(language);
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
			Dictionary<string, Creatures.Spec> dictionary2 = new Dictionary<string, Creatures.Spec>(StringComparer.Ordinal);
			Creatures.Spec[] all = Creatures.All;
			foreach (Creatures.Spec spec in all)
			{
				dictionary2[spec.Key] = spec;
			}
			Dictionary<string, Creatures.Spec> dictionary3 = new Dictionary<string, Creatures.Spec>(StringComparer.Ordinal);
			all = Creatures.GameTames;
			foreach (Creatures.Spec spec2 in all)
			{
				dictionary3[spec2.Key] = spec2;
			}
			foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in config)
			{
				string text = Slug(item.Key.Section);
				string text2 = text + "." + Slug(item.Key.Key);
				ConfigDescription description = item.Value.Description;
				string text3 = ((description != null) ? description.Description : null) ?? string.Empty;
				if (text3.EndsWith(" Synced from the server.", StringComparison.Ordinal))
				{
					text3 = text3.Substring(0, text3.Length - " Synced from the server.".Length);
				}
				if (!dictionary.ContainsKey(text))
				{
					dictionary[text] = ((language2 != null && language2.Texts.TryGetValue(text, out var value)) ? value : Title(item.Key.Section));
				}
				Creatures.Spec value3;
				if (item.Key.Section == "6 - Tameable creatures" && dictionary2.TryGetValue(item.Key.Key, out var value2))
				{
					dictionary[text2] = ((language2 != null) ? language2.Name(value2) : value2.English);
					dictionary[text2 + ".d"] = ((language2 != null) ? language2.Food : text3);
				}
				else if (item.Key.Section == "9 - Game tames food" && dictionary3.TryGetValue(item.Key.Key, out value3))
				{
					dictionary[text2] = ((language2 != null) ? language2.Name(value3) : value3.English);
					dictionary[text2 + ".d"] = ((language2 != null) ? language2.GameFood : text3);
				}
				else
				{
					dictionary[text2] = ((language2 != null && language2.Texts.TryGetValue(text2, out var value4)) ? value4 : item.Key.Key);
					dictionary[text2 + ".d"] = ((language2 != null && language2.Texts.TryGetValue(text2 + ".d", out var value5)) ? value5 : text3);
				}
			}
			return dictionary;
		}

		private static string Title(string section)
		{
			int num = section.IndexOf(" - ", StringComparison.Ordinal);
			if (num <= 0)
			{
				return section;
			}
			return section.Substring(num + 3);
		}

		private static string Slug(string text)
		{
			StringBuilder stringBuilder = new StringBuilder(text.Length);
			bool flag = false;
			string text2 = text.ToLowerInvariant();
			foreach (char c in text2)
			{
				if (char.IsLetterOrDigit(c))
				{
					if (flag && stringBuilder.Length > 0)
					{
						stringBuilder.Append('_');
					}
					stringBuilder.Append(c);
					flag = false;
				}
				else
				{
					flag = true;
				}
			}
			return stringBuilder.ToString();
		}
	}
	internal static class Spawns
	{
		private struct Original
		{
			public int Max;

			public float Interval;
		}

		[HarmonyPatch(typeof(SpawnSystem), "Awake")]
		private static class Adopt
		{
			private static void Postfix(SpawnSystem __instance)
			{
				foreach (SpawnSystemList spawnList in __instance.m_spawnLists)
				{
					if ((Object)(object)spawnList != (Object)null)
					{
						Take(spawnList.m_spawners);
					}
				}
				foreach (AltBiome altBiome in AltBiomeList.m_altBiomes)
				{
					if (altBiome != null)
					{
						Take(altBiome.m_spawn);
					}
				}
			}
		}

		private static readonly Dictionary<SpawnData, Original> Originals = new Dictionary<SpawnData, Original>();

		private static readonly Dictionary<List<SpawnData>, int> Lists = new Dictionary<List<SpawnData>, int>();

		public static void Refresh(string section)
		{
			if (section == "10 - More monsters" && Originals.Count > 0)
			{
				ApplyAll();
			}
		}

		private static void Take(List<SpawnData> list)
		{
			if (list == null || (Lists.TryGetValue(list, out var value) && value == list.Count))
			{
				return;
			}
			Lists[list] = list.Count;
			foreach (SpawnData item in list)
			{
				if (item != null && !Originals.ContainsKey(item))
				{
					Originals[item] = new Original
					{
						Max = item.m_maxSpawned,
						Interval = item.m_spawnInterval
					};
					Apply(item, Originals[item]);
				}
			}
		}

		private static void ApplyAll()
		{
			foreach (KeyValuePair<SpawnData, Original> original in Originals)
			{
				Apply(original.Key, original.Value);
			}
		}

		private static void Apply(SpawnData data, Original original)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			float num = Factor(data.m_biome);
			data.m_maxSpawned = ((original.Max > 0) ? Mathf.Max(1, Mathf.RoundToInt((float)original.Max * num)) : 0);
			data.m_spawnInterval = original.Interval / num;
		}

		private static float Factor(Biome biomes)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			float value = Settings.Monsters.Value;
			float num = 0f;
			bool flag = false;
			foreach (KeyValuePair<Biome, ConfigEntry<float>> biomeMonster in Settings.BiomeMonsters)
			{
				if ((biomes & biomeMonster.Key) != 0)
				{
					float value2 = biomeMonster.Value.Value;
					float num2 = ((value2 > 0f) ? value2 : value);
					num = (flag ? Mathf.Max(num, num2) : num2);
					flag = true;
				}
			}
			return Mathf.Clamp(flag ? num : value, 0.25f, 5f);
		}
	}
	internal static class StarHud
	{
		private sealed class Count
		{
			public TextMeshProUGUI Text;

			public int Stars = -1;
		}

		[HarmonyPatch(typeof(EnemyHud), "UpdateHuds")]
		private static class Weave
		{
			private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
			{
				//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
				//IL_00bd: Expected O, but got Unknown
				//IL_012a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0134: Expected O, but got Unknown
				//IL_0141: Unknown result type (might be due to invalid IL or missing references)
				//IL_014b: Expected O, but got Unknown
				FieldInfo fieldInfo = AccessTools.Field(HudData, "m_level3");
				MethodInfo methodInfo = AccessTools.Method(typeof(GameObject), "SetActive", (Type[])null, (Type[])null);
				MethodInfo getLevel = AccessTools.Method(typeof(Character), "GetLevel", (Type[])null, (Type[])null);
				MethodInfo methodInfo2 = AccessTools.Method(typeof(StarHud), "Shown", (Type[])null, (Type[])null);
				MethodInfo methodInfo3 = AccessTools.Method(typeof(StarHud), "Apply", (Type[])null, (Type[])null);
				List<CodeInstruction> list = new List<CodeInstruction>(instructions);
				int num = list.FindIndex((CodeInstruction c) => CodeInstructionExtensions.Calls(c, getLevel));
				if (num < 0)
				{
					Log.Warning("The star count above two could not be set up; bars show the game's own stars.");
					return list;
				}
				list.Insert(num + 1, new CodeInstruction(OpCodes.Call, (object)methodInfo2));
				for (int num2 = num + 2; num2 < list.Count; num2++)
				{
					if (!CodeInstructionExtensions.LoadsField(list[num2], fieldInfo, false) || !CodeInstructionExtensions.IsLdloc(list[num2 - 1], (LocalBuilder)null))
					{
						continue;
					}
					CodeInstruction val = list[num2 - 1];
					for (int num3 = num2 + 1; num3 < list.Count; num3++)
					{
						if (CodeInstructionExtensions.Calls(list[num3], methodInfo))
						{
							list.Insert(num3 + 1, new CodeInstruction(val.opcode, val.operand));
							list.Insert(num3 + 2, new CodeInstruction(OpCodes.Call, (object)methodInfo3));
							return list;
						}
					}
					break;
				}
				Log.Warning("The star count above two could not be set up; bars show the game's own stars.");
				return list;
			}
		}

		private static readonly Type HudData = AccessTools.Inner(typeof(EnemyHud), "HudData");

		private static readonly FieldRef<object, Character> CharacterOf = AccessTools.FieldRefAccess<object, Character>(AccessTools.Field(HudData, "m_character"));

		private static readonly FieldRef<object, RectTransform> OneStarOf = AccessTools.FieldRefAccess<object, RectTransform>(AccessTools.Field(HudData, "m_level2"));

		private static readonly FieldRef<object, TextMeshProUGUI> NameOf = AccessTools.FieldRefAccess<object, TextMeshProUGUI>(AccessTools.Field(HudData, "m_name"));

		private static readonly ConditionalWeakTable<RectTransform, Count> Counts = new ConditionalWeakTable<RectTransform, Count>();

		private static bool _anyCount;

		private static readonly Color StarColor = new Color(1f, 0.85f, 0.25f, 1f);

		private const string BetrValheimGuid = "dezzycode.valheim.betrvalheim";

		private static ConfigEntry<bool> _siblingStars;

		private static bool _siblingLooked;

		private static int Shown(int level)
		{
			if (level <= 3 || !OwnCount())
			{
				return level;
			}
			return 2;
		}

		private static bool OwnCount()
		{
			if (Settings.StarNumber.Value)
			{
				return !SiblingStars();
			}
			return false;
		}

		private static bool SiblingStars()
		{
			if (!_siblingLooked)
			{
				_siblingLooked = true;
				if (Chainloader.PluginInfos.TryGetValue("dezzycode.valheim.betrvalheim", out var value) && (Object)(object)((value != null) ? value.Instance : null) != (Object)null)
				{
					value.Instance.Config.TryGetEntry<bool>("9 - HUD (local)", "Enemy stars", ref _siblingStars);
				}
			}
			if (_siblingStars != null)
			{
				return _siblingStars.Value;
			}
			return false;
		}

		private static void Apply(object bar)
		{
			Character val = CharacterOf.Invoke(bar);
			RectTransform val2 = OneStarOf.Invoke(bar);
			if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null)
			{
				return;
			}
			int level = val.GetLevel();
			bool flag = level > 3 && OwnCount();
			if (!flag && !_anyCount)
			{
				return;
			}
			if (!Counts.TryGetValue(val2, out var value))
			{
				if (!flag)
				{
					return;
				}
				value = new Count
				{
					Text = Make(val2, NameOf.Invoke(bar))
				};
				Counts.Add(val2, value);
				_anyCount = true;
			}
			if (!flag)
			{
				if (value.Stars != -1)
				{
					((Component)value.Text).gameObject.SetActive(false);
					value.Stars = -1;
				}
				return;
			}
			int num = level - 1;
			if (value.Stars != num)
			{
				value.Stars = num;
				((TMP_Text)value.Text).text = num.ToString();
				((Component)value.Text).gameObject.SetActive(true);
			}
		}

		private static TextMeshProUGUI Make(RectTransform one, TextMeshProUGUI name)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: 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_0047: 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_0052: 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_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: 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_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			Transform obj = ((Transform)one).Find("star");
			RectTransform