Decompiled source of ValkyriesHatchery v0.6.0

ValkyriesHatchery.dll

Decompiled 2 weeks ago
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.Configuration;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("ValkyriesHatchery")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("ValkyriesHatchery")]
[assembly: AssemblyTitle("ValkyriesHatchery")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ValkyriesHatchery
{
	public enum ProgressionSource
	{
		World,
		Player
	}
	[BepInPlugin("com.drakexi.valkyrieshatchery", "Valkyrie's Hatchery", "0.6.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		public const string PluginGUID = "com.drakexi.valkyrieshatchery";

		public const string PluginName = "Valkyrie's Hatchery";

		public const string PluginVersion = "0.6.0";

		public static Plugin Instance;

		public static ConfigEntry<int> Slots;

		public static ConfigEntry<float> HatchMinutes;

		public static ConfigEntry<float> XpMultiplier;

		public static ConfigEntry<int> HomeBiomeChance;

		public static ConfigEntry<ProgressionSource> Progression;

		public static ConfigEntry<int> RoePrice;

		public static ConfigEntry<bool> ShowXpMessage;

		public static ConfigEntry<bool> PlayHatchSoundSetting;

		private const string PiecePrefabName = "piece_ValkyriesHatchery";

		private const string DisplayName = "Valkyrie's Hatchery";

		public const string RoePrefabName = "ValkyriesHatchery_Roe";

		private static readonly Color DarkStain = new Color(0.45f, 0.36f, 0.3f, 1f);

		private static readonly Color CaviarBlack = new Color(0.12f, 0.12f, 0.14f, 1f);

		public static readonly string[] FishIds = new string[12]
		{
			"Fish1", "Fish2", "Fish3", "Fish4_cave", "Fish5", "Fish6", "Fish7", "Fish8", "Fish9", "Fish10",
			"Fish11", "Fish12"
		};

		private static readonly string[] HatchSequence = new string[4] { "fx_float_nibble", "fx_float_hitwater", "fx_float_nibble", "fx_float_hitwater" };

		private const float HatchSoundGap = 0.5f;

		private TradeItem _roeTrade;

		private void Awake()
		{
			Instance = this;
			BindConfig();
			PrefabManager.OnVanillaPrefabsAvailable += AddHatcheryPiece;
			PrefabManager.OnVanillaPrefabsAvailable += AddRoe;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Valkyrie's Hatchery 0.6.0 loaded.");
		}

		private void BindConfig()
		{
			Slots = ((BaseUnityPlugin)this).Config.Bind<int>("Server", "Slots", 12, Server("Storage slots in each hatchery. Hatcheries resize live; anything in removed slots is moved to a free slot or dropped in front of the hatchery.", (AcceptableValueBase)(object)new AcceptableValueList<int>(new int[4] { 4, 6, 8, 12 })));
			HatchMinutes = ((BaseUnityPlugin)this).Config.Bind<float>("Server", "HatchMinutes", 10f, Server("Real minutes for one roe to hatch into a fish.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 120f)));
			XpMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Server", "XpMultiplier", 1f, Server("Scales the Fishing XP for each fish collected (2 = double, 0 = none).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f)));
			HomeBiomeChance = ((BaseUnityPlugin)this).Config.Bind<int>("Server", "HomeBiomeChance", 80, Server("Percent of hatches that are fish from the hatchery's own biome, when other fish are also unlocked.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100)));
			Progression = ((BaseUnityPlugin)this).Config.Bind<ProgressionSource>("Server", "ProgressionSource", ProgressionSource.World, Server("Which boss kills unlock other biomes' fish: World (the world's kills) or Player (the kills of the player whose game is running the hatchery).", null));
			RoePrice = ((BaseUnityPlugin)this).Config.Bind<int>("Server", "RoePrice", 10, Server("Haldor's price for one Fish Roe.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000)));
			ShowXpMessage = ((BaseUnityPlugin)this).Config.Bind<bool>("Personal", "ShowXpMessage", true, "Show a message with the Fishing XP gained when you collect fish.");
			PlayHatchSoundSetting = ((BaseUnityPlugin)this).Config.Bind<bool>("Personal", "PlayHatchSound", true, "Play the nibble-and-splash sound when a fish hatches.");
			RoePrice.SettingChanged += delegate
			{
				if (_roeTrade != null)
				{
					_roeTrade.m_price = RoePrice.Value;
				}
			};
		}

		private static ConfigDescription Server(string text, AcceptableValueBase range)
		{
			//IL_000a: 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)
			//IL_0017: Expected O, but got Unknown
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			return new ConfigDescription(text, range, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} });
		}

		public static Vector2Int GridFor(int slots)
		{
			//IL_0020: 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_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			return (Vector2Int)(slots switch
			{
				4 => new Vector2Int(4, 1), 
				6 => new Vector2Int(3, 2), 
				8 => new Vector2Int(4, 2), 
				_ => new Vector2Int(4, 3), 
			});
		}

		private void AddHatcheryPiece()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Expected O, but got Unknown
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Expected O, but got Unknown
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Expected O, but got Unknown
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Expected O, but got Unknown
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Expected O, but got Unknown
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			PrefabManager.OnVanillaPrefabsAvailable -= AddHatcheryPiece;
			PieceConfig val = new PieceConfig();
			val.Name = "Valkyrie's Hatchery";
			val.Description = "Fill it with roe, wait, and collect your fish.";
			val.PieceTable = "Hammer";
			val.Category = "Crafting";
			val.CraftingStation = "piece_workbench";
			val.Requirements = (RequirementConfig[])(object)new RequirementConfig[3]
			{
				new RequirementConfig("Wood", 20, 0, true),
				new RequirementConfig("FineWood", 10, 0, true),
				new RequirementConfig("Stone", 8, 0, true)
			};
			PieceConfig val2 = val;
			CustomPiece val3 = new CustomPiece("piece_ValkyriesHatchery", "piece_bathtub", val2);
			if ((Object)(object)val3.PiecePrefab == (Object)null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Could not clone piece_bathtub.");
				return;
			}
			GameObject piecePrefab = val3.PiecePrefab;
			StripHotTubParts(piecePrefab);
			AddStorage(piecePrefab);
			TintRenderers(piecePrefab, DarkStain, "WaterSurface");
			Transform val4 = piecePrefab.transform.Find("water");
			if ((Object)(object)val4 != (Object)null)
			{
				((Component)val4).gameObject.SetActive(false);
			}
			piecePrefab.AddComponent<HatcheryBrain>();
			PieceManager.Instance.AddPiece(val3);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Added Valkyrie's Hatchery piece.");
		}

		private void StripHotTubParts(GameObject prefab)
		{
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			Smelter component = prefab.GetComponent<Smelter>();
			if ((Object)(object)component != (Object)null)
			{
				Object.DestroyImmediate((Object)(object)component);
			}
			string[] array = new string[4] { "add_fuel", "SitPoints", "_Enabled/fire", "_Enabled/WarmAndCozy" };
			foreach (string text in array)
			{
				Transform val = prefab.transform.Find(text);
				if ((Object)(object)val != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)((Component)val).gameObject);
				}
				else
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Hatchery: part '" + text + "' not found on the hot tub clone."));
				}
			}
			Piece component2 = prefab.GetComponent<Piece>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.m_comfort = 0;
				component2.m_comfortObject = null;
				component2.m_category = (PieceCategory)1;
				component2.m_usage = (UsageTagFlags)2;
			}
		}

		private void AddStorage(GameObject prefab)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			GameObject prefab2 = PrefabManager.Instance.GetPrefab("piece_chest_wood");
			Container val = ((prefab2 != null) ? prefab2.GetComponent<Container>() : null);
			Container val2 = prefab.AddComponent<Container>();
			Vector2Int val3 = GridFor(Slots.Value);
			val2.m_name = "Valkyrie's Hatchery";
			val2.m_width = ((Vector2Int)(ref val3)).x;
			val2.m_height = ((Vector2Int)(ref val3)).y;
			if ((Object)(object)val != (Object)null)
			{
				val2.m_bkg = val.m_bkg;
				val2.m_openEffects = val.m_openEffects;
				val2.m_closeEffects = val.m_closeEffects;
			}
		}

		private static void TintRenderers(GameObject prefab, Color tint, string skipName = null)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Expected O, but got Unknown
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			Renderer[] componentsInChildren = prefab.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val in componentsInChildren)
			{
				if ((skipName != null && ((Object)((Component)val).transform).name == skipName) || val is ParticleSystemRenderer)
				{
					continue;
				}
				Material[] sharedMaterials = val.sharedMaterials;
				for (int j = 0; j < sharedMaterials.Length; j++)
				{
					if (!((Object)(object)sharedMaterials[j] == (Object)null) && sharedMaterials[j].HasProperty("_Color"))
					{
						Material val2 = new Material(sharedMaterials[j]);
						val2.color = sharedMaterials[j].color * tint;
						sharedMaterials[j] = val2;
					}
				}
				val.sharedMaterials = sharedMaterials;
			}
		}

		private void AddRoe()
		{
			//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_0021: 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_0037: 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_0073: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Expected O, but got Unknown
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			PrefabManager.OnVanillaPrefabsAvailable -= AddRoe;
			ItemConfig val = new ItemConfig
			{
				Name = "Fish Roe",
				Description = "Fish eggs, glistening and black. Place them in a Valkyrie's Hatchery to raise fish.",
				CraftingStation = "piece_preptable",
				Amount = 2,
				Requirements = ((IEnumerable<string>)FishIds).Select((Func<string, RequirementConfig>)((string id) => new RequirementConfig(id, 1, 0, true))).ToArray()
			};
			CustomItem val2 = new CustomItem("ValkyriesHatchery_Roe", "HatefulBlood", val);
			if ((Object)(object)val2.ItemPrefab == (Object)null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Could not clone HatefulBlood for roe.");
				return;
			}
			Renderer[] componentsInChildren = val2.ItemPrefab.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val3 in componentsInChildren)
			{
				Material[] sharedMaterials = val3.sharedMaterials;
				for (int num2 = 0; num2 < sharedMaterials.Length; num2++)
				{
					if (!((Object)(object)sharedMaterials[num2] == (Object)null) && sharedMaterials[num2].HasProperty("_Color"))
					{
						Material val4 = new Material(sharedMaterials[num2]);
						val4.color = sharedMaterials[num2].color * CaviarBlack;
						sharedMaterials[num2] = val4;
					}
				}
				val3.sharedMaterials = sharedMaterials;
			}
			SharedData shared = val2.ItemDrop.m_itemData.m_shared;
			shared.m_maxStackSize = 50;
			shared.m_weight = 0.1f;
			shared.m_teleportable = true;
			Sprite val5 = RenderManager.Instance.Render(val2.ItemPrefab, RenderManager.IsometricRotation);
			if ((Object)(object)val5 != (Object)null)
			{
				shared.m_icons = (Sprite[])(object)new Sprite[1] { val5 };
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Hatchery: could not render roe icon; keeping the original.");
			}
			ItemManager.Instance.AddItem(val2);
			CustomRecipe recipe = val2.Recipe;
			if ((Object)(object)((recipe != null) ? recipe.Recipe : null) != (Object)null)
			{
				val2.Recipe.Recipe.m_requireOnlyOneIngredient = true;
				Recipe prefab = Cache.GetPrefab<Recipe>("Recipe_Fish1");
				val2.Recipe.Recipe.m_qualityResultAmountMultiplier = (((Object)(object)prefab != (Object)null) ? prefab.m_qualityResultAmountMultiplier : 1f);
			}
			else
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Hatchery: roe recipe was not created.");
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Added Fish Roe.");
			AddRoeToHaldor(val2.ItemDrop);
		}

		private void AddRoeToHaldor(ItemDrop roeDrop)
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected O, but got Unknown
			GameObject prefab = PrefabManager.Instance.GetPrefab("Haldor");
			Trader val = ((prefab != null) ? prefab.GetComponent<Trader>() : null);
			if ((Object)(object)val == (Object)null)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Hatchery: couldn't find Haldor; roe won't be sold.");
				return;
			}
			_roeTrade = ((IEnumerable<TradeItem>)val.m_items).FirstOrDefault((Func<TradeItem, bool>)((TradeItem t) => (Object)(object)t.m_prefab != (Object)null && ((Object)t.m_prefab).name == "ValkyriesHatchery_Roe"));
			if (_roeTrade == null)
			{
				_roeTrade = new TradeItem
				{
					m_prefab = roeDrop,
					m_stack = 1,
					m_price = RoePrice.Value
				};
				val.m_items.Add(_roeTrade);
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Added Fish Roe to Haldor's shop.");
		}

		public static void PlayHatchSound(Vector3 position)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Instance != (Object)null)
			{
				((MonoBehaviour)Instance).StartCoroutine(HatchSoundRoutine(position));
			}
		}

		private static IEnumerator HatchSoundRoutine(Vector3 position)
		{
			//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)
			string[] hatchSequence = HatchSequence;
			for (int i = 0; i < hatchSequence.Length; i++)
			{
				GameObject prefab = Cache.GetPrefab<GameObject>(hatchSequence[i]);
				if ((Object)(object)prefab != (Object)null)
				{
					Object.Instantiate<GameObject>(prefab, position, Quaternion.identity);
				}
				yield return (object)new WaitForSeconds(0.5f);
			}
		}

		public static void Log(string msg)
		{
			Plugin instance = Instance;
			if (instance != null)
			{
				((BaseUnityPlugin)instance).Logger.LogInfo((object)msg);
			}
		}

		public static void Warn(string msg)
		{
			Plugin instance = Instance;
			if (instance != null)
			{
				((BaseUnityPlugin)instance).Logger.LogWarning((object)msg);
			}
		}
	}
	public static class HatchRules
	{
		private class FishInfo
		{
			public string Id;

			public Biome Home;

			public string UnlockKey;

			public float Xp;

			public float Weight;

			public FishInfo(string id, Biome home, string key, float xp, float weight = 1f)
			{
				//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)
				Id = id;
				Home = home;
				UnlockKey = key;
				Xp = xp;
				Weight = weight;
			}
		}

		private static readonly FishInfo[] Fish = new FishInfo[12]
		{
			new FishInfo("Fish1", (Biome)9, "defeated_eikthyr", 10f),
			new FishInfo("Fish2", (Biome)9, "defeated_eikthyr", 10f),
			new FishInfo("Fish5", (Biome)8, "defeated_gdking", 15f),
			new FishInfo("Fish6", (Biome)2, "defeated_bonemass", 20f),
			new FishInfo("Fish4_cave", (Biome)4, "defeated_dragon", 25f, 0.25f),
			new FishInfo("Fish7", (Biome)16, "defeated_goblinking", 25f),
			new FishInfo("Fish3", (Biome)256, "defeated_queen", 30f),
			new FishInfo("Fish8", (Biome)256, "defeated_queen", 30f),
			new FishInfo("Fish12", (Biome)768, "defeated_queen", 35f),
			new FishInfo("Fish9", (Biome)512, "defeated_queen", 35f),
			new FishInfo("Fish11", (Biome)32, "defeated_fader", 40f),
			new FishInfo("Fish10", (Biome)64, "defeated_frozenking_p3", 40f)
		};

		public static float XpFor(string fishId)
		{
			return Fish.FirstOrDefault((FishInfo f) => f.Id == fishId)?.Xp ?? 10f;
		}

		public static string RollFish(Vector3 position, Func<string, bool> fits)
		{
			//IL_001d: 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_0023: Unknown result type (might be due to invalid IL or missing references)
			Biome biome = (Biome)((WorldGenerator.instance == null) ? 1 : ((int)WorldGenerator.instance.GetBiome(position)));
			List<FishInfo> list = Fish.Where((FishInfo f) => (f.Home & biome) != 0 && fits(f.Id)).ToList();
			List<FishInfo> list2 = Fish.Where((FishInfo f) => (f.Home & biome) == 0 && KeySet(f.UnlockKey) && fits(f.Id)).ToList();
			if (list.Count == 0 && list2.Count == 0)
			{
				if (Fish.Any((FishInfo f) => (f.Home & biome) > 0) || !fits("Fish1"))
				{
					return null;
				}
				return "Fish1";
			}
			if (list.Count == 0)
			{
				return Pick(list2);
			}
			if (list2.Count == 0 || Random.Range(0, 100) < Plugin.HomeBiomeChance.Value)
			{
				return Pick(list);
			}
			return Pick(list2);
		}

		private static string Pick(List<FishInfo> pool)
		{
			float num = pool.Sum((FishInfo f) => f.Weight);
			float num2 = Random.Range(0f, num);
			foreach (FishInfo item in pool)
			{
				if (num2 < item.Weight)
				{
					return item.Id;
				}
				num2 -= item.Weight;
			}
			return pool[pool.Count - 1].Id;
		}

		private static bool KeySet(string key)
		{
			if (Plugin.Progression.Value == ProgressionSource.Player)
			{
				if ((Object)(object)Player.m_localPlayer != (Object)null)
				{
					return ((Humanoid)Player.m_localPlayer).HaveUniqueKey(key);
				}
				return false;
			}
			if ((Object)(object)ZoneSystem.instance != (Object)null)
			{
				return ZoneSystem.instance.GetGlobalKey(key);
			}
			return false;
		}
	}
	public class HatcheryBrain : MonoBehaviour
	{
		private const string StartKey = "hatchery_start_ticks";

		private const string HatchRpc = "RPC_HatcheryHatch";

		private static readonly FieldInfo InvWidth = typeof(Inventory).GetField("m_width", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static readonly MethodInfo InvChanged = typeof(Inventory).GetMethod("Changed", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);

		private static readonly FieldInfo InvHeight = typeof(Inventory).GetField("m_height", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private ZNetView _nview;

		private Container _container;

		private GameObject _water;

		private GameObject _bubbles;

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

		private bool _busy;

		private bool _checkQueued;

		private void Start()
		{
			_nview = ((Component)this).GetComponent<ZNetView>();
			_container = ((Component)this).GetComponent<Container>();
			Transform obj = ((Component)this).transform.Find("water");
			_water = ((obj != null) ? ((Component)obj).gameObject : null);
			Transform obj2 = ((Component)this).transform.Find("_Enabled");
			_bubbles = ((obj2 != null) ? ((Component)obj2).gameObject : null);
			if ((Object)(object)_nview == (Object)null || _nview.GetZDO() == null || (Object)(object)_container == (Object)null)
			{
				return;
			}
			_nview.Register("RPC_HatcheryHatch", (Action<long>)delegate
			{
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				if (Plugin.PlayHatchSoundSetting.Value)
				{
					Plugin.PlayHatchSound(((Component)this).transform.position);
				}
			});
			Inventory inventory = _container.GetInventory();
			if (inventory != null)
			{
				_fishCounts = CountFish(inventory);
				inventory.m_onChanged = (Action)Delegate.Combine(inventory.m_onChanged, new Action(OnInventoryChanged));
			}
			((MonoBehaviour)this).InvokeRepeating("Tick", 1f, 1f);
		}

		private void Tick()
		{
			if ((Object)(object)_nview == (Object)null || !_nview.IsValid())
			{
				return;
			}
			Inventory inventory = _container.GetInventory();
			if (inventory != null)
			{
				ApplySlotCount(inventory);
				if (_nview.IsOwner())
				{
					Hatch(inventory);
				}
				UpdateVisuals(inventory);
			}
		}

		private void ApplySlotCount(Inventory inv)
		{
			//IL_000a: 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)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			Vector2Int grid = Plugin.GridFor(Plugin.Slots.Value);
			if ((inv.GetWidth() == ((Vector2Int)(ref grid)).x && inv.GetHeight() == ((Vector2Int)(ref grid)).y) || InvWidth == null || InvHeight == null || _container.IsInUse())
			{
				return;
			}
			_container.m_width = ((Vector2Int)(ref grid)).x;
			_container.m_height = ((Vector2Int)(ref grid)).y;
			InvWidth.SetValue(inv, ((Vector2Int)(ref grid)).x);
			InvHeight.SetValue(inv, ((Vector2Int)(ref grid)).y);
			if (!_nview.IsOwner())
			{
				return;
			}
			_busy = true;
			try
			{
				foreach (ItemData item in inv.GetAllItems().ToList())
				{
					if (item.m_gridPos.x >= ((Vector2Int)(ref grid)).x || item.m_gridPos.y >= ((Vector2Int)(ref grid)).y)
					{
						Vector2i? val = FindFreeSlot(inv, grid);
						if (val.HasValue)
						{
							item.m_gridPos = val.Value;
							continue;
						}
						ItemDrop.DropItem(item.Clone(), item.m_stack, ((Component)this).transform.position + ((Component)this).transform.forward * 2f + Vector3.up, Quaternion.identity);
						inv.RemoveItem(item);
					}
				}
				InvChanged?.Invoke(inv, null);
			}
			finally
			{
				_busy = false;
			}
			_fishCounts = CountFish(inv);
			Plugin.Log($"Hatchery resized to {((Vector2Int)(ref grid)).x}x{((Vector2Int)(ref grid)).y}.");
		}

		private static Vector2i? FindFreeSlot(Inventory inv, Vector2Int grid)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < ((Vector2Int)(ref grid)).y; i++)
			{
				for (int j = 0; j < ((Vector2Int)(ref grid)).x; j++)
				{
					if (inv.GetItemAt(j, i) == null)
					{
						return new Vector2i(j, i);
					}
				}
			}
			return null;
		}

		private void Hatch(Inventory inv)
		{
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			ZDO zDO = _nview.GetZDO();
			long ticks = ZNet.instance.GetTime().Ticks;
			long num = zDO.GetLong("hatchery_start_ticks", 0L);
			long num2 = (long)(Plugin.HatchMinutes.Value * 60f * 10000000f);
			if (FindRoe(inv) == null)
			{
				if (num != 0L)
				{
					zDO.Set("hatchery_start_ticks", 0L);
				}
				return;
			}
			if (num == 0L)
			{
				zDO.Set("hatchery_start_ticks", ticks);
				return;
			}
			int num3 = 0;
			while (ticks - num >= num2)
			{
				ItemData val = FindRoe(inv);
				if (val == null)
				{
					num = 0L;
					break;
				}
				string text = HatchRules.RollFish(((Component)this).transform.position, (string id) => CanFit(inv, id));
				if (text == null || !AddFish(inv, text))
				{
					num = ticks;
					break;
				}
				_fishCounts.TryGetValue(text, out var value);
				_fishCounts[text] = value + 1;
				inv.RemoveItem(val, 1);
				num += num2;
				num3++;
			}
			if (num != 0L && FindRoe(inv) == null)
			{
				num = 0L;
			}
			zDO.Set("hatchery_start_ticks", num);
			if (num3 > 0)
			{
				_nview.InvokeRPC(ZNetView.Everybody, "RPC_HatcheryHatch", Array.Empty<object>());
				Plugin.Log($"Hatchery hatched {num3} fish.");
			}
		}

		private static bool CanFit(Inventory inv, string fishId)
		{
			if (inv.GetEmptySlots() > 0)
			{
				return true;
			}
			return inv.GetAllItems().Any((ItemData i) => (Object)(object)i.m_dropPrefab != (Object)null && ((Object)i.m_dropPrefab).name == fishId && i.m_quality == 1 && i.m_stack < i.m_shared.m_maxStackSize);
		}

		private static ItemData FindRoe(Inventory inv)
		{
			return ((IEnumerable<ItemData>)inv.GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => (Object)(object)i.m_dropPrefab != (Object)null && ((Object)i.m_dropPrefab).name == "ValkyriesHatchery_Roe"));
		}

		private static bool AddFish(Inventory inv, string fishId)
		{
			ObjectDB instance = ObjectDB.instance;
			GameObject val = ((instance != null) ? instance.GetItemPrefab(fishId) : null);
			ItemDrop val2 = ((val != null) ? val.GetComponent<ItemDrop>() : null);
			if ((Object)(object)val2 == (Object)null)
			{
				return false;
			}
			ItemData val3 = val2.m_itemData.Clone();
			val3.m_dropPrefab = val;
			val3.m_stack = 1;
			val3.m_quality = 1;
			val3.m_durability = val3.GetMaxDurability();
			return inv.AddItem(val3);
		}

		private void UpdateVisuals(Inventory inv)
		{
			bool flag = inv.NrOfItems() > 0;
			bool flag2 = inv.GetAllItems().Any(IsFish);
			if ((Object)(object)_water != (Object)null && _water.activeSelf != flag)
			{
				_water.SetActive(flag);
			}
			if ((Object)(object)_bubbles != (Object)null && _bubbles.activeSelf != flag2)
			{
				_bubbles.SetActive(flag2);
			}
		}

		private static bool IsRoe(ItemData i)
		{
			if ((Object)(object)i.m_dropPrefab != (Object)null)
			{
				return ((Object)i.m_dropPrefab).name == "ValkyriesHatchery_Roe";
			}
			return false;
		}

		private static bool IsFish(ItemData i)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			if ((int)i.m_shared.m_itemType == 21)
			{
				return (Object)(object)i.m_dropPrefab != (Object)null;
			}
			return false;
		}

		private static Dictionary<string, int> CountFish(Inventory inv)
		{
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			foreach (ItemData allItem in inv.GetAllItems())
			{
				if (IsFish(allItem))
				{
					dictionary.TryGetValue(((Object)allItem.m_dropPrefab).name, out var value);
					dictionary[((Object)allItem.m_dropPrefab).name] = value + allItem.m_stack;
				}
			}
			return dictionary;
		}

		private bool LocalPlayerHasItOpen()
		{
			if (_container.IsInUse() && _nview.IsOwner())
			{
				return InventoryGui.IsVisible();
			}
			return false;
		}

		private void OnInventoryChanged()
		{
			if (!_busy && !_checkQueued)
			{
				_checkQueued = true;
				((MonoBehaviour)this).Invoke("Enforce", 0f);
			}
		}

		private void Enforce()
		{
			_checkQueued = false;
			if (_busy || (Object)(object)_nview == (Object)null || !_nview.IsValid())
			{
				return;
			}
			Inventory inventory = _container.GetInventory();
			if (inventory == null)
			{
				return;
			}
			if (!_nview.IsOwner())
			{
				_fishCounts = CountFish(inventory);
				return;
			}
			_busy = true;
			try
			{
				bool flag = false;
				foreach (ItemData item in inventory.GetAllItems().ToList())
				{
					if (!IsRoe(item) && (!IsFish(item) || item.m_quality != 1))
					{
						Eject(inventory, item, item.m_stack);
						flag = true;
					}
				}
				Dictionary<string, int> source = CountFish(inventory);
				foreach (KeyValuePair<string, int> kv in source.ToList())
				{
					_fishCounts.TryGetValue(kv.Key, out var value);
					int num = kv.Value - value;
					while (num > 0)
					{
						ItemData val = ((IEnumerable<ItemData>)inventory.GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => IsFish(i) && ((Object)i.m_dropPrefab).name == kv.Key));
						if (val == null)
						{
							break;
						}
						int num2 = Mathf.Min(num, val.m_stack);
						Eject(inventory, val, num2);
						num -= num2;
						flag = true;
					}
				}
				source = CountFish(inventory);
				Dictionary<string, int> dictionary = new Dictionary<string, int>();
				foreach (KeyValuePair<string, int> fishCount in _fishCounts)
				{
					source.TryGetValue(fishCount.Key, out var value2);
					if (value2 < fishCount.Value)
					{
						dictionary[fishCount.Key] = fishCount.Value - value2;
					}
				}
				if (dictionary.Count > 0 && LocalPlayerHasItOpen())
				{
					AwardXp(dictionary);
				}
				_fishCounts = source;
				if (flag && LocalPlayerHasItOpen())
				{
					Player localPlayer = Player.m_localPlayer;
					if (localPlayer != null)
					{
						((Character)localPlayer).Message((MessageType)2, "Only Fish Roe can go in the hatchery.", 0, (Sprite)null, false);
					}
				}
			}
			finally
			{
				_busy = false;
			}
		}

		private void Eject(Inventory inv, ItemData item, int amount)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			ItemData val = item.Clone();
			val.m_stack = amount;
			Player localPlayer = Player.m_localPlayer;
			bool flag = false;
			if ((Object)(object)localPlayer != (Object)null && LocalPlayerHasItOpen() && ((Humanoid)localPlayer).GetInventory().CanAddItem(val, -1))
			{
				flag = ((Humanoid)localPlayer).GetInventory().AddItem(val);
			}
			if (!flag)
			{
				ItemDrop.DropItem(val, amount, ((Component)this).transform.position + ((Component)this).transform.forward * 2f + Vector3.up, Quaternion.identity);
			}
			inv.RemoveItem(item, amount);
		}

		private static void AwardXp(Dictionary<string, int> removed)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			float num = 0f;
			int num2 = 0;
			foreach (KeyValuePair<string, int> item in removed)
			{
				num += HatchRules.XpFor(item.Key) * (float)item.Value;
				num2 += item.Value;
			}
			num *= Plugin.XpMultiplier.Value;
			if (!(num <= 0f))
			{
				((Character)localPlayer).RaiseSkill((SkillType)104, num);
				if (Plugin.ShowXpMessage.Value)
				{
					((Character)localPlayer).Message((MessageType)1, $"Fishing +{num:0} ({num2} fish)", 0, (Sprite)null, false);
				}
				Plugin.Log($"Hatchery: awarded {num} Fishing XP for {num2} fish.");
			}
		}
	}
}