using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Logging; using HarmonyLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("TuckMyChooksIn")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+52e01f41167f74e9be2c5be814fc0ad6e10c7558")] [assembly: AssemblyProduct("TuckMyChooksIn")] [assembly: AssemblyTitle("TuckMyChooksIn")] [assembly: AssemblyVersion("1.0.0.0")] namespace TuckMyChooksIn; public class ChickenBed : MonoBehaviour, Hoverable, Interactable { private struct HuddleInfo { public Character hen; public MonsterAI ai; } public static readonly List AllBeds = new List(); public Transform m_spawnPoint; private ZNetView m_nview; private Coroutine m_tuckCoroutine; private float m_originalWalkSpeed; public static int s_pendingTucks = 0; private static readonly List s_huddlers = new List(); public static float s_sleepBlockStart = 0f; public MonsterAI OccupantAI { get; set; } public Character Occupant { get; set; } public bool IsOccupied { get { if ((Object)(object)Occupant != (Object)null) { return (Object)(object)OccupantAI != (Object)null; } return false; } } internal static void AddHuddler(Character hen, MonsterAI ai, ChickenBed nearestBed) { s_huddlers.Add(new HuddleInfo { hen = hen, ai = ai }); ai.SetFollowTarget(((Component)nearestBed).gameObject); ((MonoBehaviour)nearestBed).StartCoroutine(nearestBed.HuddleAndSleep(hen, ai)); } private IEnumerator HuddleAndSleep(Character hen, MonsterAI ai) { float timeout = 20f; float elapsed = 0f; while (elapsed < timeout) { if ((Object)(object)hen == (Object)null || (Object)(object)ai == (Object)null) { yield break; } if (Vector3.Distance(((Component)hen).transform.position, ((Component)this).transform.position) <= 3f) { break; } elapsed += Time.deltaTime; yield return null; } if ((Object)(object)hen == (Object)null || (Object)(object)ai == (Object)null) { yield break; } ZNetView component = ((Component)hen).GetComponent(); if ((Object)(object)component != (Object)null && component.IsValid()) { Animator componentInChildren = ((Component)hen).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.SetBool("sleeping", true); } component.GetZDO().Set(ZDOVars.s_sleeping, true); } Plugin.Log.LogInfo((object)"Huddler settled near bed."); } private void Awake() { m_nview = ((Component)this).GetComponent(); if ((Object)(object)m_spawnPoint == (Object)null) { m_spawnPoint = ((Component)this).transform; } AllBeds.Add(this); } private void OnDestroy() { AllBeds.Remove(this); } public Vector3 GetSleepPoint() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return m_spawnPoint.position; } public string GetHoverText() { if (IsOccupied) { return Occupant.GetHoverName() + " is tucked in <3"; } return "Chicken Bed (vacant)"; } public string GetHoverName() { return "Chicken Bed"; } public bool Interact(Humanoid user, bool hold, bool alt) { if (hold) { return false; } if (IsOccupied) { ((Character)user).Message((MessageType)2, "A chook is sleeping here!", 0, (Sprite)null); } else { ((Character)user).Message((MessageType)2, "This chicken bed is vacant.", 0, (Sprite)null); } return false; } public bool UseItem(Humanoid user, ItemData item) { return false; } public void TuckIn(Character hen, MonsterAI ai) { Occupant = hen; OccupantAI = ai; if (!((Object)(object)hen == (Object)null) && !((Object)(object)ai == (Object)null)) { if (s_pendingTucks == 0) { s_sleepBlockStart = Time.time; } s_pendingTucks++; m_tuckCoroutine = ((MonoBehaviour)this).StartCoroutine(WaitAndSleep(hen, ai)); } } private IEnumerator WaitAndSleep(Character hen, MonsterAI ai) { m_originalWalkSpeed = hen.m_walkSpeed; hen.m_walkSpeed = hen.m_runSpeed * 2f; ai.SetFollowTarget(((Component)this).gameObject); float timeout = 20f; float elapsed = 0f; while (elapsed < timeout) { if ((Object)(object)hen == (Object)null || (Object)(object)ai == (Object)null) { yield break; } if (Vector3.Distance(((Component)hen).transform.position, ((Component)this).transform.position) <= 3f) { break; } elapsed += Time.deltaTime; yield return null; } if ((Object)(object)hen == (Object)null || (Object)(object)ai == (Object)null) { yield break; } hen.m_walkSpeed = m_originalWalkSpeed; ai.SetFollowTarget(((Component)this).gameObject); ((Component)hen).transform.position = GetSleepPoint(); ((Component)hen).transform.rotation = m_spawnPoint.rotation; ZNetView component = ((Component)hen).GetComponent(); if ((Object)(object)component != (Object)null && component.IsValid()) { Animator componentInChildren = ((Component)hen).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.SetBool("sleeping", true); } component.GetZDO().Set(ZDOVars.s_sleeping, true); } Plugin.Log.LogInfo((object)$"Chook tucked in at {GetSleepPoint()}!"); s_pendingTucks = Mathf.Max(0, s_pendingTucks - 1); m_tuckCoroutine = null; } public void WakeUp() { if (m_tuckCoroutine != null) { ((MonoBehaviour)this).StopCoroutine(m_tuckCoroutine); m_tuckCoroutine = null; s_pendingTucks = Mathf.Max(0, s_pendingTucks - 1); if ((Object)(object)Occupant != (Object)null) { Occupant.m_walkSpeed = m_originalWalkSpeed; } } if ((Object)(object)Occupant != (Object)null && (Object)(object)OccupantAI != (Object)null) { OccupantAI.SetFollowTarget((GameObject)null); ZNetView component = ((Component)Occupant).GetComponent(); if ((Object)(object)component != (Object)null && component.IsValid()) { Animator componentInChildren = ((Component)Occupant).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.SetBool("sleeping", false); } component.GetZDO().Set(ZDOVars.s_sleeping, false); } Plugin.Log.LogInfo((object)"Chook woke up!"); } Vacate(); } public void Vacate() { Occupant = null; OccupantAI = null; } public static List GetVacantBeds() { List list = new List(); foreach (ChickenBed allBed in AllBeds) { if ((Object)(object)allBed != (Object)null && !allBed.IsOccupied) { list.Add(allBed); } } return list; } public static void WakeAllChickens() { if (s_huddlers.Count <= 0 && !AllBeds.Exists((ChickenBed b) => (Object)(object)b != (Object)null && b.IsOccupied)) { return; } foreach (ChickenBed allBed in AllBeds) { if ((Object)(object)allBed != (Object)null && allBed.IsOccupied) { allBed.WakeUp(); } } foreach (HuddleInfo s_huddler in s_huddlers) { if ((Object)(object)s_huddler.hen == (Object)null || (Object)(object)s_huddler.ai == (Object)null) { continue; } s_huddler.ai.SetFollowTarget((GameObject)null); ZNetView component = ((Component)s_huddler.hen).GetComponent(); if ((Object)(object)component != (Object)null && component.IsValid()) { Animator componentInChildren = ((Component)s_huddler.hen).GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.SetBool("sleeping", false); } component.GetZDO().Set(ZDOVars.s_sleeping, false); } } s_huddlers.Clear(); } } [HarmonyPatch(typeof(Player), "AttachStart")] public static class PlayerAttachStartPatch { private struct HenInfo { public Character character; public MonsterAI ai; } private static readonly HashSet HenPrefabs = new HashSet { "Hen", "Chicken", "hen", "chicken" }; private static void Postfix(Player __instance, bool isBed) { if (isBed && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { Plugin.Log.LogInfo((object)"Player got into bed — time to tuck some chooks in!"); TuckChickensIn(); } } private static void TuckChickensIn() { //IL_017c: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) int num = ZNet.instance.GetNrOfPlayers(); if (num < 1) { num = 1; } List list = FindTamedHens(); if (list.Count == 0) { Plugin.Log.LogInfo((object)"No tamed chooks found nearby."); return; } int num2 = Mathf.Max(1, Mathf.CeilToInt((float)list.Count / (float)num)); int num3 = CountPlayersInBed(); int num4 = Mathf.Min(list.Count, num2 * num3); int num5 = ChickenBed.AllBeds.Count((ChickenBed b) => (Object)(object)b != (Object)null && b.IsOccupied); int num6 = num4 - num5; if (num6 <= 0) { Plugin.Log.LogInfo((object)$"Already enough chooks tucked in ({num5}/{num4})."); return; } List vacantBeds = ChickenBed.GetVacantBeds(); if (vacantBeds.Count == 0) { Plugin.Log.LogInfo((object)"No vacant chicken beds available!"); return; } HashSet tuckedHens = new HashSet(); foreach (ChickenBed allBed in ChickenBed.AllBeds) { if ((Object)(object)allBed != (Object)null && (Object)(object)allBed.Occupant != (Object)null) { tuckedHens.Add(allBed.Occupant); } } List list2 = list.Where((HenInfo h) => !tuckedHens.Contains(h.character)).ToList(); int num7 = 0; for (int i = 0; i < num6 && i < vacantBeds.Count && i < list2.Count; i++) { HenInfo henInfo = list2[i]; ChickenBed chickenBed = FindClosestBed(((Component)henInfo.character).transform.position, vacantBeds); if ((Object)(object)chickenBed == (Object)null) { break; } vacantBeds.Remove(chickenBed); chickenBed.TuckIn(henInfo.character, henInfo.ai); num7++; } Plugin.Log.LogInfo((object)$"Tucked {num7} chooks into bed! (total: {num5 + num7}/{list.Count})"); List list3 = ChickenBed.AllBeds.FindAll((ChickenBed b) => (Object)(object)b != (Object)null); if (list3.Count <= 0) { return; } int num8 = 0; for (int j = num7; j < list2.Count; j++) { HenInfo henInfo2 = list2[j]; ChickenBed chickenBed2 = FindClosestBed(((Component)henInfo2.character).transform.position, list3); if (!((Object)(object)chickenBed2 == (Object)null)) { ChickenBed.AddHuddler(henInfo2.character, henInfo2.ai, chickenBed2); num8++; } } if (num8 > 0) { Plugin.Log.LogInfo((object)$"Sent {num8} bedless chooks to huddle near the nearest bed."); } } private static List FindTamedHens() { List list = new List(); foreach (Character allCharacter in Character.GetAllCharacters()) { if (!((Object)(object)allCharacter == (Object)null) && !allCharacter.IsPlayer() && allCharacter.IsTamed() && IsHenPrefab(((Object)((Component)allCharacter).gameObject).name)) { MonsterAI component = ((Component)allCharacter).GetComponent(); if (!((Object)(object)component == (Object)null)) { list.Add(new HenInfo { character = allCharacter, ai = component }); } } } return list; } private static bool IsHenPrefab(string name) { string text = name.ToLowerInvariant(); if (!text.Contains("hen")) { return text.Contains("chicken"); } return true; } private static int CountPlayersInBed() { int num = 0; foreach (ZDO allCharacterZDO in ZNet.instance.GetAllCharacterZDOS()) { if (allCharacterZDO.GetBool(ZDOVars.s_inBed, false)) { num++; } } return num; } private static ChickenBed FindClosestBed(Vector3 pos, List beds) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) ChickenBed result = null; float num = float.MaxValue; foreach (ChickenBed bed in beds) { if (!((Object)(object)bed == (Object)null)) { float num2 = Vector3.Distance(pos, bed.GetSleepPoint()); if (num2 < num) { num = num2; result = bed; } } } return result; } } [HarmonyPatch(typeof(Game), "EverybodyIsTryingToSleep")] public static class GameEverybodyIsTryingSleepPatch { private static bool Prepare() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 return (int)SystemInfo.graphicsDeviceType != 4; } private static void Postfix(ref bool __result) { if (__result && ChickenBed.s_pendingTucks > 0) { if (Time.time - ChickenBed.s_sleepBlockStart < 20f) { __result = false; } else { ChickenBed.s_pendingTucks = 0; } } } } [HarmonyPatch(typeof(Game), "SleepStop")] public static class GameSleepStopPatch { private static bool Prepare() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Invalid comparison between Unknown and I4 return (int)SystemInfo.graphicsDeviceType != 4; } private static void Postfix() { Plugin.Log.LogInfo((object)"Morning! Waking up the chooks."); ChickenBed.WakeAllChickens(); } } [HarmonyPatch(typeof(Player), "AttachStop")] public static class PlayerAttachStopPatch { private static void Postfix(Player __instance) { if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer)) { Plugin.Log.LogInfo((object)"Player got out of bed — waking chooks."); ChickenBed.WakeAllChickens(); } } } [BepInPlugin("tuckmychooksin", "TuckMyChooksIn", "1.1.1")] public class Plugin : BaseUnityPlugin { public const string PluginGUID = "tuckmychooksin"; public const string PluginName = "TuckMyChooksIn"; public const string PluginVersion = "1.1.1"; internal static ManualLogSource Log; internal static Plugin Instance; private Harmony _harmony; private void Awake() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Instance = this; _harmony = new Harmony("tuckmychooksin"); _harmony.PatchAll(); Log.LogInfo((object)"TuckMyChooksIn v1.1.1 loaded. Time to tuck those chooks in!"); } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } [HarmonyPatch(typeof(ZNetScene), "OnDestroy")] public static class ZNetSceneDestroyPatch { private static void Prefix(ZNetScene __instance) { if ((Object)(object)PrefabSetup.s_prefabTemplate != (Object)null) { __instance.m_prefabs.Remove(PrefabSetup.s_prefabTemplate); } } } [HarmonyPatch(typeof(ZNetScene), "Awake")] public static class PrefabSetup { private const string ChickenBedName = "piece_chickenbed"; private const float BedScale = 0.35f; internal static GameObject s_prefabTemplate; private static void Postfix(ZNetScene __instance) { CreateChickenBedPrefab(__instance); } private static void CreateChickenBedPrefab(ZNetScene zns) { //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Expected O, but got Unknown if ((Object)(object)zns.GetPrefab("piece_chickenbed") != (Object)null) { return; } if ((Object)(object)s_prefabTemplate != (Object)null) { zns.m_prefabs.Add(s_prefabTemplate); Dictionary value = Traverse.Create((object)zns).Field("m_namedPrefabs").GetValue>(); if (value != null) { value[StringExtensionMethods.GetStableHashCode("piece_chickenbed")] = s_prefabTemplate; } Plugin.Log.LogInfo((object)"Chicken bed re-registered with new ZNetScene."); return; } GameObject val = FindBedPrefab(zns); if ((Object)(object)val == (Object)null) { Plugin.Log.LogWarning((object)"Could not find a bed prefab to clone for the chicken bed!"); return; } Plugin.Log.LogInfo((object)("Cloning bed prefab '" + ((Object)val).name + "' for chicken bed.")); bool activeSelf = val.activeSelf; val.SetActive(false); GameObject val2 = Object.Instantiate(val); val.SetActive(activeSelf); ((Object)val2).name = "piece_chickenbed"; val2.transform.localScale = Vector3.one * 0.35f; Bed component = val2.GetComponent(); if ((Object)(object)component != (Object)null) { Transform spawnPoint = component.m_spawnPoint; Object.DestroyImmediate((Object)(object)component); val2.AddComponent().m_spawnPoint = spawnPoint; } else { val2.AddComponent(); } Piece component2 = val2.GetComponent(); if ((Object)(object)component2 != (Object)null) { component2.m_name = "$piece_chickenbed"; component2.m_description = "$piece_chickenbed_desc"; component2.m_category = (PieceCategory)4; GameObject prefab = zns.GetPrefab("Wood"); component2.m_resources = (Requirement[])(object)((!((Object)(object)prefab != (Object)null)) ? new Requirement[0] : new Requirement[1] { new Requirement { m_resItem = prefab.GetComponent(), m_amount = 1, m_recover = true } }); } val2.SetActive(true); ZNetView component3 = val2.GetComponent(); if ((Object)(object)component3 != (Object)null) { ZDO zDO = component3.GetZDO(); if (zDO != null) { component3.ResetZDO(); Traverse.Create((object)zns).Field("m_instances").GetValue>()?.Remove(zDO); ZDOMan.instance.DestroyZDO(zDO); Plugin.Log.LogInfo((object)"Cleaned up spurious ZDO from chicken bed template."); } } ChickenBed component4 = val2.GetComponent(); if ((Object)(object)component4 != (Object)null) { ChickenBed.AllBeds.Remove(component4); } zns.m_prefabs.Add(val2); Dictionary value2 = Traverse.Create((object)zns).Field("m_namedPrefabs").GetValue>(); if (value2 != null) { value2[StringExtensionMethods.GetStableHashCode("piece_chickenbed")] = val2; } Object.DontDestroyOnLoad((Object)(object)val2); s_prefabTemplate = val2; AddToHammer(zns, val2); RegisterLocalization(); Plugin.Log.LogInfo((object)"Chicken bed prefab created and registered!"); } private static GameObject FindBedPrefab(ZNetScene zns) { string[] array = new string[3] { "bed", "piece_bed02", "piece_bed01" }; foreach (string text in array) { GameObject prefab = zns.GetPrefab(text); if ((Object)(object)prefab != (Object)null) { return prefab; } } foreach (GameObject prefab2 in zns.m_prefabs) { if ((Object)(object)prefab2 != (Object)null && (Object)(object)prefab2.GetComponent() != (Object)null) { return prefab2; } } return null; } private static void AddToHammer(ZNetScene zns, GameObject chickenBed) { GameObject prefab = zns.GetPrefab("Hammer"); if ((Object)(object)prefab == (Object)null) { Plugin.Log.LogWarning((object)"Could not find Hammer prefab!"); return; } ItemDrop component = prefab.GetComponent(); if ((Object)(object)component == (Object)null) { Plugin.Log.LogWarning((object)"Hammer has no ItemDrop component!"); return; } PieceTable buildPieces = component.m_itemData.m_shared.m_buildPieces; if ((Object)(object)buildPieces == (Object)null) { Plugin.Log.LogWarning((object)"Hammer has no PieceTable!"); return; } buildPieces.m_pieces.Add(chickenBed); Plugin.Log.LogInfo((object)"Chicken bed added to hammer build menu."); } private static void RegisterLocalization() { Localization instance = Localization.instance; if (instance != null) { Dictionary value = Traverse.Create((object)instance).Field("m_translations").GetValue>(); if (value == null) { Plugin.Log.LogWarning((object)"Could not access localisation translations."); return; } value["piece_chickenbed"] = "Chicken Bed"; value["piece_chickenbed_desc"] = "A tiny bed for your chooks. They'll tuck themselves in when you go to sleep!"; } } } [HarmonyPatch(typeof(Player), "OnSpawned")] public static class PlayerSpawnPatch { private static void Postfix(Player __instance) { Traverse.Create((object)__instance).Method("UpdateAvailablePiecesList", Array.Empty()).GetValue(); } }