Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of Starborn Breeding v1.0.1
plugins/StarbornBreeding.dll
Decompiled 2 days agousing 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) { } } }