Decompiled source of Starborn Breeding v1.0.1

plugins/StarbornBreeding.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using StarbornBreeding.Core;
using StarbornBreeding.Dev;
using StarbornBreeding.Gameplay;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[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 StarbornBreeding
{
	public static class Cfg
	{
		public static ConfigEntry<int> ChanceOneIn;

		public static ConfigEntry<string> ChancePerAnimal;

		public static ConfigEntry<bool> LuckCanRepeat;

		public static ConfigEntry<int> MaxStars;

		public static ConfigEntry<bool> Announce;

		public static ConfigEntry<bool> ShowBlessing;

		private static string _tableText;

		private static Dictionary<string, int> _table = new Dictionary<string, int>();

		public static Dictionary<string, int> Table
		{
			get
			{
				string text = ChancePerAnimal?.Value ?? "";
				if (text != _tableText)
				{
					List<string> list = new List<string>();
					_table = Luck.ParseTable(text, list);
					_tableText = text;
					if (list.Count > 0)
					{
						ManualLogSource log = StarbornBreedingPlugin.Log;
						if (log != null)
						{
							log.LogWarning((object)("ChancePerAnimal: ignored " + string.Join(", ", list) + " (expected Prefab:N)"));
						}
					}
				}
				return _table;
			}
		}

		public static void Bind(ConfigFile c)
		{
			//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_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Expected O, but got Unknown
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Expected O, but got Unknown
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Expected O, but got Unknown
			ConfigurationManagerAttributes val = new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			};
			ChanceOneIn = c.Bind<int>("1 - Luck", "ChanceOneIn", 30, new ConfigDescription("A newborn tame animal gains a star with a chance of 1 in this number: 30 means about one birth in thirty. 1 makes every birth lucky, 0 turns the luck off.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100000), new object[1] { val }));
			ChancePerAnimal = c.Bind<string>("1 - Luck", "ChancePerAnimal", "", new ConfigDescription("Own chances for single animals as \"Prefab:N\", comma separated, for example \"Wolf:20, Lox:50, Hen:0\". The parent's prefab name counts first, then the newborn's (Wolf_cub, ChickenEgg). 0 turns the luck off for that animal. Animals not listed use ChanceOneIn.", (AcceptableValueBase)null, new object[1] { val }));
			LuckCanRepeat = c.Bind<bool>("1 - Luck", "LuckCanRepeat", true, new ConfigDescription("After a lucky roll the Norns roll once more at the same chance, and every success adds another star: at 1 in 30, two extra stars are 1 in 900. Off: never more than one extra star per birth.", (AcceptableValueBase)null, new object[1] { val }));
			MaxStars = c.Bind<int>("1 - Luck", "MaxStars", 2, new ConfigDescription("Luck never lifts a newborn above this many stars. Vanilla creatures show at most 2 stars; raise it only with a mod that knows higher levels. A parent already at the cap has young at the cap, as in vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 9), new object[1] { val }));
			Announce = c.Bind<bool>("2 - Client", "Announce", true, "Show a message and play a chime when a lucky animal is born near you (within 64 m).");
			ShowBlessing = c.Bind<bool>("2 - Client", "ShowBlessing", true, "Lucky animals and eggs keep a line in their hover text that says how many stars they gained at birth.");
		}
	}
	public static class Lang
	{
		private static readonly (string key, string en, string de)[] Texts = new(string, string, string)[11]
		{
			("sb_msg_title", "The Norns smile on your herd", "Die Nornen sind deiner Herde hold"),
			("sb_msg_title_twice", "The Norns smile twice on your herd", "Die Nornen sind deiner Herde doppelt hold"),
			("sb_msg_born_1", "{0} – born with one star", "{0} – mit einem Stern geboren"),
			("sb_msg_born_2", "{0} – born with two stars", "{0} – mit zwei Sternen geboren"),
			("sb_msg_born_n", "{0} – born with {1} stars", "{0} – mit {1} Sternen geboren"),
			("sb_msg_laid_1", "{0} – laid with one star", "{0} – mit einem Stern gelegt"),
			("sb_msg_laid_2", "{0} – laid with two stars", "{0} – mit zwei Sternen gelegt"),
			("sb_msg_laid_n", "{0} – laid with {1} stars", "{0} – mit {1} Sternen gelegt"),
			("sb_blessed_1", "Norn-blessed: one star more at birth", "Nornensegen: ein Stern mehr bei der Geburt"),
			("sb_blessed_2", "Norn-blessed: two stars more at birth", "Nornensegen: zwei Sterne mehr bei der Geburt"),
			("sb_blessed_n", "Norn-blessed: {0} stars more at birth", "Nornensegen: {0} Sterne mehr bei der Geburt")
		};

		public static void Register()
		{
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			Dictionary<string, string> dictionary2 = new Dictionary<string, string>();
			(string, string, string)[] texts = Texts;
			for (int i = 0; i < texts.Length; i++)
			{
				(string, string, string) tuple = texts[i];
				string item = tuple.Item1;
				string item2 = tuple.Item2;
				string item3 = tuple.Item3;
				dictionary[item] = item2;
				dictionary2[item] = item3;
			}
			string text = "English";
			localization.AddTranslation(ref text, dictionary);
			text = "German";
			localization.AddTranslation(ref text, dictionary2);
		}

		public static string T(string token, params object[] args)
		{
			string text = ((Localization.instance != null) ? Localization.instance.Localize("$" + token) : token);
			if (args == null || args.Length == 0)
			{
				return text;
			}
			try
			{
				return string.Format(text, args);
			}
			catch (FormatException)
			{
				return text;
			}
		}

		public static string L(string token)
		{
			if (Localization.instance == null)
			{
				return token;
			}
			return Localization.instance.Localize(token);
		}
	}
	[BepInPlugin("zap.starbornbreeding", "Starborn Breeding", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class StarbornBreedingPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "zap.starbornbreeding";

		public const string PluginName = "Starborn Breeding";

		public const string PluginVersion = "1.0.1";

		private Harmony _harmony;

		public static StarbornBreedingPlugin Instance { get; private set; }

		public static string PluginDir { get; private set; }

		public static ManualLogSource Log { get; private set; }

		public static string AssetPath(string fileName)
		{
			return Path.Combine(PluginDir ?? "", "Assets", fileName);
		}

		private void Awake()
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			PluginDir = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
			Cfg.Bind(((BaseUnityPlugin)this).Config);
			Lang.Register();
			_harmony = new Harmony("zap.starbornbreeding");
			_harmony.PatchAll(typeof(StarbornBreedingPlugin).Assembly);
			SelfTest.TryStart(((Component)this).gameObject);
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
}
namespace StarbornBreeding.Gameplay
{
	public static class Announce
	{
		[HarmonyPatch(typeof(Game), "Start")]
		private static class RegisterOnGameStart
		{
			private static void Postfix()
			{
				_effects = null;
				try
				{
					Register();
				}
				catch (Exception ex)
				{
					StarbornBreedingPlugin.Log.LogWarning((object)("Could not register SB_LuckyBirth: " + ex.Message));
				}
			}
		}

		public const string RpcName = "SB_LuckyBirth";

		public const float Range = 64f;

		private static ZRoutedRpc _registeredOn;

		private static EffectList _effects;

		public static string LastShown;

		public static void Register()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null && instance != _registeredOn)
			{
				instance.Register<ZPackage>("SB_LuckyBirth", (Action<long, ZPackage>)RPC_LuckyBirth);
				_registeredOn = instance;
			}
		}

		public static void Send(Vector3 pos, string nameToken, int stars, int gained, bool egg)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			Register();
			if (ZRoutedRpc.instance != null)
			{
				ZPackage val = new ZPackage();
				val.Write(pos);
				val.Write(nameToken ?? "");
				val.Write(stars);
				val.Write(gained);
				val.Write(egg);
				ZRoutedRpc.instance.InvokeRoutedRPC(0L, "SB_LuckyBirth", new object[1] { val });
			}
		}

		private static void RPC_LuckyBirth(long sender, ZPackage p)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Vector3 val = p.ReadVector3();
				string nameToken = p.ReadString();
				int stars = p.ReadInt();
				int gained = p.ReadInt();
				bool egg = p.ReadBool();
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)localPlayer == (Object)null) && !GUIManager.IsHeadless() && Cfg.Announce.Value && !(Vector3.Distance(((Component)localPlayer).transform.position, val) > 64f))
				{
					Show(val, Message(nameToken, stars, gained, egg));
				}
			}
			catch (Exception ex)
			{
				StarbornBreedingPlugin.Log.LogWarning((object)("Lucky birth message: " + ex.Message));
			}
		}

		public static string Message(string nameToken, int stars, int gained, bool egg)
		{
			string text = (string.IsNullOrEmpty(nameToken) ? "" : Lang.L(nameToken));
			string text2 = Lang.T((gained >= 2) ? "sb_msg_title_twice" : "sb_msg_title");
			string text3 = Lang.T((egg ? "sb_msg_laid_" : "sb_msg_born_") + ((stars <= 2) ? stars.ToString() : "n"), text, stars);
			return text2 + "\n<size=80%>" + text3 + "</size>";
		}

		private static void Show(Vector3 pos, string text)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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)
			LastShown = text;
			MessageHud instance = MessageHud.instance;
			if (instance != null)
			{
				instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false, true);
			}
			EffectList val = Effects();
			if (val != null)
			{
				val.Create(pos + Vector3.up * 0.5f, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
			}
		}

		private static EffectList Effects()
		{
			//IL_00d0: 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_00e6: Expected O, but got Unknown
			//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)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Expected O, but got Unknown
			if (_effects != null)
			{
				return _effects;
			}
			ZNetScene instance = ZNetScene.instance;
			object obj;
			if (instance == null)
			{
				obj = null;
			}
			else
			{
				GameObject prefab = instance.GetPrefab("Player");
				obj = ((prefab != null) ? prefab.GetComponent<Player>() : null);
			}
			Player val = (Player)obj;
			if (val?.m_skillLevelupEffects?.m_effectPrefabs == null)
			{
				return null;
			}
			List<EffectData> list = new List<EffectData>();
			EffectData[] effectPrefabs = val.m_skillLevelupEffects.m_effectPrefabs;
			foreach (EffectData val2 in effectPrefabs)
			{
				if (!((Object)(object)val2?.m_prefab == (Object)null) && val2.m_enabled && !((Object)(object)val2.m_prefab.GetComponent<ZNetView>() != (Object)null))
				{
					list.Add(new EffectData
					{
						m_prefab = val2.m_prefab,
						m_enabled = true,
						m_variant = val2.m_variant
					});
				}
			}
			_effects = new EffectList
			{
				m_effectPrefabs = list.ToArray()
			};
			return _effects;
		}
	}
	public static class BirthLuck
	{
		private sealed class Birth
		{
			public Character Parent;

			public string ParentName;

			public int BaseLevel;

			public int Target;

			public int OneIn;

			public GameObject Newborn;

			public int Gained;

			public bool Egg;
		}

		[HarmonyPatch(typeof(Procreation), "Procreate")]
		private static class RollWhenDue
		{
			private static void Prefix(Procreation __instance)
			{
				try
				{
					Begin(__instance);
				}
				catch (Exception ex)
				{
					_birth = null;
					StarbornBreedingPlugin.Log.LogWarning((object)("Birth roll failed: " + ex.Message));
				}
			}

			private static Exception Finalizer(Exception __exception)
			{
				try
				{
					End();
				}
				catch (Exception ex)
				{
					StarbornBreedingPlugin.Log.LogWarning((object)("Lucky birth: " + ex.Message));
				}
				return __exception;
			}
		}

		[HarmonyPatch(typeof(Character), "SetLevel")]
		private static class LiftNewborn
		{
			private static void Prefix(Character __instance, ref int level)
			{
				Birth birth = _birth;
				if (birth != null && (Object)(object)__instance != (Object)(object)birth.Parent && !__instance.IsPlayer())
				{
					if ((!((Object)(object)birth.Newborn != (Object)null) || !((Object)(object)birth.Newborn != (Object)(object)((Component)__instance).gameObject)) && level >= 1 && level < birth.Target)
					{
						birth.Gained = birth.Target - birth.BaseLevel;
						level = birth.Target;
						birth.Newborn = ((Component)__instance).gameObject;
						birth.Egg = false;
						Blessing.Mark(__instance, birth.Gained);
					}
				}
				else
				{
					Blessing.CarryTo(__instance);
				}
			}
		}

		[HarmonyPatch(typeof(ItemDrop), "SetQuality")]
		private static class LiftEgg
		{
			private static void Prefix(ItemDrop __instance, ref int quality)
			{
				Birth birth = _birth;
				if (birth != null && quality >= 1 && quality < birth.Target && !((Object)(object)((Component)__instance).GetComponent<EggGrow>() == (Object)null) && (!((Object)(object)birth.Newborn != (Object)null) || !((Object)(object)birth.Newborn != (Object)(object)((Component)__instance).gameObject)))
				{
					birth.Gained = birth.Target - birth.BaseLevel;
					quality = birth.Target;
					birth.Newborn = ((Component)__instance).gameObject;
					birth.Egg = true;
					Blessing.Mark(__instance.m_itemData, birth.Gained);
				}
			}
		}

		private static Birth _birth;

		public static int Rolls;

		public static int Lucky;

		public static GameObject LastLucky;

		private static void Begin(Procreation proc)
		{
			_birth = null;
			if ((Object)(object)proc == (Object)null || (Object)(object)proc.m_nview == (Object)null || !proc.m_nview.IsValid() || !proc.m_nview.IsOwner() || (Object)(object)proc.m_tameable == (Object)null || !proc.m_tameable.IsTamed() || (Object)(object)ZNet.instance == (Object)null || !proc.IsPregnant() || !proc.IsDue())
			{
				return;
			}
			int num = Luck.VanillaLevel(Object.op_Implicit((Object)(object)proc.m_character) ? proc.m_character.GetLevel() : proc.m_minOffspringLevel, proc.m_minOffspringLevel);
			string prefabName = Utils.GetPrefabName(((Component)proc).gameObject);
			string offspring = (Object.op_Implicit((Object)(object)proc.m_offspring) ? Utils.GetPrefabName(proc.m_offspring) : "");
			int num2 = Luck.OneInFor(Cfg.Table, prefabName, offspring, Cfg.ChanceOneIn.Value);
			int num3 = Luck.Room(num, Cfg.MaxStars.Value);
			if (num2 > 0 && num3 > 0)
			{
				Rolls++;
				int num4 = Luck.ExtraStars(num2, Cfg.LuckCanRepeat.Value, num3, () => Random.value);
				if (num4 > 0)
				{
					_birth = new Birth
					{
						Parent = proc.m_character,
						ParentName = prefabName,
						BaseLevel = num,
						Target = num + num4,
						OneIn = num2
					};
				}
			}
		}

		private static void End()
		{
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			Birth birth = _birth;
			_birth = null;
			if (!((Object)(object)birth?.Newborn == (Object)null))
			{
				Lucky++;
				LastLucky = birth.Newborn;
				string text = ((!birth.Egg) ? birth.Newborn.GetComponent<Character>()?.m_name : birth.Newborn.GetComponent<ItemDrop>()?.m_itemData?.m_shared?.m_name);
				StarbornBreedingPlugin.Log.LogInfo((object)($"Lucky birth: {birth.ParentName} level {birth.BaseLevel} had {Utils.GetPrefabName(birth.Newborn)} at level {birth.Target} " + $"(+{birth.Gained}, 1 in {birth.OneIn}; {Lucky} of {Rolls} births this session)"));
				Announce.Send(birth.Newborn.transform.position, text ?? "", birth.Target - 1, birth.Gained, birth.Egg);
			}
		}
	}
	public static class Blessing
	{
		[HarmonyPatch(typeof(Growup), "GrowUpdate")]
		private static class KeepWhenGrowingUp
		{
			private static void Prefix(Growup __instance)
			{
				BeginCarry(((Component)__instance).GetComponent<Character>());
			}

			private static Exception Finalizer(Exception __exception)
			{
				EndCarry();
				return __exception;
			}
		}

		[HarmonyPatch(typeof(EggGrow), "GrowUpdate")]
		private static class KeepWhenHatching
		{
			private static void Prefix(EggGrow __instance)
			{
				int num = Of(((Object)(object)__instance.m_item != (Object)null) ? __instance.m_item.m_itemData : ((Component)__instance).GetComponent<ItemDrop>()?.m_itemData);
				if (num > 0)
				{
					_carry = num;
					_carrySource = null;
				}
			}

			private static Exception Finalizer(Exception __exception)
			{
				EndCarry();
				return __exception;
			}
		}

		[HarmonyPatch(typeof(Tameable), "GetHoverText")]
		private static class TameHover
		{
			private static void Postfix(Tameable __instance, ref string __result)
			{
				try
				{
					__result = WithLine(__result, Of(__instance.m_character));
				}
				catch (Exception ex)
				{
					StarbornBreedingPlugin.Log.LogWarning((object)("Hover: " + ex.Message));
				}
			}
		}

		[HarmonyPatch(typeof(EggGrow), "GetHoverText")]
		private static class EggHover
		{
			private static void Postfix(EggGrow __instance, ref string __result)
			{
				try
				{
					__result = WithLine(__result, Of(__instance.m_item?.m_itemData));
				}
				catch (Exception ex)
				{
					StarbornBreedingPlugin.Log.LogWarning((object)("Hover: " + ex.Message));
				}
			}
		}

		[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[]
		{
			typeof(ItemData),
			typeof(int),
			typeof(bool),
			typeof(float),
			typeof(int),
			typeof(bool)
		})]
		private static class EggTooltip
		{
			private static void Postfix(ItemData item, ref string __result)
			{
				int num = Of(item);
				if (num > 0 && Cfg.ShowBlessing.Value)
				{
					__result = Line(num) + "\n" + __result;
				}
			}
		}

		public static readonly int ZdoKey = StringExtensionMethods.GetStableHashCode("sb_luck");

		public const string ItemKey = "zap.starbornbreeding.luck";

		private const string Color = "#F2C94C";

		private static int _carry;

		private static Character _carrySource;

		public static void Mark(Character c, int stars)
		{
			ZDO val = (((Object)(object)c != (Object)null && (Object)(object)c.m_nview != (Object)null && c.m_nview.IsValid()) ? c.m_nview.GetZDO() : null);
			if (val != null && stars > 0)
			{
				val.Set(ZdoKey, stars, false);
			}
		}

		public static void Mark(ItemData item, int stars)
		{
			if (item != null && stars > 0)
			{
				item.m_customData["zap.starbornbreeding.luck"] = stars.ToString(CultureInfo.InvariantCulture);
			}
		}

		public static int Of(Character c)
		{
			ZDO val = (((Object)(object)c != (Object)null && (Object)(object)c.m_nview != (Object)null && c.m_nview.IsValid()) ? c.m_nview.GetZDO() : null);
			if (val == null)
			{
				return 0;
			}
			return val.GetInt(ZdoKey, 0);
		}

		public static int Of(ItemData item)
		{
			if (item?.m_customData == null || !item.m_customData.TryGetValue("zap.starbornbreeding.luck", out var value))
			{
				return 0;
			}
			if (!int.TryParse(value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
			{
				return 0;
			}
			return result;
		}

		public static void CarryTo(Character c)
		{
			if (_carry > 0 && (Object)(object)c != (Object)(object)_carrySource && !c.IsPlayer())
			{
				Mark(c, _carry);
			}
		}

		internal static void BeginCarry(Character source)
		{
			int num = Of(source);
			if (num > 0)
			{
				_carry = num;
				_carrySource = source;
			}
		}

		internal static void EndCarry()
		{
			_carry = 0;
			_carrySource = null;
		}

		public static string Line(int stars)
		{
			return "<color=#F2C94C>" + Lang.T("sb_blessed_" + ((stars <= 2) ? stars.ToString() : "n"), stars) + "</color>";
		}

		private static string WithLine(string text, int stars)
		{
			if (stars <= 0 || string.IsNullOrEmpty(text) || !Cfg.ShowBlessing.Value)
			{
				return text;
			}
			int num = text.IndexOf('\n');
			if (num >= 0)
			{
				return text.Substring(0, num + 1) + Line(stars) + text.Substring(num);
			}
			return text + "\n" + Line(stars);
		}
	}
	public static class Compat
	{
		[HarmonyPatch]
		private static class HenTurnsFighter
		{
			private static MethodBase Target()
			{
				return AccessTools.Method(AccessTools.TypeByName("CluckFighters.Gameplay.HenBrood"), "Turn", (Type[])null, (Type[])null);
			}

			private static bool Prepare()
			{
				return Target() != null;
			}

			private static MethodBase TargetMethod()
			{
				return Target();
			}

			private static void Prefix(Component __instance)
			{
				Blessing.BeginCarry(((Object)(object)__instance != (Object)null) ? __instance.GetComponent<Character>() : null);
			}

			private static Exception Finalizer(Exception __exception)
			{
				Blessing.EndCarry();
				return __exception;
			}
		}
	}
	public static class EggRecipes
	{
		[HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[]
		{
			typeof(string),
			typeof(int),
			typeof(int),
			typeof(bool)
		})]
		private static class OrdinaryEggsFirst
		{
			private static bool Prefix(Inventory __instance, string name, int amount, int itemQuality, bool worldLevelBased)
			{
				if (itemQuality >= 0 || amount <= 0 || !IsEggName(name))
				{
					return true;
				}
				List<ItemData> list = new List<ItemData>();
				foreach (ItemData item in __instance.m_inventory)
				{
					if (item.m_shared.m_name == name && (!worldLevelBased || item.m_worldLevel >= Game.m_worldLevel))
					{
						list.Add(item);
					}
				}
				list.Sort(delegate(ItemData a, ItemData b)
				{
					int num2 = Blessing.Of(a).CompareTo(Blessing.Of(b));
					return (num2 == 0) ? a.m_quality.CompareTo(b.m_quality) : num2;
				});
				foreach (ItemData item2 in list)
				{
					int num = Math.Min(item2.m_stack, amount);
					item2.m_stack -= num;
					amount -= num;
					if (amount <= 0)
					{
						break;
					}
				}
				__instance.m_inventory.RemoveAll((ItemData x) => x.m_stack <= 0);
				__instance.Changed(false, false);
				return false;
			}
		}

		[HarmonyPatch(typeof(Player), "HaveRequirementItems", new Type[]
		{
			typeof(Recipe),
			typeof(bool),
			typeof(int),
			typeof(int)
		})]
		private static class CountAllEggs
		{
			private static void Postfix(Player __instance, Recipe piece, bool discover, int qualityLevel, int amount, ref bool __result)
			{
				if (__result || discover || !HasEgg(piece))
				{
					return;
				}
				try
				{
					__result = Recount(__instance, piece, qualityLevel, amount);
				}
				catch (Exception ex)
				{
					StarbornBreedingPlugin.Log.LogWarning((object)("Egg recipe: " + ex.Message));
				}
			}
		}

		private static ObjectDB _db;

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

		private static bool IsEgg(Requirement r)
		{
			if ((Object)(object)r.m_resItem != (Object)null)
			{
				return (Object)(object)((Component)r.m_resItem).GetComponent<EggGrow>() != (Object)null;
			}
			return false;
		}

		private static bool HasEgg(Recipe recipe)
		{
			if (recipe?.m_resources == null)
			{
				return false;
			}
			Requirement[] resources = recipe.m_resources;
			for (int i = 0; i < resources.Length; i++)
			{
				if (IsEgg(resources[i]))
				{
					return true;
				}
			}
			return false;
		}

		private static bool Recount(Player p, Recipe recipe, int qualityLevel, int amount)
		{
			CraftingStation currentCraftingStation = p.GetCurrentCraftingStation();
			Inventory inventory = ((Humanoid)p).GetInventory();
			bool requireOnlyOneIngredient = recipe.m_requireOnlyOneIngredient;
			Requirement[] resources = recipe.m_resources;
			foreach (Requirement val in resources)
			{
				if (((Object)(object)currentCraftingStation != (Object)null && currentCraftingStation.m_upgrader != val.m_upgraderResource) || ((Object)(object)currentCraftingStation == (Object)null && val.m_upgraderResource) || !Object.op_Implicit((Object)(object)val.m_resItem))
				{
					continue;
				}
				string name = val.m_resItem.m_itemData.m_shared.m_name;
				int num = val.GetAmount(qualityLevel) * amount;
				int num2 = 0;
				if (IsEgg(val))
				{
					num2 = inventory.CountItems(name, -1, true);
				}
				else
				{
					for (int j = 1; j <= val.m_resItem.m_itemData.m_shared.m_maxQuality; j++)
					{
						num2 = Math.Max(num2, inventory.CountItems(name, j, true));
					}
				}
				if (requireOnlyOneIngredient)
				{
					if (num2 >= num)
					{
						return true;
					}
				}
				else if (num2 < num)
				{
					return false;
				}
			}
			return !requireOnlyOneIngredient;
		}

		private static bool IsEggName(string name)
		{
			if ((Object)(object)ObjectDB.instance == (Object)null || string.IsNullOrEmpty(name))
			{
				return false;
			}
			if ((Object)(object)_db != (Object)(object)ObjectDB.instance)
			{
				_db = ObjectDB.instance;
				EggNames.Clear();
				foreach (GameObject item in _db.m_items)
				{
					if ((Object)(object)item != (Object)null && (Object)(object)item.GetComponent<EggGrow>() != (Object)null)
					{
						ItemDrop component = item.GetComponent<ItemDrop>();
						if (component != null)
						{
							EggNames.Add(component.m_itemData.m_shared.m_name);
						}
					}
				}
			}
			return EggNames.Contains(name);
		}
	}
	public static class EggStacks
	{
		[HarmonyPatch(typeof(ItemData), "IsSameType")]
		private static class SlotStacking
		{
			private static void Postfix(ItemData __instance, ItemData other, ref bool __result)
			{
				if (__result && !SameBlessing(__instance, other))
				{
					__result = false;
				}
			}
		}

		[HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })]
		private static class AddAnywhere
		{
			private static void Prefix(ItemData item)
			{
				_adding = item;
			}

			private static Exception Finalizer(Exception __exception)
			{
				_adding = null;
				return __exception;
			}
		}

		[HarmonyPatch(typeof(Inventory), "AddItem", new Type[]
		{
			typeof(ItemData),
			typeof(Vector2i)
		})]
		private static class AddAtSlot
		{
			private static void Prefix(ItemData item)
			{
				_adding = item;
			}

			private static Exception Finalizer(Exception __exception)
			{
				_adding = null;
				return __exception;
			}
		}

		[HarmonyPatch(typeof(Inventory), "FindFreeStackItem")]
		private static class PickupStacking
		{
			private static void Postfix(Inventory __instance, string name, int quality, float worldLevel, ref ItemData __result)
			{
				ItemData adding = _adding;
				if (adding == null || __result == null || SameBlessing(__result, adding))
				{
					return;
				}
				__result = null;
				foreach (ItemData item in __instance.m_inventory)
				{
					if (item.m_shared.m_name == name && item.m_quality == quality && item.m_stack < item.m_shared.m_maxStackSize && (float)item.m_worldLevel == worldLevel && SameBlessing(item, adding))
					{
						__result = item;
						break;
					}
				}
			}
		}

		[HarmonyPatch(typeof(ItemDrop), "AutoStackItems")]
		private static class GroundStacking
		{
			private static bool Prefix(ItemDrop __instance)
			{
				//IL_0022: Unknown result type (might be due to invalid IL or missing references)
				if ((Object)(object)((Component)__instance).GetComponent<EggGrow>() == (Object)null)
				{
					return true;
				}
				int num = Blessing.Of(__instance.m_itemData);
				Collider[] array = Physics.OverlapSphere(((Component)__instance).transform.position, 4f, LayerMask.GetMask(new string[1] { "item" }));
				foreach (Collider val in array)
				{
					ItemDrop val2 = (Object.op_Implicit((Object)(object)val.attachedRigidbody) ? ((Component)val.attachedRigidbody).GetComponent<ItemDrop>() : null);
					if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2 == (Object)(object)__instance) && !(val2.m_itemData.m_shared.m_name != __instance.m_itemData.m_shared.m_name) && Blessing.Of(val2.m_itemData) != num)
					{
						return false;
					}
				}
				return true;
			}
		}

		[ThreadStatic]
		private static ItemData _adding;

		private static bool SameBlessing(ItemData a, ItemData b)
		{
			return Blessing.Of(a) == Blessing.Of(b);
		}
	}
}
namespace StarbornBreeding.Dev
{
	public class SelfTest : MonoBehaviour
	{
		private struct Born
		{
			public Character Parent;

			public Character Young;

			public ItemDrop Egg;

			public int Level
			{
				get
				{
					if (!((Object)(object)Young != (Object)null))
					{
						if (!((Object)(object)Egg != (Object)null))
						{
							return 0;
						}
						return Egg.m_itemData.m_quality;
					}
					return Young.GetLevel();
				}
			}

			public int Mark
			{
				get
				{
					if (!((Object)(object)Young != (Object)null))
					{
						if (!((Object)(object)Egg != (Object)null))
						{
							return 0;
						}
						return Blessing.Of(Egg.m_itemData);
					}
					return Blessing.Of(Young);
				}
			}

			public string Name
			{
				get
				{
					if (!((Object)(object)Young != (Object)null))
					{
						if (!((Object)(object)Egg != (Object)null))
						{
							return "nothing";
						}
						return Utils.GetPrefabName(((Component)Egg).gameObject);
					}
					return Utils.GetPrefabName(((Component)Young).gameObject);
				}
			}
		}

		private int _oneIn;

		private int _max;

		private bool _repeat;

		private string _table;

		private string _out;

		private readonly List<string> _report = new List<string>();

		private int _pass;

		private int _fail;

		private unsafe IEnumerator Births()
		{
			Player p = Player.m_localPlayer;
			if (Flag("DE"))
			{
				Localization.instance.SetLanguage("German");
			}
			EnvMan.instance.m_debugTimeOfDay = true;
			EnvMan.instance.m_debugTime = 0.38f;
			EnvMan.instance.SetForceEnvironment("Clear");
			p.SetGodMode(true);
			Raven.m_tutorialsEnabled = false;
			ClutterSystem.instance.m_amountScale = 0.35f;
			ClutterSystem.instance.ClearAll();
			Vector3 c = FlatSpot(((Component)p).transform.position);
			SelfTest selfTest = this;
			Vector3 val = c;
			selfTest.Note("paddock at " + ((object)(*(Vector3*)(&val))/*cast due to .constrained prefix*/).ToString());
			Stand(p, c + new Vector3(0f, 0f, -7f));
			yield return (object)new WaitForSecondsRealtime(3f);
			ClearArea(c, 45f);
			yield return (object)new WaitForSecondsRealtime(1f);
			_oneIn = Cfg.ChanceOneIn.Value;
			_repeat = Cfg.LuckCanRepeat.Value;
			_table = Cfg.ChancePerAnimal.Value;
			_max = Cfg.MaxStars.Value;
			Check(_oneIn == 30 && _repeat && _table == "" && _max == 2, $"defaults 1 in {_oneIn}, repeat {_repeat}, table '{_table}', max {_max} (must be 30, True, '', 2)");
			Set(1, repeat: false, "", 2);
			Stand(p, c + new Vector3(-4f, 0f, -4.5f));
			yield return (object)new WaitForSecondsRealtime(1f);
			Born a = Birth("Wolf", 1, c + new Vector3(-4f, 0f, 0f));
			Check((Object)(object)a.Young != (Object)null && a.Name == "Wolf_cub", "wolf birth gives " + a.Name + " (must be Wolf_cub)");
			Check(a.Level == 2, $"lucky cub level {a.Level} (must be 2: one star more than its 0-star mother)");
			Check((Object)(object)a.Young != (Object)null && a.Young.m_nview.GetZDO().GetInt(ZDOVars.s_level, 0) == 2, "lucky cub level is in its ZDO");
			Check(a.Mark == 1, $"lucky cub blessing {a.Mark} (must be 1)");
			SelfTest selfTest2 = this;
			bool ok = (Object)(object)a.Parent != (Object)null && a.Parent.GetLevel() == 1;
			Character parent = a.Parent;
			selfTest2.Check(ok, $"mother level {((parent != null) ? new int?(parent.GetLevel()) : ((int?)null))} (must stay 1)");
			Check(Announce.LastShown != null && Announce.LastShown.Contains(Lang.L("$enemy_wolfcub")), "message names the cub: " + Plain(Announce.LastShown));
			SelfTest selfTest3 = this;
			bool ok2 = (Object)(object)MessageHud.instance != (Object)null && Plain(MessageHud.instance.m_messageCenterText.text).Contains(Lang.T("sb_msg_title"));
			MessageHud instance = MessageHud.instance;
			selfTest3.Check(ok2, "centre message shows: " + Plain((instance != null) ? instance.m_messageCenterText.text : null));
			yield return ((MonoBehaviour)this).StartCoroutine(MainShot("s1_lucky_birth", Look(((Component)a.Young).transform.position + Vector3.up * 0.45f, 25f, 3.4f, 1.2f), 50f, hud: true));
			Set(1, repeat: true, "", 2);
			Born b = Birth("Wolf", 1, c + new Vector3(-1.5f, 0f, 0f));
			Check(b.Level == 3, $"twice lucky cub level {b.Level} (must be 3: two stars from a 0-star mother)");
			Check(b.Mark == 2, $"twice lucky blessing {b.Mark} (must be 2)");
			Check(Plain(Announce.LastShown).Contains(Lang.T("sb_msg_title_twice")), "twice message: " + Plain(Announce.LastShown));
			Born cap = Birth("Wolf", 3, c + new Vector3(1.5f, 0f, 0f));
			Check(cap.Level == 3, $"cub of a 2-star mother level {cap.Level} (must be 3: already at MaxStars 2)");
			Check(cap.Mark == 0 && Announce.LastShown == null, $"no blessing {cap.Mark} and no message '{Plain(Announce.LastShown)}' at the cap (must be 0 and none)");
			Born born = Birth("Wolf", 2, c + new Vector3(3f, 0f, 0f));
			Check(born.Level == 3 && born.Mark == 1, $"1-star mother, repeat on: level {born.Level} blessing {born.Mark} (must be 3 and 1: only one star of room)");
			Set(1, repeat: true, "", 4);
			Born born2 = Birth("Wolf", 1, c + new Vector3(4.5f, 0f, 0f));
			Check(born2.Level == 5 && born2.Mark == 4, $"MaxStars 4: level {born2.Level} blessing {born2.Mark} (must be 5 and 4)");
			Remove((Component)(object)born2.Young);
			Remove((Component)(object)born2.Parent);
			Set(0, repeat: true, "", 2);
			int rolls = BirthLuck.Rolls;
			Born born3 = Birth("Wolf", 2, c + new Vector3(6f, 0f, 0f));
			Check(born3.Level == 2 && born3.Mark == 0, $"ChanceOneIn 0: cub level {born3.Level} blessing {born3.Mark} (must be 2 and 0, like vanilla)");
			Check(BirthLuck.Rolls == rolls, "ChanceOneIn 0 does not roll at all");
			Remove((Component)(object)born3.Young);
			Remove((Component)(object)born3.Parent);
			Set(1, repeat: false, "Wolf:0", 2);
			Born born4 = Birth("Wolf", 1, c + new Vector3(6f, 0f, 2f));
			Check(born4.Level == 1, $"table Wolf:0 with ChanceOneIn 1: cub level {born4.Level} (must be 1)");
			Remove((Component)(object)born4.Young);
			Remove((Component)(object)born4.Parent);
			Set(0, repeat: false, "Boar_piggy:1", 2);
			Born born5 = Birth("Boar", 1, c + new Vector3(6f, 0f, 4f));
			Check(born5.Name == "Boar_piggy" && born5.Level == 2, $"table Boar_piggy:1 with ChanceOneIn 0: {born5.Name} level {born5.Level} (must be Boar_piggy, 2: the newborn's name counts too)");
			Remove((Component)(object)born5.Young);
			Remove((Component)(object)born5.Parent);
			Set(1, repeat: false, "", 2);
			(string, string)[] array = new(string, string)[4]
			{
				("Boar", "Boar_piggy"),
				("Lox", "Lox_Calf"),
				("Moose", "Moose_calf"),
				("Wolfborn1", "Wolf_cub")
			};
			for (int i = 0; i < array.Length; i++)
			{
				var (text, text2) = array[i];
				if ((Object)(object)ZNetScene.instance.GetPrefab(text) == (Object)null)
				{
					Note(text + " not in this pack, skipped");
					continue;
				}
				Born born6 = Birth(text, 1, c + new Vector3(-8f, 0f, 6f));
				Check(born6.Name == text2 && born6.Level == 2 && born6.Mark == 1, $"{text}: {born6.Name} level {born6.Level} blessing {born6.Mark} (must be {text2}, 2, 1)");
				Remove((Component)(object)born6.Young);
				Remove((Component)(object)born6.Parent);
			}
			yield return null;
			Character adult = GrowUp(a.Young);
			Check((Object)(object)adult != (Object)null && Utils.GetPrefabName(((Component)adult).gameObject) == "Wolf", "lucky cub grew into " + (Object.op_Implicit((Object)(object)adult) ? Utils.GetPrefabName(((Component)adult).gameObject) : "nothing") + " (must be Wolf)");
			Check((Object)(object)adult != (Object)null && adult.GetLevel() == 2 && Blessing.Of(adult) == 1, $"grown wolf level {((adult != null) ? new int?(adult.GetLevel()) : ((int?)null))} blessing {Blessing.Of(adult)} (must be 2 and 1)");
			Character adult2 = GrowUp(b.Young);
			Check((Object)(object)adult2 != (Object)null && adult2.GetLevel() == 3 && Blessing.Of(adult2) == 2, $"twice lucky grown wolf level {((adult2 != null) ? new int?(adult2.GetLevel()) : ((int?)null))} blessing {Blessing.Of(adult2)} (must be 3 and 2)");
			Character plain = GrowUp(cap.Young);
			Check((Object)(object)plain != (Object)null && Blessing.Of(plain) == 0, $"ordinary cub grown: blessing {Blessing.Of(plain)} (must be 0)");
			string s = (((Object)(object)adult != (Object)null) ? ((Component)adult).GetComponent<Tameable>().GetHoverText() : "");
			Check(Plain(s).Contains(Plain(Blessing.Line(1))), "grown wolf hover: " + Plain(s));
			string s2 = (((Object)(object)plain != (Object)null) ? ((Component)plain).GetComponent<Tameable>().GetHoverText() : "");
			Check(!Plain(s2).Contains(Lang.T("sb_blessed_1")), "ordinary wolf hover has no blessing: " + Plain(s2));
			Cfg.ShowBlessing.Value = false;
			Check(!Plain(((Component)adult).GetComponent<Tameable>().GetHoverText()).Contains(Plain(Blessing.Line(1))), "ShowBlessing off hides the line");
			Cfg.ShowBlessing.Value = true;
			Born egg = Birth("Hen", 1, c + new Vector3(-6f, 0f, -3f));
			Check((Object)(object)egg.Egg != (Object)null && egg.Name == "ChickenEgg", "hen lays " + egg.Name + " (must be ChickenEgg)");
			Check(egg.Level == 2 && egg.Mark == 1, $"lucky egg quality {egg.Level} blessing {egg.Mark} (must be 2 and 1)");
			yield return null;
			yield return null;
			if ((Object)(object)egg.Egg != (Object)null)
			{
				ItemData val2 = egg.Egg.m_itemData.Clone();
				ItemDrop.LoadFromZDO(val2, egg.Egg.m_nview.GetZDO(), -1);
				Check(val2.m_quality == 2 && Blessing.Of(val2) == 1, $"egg saved in its ZDO: quality {val2.m_quality} blessing {Blessing.Of(val2)} (must be 2 and 1)");
				Check(InventoryRoundTrip(egg.Egg.m_itemData), "egg keeps quality and blessing through an inventory save and load");
				Check(Stacks(egg.Egg.m_itemData), "blessed and ordinary eggs of the same quality keep separate stacks");
				string hoverText = ((Component)egg.Egg).GetComponent<EggGrow>().GetHoverText();
				Check(Plain(hoverText).Contains(Plain(Blessing.Line(1))), "egg hover: " + Plain(hoverText));
				string tooltip = ItemData.GetTooltip(egg.Egg.m_itemData, 2, false, 0f, -1, false);
				Check(Plain(tooltip).Contains(Plain(Blessing.Line(1))), "egg tooltip: " + Plain(tooltip).Substring(0, Mathf.Min(90, Plain(tooltip).Length)));
				Remove((Component)(object)egg.Parent);
				Stand(p, ((Component)egg.Egg).transform.position + new Vector3(0.3f, 0f, -1f));
				Collider componentInChildren = ((Component)egg.Egg).GetComponentInChildren<Collider>();
				Vector3 val3;
				if (!((Object)(object)componentInChildren != (Object)null))
				{
					val3 = ((Component)egg.Egg).transform.position + Vector3.up * 0.08f;
				}
				else
				{
					Bounds bounds = componentInChildren.bounds;
					val3 = ((Bounds)(ref bounds)).center;
				}
				Vector3 lookAt = val3;
				yield return ((MonoBehaviour)this).StartCoroutine(HudShot("s4_egg_hover", p, lookAt, 1.5f));
				Note("egg shot hover object: " + (((Object)(object)((Humanoid)p).GetHoverObject() != (Object)null) ? ((Object)((Humanoid)p).GetHoverObject()).name : "none"));
				Character chick = null;
				yield return ((MonoBehaviour)this).StartCoroutine(Hatch(egg.Egg, delegate(Character x)
				{
					chick = x;
				}));
				SelfTest selfTest4 = this;
				bool ok3 = (Object)(object)chick != (Object)null && Utils.GetPrefabName(((Component)chick).gameObject) == "Chicken" && chick.GetLevel() == 2 && Blessing.Of(chick) == 1;
				string arg = (Object.op_Implicit((Object)(object)chick) ? Utils.GetPrefabName(((Component)chick).gameObject) : "nothing");
				Character obj = chick;
				selfTest4.Check(ok3, $"egg hatched into {arg} level {((obj != null) ? new int?(obj.GetLevel()) : ((int?)null))} blessing {Blessing.Of(chick)} (must be Chicken, 2, 1)");
				yield return null;
				Character val4 = GrowUp(chick);
				Check((Object)(object)val4 != (Object)null && Utils.GetPrefabName(((Component)val4).gameObject) == "Hen" && val4.GetLevel() == 2 && Blessing.Of(val4) == 1, string.Format("chick grew into {0} level {1} blessing {2} (must be Hen, 2, 1)", Object.op_Implicit((Object)(object)val4) ? Utils.GetPrefabName(((Component)val4).gameObject) : "nothing", (val4 != null) ? new int?(val4.GetLevel()) : ((int?)null), Blessing.Of(val4)));
				Remove((Component)(object)val4);
			}
			Remove((Component)(object)egg.Parent);
			Born ask = Birth("Asksvin", 2, c + new Vector3(-9f, 0f, -3f));
			Check(ask.Name == "AsksvinEgg" && ask.Level == 3 && ask.Mark == 1, $"1-star asksvin lays {ask.Name} quality {ask.Level} blessing {ask.Mark} (must be AsksvinEgg, 3, 1)");
			yield return null;
			if ((Object)(object)ask.Egg != (Object)null)
			{
				Character hatchling = null;
				yield return ((MonoBehaviour)this).StartCoroutine(Hatch(ask.Egg, delegate(Character x)
				{
					hatchling = x;
				}));
				SelfTest selfTest5 = this;
				bool ok4 = (Object)(object)hatchling != (Object)null && hatchling.GetLevel() == 3 && Blessing.Of(hatchling) == 1;
				Character obj2 = hatchling;
				selfTest5.Check(ok4, $"asksvin hatchling level {((obj2 != null) ? new int?(obj2.GetLevel()) : ((int?)null))} blessing {Blessing.Of(hatchling)} (must be 3 and 1)");
				Remove((Component)(object)hatchling);
			}
			Remove((Component)(object)ask.Parent);
			Announce.LastShown = null;
			Announce.Send(((Component)p).transform.position + new Vector3(90f, 0f, 0f), "$enemy_wolfcub", 1, 1, egg: false);
			Check(Announce.LastShown == null, "a lucky birth 90 m away shows no message");
			Announce.Send(((Component)p).transform.position + new Vector3(30f, 0f, 0f), "$enemy_wolfcub", 1, 1, egg: false);
			Check(Announce.LastShown != null, "a lucky birth 30 m away shows the message");
			Cfg.Announce.Value = false;
			Announce.LastShown = null;
			Announce.Send(((Component)p).transform.position, "$enemy_wolfcub", 1, 1, egg: false);
			Check(Announce.LastShown == null, "Announce off shows no message");
			Cfg.Announce.Value = true;
			yield return ((MonoBehaviour)this).StartCoroutine(Rate(c + new Vector3(10f, 0f, -6f)));
			yield return (object)new WaitForSecondsRealtime(12f);
			int num = Object.FindObjectsByType<Transform>((FindObjectsSortMode)0).Count((Transform t) => (Object)(object)t.parent == (Object)null && ((Object)t).name.Contains("levelup"));
			Check(num == 0, $"glow and chime objects left after 12 s: {num} (must be 0)");
			yield return ((MonoBehaviour)this).StartCoroutine(Compat(c, adult));
			Set(_oneIn, _repeat, _table, _max);
			yield return ((MonoBehaviour)this).StartCoroutine(Pictures(p, c, a.Parent, adult, adult2, plain));
			Note($"births rolled {BirthLuck.Rolls}, lucky {BirthLuck.Lucky}");
		}

		private void Set(int oneIn, bool repeat, string table, int max)
		{
			Cfg.ChanceOneIn.Value = oneIn;
			Cfg.LuckCanRepeat.Value = repeat;
			Cfg.ChancePerAnimal.Value = table;
			Cfg.MaxStars.Value = max;
		}

		private Born Birth(string parent, int level, Vector3 at)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			HashSet<Character> chars = new HashSet<Character>(Character.GetAllCharacters());
			HashSet<ItemDrop> before = new HashSet<ItemDrop>(Object.FindObjectsByType<ItemDrop>((FindObjectsSortMode)0));
			Born result = new Born
			{
				Parent = SpawnTame(parent, at, level, 180f)
			};
			if ((Object)(object)result.Parent == (Object)null)
			{
				return result;
			}
			chars.Add(result.Parent);
			Procreation component = ((Component)result.Parent).GetComponent<Procreation>();
			result.Parent.m_nview.GetZDO().Set(ZDOVars.s_pregnant, 1L);
			Announce.LastShown = null;
			component.Procreate();
			result.Young = (from x in Character.GetAllCharacters()
				where (Object)(object)x != (Object)null && !chars.Contains(x) && Vector3.Distance(((Component)x).transform.position, at) < 8f
				select x).FirstOrDefault();
			if ((Object)(object)result.Young == (Object)null)
			{
				result.Egg = Newest<ItemDrop>(at, 8f, (Func<ItemDrop, bool>)((ItemDrop x) => (Object)(object)((Component)x).GetComponent<EggGrow>() != (Object)null), before);
			}
			Note($"birth {parent} level {level}: {result.Name} level {result.Level} blessing {result.Mark}");
			return result;
		}

		private Character GrowUp(Character young)
		{
			if ((Object)(object)young == (Object)null)
			{
				return null;
			}
			Growup component = ((Component)young).GetComponent<Growup>();
			if ((Object)(object)component == (Object)null || (Object)(object)component.m_baseAI == (Object)null)
			{
				Note("grow: " + ((Object)young).name + " has no started Growup");
				return null;
			}
			HashSet<Character> chars = new HashSet<Character>(Character.GetAllCharacters());
			component.m_growTime = -1f;
			component.GrowUpdate();
			return ((IEnumerable<Character>)Character.GetAllCharacters()).FirstOrDefault((Func<Character, bool>)((Character x) => (Object)(object)x != (Object)null && !chars.Contains(x) && Vector3.Distance(((Component)x).transform.position, ((Component)young).transform.position) < 3f));
		}

		private IEnumerator Hatch(ItemDrop egg, Action<Character> done)
		{
			EggGrow e = ((Component)egg).GetComponent<EggGrow>();
			Vector3 at = ((Component)egg).transform.position;
			HashSet<Character> chars = new HashSet<Character>(Character.GetAllCharacters());
			e.m_requireNearbyFire = false;
			e.m_requireUnderRoof = false;
			e.m_growTime = 0f;
			while ((Object)(object)e != (Object)null && (Object)(object)e.m_nview == (Object)null)
			{
				yield return null;
			}
			if ((Object)(object)e == (Object)null)
			{
				done(null);
				yield break;
			}
			e.GrowUpdate();
			float until = Time.realtimeSinceStartup + 4f;
			while (Time.realtimeSinceStartup < until && (Object)(object)e != (Object)null && (Object)(object)e.m_nview != (Object)null && e.m_nview.IsValid())
			{
				yield return (object)new WaitForSecondsRealtime(0.5f);
				if ((Object)(object)e != (Object)null && (Object)(object)e.m_nview != (Object)null && e.m_nview.IsValid())
				{
					e.GrowUpdate();
				}
			}
			yield return null;
			done(((IEnumerable<Character>)Character.GetAllCharacters()).FirstOrDefault((Func<Character, bool>)((Character x) => (Object)(object)x != (Object)null && !chars.Contains(x) && Vector3.Distance(((Component)x).transform.position, at) < 3f)));
		}

		private static bool InventoryRoundTrip(ItemData item)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Expected O, but got Unknown
			Inventory val = new Inventory("test", (Sprite)null, 4, 2);
			val.AddItem(item.Clone());
			ZPackage val2 = new ZPackage();
			val.Save(val2);
			Inventory val3 = new Inventory("test", (Sprite)null, 4, 2);
			val3.Load(new ZPackage(val2.GetArray()));
			ItemData val4 = val3.GetAllItems().FirstOrDefault();
			if (val4 != null && val4.m_quality == item.m_quality)
			{
				return Blessing.Of(val4) == Blessing.Of(item);
			}
			return false;
		}

		private bool Stacks(ItemData blessed)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: 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)
			ItemData val = blessed.Clone();
			val.m_customData.Remove("zap.starbornbreeding.luck");
			val.m_stack = 1;
			ItemData val2 = blessed.Clone();
			val2.m_stack = 1;
			Inventory val3 = new Inventory("test", (Sprite)null, 4, 2);
			val3.AddItem(val.Clone());
			val3.AddItem(val2.Clone());
			val3.AddItem(val.Clone());
			List<ItemData> allItems = val3.GetAllItems();
			int num = allItems.Count((ItemData i) => Blessing.Of(i) > 0);
			int num2 = allItems.Where((ItemData i) => Blessing.Of(i) == 0).Sum((ItemData i) => i.m_stack);
			Note("egg stacks: " + string.Join(", ", allItems.Select((ItemData i) => $"{i.m_stack}x q{i.m_quality} blessing {Blessing.Of(i)}")));
			if (allItems.Count == 2 && num == 1 && num2 == 2 && !val2.IsSameType(val))
			{
				return val.IsSameType(val.Clone());
			}
			return false;
		}

		private IEnumerator Rate(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)
			Set(30, repeat: true, "", 2);
			Character sow = SpawnTame("Boar", at, 1, 180f);
			Procreation proc = ((Component)sow).GetComponent<Procreation>();
			int rolls0 = BirthLuck.Rolls;
			int lucky0 = BirthLuck.Lucky;
			int one = 0;
			int two = 0;
			HashSet<Character> before = new HashSet<Character>(Character.GetAllCharacters());
			for (int i = 0; i < 1800; i++)
			{
				sow.m_nview.GetZDO().Set(ZDOVars.s_pregnant, 1L);
				proc.Procreate();
				foreach (Character item in (from x in Character.GetAllCharacters()
					where (Object)(object)x != (Object)null && !before.Contains(x)
					select x).ToList())
				{
					before.Add(item);
					if (item.GetLevel() == 2)
					{
						one++;
					}
					if (item.GetLevel() == 3)
					{
						two++;
					}
					ZNetScene.instance.Destroy(((Component)item).gameObject);
				}
				if (i % 100 == 99)
				{
					yield return null;
				}
			}
			int num = BirthLuck.Rolls - rolls0;
			int num2 = BirthLuck.Lucky - lucky0;
			Check(num == 1800, $"{1800} births rolled {num} times (must be {1800})");
			Check(num2 >= 35 && num2 <= 85 && num2 == one + two, $"lucky births {num2} of {1800} = 1 in {((num2 > 0) ? (1800f / (float)num2) : 0f):0.0} (one star {one}, two stars {two}; must be 35..85, about 1 in 30)");
			Remove((Component)(object)sow);
		}

		private IEnumerator Pictures(Player p, Vector3 c, Character mother, Character lucky, Character twice, Character plain)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			Vector3 row = c + new Vector3(0f, 0f, 5f);
			Character[] array = (Character[])(object)new Character[3] { plain, lucky, twice };
			foreach (Character item in Character.GetAllCharacters().ToList())
			{
				if ((Object)(object)item != (Object)null && !item.IsPlayer() && !array.Contains(item) && Vector3.Distance(((Component)item).transform.position, c) < 40f)
				{
					Remove((Component)(object)item);
				}
			}
			Stand(p, row + new Vector3(0f, 0f, -7f));
			for (int i = 0; i < array.Length; i++)
			{
				Freeze(array[i], Ground(row + new Vector3((float)(i - 1) * 2.4f, 0f, 0f)), 200f - (float)i * 20f);
			}
			yield return (object)new WaitForSecondsRealtime(2.5f);
			yield return ((MonoBehaviour)this).StartCoroutine(MainShot("s2_three_wolves", Look(row + Vector3.up * 0.55f, 180f, 6f, 1.9f)));
			if ((Object)(object)lucky != (Object)null)
			{
				Stand(p, ((Component)lucky).transform.position + new Vector3(0f, 0f, -2.6f));
				yield return ((MonoBehaviour)this).StartCoroutine(HudShot("s3_lucky_wolf_hover", p, ((Component)lucky).transform.position + Vector3.up * 0.6f, 1.5f));
			}
			Note("screenshots in " + Path.GetFileName(_out));
		}

		private static void Freeze(Character c, Vector3 at, float yaw)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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)
			if (!((Object)(object)c == (Object)null))
			{
				c.m_speed = (c.m_walkSpeed = (c.m_runSpeed = (c.m_turnSpeed = (c.m_runTurnSpeed = 0f))));
				Quaternion val = Quaternion.Euler(0f, yaw, 0f);
				((Component)c).transform.SetPositionAndRotation(at, val);
				c.m_body.position = at;
				c.m_body.rotation = val;
				c.m_body.linearVelocity = Vector3.zero;
			}
		}

		private IEnumerator Compat(Vector3 c, Character luckyWolf)
		{
			//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)
			yield return ((MonoBehaviour)this).StartCoroutine(Drops(c, luckyWolf));
			EggCooking();
			yield return ((MonoBehaviour)this).StartCoroutine(StationMeal(c));
		}

		private void EggCooking()
		{
			Player localPlayer = Player.m_localPlayer;
			Inventory inv = ((Humanoid)localPlayer).GetInventory();
			List<Recipe> list = ObjectDB.instance.m_recipes.Where((Recipe val6) => (Object)(object)val6 != (Object)null && (Object)(object)val6.m_item != (Object)null && val6.m_enabled && val6.m_resources != null && val6.m_resources.Any((Requirement q) => (Object)(object)q.m_resItem != (Object)null && (Object)(object)((Component)q.m_resItem).GetComponent<EggGrow>() != (Object)null)).ToList();
			Note("recipes with eggs: " + string.Join(", ", list.Select((Recipe val6) => ((Object)val6.m_item).name + " at " + (Object.op_Implicit((Object)(object)val6.m_craftingStation) ? ((Object)val6.m_craftingStation).name : "hand") + " (" + string.Join(" + ", from q in val6.m_resources
				where Object.op_Implicit((Object)(object)q.m_resItem)
				select q.m_amount + " " + ((Object)q.m_resItem).name) + (val6.m_requireOnlyOneIngredient ? ", one of them" : "") + ")")));
			Check(list.Count > 0, $"the game has recipes with eggs ({list.Count})");
			foreach (Recipe r in list)
			{
				foreach (ItemData item in inv.GetAllItems().ToList())
				{
					inv.RemoveItem(item);
				}
				Requirement eggReq = r.m_resources.First((Requirement q) => (Object)(object)q.m_resItem != (Object)null && (Object)(object)((Component)q.m_resItem).GetComponent<EggGrow>() != (Object)null);
				string egg = eggReq.m_resItem.m_itemData.m_shared.m_name;
				foreach (Requirement item2 in r.m_resources.Where((Requirement q) => (Object)(object)q.m_resItem != (Object)null && q != eggReq && q.m_amount > 0 && !r.m_requireOnlyOneIngredient))
				{
					inv.AddItem(((Object)((Component)item2.m_resItem).gameObject).name, item2.GetAmount(1), 1, 0, 0L, "", false, false);
				}
				int num = Mathf.Max(1, eggReq.GetAmount(1));
				for (int num2 = 0; num2 < num; num2++)
				{
					ItemData val = eggReq.m_resItem.m_itemData.Clone();
					val.m_dropPrefab = ((Component)eggReq.m_resItem).gameObject;
					val.m_stack = 1;
					val.m_quality = 2;
					Blessing.Mark(val, 1);
					inv.AddItem(val);
				}
				int num3 = Enumerable.Range(1, eggReq.m_resItem.m_itemData.m_shared.m_maxQuality).Max((int q) => inv.CountItems(egg, q, true));
				bool flag = localPlayer.HaveRequirementItems(r, false, 1, 1);
				Check(flag, $"{((Object)r.m_item).name}: {num} egg(s) of quality 2 are enough (the game alone sees {num3} of them)");
				if (flag && !r.m_requireOnlyOneIngredient)
				{
					localPlayer.ConsumeResources(r.m_resources, 1, -1, 1);
					Check(inv.CountItems(egg, -1, true) == 0, $"{((Object)r.m_item).name}: making it takes the eggs ({inv.CountItems(egg, -1, true)} left, must be 0)");
				}
			}
			foreach (ItemData item3 in inv.GetAllItems().ToList())
			{
				inv.RemoveItem(item3);
			}
			Recipe val2 = ((IEnumerable<Recipe>)list).FirstOrDefault((Func<Recipe, bool>)((Recipe val6) => ((Object)val6.m_item).name == "CookedEgg"));
			GameObject henEgg = ObjectDB.instance.GetItemPrefab("ChickenEgg");
			if ((Object)(object)val2 != (Object)null && (Object)(object)henEgg != (Object)null)
			{
				ItemData val3 = henEgg.GetComponent<ItemDrop>().m_itemData.Clone();
				val3.m_dropPrefab = henEgg;
				val3.m_stack = 1;
				val3.m_quality = 2;
				Blessing.Mark(val3, 1);
				inv.AddItem(val3);
				inv.AddItem("ChickenEgg", 1, 2, 0, 0L, "", false, false);
				inv.AddItem("ChickenEgg", 1, 1, 0, 0L, "", false, false);
				for (int num4 = 0; num4 < 2; num4++)
				{
					localPlayer.ConsumeResources(val2.m_resources, 1, -1, 1);
				}
				List<ItemData> list2 = (from it in inv.GetAllItems()
					where it.m_shared.m_name == henEgg.GetComponent<ItemDrop>().m_itemData.m_shared.m_name
					select it).ToList();
				Check(list2.Count == 1 && Blessing.Of(list2[0]) == 1, "two cooked eggs from a lucky, a 1-star and a plain egg: the lucky one is left (" + string.Join(", ", list2.Select((ItemData it) => $"q{it.m_quality} blessing {Blessing.Of(it)}")) + ")");
				foreach (ItemData item4 in inv.GetAllItems().ToList())
				{
					inv.RemoveItem(item4);
				}
			}
			foreach (GameObject prefab in ZNetScene.instance.m_prefabs)
			{
				CookingStation val4 = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<CookingStation>() : null);
				if ((Object)(object)val4 == (Object)null)
				{
					continue;
				}
				foreach (ItemConversion item5 in val4.m_conversion)
				{
					if (!((Object)(object)item5.m_from == (Object)null) && !((Object)(object)((Component)item5.m_from).GetComponent<EggGrow>() == (Object)null))
					{
						ItemData val5 = item5.m_from.m_itemData.Clone();
						val5.m_quality = 2;
						bool ok = val4.IsItemAllowed(val5);
						string[] obj = new string[5]
						{
							((Object)prefab).name,
							" takes a quality 2 ",
							((Object)item5.m_from).name,
							" to make ",
							null
						};
						ItemDrop to = item5.m_to;
						obj[4] = ((to != null) ? ((Object)to).name : null);
						Check(ok, string.Concat(obj));
					}
				}
			}
		}

		private static Dictionary<string, float> AverageDrops(Character ch, int rolls)
		{
			Dictionary<string, float> dictionary = new Dictionary<string, float>();
			CharacterDrop component = ((Component)ch).GetComponent<CharacterDrop>();
			if ((Object)(object)component == (Object)null)
			{
				return dictionary;
			}
			for (int i = 0; i < rolls; i++)
			{
				foreach (KeyValuePair<GameObject, int> item in component.GenerateDropList())
				{
					dictionary.TryGetValue(((Object)item.Key).name, out var value);
					dictionary[((Object)item.Key).name] = value + (float)item.Value;
				}
			}
			return dictionary.ToDictionary((KeyValuePair<string, float> kv) => kv.Key, (KeyValuePair<string, float> kv) => kv.Value / (float)rolls);
		}

		private static HashSet<string> Scaled(Character ch)
		{
			return new HashSet<string>((from d in ((Component)ch).GetComponent<CharacterDrop>()?.m_drops
				where (Object)(object)d.m_prefab != (Object)null && d.m_levelMultiplier && !d.m_onePerPlayer
				select ((Object)d.m_prefab).name) ?? Enumerable.Empty<string>());
		}

		private static string Show(Dictionary<string, float> d)
		{
			return string.Join(", ", from kv in d
				orderby kv.Key
				select $"{kv.Key} {kv.Value:0.00}");
		}

		private IEnumerator Drops(Vector3 c, Character luckyWolf)
		{
			//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)
			string[] array = new string[6] { "Wolf", "Boar", "Lox", "Moose", "Hen", "Asksvin" };
			foreach (string prefab in array)
			{
				if ((Object)(object)ZNetScene.instance.GetPrefab(prefab) == (Object)null)
				{
					Note(prefab + " not in this pack, skipped");
					continue;
				}
				Dictionary<string, float>[] lv = new Dictionary<string, float>[3];
				Character[] animals = (Character[])(object)new Character[3];
				for (int j = 1; j <= 3; j++)
				{
					animals[j - 1] = SpawnTame(prefab, c + new Vector3(14f, 0f, 8f + (float)j * 3f), j);
				}
				yield return null;
				yield return null;
				HashSet<string> hashSet = Scaled(animals[0]);
				for (int k = 0; k < 3; k++)
				{
					lv[k] = AverageDrops(animals[k], 600);
					Remove((Component)(object)animals[k]);
				}
				Note(prefab + " drops per kill  0★: " + Show(lv[0]) + " | 1★: " + Show(lv[1]) + " | 2★: " + Show(lv[2]));
				List<string> list = lv[0].Keys.Where(hashSet.Contains).ToList();
				bool ok = list.Count > 0;
				List<string> list2 = new List<string>();
				foreach (string item in list)
				{
					float num = lv[0][item];
					float value;
					float num2 = (lv[1].TryGetValue(item, out value) ? value : 0f);
					float value2;
					float num3 = (lv[2].TryGetValue(item, out value2) ? value2 : 0f);
					list2.Add($"{item} ×{num2 / num:0.0} / ×{num3 / num:0.0}");
					float num4 = ((num < 0.5f) ? 0.75f : 0.9f);
					if (num2 < num * 2f * num4 || num3 < num * 4f * num4)
					{
						ok = false;
					}
				}
				Check(ok, prefab + ": one star drops about twice, two stars about four times as much (" + string.Join(", ", list2) + "; must be ≥ ×1.8 / ×3.6, rare drops ×1.5 / ×3)");
			}
			if ((Object)(object)luckyWolf != (Object)null)
			{
				Character plain = SpawnTame("Wolf", c + new Vector3(14f, 0f, 22f), 1);
				yield return null;
				yield return null;
				Dictionary<string, float> dictionary = AverageDrops(luckyWolf, 600);
				Dictionary<string, float> source = AverageDrops(plain, 600);
				Remove((Component)(object)plain);
				float num5 = dictionary.Where((KeyValuePair<string, float> kv) => kv.Key != "TrophyWolf").Sum((KeyValuePair<string, float> kv) => kv.Value);
				float num6 = source.Where((KeyValuePair<string, float> kv) => kv.Key != "TrophyWolf").Sum((KeyValuePair<string, float> kv) => kv.Value);
				Check(num5 >= num6 * 1.8f, $"a lucky-born 1-star wolf drops like a wild 1-star one: {num5:0.00} items per kill against {num6:0.00} for a 0-star wolf ({Show(dictionary)})");
			}
		}

		private IEnumerator StationMeal(Vector3 c)
		{
			//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)
			Type stationType = AccessTools.TypeByName("FeedingTroughs.Gameplay.FeedingStation");
			if (stationType == null)
			{
				Note("FeedingTroughs not installed, station test skipped");
				yield break;
			}
			FieldInfo herdField = AccessTools.Field(stationType, "HerdId");
			FieldInfo tierField = AccessTools.Field(stationType, "Tier");
			GameObject bowlPrefab = ((IEnumerable<GameObject>)ZNetScene.instance.m_prefabs).FirstOrDefault((Func<GameObject, bool>)delegate(GameObject pf)
			{
				if ((Object)(object)pf != (Object)null)
				{
					Component component4 = pf.GetComponent(stationType);
					if (component4 != null && (string)herdField.GetValue(component4) == "hen")
					{
						return tierField.GetValue(component4).ToString() == "Small";
					}
				}
				return false;
			});
			if ((Object)(object)bowlPrefab == (Object)null)
			{
				Check(ok: false, "FeedingTroughs is installed but has no hen bowl");
				yield break;
			}
			Player p = Player.m_localPlayer;
			Vector3 at = c + new Vector3(-14f, 0f, 10f);
			Stand(p, at + new Vector3(0f, 0f, -6f));
			yield return (object)new WaitForSecondsRealtime(1f);
			GameObject bowl = Object.Instantiate<GameObject>(bowlPrefab, Ground(at), Quaternion.identity);
			yield return null;
			Set(1, repeat: false, "", 2);
			Born e = Birth("Hen", 1, at + new Vector3(3f, 0f, 3f));
			Remove((Component)(object)e.Parent);
			yield return null;
			yield return null;
			Character chick = null;
			Character lucky = null;
			if ((Object)(object)e.Egg != (Object)null)
			{
				yield return ((MonoBehaviour)this).StartCoroutine(Hatch(e.Egg, delegate(Character x)
				{
					chick = x;
				}));
				if ((Object)(object)chick != (Object)null)
				{
					MonsterAI component = ((Component)chick).GetComponent<MonsterAI>();
					Note($"a chick: Tameable {(Object)(object)((Component)chick).GetComponent<Tameable>() != (Object)null}, eats {(component?.m_consumeItems?.Count).GetValueOrDefault()} kinds of food (chicks never eat in the game; they grow up on their own)");
				}
				yield return null;
				lucky = GrowUp(chick);
			}
			SelfTest selfTest = this;
			bool ok = (Object)(object)lucky != (Object)null && lucky.GetLevel() == 2 && Blessing.Of(lucky) == 1;
			Character obj = lucky;
			selfTest.Check(ok, $"lucky hen for the bowl: level {((obj != null) ? new int?(obj.GetLevel()) : ((int?)null))} blessing {Blessing.Of(lucky)} (must be 2 and 1)");
			Character plainHen = SpawnTame("Hen", at + new Vector3(-3f, 0f, 3f), 1);
			Component component2 = bowl.GetComponent(stationType);
			Interactable val = (Interactable)(object)((component2 is Interactable) ? component2 : null);
			MonsterAI component3 = ((Component)plainHen).GetComponent<MonsterAI>();
			bool ok2 = false;
			foreach (ItemDrop consumeItem in component3.m_consumeItems)
			{
				((Humanoid)p).GetInventory().AddItem(((Object)((Component)consumeItem).gameObject).name, 20, 1, 0, 0L, "", false, false);
				ItemData item = ((Humanoid)p).GetInventory().GetItem(consumeItem.m_itemData.m_shared.m_name, -1, false);
				int num = ((Humanoid)p).GetInventory().CountItems(consumeItem.m_itemData.m_shared.m_name, -1, true);
				if (item != null && val.UseItem((Humanoid)(object)p, item) && ((Humanoid)p).GetInventory().CountItems(consumeItem.m_itemData.m_shared.m_name, -1, true) < num)
				{
					Note($"bowl filled with {num - ((Humanoid)p).GetInventory().CountItems(consumeItem.m_itemData.m_shared.m_name, -1, true)} {((Object)((Component)consumeItem).gameObject).name}");
					ok2 = true;
					break;
				}
			}
			Check(ok2, "the hen bowl takes hen food");
			FieldInfo served = AccessTools.Field(AccessTools.TypeByName("FeedingTroughs.Gameplay.FeedingAI"), "MealsServed");
			int meals = ((served != null) ? ((int)served.GetValue(null)) : (-1));
			List<Character> hens = ((IEnumerable<Character>)(object)new Character[2] { plainHen, lucky }).Where((Character h) => (Object)(object)h != (Object)null).ToList();
			foreach (Character item2 in hens)
			{
				item2.m_nview.GetZDO().Set(ZDOVars.s_tameLastFeeding, 0L);
			}
			Check(hens.All((Character h) => ((Component)h).GetComponent<Tameable>().IsHungry()), "both hens are hungry");
			float until = Time.realtimeSinceStartup + 75f;
			while (Time.realtimeSinceStartup < until && hens.Any((Character h) => (Object)(object)h != (Object)null && ((Component)h).GetComponent<Tameable>().IsHungry()))
			{
				yield return (object)new WaitForSecondsRealtime(1f);
			}
			int num2 = ((served != null) ? ((int)served.GetValue(null) - meals) : (-1));
			Check((Object)(object)plainHen != (Object)null && !((Component)plainHen).GetComponent<Tameable>().IsHungry(), "the 0-star hen ate at the bowl");
			Check((Object)(object)lucky != (Object)null && !((Component)lucky).GetComponent<Tameable>().IsHungry(), $"the lucky 1-star hen ate at the bowl too (station meals {num2}, must be 2)");
			Check(num2 == 2, $"both meals came from the bowl, not from the ground ({num2})");
			yield return ((MonoBehaviour)this).StartCoroutine(Fighter(lucky));
			Remove((Component)(object)plainHen);
			Remove((Component)(object)bowl.GetComponent<ZNetView>());
		}

		private IEnumerator Fighter(Character hen)
		{
			Type type = AccessTools.TypeByName("CluckFighters.Gameplay.HenBrood");
			if (type == null)
			{
				Note("CluckFighters not installed, fighter test skipped");
				yield break;
			}
			Component val = (((Object)(object)hen != (Object)null) ? ((Component)hen).GetComponent(type) : null);
			if ((Object)(object)val == (Object)null)
			{
				Check(ok: false, "the lucky hen has CluckFighters' HenBrood");
				yield break;
			}
			HashSet<Character> chars = new HashSet<Character>(Character.GetAllCharacters());
			Vector3 at = ((Component)hen).transform.position;
			AccessTools.Method(type, "Turn", (Type[])null, (Type[])null).Invoke(val, null);
			yield return null;
			Character val2 = ((IEnumerable<Character>)Character.GetAllCharacters()).FirstOrDefault((Func<Character, bool>)((Character x) => (Object)(object)x != (Object)null && !chars.Contains(x) && Vector3.Distance(((Component)x).transform.position, at) < 3f));
			Check((Object)(object)val2 != (Object)null && val2.GetLevel() == 2 && Blessing.Of(val2) == 1, string.Format("the lucky hen turned into {0} level {1} blessing {2} (must be 2 and 1)", Object.op_Implicit((Object)(object)val2) ? Utils.GetPrefabName(((Component)val2).gameObject) : "nothing", (val2 != null) ? new int?(val2.GetLevel()) : ((int?)null), Blessing.Of(val2)));
			string s = (((Object)(object)val2 != (Object)null && (Object)(object)((Component)val2).GetComponent<Tameable>() != (Object)null) ? ((Component)val2).GetComponent<Tameable>().GetHoverText() : "");
			Check(Plain(s).Contains(Plain(Blessing.Line(1))), "fighter hover keeps the blessing: " + Plain(s));
			Remove((Component)(object)val2);
		}

		public static void TryStart(GameObject host)
		{
			string environmentVariable = Environment.GetEnvironmentVariable("STARBORNBREEDING_SELFTEST");
			if (string.IsNullOrEmpty(environmentVariable))
			{
				return;
			}
			Application.runInBackground = true;
			string[] commandLineArgs = Environment.GetCommandLineArgs();
			bool flag = false;
			for (int i = 0; i < commandLineArgs.Length - 1; i++)
			{
				if (commandLineArgs[i] == "-savedir" && !string.IsNullOrEmpty(commandLineArgs[i + 1]))
				{
					Directory.CreateDirectory(commandLineArgs[i + 1]);
					Utils.SetSaveDataPath(commandLineArgs[i + 1]);
					flag = true;
				}
			}
			if (!flag)
			{
				StarbornBreedingPlugin.Log.LogError((object)"Self test needs -savedir <scratch folder>; not starting it.");
			}
			else
			{
				host.AddComponent<SelfTest>()._out = environmentVariable;
			}
		}

		private static bool Flag(string name)
		{
			return (Environment.GetEnvironmentVariable("STARBORNBREEDING_SELFTEST_" + name) ?? "") == "1";
		}

		private void Start()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			Directory.CreateDirectory(_out);
			Application.logMessageReceived += new LogCallback(OnLog);
			((MonoBehaviour)this).StartCoroutine(Run());
		}

		private void OnLog(string condition, string stackTrace, LogType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			if ((int)type == 4 || (int)type == 0)
			{
				_report.Add($"[{type}] {condition}\n{stackTrace}");
			}
		}

		private IEnumerator Run()
		{
			float timeout = Time.realtimeSinceStartup + 600f;
			while (((Object)(object)FejdStartup.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) && Time.realtimeSinceStartup < timeout)
			{
				yield return null;
			}
			yield return (object)new WaitForSecondsRealtime(4f);
			if (Flag("WORLD"))
			{
				yield return ((MonoBehaviour)this).StartCoroutine(Safe(EnterWorld()));
				if ((Object)(object)Player.m_localPlayer != (Object)null)
				{
					if (Flag("RESEARCH"))
					{
						yield return ((MonoBehaviour)this).StartCoroutine(Safe(Research()));
					}
					else
					{
						yield return ((MonoBehaviour)this).StartCoroutine(Safe(Births()));
					}
				}
			}
			_report.Add($"RESULT pass {_pass} fail {_fail}");
			Flush();
			Application.logMessageReceived -= new LogCallback(OnLog);
			yield return (object)new WaitForSecondsRealtime(0.5f);
			Application.Quit();
		}

		private IEnumerator Safe(IEnumerator inner)
		{
			while (true)
			{
				object current;
				try
				{
					if (!inner.MoveNext())
					{
						break;
					}
					current = inner.Current;
				}
				catch (Exception ex)
				{
					_report.Add("step failed: " + ex);
					_fail++;
					break;
				}
				yield return current;
			}
		}

		private void Flush()
		{
			File.WriteAllLines(Path.Combine(_out, "report.txt"), _report);
		}

		private IEnumerator Research()
		{
			ZNetScene instance = ZNetScene.instance;
			List<GameObject> list = (from p in instance.m_prefabs.Concat(instance.m_namedPrefabs.Values)
				where (Object)(object)p != (Object)null
				group p by ((Object)p).name into g
				select g.First() into p
				orderby ((Object)p).name
				select p).ToList();
			_report.Add("research: prefabs " + list.Count);
			foreach (GameObject item in list)
			{
				Procreation component = item.GetComponent<Procreation>();
				if (!((Object)(object)component == (Object)null))
				{
					List<string> report = _report;
					string text = $"PROC {((Object)item).name} lvlSetups {LevelSetups(item)} | offspring {Describe(component.m_offspring)} | noPartner {Describe(component.m_noPartnerOffspring)} ";
					object[] array = new object[6];
					GameObject seperatePartner = component.m_seperatePartner;
					array[0] = ((seperatePartner != null) ? ((Object)seperatePartner).name : null);
					array[1] = component.m_minOffspringLevel;
					array[2] = component.m_requiredLovePoints;
					array[3] = component.m_pregnancyDuration;
					array[4] = component.m_pregnancyChance;
					array[5] = component.m_maxCreatures;
					report.Add(text + string.Format("| partner {0} minLevel {1} love {2} preg {3} s chance {4} max {5}", array));
				}
			}
			foreach (GameObject item2 in list)
			{
				Growup component2 = item2.GetComponent<Growup>();
				if ((Object)(object)component2 != (Object)null)
				{
					List<string> report2 = _report;
					object[] obj = new object[5]
					{
						((Object)item2).name,
						(Object)(object)item2.GetComponent<Tameable>() != (Object)null,
						LevelSetups(item2),
						null,
						null
					};
					GameObject grownPrefab = component2.m_grownPrefab;
					obj[3] = ((grownPrefab != null) ? ((Object)grownPrefab).name : null);
					obj[4] = component2.m_growTime;
					report2.Add(string.Format("GROW {0} tame {1} lvlSetups {2} → {3} in {4} s ", obj) + "alt " + ((component2.m_altGrownPrefabs == null) ? "" : string.Join("/", component2.m_altGrownPrefabs.Select(delegate(GrownEntry a)
					{
						GameObject prefab2 = a.m_prefab;
						return ((prefab2 != null) ? ((Object)prefab2).name : null) + ":" + a.m_weight;
					}))));
				}
				EggGrow component3 = item2.GetComponent<EggGrow>();
				ItemDrop component4 = item2.GetComponent<ItemDrop>();
				if ((Object)(object)component3 != (Object)null)
				{
					List<string> report3 = _report;
					object[] obj2 = new object[7]
					{
						((Object)item2).name,
						component4?.m_itemData?.m_shared?.m_name,
						null,
						null,
						null,
						null,
						null
					};
					GameObject grownPrefab2 = component3.m_grownPrefab;
					obj2[2] = ((grownPrefab2 != null) ? ((Object)grownPrefab2).name : null);
					obj2[3] = component3.m_growTime;
					obj2[4] = component3.m_tamed;
					obj2[5] = component3.m_requireNearbyFire;
					obj2[6] = component3.m_requireUnderRoof;
					report3.Add(string.Format("EGG {0} '{1}' → {2} in {3} s tamed {4} fire {5} roof {6} ", obj2) + $"maxQ {component4?.m_itemData?.m_shared?.m_maxQuality} scaleByQ {component4?.m_itemData?.m_shared?.m_scaleByQuality} stack {component4?.m_itemData?.m_shared?.m_maxStackSize}");
				}
			}
			GameObject prefab = instance.GetPrefab("Player");
			Player val = ((prefab != null) ? prefab.GetComponent<Player>() : null);
			if ((Object)(object)val != (Object)null)
			{
				foreach (FieldInfo item3 in from f in typeof(Player).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
					where f.FieldType == typeof(EffectList)
					select f)
				{
					EffectList val2 = (EffectList)item3.GetValue(val);
					if (val2?.m_effectPrefabs != null && val2.m_effectPrefabs.Length != 0)
					{
						_report.Add("PLAYERFX " + item3.Name + ": " + string.Join(", ", val2.m_effectPrefabs.Select((EffectData d) => (!((Object)(object)d.m_prefab == (Object)null)) ? (((Object)d.m_prefab).name + (Object.op_Implicit((Object)(object)d.m_prefab.GetComponent<ZNetView>()) ? "[net]" : "") + (d.m_attach ? "[attach]" : "")) : "null")));
					}
				}
			}
			string[] look = new string[17]
			{
				"levelup", "upgrade", "spark", "luck", "gift", "magic", "shimmer", "bless", "star", "rune",
				"pray", "offer", "guardian", "power", "fx_lootspawn", "chime", "bell"
			};
			List<string> values = (from p in list
				where (((Object)p).name.StartsWith("vfx_") || ((Object)p).name.StartsWith("sfx_") || ((Object)p).name.StartsWith("fx_")) && look.Any((string l) => ((Object)p).name.IndexOf(l, StringComparison.OrdinalIgnoreCase) >= 0)
				select ((Object)p).name + (Object.op_Implicit((Object)(object)p.GetComponent<ZNetView>()) ? "[net]" : "")).ToList();
			_report.Add("FX " + string.Join(" ", values));
			Flush();
			yield break;
		}

		private static int LevelSetups(GameObject go)
		{
			return (((Object)(object)go != (Object)null) ? go.GetComponentInChildren<LevelEffects>(true) : null)?.m_levelSetups?.Count ?? (-1);
		}

		private static string Describe(GameObject go)
		{
			if ((Object)(object)go == (Object)null)
			{
				return "-";
			}
			string text = (Object.op_Implicit((Object)(object)go.GetComponent<Character>()) ? "creature" : (Object.op_Implicit((Object)(object)go.GetComponent<EggGrow>()) ? "egg" : (Object.op_Implicit((Object)(object)go.GetComponent<ItemDrop>()) ? "item" : "other")));
			return $"{((Object)go).name} ({text}, tame {(Object)(object)go.GetComponent<Tameable>() != (Object)null}, lvlSetups {LevelSetups(go)})";
		}

		private IEnumerator EnterWorld()
		{
			FejdStartup instance = FejdStartup.instance;
			if ((Object)(object)instance == (Object)null)
			{
				_report.Add("world: no FejdStartup");
				yield break;
			}
			PlayerProfile val = new PlayerProfile("starborntester", (FileSource)2);
			val.SetName("Breeder");
			val.m_firstSpawn = false;
			val.Save();
			Game.SetProfile("starborntester", (FileSource)2);
			World val2 = new World("StarbornTest", "HQ7GnTiegh")
			{
				m_fileSource = (FileSource)2
			};
			val2.SaveWorldFWLData(DateTime.Now);
			instance.m_world = val2;
			instance.m_startingWorld = true;
			ZNet.SetServer(true, false, false, val2.m_name, "", val2);
			ZNet.ResetServerHost();
			instance.TransitionToMainScene();
			_report.Add("world: loading " + val2.m_name + " seed " + val2.m_seedName);
			float timeout = Time.realtimeSinceStartup + 300f;
			while (((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNetScene.instance == (Object)null || ((Character)Player.m_localPlayer).IsTeleporting()) && Time.realtimeSinceStartup < timeout)
			{
				yield return null;
			}
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				_report.Add("world: player never spawned");
				yield break;
			}
			yield return (object)new WaitForSecondsRealtime(6f);
			_report.Add("world: spawned at " + ((object)((Component)Player.m_localPlayer).transform.position/*cast due to .constrained prefix*/).ToString());
		}

		private void Check(bool ok, string what)
		{
			if (ok)
			{
				_pass++;
			}
			else
			{
				_fail++;
			}
			_report.Add((ok ? "PASS " : "FAIL ") + what);
			Flush();
		}

		private void Note(string s)
		{
			_report.Add("  " + s);
			StarbornBreedingPlugin.Log.LogInfo((object)("[selftest] " + s));
		}

		private static void Stand(Player p, Vector3 at)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			at.y = ZoneSystem.instance.GetGroundHeight(at) + 0.1f;
			((Component)p).transform.position = at;
			((Character)p).m_body.position = at;
			((Character)p).m_body.linearVelocity = Vector3.zero;
		}

		private static Vector3 Ground(Vector3 at)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			at.y = ZoneSystem.instance.GetGroundHeight(at);
			return at;
		}

		private Character SpawnTame(string prefab, Vector3 at, int level, float yaw = 0f)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: 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)
			GameObject prefab2 = ZNetScene.instance.GetPrefab(prefab);
			if ((Object)(object)prefab2 == (Object)null)
			{
				Note("spawn: missing " + prefab);
				return null;
			}
			GameObject obj = Object.Instantiate<GameObject>(prefab2, Ground(at) + Vector3.up * 0.2f, Quaternion.Euler(0f, yaw, 0f));
			Character component = obj.GetComponent<Character>();
			component.SetTamed(true);
			if (level > 1)
			{
				component.SetLevel(level);
			}
			obj.GetComponent<ZNetView>().GetZDO().Set(ZDOVars.s_tameLastFeeding, ZNet.instance.GetTime().Ticks);
			return component;
		}

		private static void Remove(Component c)
		{
			if (!((Object)(object)c == (Object)null))
			{
				ZNetView component = c.GetComponent<ZNetView>();
				if ((Object)(object)component != (Object)null && component.IsValid())
				{
					component.Destroy();
				}
			}
		}

		private void ClearArea(Vector3 c, float r)
		{
			//IL_0042: 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)
			int num = 0;
			ZNetView[] array = Object.FindObjectsByType<ZNetView>((FindObjectsSortMode)0);
			foreach (ZNetView val in array)
			{
				if (!((Object)(object)val == (Object)null) && val.IsValid() && !((Object)(object)((Component)val).GetComponent<Player>() != (Object)null) && !(Utils.DistanceXZ(((Component)val).transform.position, c) > r) && (Object.op_Implicit((Object)(object)((Component)val).GetComponent<TreeBase>()) || Object.op_Implicit((Object)(object)((Component)val).GetComponent<TreeLog>()) || Object.op_Implicit((Object)(object)((Component)val).GetComponent<Destructible>()) || Object.op_Implicit((Object)(object)((Component)val).GetComponent<MineRock>()) || Object.op_Implicit((Object)(object)((Component)val).GetComponent<MineRock5>()) || Object.op_Implicit((Object)(object)((Component)val).GetComponent<Pickable>()) || Object.op_Implicit((Object)(object)((Component)val).GetComponent<ItemDrop>()) || (Object.op_Implicit((Object)(object)((Component)val).GetComponent<Character>()) && !((Component)val).GetComponent<Character>().IsTamed())))
				{
					val.Destroy();
					num++;
				}
			}
			Note("cleared " + num + " objects");
		}

		private Vector3 FlatSpot(Vector3 from)
		{
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Invalid comparison between Unknown and I4
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			float waterLevel = ZoneSystem.instance.m_waterLevel;
			Vector3 val = default(Vector3);
			for (float num = 40f; num < 300f; num += 8f)
			{
				for (int i = 0; i < 24; i++)
				{
					float num2 = (float)i * (float)Math.PI * 2f / 24f;
					((Vector3)(ref val))..ctor(from.x + Mathf.Cos(num2) * num, 0f, from.z + Mathf.Sin(num2) * num);
					if ((int)WorldGenerator.instance.GetBiome(val) != 1)
					{
						continue;
					}
					float groundHeight = ZoneSystem.instance.GetGroundHeight(val);
					bool flag = groundHeight > waterLevel + 2f;
					for (int j = 0; j < 12 && flag; j++)
					{
						float num3 = (float)j * (float)Math.PI / 6f;
						float[] array = new float[3] { 6f, 12f, 18f };
						foreach (float num4 in array)
						{
							float groundHeight2 = ZoneSystem.instance.GetGroundHeight(val + new Vector3(Mathf.Cos(num3), 0f, Mathf.Sin(num3)) * num4);
							flag &= groundHeight2 > waterLevel + 1.5f && Mathf.Abs(groundHeight2 - groundHeight) < 1.6f;
						}
					}
					if (flag)
					{
						return new Vector3(val.x, groundHeight, val.z);
					}
				}
			}
			Note("no flat spot found");
			return from + new Vector3(40f, 0f, 0f);
		}

		private static Pose Look(Vector3 target, float bearing, float dist, float height)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: 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)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = target + Quaternion.Euler(0f, bearing, 0f) * Vector3.forward * dist + Vector3.up * height;
			return new Pose(val, Quaternion.LookRotation(target - val));
		}

		private IEnumerator MainShot(string name, Pose pose, float fov = 45f, bool hud = false)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			Camera main = Utils.GetMainCamera();
			GameCamera gc = GameCamera.instance;
			if ((Object)(object)main == (Object)null)
			{
				Note("mainshot: no camera");
				yield break;
			}
			if ((Object)(object)gc != (Object)null)
			{
				((Behaviour)gc).enabled = false;
			}
			if ((Object)(object)Hud.instance != (Object)null)
			{
				Hud.instance.m_userHidden = !hud;
			}
			((Component)main).transform.SetPositionAndRotation(pose.position, pose.rotation);
			float old = main.fieldOfView;
			main.fieldOfView = fov;
			yield return null;
			yield return null;
			yield return (object)new WaitForEndOfFrame();
			ScreenCapture.CaptureScreenshot(Path.Combine(_out, name + ".png"));
			yield return null;
			yield return null;
			main.fieldOfView = old;
			if ((Object)(object)gc != (Object)null)
			{
				((Behaviour)gc).enabled = true;
			}
			if ((Object)(object)Hud.instance != (Object)null)
			{
				Hud.instance.m_userHidden = false;
			}
		}

		private IEnumerator HudShot(string name, Player p, Vector3 lookAt, float seconds = 1.2f)
		{
			//IL_001c: 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)
			float until = Time.realtimeSinceStartup + seconds;
			while (Time.realtimeSinceStartup < until)
			{
				Camera mainCamera = Utils.GetMainCamera();
				Vector3 val = (((Object)(object)mainCamera != (Object)null) ? ((Component)mainCamera).transform.position : ((Character)p).m_eye.position);
				Vector3 val2 = lookAt - val;
				Vector3 normalized = ((Vector3)(ref val2)).normalized;
				((Character)p).SetLookDir(normalized, 0f);
				p.m_lookPitch = (0f - Mathf.Asin(normalized.y)) * 57.29578f;
				((Character)p).UpdateEyeRotation();
				yield return null;
			}
			yield return (object)new WaitForEndOfFrame();
			ScreenCapture.CaptureScreenshot(Path.Combine(_out, name + ".png"));
			yield return null;
			yield return null;
		}

		private static string Flat(string s)
		{
			if (s != null)
			{
				return s.Replace('\n', '|');
			}
			return "null";
		}

		private static string Plain(string s)
		{
			if (s != null)
			{
				return Regex.Replace(Flat(s), "<[^>]+>", "");
			}
			return "null";
		}

		private static T Newest<T>(Vector3 near, float r, Func<T, bool> where, HashSet<T> before) where T : Component
		{
			//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)
			return (from x in Object.FindObjectsByType<T>((FindObjectsSortMode)0)
				where (Object)(object)x != (Object)null && !before.Contains(x) && Vector3.Distance(((Component)x).transform.position, near) < r && @where(x)
				orderby Vector3.Distance(((Component)x).transform.position, near)
				select x).FirstOrDefault();
		}
	}
}
namespace StarbornBreeding.Core
{
	public static class Luck
	{
		public static int ExtraStars(int oneIn, bool repeat, int room, Func<double> next)
		{
			if (oneIn <= 0 || room <= 0 || next == null)
			{
				return 0;
			}
			int num = 0;
			double num2 = 1.0 / (double)oneIn;
			while (num < room && next() < num2)
			{
				num++;
				if (!repeat)
				{
					break;
				}
			}
			return num;
		}

		public static int VanillaLevel(int parentLevel, int minOffspringLevel)
		{
			return Math.Max(minOffspringLevel, parentLevel);
		}

		public static int Room(int baseLevel, int maxStars)
		{
			return Math.Max(0, maxStars + 1 - baseLevel);
		}

		public static double ChanceOfAtLeast(int oneIn, bool repeat, int stars)
		{
			if (oneIn <= 0)
			{
				if (stars > 0)
				{
					return 0.0;
				}
				return 1.0;
			}
			if (stars <= 0)
			{
				return 1.0;
			}
			if (!repeat && stars > 1)
			{
				return 0.0;
			}
			return Math.Pow(1.0 / (double)oneIn, stars);
		}

		public static Dictionary<string, int> ParseTable(string text, List<string> bad = null)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
			if (string.IsNullOrWhiteSpace(text))
			{
				return dictionary;
			}
			string[] array = text.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length != 0)
				{
					int num = text2.IndexOfAny(new char[2] { ':', '=' });
					if (num <= 0 || !int.TryParse(text2.Substring(num + 1).Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) || result < 0)
					{
						bad?.Add(text2);
					}
					else
					{
						dictionary[text2.Substring(0, num).Trim()] = result;
					}
				}
			}
			return dictionary;
		}

		public static int OneInFor(Dictionary<string, int> table, string parent, string offspring, int fallback)
		{
			if (table != null)
			{
				if (!string.IsNullOrEmpty(parent) && table.TryGetValue(parent, out var value))
				{
					return value;
				}
				if (!string.IsNullOrEmpty(offspring) && table.TryGetValue(offspring, out var value2))
				{
					return value2;
				}
			}
			return fallback;
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}