Decompiled source of SimpleFishingTrap v0.1.0

SimpleFishingTrap.dll

Decompiled 20 hours ago
using System;
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.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("SimpleFishingTrap")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0")]
[assembly: AssemblyProduct("SimpleFishingTrap")]
[assembly: AssemblyTitle("SimpleFishingTrap")]
[assembly: AssemblyVersion("0.1.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 SimpleFishingTrap
{
	internal static class ConfigSync
	{
		private const string RpcName = "SimpleFishingTrap_SyncConfig";

		private const int PackageVersion = 1;

		private static float _secondsPerFish;

		private static int _maxFish;

		private static bool _catchUp;

		private static string _itemPrefab;

		private static float _maxDistance;

		private static float _maxHeight;

		private static string _recipe;

		private static string _station;

		internal static bool HasServerConfig { get; private set; }

		internal static float SecondsPerFish => Math.Max(1f, HasServerConfig ? _secondsPerFish : Plugin.SecondsPerFish.Value);

		internal static int MaxFish => Math.Max(1, HasServerConfig ? _maxFish : Plugin.MaxFish.Value);

		internal static bool CatchUpWhileAway => HasServerConfig ? _catchUp : Plugin.CatchUpWhileAway.Value;

		internal static string ItemPrefab => HasServerConfig ? _itemPrefab : Plugin.ItemPrefab.Value;

		internal static float MaxWaterDistance => HasServerConfig ? _maxDistance : Plugin.MaxWaterDistance.Value;

		internal static float MaxHeightAboveWater => HasServerConfig ? _maxHeight : Plugin.MaxHeightAboveWater.Value;

		internal static string Recipe => HasServerConfig ? _recipe : Plugin.Recipe.Value;

		internal static string CraftingStation => HasServerConfig ? _station : Plugin.CraftingStationName.Value;

		internal static void OnNewConnection_Postfix(ZNetPeer peer)
		{
			try
			{
				if (peer != null)
				{
					ZRpc rpc = peer.m_rpc;
					if (rpc != null)
					{
						rpc.Register<ZPackage>("SimpleFishingTrap_SyncConfig", (Action<ZRpc, ZPackage>)RPC_Receive);
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not register sync RPC: " + ex.Message));
			}
		}

		internal static void RPC_PeerInfo_Postfix(ZNet __instance, ZRpc rpc)
		{
			if ((Object)(object)__instance == (Object)null || rpc == null || !__instance.IsServer() || !Plugin.EnforceServerConfig.Value)
			{
				return;
			}
			try
			{
				rpc.Invoke("SimpleFishingTrap_SyncConfig", new object[1] { Build() });
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not send settings to joining player: " + ex.Message));
			}
		}

		internal static void ZNet_OnDestroy_Postfix()
		{
			if (HasServerConfig)
			{
				HasServerConfig = false;
				TrapPrefab.ApplyRecipe();
				Plugin.Log.LogInfo((object)"Left server; using local settings again.");
			}
		}

		internal static void BroadcastIfServer()
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null || !instance.IsServer() || !Plugin.EnforceServerConfig.Value)
			{
				return;
			}
			foreach (ZNetPeer peer in instance.GetPeers())
			{
				if (peer?.m_rpc != null)
				{
					try
					{
						peer.m_rpc.Invoke("SimpleFishingTrap_SyncConfig", new object[1] { Build() });
					}
					catch (Exception ex)
					{
						Plugin.Log.LogWarning((object)("Sync to a peer failed: " + ex.Message));
					}
				}
			}
		}

		private static ZPackage Build()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(1);
			val.Write(Plugin.SecondsPerFish.Value);
			val.Write(Plugin.MaxFish.Value);
			val.Write(Plugin.CatchUpWhileAway.Value);
			val.Write(Plugin.ItemPrefab.Value ?? "FishRaw");
			val.Write(Plugin.MaxWaterDistance.Value);
			val.Write(Plugin.MaxHeightAboveWater.Value);
			val.Write(Plugin.Recipe.Value ?? "");
			val.Write(Plugin.CraftingStationName.Value ?? "");
			return val;
		}

		private static void RPC_Receive(ZRpc rpc, ZPackage p)
		{
			if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer())
			{
				return;
			}
			try
			{
				int num = p.ReadInt();
				if (num != 1)
				{
					Plugin.Log.LogWarning((object)($"Server settings format v{num}, expected v{1}. " + "Update the mod to match the server. Using local settings."));
					return;
				}
				_secondsPerFish = p.ReadSingle();
				_maxFish = p.ReadInt();
				_catchUp = p.ReadBool();
				_itemPrefab = p.ReadString();
				_maxDistance = p.ReadSingle();
				_maxHeight = p.ReadSingle();
				_recipe = p.ReadString();
				_station = p.ReadString();
				HasServerConfig = true;
				TrapPrefab.ApplyRecipe();
				Plugin.Log.LogInfo((object)"Using server settings for Simple Fishing Trap.");
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Bad settings package from server: " + ex.Message));
			}
		}
	}
	public class FishTrap : MonoBehaviour, Hoverable, Interactable
	{
		private const string KeyCount = "sft_count";

		private const string KeyProgress = "sft_progress";

		private const string KeyLastTime = "sft_last";

		private const string RpcExtract = "SFT_Extract";

		private const string RpcGive = "SFT_Give";

		private const float TickSeconds = 2f;

		private const float MaxCatchUpWhenDisabled = 10f;

		private ZNetView _nview;

		private Piece _piece;

		private List<GameObject> _fishVisuals;

		private bool Valid => (Object)(object)_nview != (Object)null && _nview.IsValid();

		private int Count => Valid ? _nview.GetZDO().GetInt("sft_count", 0) : 0;

		private float Progress => Valid ? _nview.GetZDO().GetFloat("sft_progress", 0f) : 0f;

		private void Awake()
		{
			_nview = ((Component)this).GetComponent<ZNetView>();
			_piece = ((Component)this).GetComponent<Piece>();
			if (!((Object)(object)_nview == (Object)null) && _nview.GetZDO() != null)
			{
				_nview.Register("SFT_Extract", (Action<long>)RPC_Extract);
				_nview.Register<int>("SFT_Give", (Action<long, int>)RPC_Give);
				_fishVisuals = (from t in ((Component)this).GetComponentsInChildren<Transform>(true)
					where ((Object)t).name.StartsWith("FishVisual", StringComparison.OrdinalIgnoreCase)
					select ((Component)t).gameObject).OrderBy<GameObject, string>((GameObject g) => ((Object)g).name, StringComparer.OrdinalIgnoreCase).ToList();
				((MonoBehaviour)this).InvokeRepeating("Tick", Random.Range(0.5f, 2f), 2f);
			}
		}

		private void Tick()
		{
			if (Valid)
			{
				if (_nview.IsOwner())
				{
					Advance();
				}
				UpdateFishVisuals();
			}
		}

		private void Advance()
		{
			ZDO zDO = _nview.GetZDO();
			long ticks = ZNet.instance.GetTime().Ticks;
			long num = zDO.GetLong("sft_last", 0L);
			zDO.Set("sft_last", ticks);
			if (num == 0)
			{
				return;
			}
			float num2 = (float)TimeSpan.FromTicks(Math.Max(0L, ticks - num)).TotalSeconds;
			if (!ConfigSync.CatchUpWhileAway)
			{
				num2 = Math.Min(num2, 10f);
			}
			int maxFish = ConfigSync.MaxFish;
			int num3 = zDO.GetInt("sft_count", 0);
			if (num3 >= maxFish)
			{
				if (zDO.GetFloat("sft_progress", 0f) != 0f)
				{
					zDO.Set("sft_progress", 0f);
				}
				return;
			}
			float secondsPerFish = ConfigSync.SecondsPerFish;
			float num4 = zDO.GetFloat("sft_progress", 0f) + num2;
			int num5 = (int)(num4 / secondsPerFish);
			if (num5 > 0)
			{
				num3 = Math.Min(maxFish, num3 + num5);
				num4 -= (float)num5 * secondsPerFish;
				if (num3 >= maxFish)
				{
					num4 = 0f;
				}
				zDO.Set("sft_count", num3);
			}
			zDO.Set("sft_progress", num4);
		}

		private void UpdateFishVisuals()
		{
			if (_fishVisuals == null || _fishVisuals.Count == 0)
			{
				return;
			}
			int maxFish = ConfigSync.MaxFish;
			int num = ((Count > 0) ? Mathf.CeilToInt((float)_fishVisuals.Count * Mathf.Clamp01((float)Count / (float)maxFish)) : 0);
			for (int i = 0; i < _fishVisuals.Count; i++)
			{
				if ((Object)(object)_fishVisuals[i] != (Object)null && _fishVisuals[i].activeSelf != i < num)
				{
					_fishVisuals[i].SetActive(i < num);
				}
			}
		}

		public string GetHoverName()
		{
			return ((Object)(object)_piece != (Object)null) ? _piece.m_name : "Simple Fishing Trap";
		}

		public float GetHoverOffset()
		{
			return 0f;
		}

		public string GetHoverText()
		{
			if (!Valid)
			{
				return "";
			}
			int count = Count;
			int maxFish = ConfigSync.MaxFish;
			string text = $"{GetHoverName()}\nRaw Fish: {count}/{maxFish}";
			if (count >= maxFish)
			{
				text += "\n<color=orange>Full — empty it to keep catching</color>";
			}
			else
			{
				float num = Mathf.Max(0f, ConfigSync.SecondsPerFish - Progress);
				TimeSpan timeSpan = TimeSpan.FromSeconds(num);
				text += $"\n<color=#a0a0a0>Next fish in ~{(int)timeSpan.TotalMinutes}:{timeSpan.Seconds:00}</color>";
			}
			if (count > 0)
			{
				text += "\n[<color=yellow><b>$KEY_Use</b></color>] Collect";
			}
			return Loc.Localize(text);
		}

		public bool Interact(Humanoid user, bool hold, bool alt)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			if (hold || !Valid)
			{
				return false;
			}
			if (!PrivateArea.CheckAccess(((Component)this).transform.position, 0f, true, false))
			{
				return true;
			}
			if (Count <= 0)
			{
				((Character)user).Message((MessageType)2, "The trap is empty", 0, (Sprite)null, false);
				return true;
			}
			_nview.InvokeRPC("SFT_Extract", Array.Empty<object>());
			return true;
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			return false;
		}

		private void RPC_Extract(long sender)
		{
			if (Valid && _nview.IsOwner())
			{
				ZDO zDO = _nview.GetZDO();
				int num = zDO.GetInt("sft_count", 0);
				if (num > 0)
				{
					zDO.Set("sft_count", 0);
					zDO.Set("sft_progress", 0f);
					zDO.Set("sft_last", ZNet.instance.GetTime().Ticks);
					UpdateFishVisuals();
					_nview.InvokeRPC(sender, "SFT_Give", new object[1] { num });
				}
			}
		}

		private void RPC_Give(long sender, int count)
		{
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: 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)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || count <= 0)
			{
				return;
			}
			GameObject val = TrapPrefab.Find(ConfigSync.ItemPrefab);
			ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
			if ((Object)(object)val2 == (Object)null)
			{
				Plugin.Log.LogError((object)$"Item prefab '{ConfigSync.ItemPrefab}' not found — {count} fish lost.");
				return;
			}
			Inventory inventory = ((Humanoid)localPlayer).GetInventory();
			int val3 = Math.Max(1, val2.m_itemData.m_shared.m_maxStackSize);
			int num = count;
			bool flag = false;
			while (num > 0)
			{
				int num2 = Math.Min(num, val3);
				ItemData val4 = val2.m_itemData.Clone();
				val4.m_stack = num2;
				val4.m_dropPrefab = val;
				if (inventory.CanAddItem(val4, -1))
				{
					inventory.AddItem(val4);
				}
				else
				{
					Vector3 val5 = ((Component)localPlayer).transform.position + ((Component)localPlayer).transform.forward * 0.5f + Vector3.up;
					ItemDrop.DropItem(val4, num2, val5, Quaternion.identity);
					flag = true;
				}
				num -= num2;
			}
			string arg = Loc.Localize(val2.m_itemData.m_shared.m_name);
			((Character)localPlayer).Message((MessageType)1, $"Collected {arg} x{count}", 0, val2.m_itemData.GetIcon(), false);
			if (flag)
			{
				((Character)localPlayer).Message((MessageType)2, "Inventory full — some fish dropped at your feet", 0, (Sprite)null, false);
			}
		}
	}
	internal static class ObjModel
	{
		private class ObjObject
		{
			public string Name;

			public readonly List<int[]> Corners = new List<int[]>();
		}

		internal const string ObjFile = "SimpleFishingTrap.obj";

		internal const string AlbedoFile = "SimpleFishingTrap_Albedo.png";

		internal const string NormalFile = "SimpleFishingTrap_Normal.png";

		internal const string IconFile = "SimpleFishingTrap_Icon.png";

		private static MethodInfo _loadImage;

		internal static GameObject Load(string objPath, Material baseMaterial, Transform holder)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Expected O, but got Unknown
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				List<Vector3> positions;
				List<Vector2> uvs;
				List<Vector3> normals;
				List<ObjObject> list = Parse(objPath, out positions, out uvs, out normals);
				if (list.Count == 0)
				{
					Plugin.Log.LogWarning((object)("'" + objPath + "' has no faces."));
					return null;
				}
				Material sharedMaterial = MakeMaterial(baseMaterial);
				GameObject val = new GameObject("SimpleFishingTrap_Model");
				val.transform.SetParent(holder, false);
				bool flag = false;
				foreach (ObjObject item in list)
				{
					Mesh val2 = BuildMesh(item, positions, uvs, normals);
					if (!((Object)(object)val2 == (Object)null))
					{
						GameObject val3 = new GameObject(item.Name);
						val3.transform.SetParent(val.transform, false);
						if (item.Name.StartsWith("Collider", StringComparison.OrdinalIgnoreCase))
						{
							BoxCollider val4 = val3.AddComponent<BoxCollider>();
							Bounds bounds = val2.bounds;
							val4.center = ((Bounds)(ref bounds)).center;
							bounds = val2.bounds;
							val4.size = ((Bounds)(ref bounds)).size;
							flag = true;
						}
						else
						{
							val3.AddComponent<MeshFilter>().sharedMesh = val2;
							MeshRenderer val5 = val3.AddComponent<MeshRenderer>();
							((Renderer)val5).sharedMaterial = sharedMaterial;
							((Renderer)val5).shadowCastingMode = (ShadowCastingMode)1;
							((Renderer)val5).receiveShadows = true;
						}
					}
				}
				if (!flag)
				{
					Plugin.Log.LogInfo((object)"OBJ has no 'Collider' object; a box will be fitted around the mesh.");
				}
				Plugin.Log.LogInfo((object)$"Loaded OBJ model ({list.Count} object(s), {positions.Count} vertices).");
				return val;
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Failed to load '{objPath}': {arg}");
				return null;
			}
		}

		private static List<ObjObject> Parse(string path, out List<Vector3> positions, out List<Vector2> uvs, out List<Vector3> normals)
		{
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			positions = new List<Vector3>();
			uvs = new List<Vector2>();
			normals = new List<Vector3>();
			List<ObjObject> list = new List<ObjObject>();
			ObjObject objObject = null;
			CultureInfo invariantCulture = CultureInfo.InvariantCulture;
			string[] array = File.ReadAllLines(path);
			foreach (string text in array)
			{
				string text2 = text.Trim();
				if (text2.Length == 0 || text2[0] == '#')
				{
					continue;
				}
				string[] array2 = text2.Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
				switch (array2[0])
				{
				case "v":
					positions.Add(new Vector3(0f - float.Parse(array2[1], invariantCulture), float.Parse(array2[2], invariantCulture), float.Parse(array2[3], invariantCulture)));
					break;
				case "vt":
					uvs.Add(new Vector2(float.Parse(array2[1], invariantCulture), (array2.Length > 2) ? float.Parse(array2[2], invariantCulture) : 0f));
					break;
				case "vn":
					normals.Add(new Vector3(0f - float.Parse(array2[1], invariantCulture), float.Parse(array2[2], invariantCulture), float.Parse(array2[3], invariantCulture)));
					break;
				case "o":
				case "g":
				{
					string name = ((array2.Length > 1) ? string.Join(" ", array2, 1, array2.Length - 1) : "Mesh");
					objObject = new ObjObject
					{
						Name = name
					};
					list.Add(objObject);
					break;
				}
				case "f":
				{
					if (objObject == null)
					{
						objObject = new ObjObject
						{
							Name = "Mesh"
						};
						list.Add(objObject);
					}
					List<int[]> list2 = new List<int[]>();
					for (int j = 1; j < array2.Length; j++)
					{
						list2.Add(ParseCorner(array2[j], positions.Count, uvs.Count, normals.Count));
					}
					for (int k = 1; k + 1 < list2.Count; k++)
					{
						objObject.Corners.Add(list2[0]);
						objObject.Corners.Add(list2[k + 1]);
						objObject.Corners.Add(list2[k]);
					}
					break;
				}
				}
			}
			list.RemoveAll((ObjObject o) => o.Corners.Count == 0);
			return list;
		}

		private static int[] ParseCorner(string token, int vCount, int vtCount, int vnCount)
		{
			string[] s = token.Split(new char[1] { '/' });
			return new int[3]
			{
				Index(s, 0, vCount),
				Index(s, 1, vtCount),
				Index(s, 2, vnCount)
			};
		}

		private static int Index(string[] s, int i, int count)
		{
			if (i >= s.Length || s[i].Length == 0)
			{
				return -1;
			}
			int num = int.Parse(s[i], CultureInfo.InvariantCulture);
			return (num > 0) ? (num - 1) : (count + num);
		}

		private static Mesh BuildMesh(ObjObject o, List<Vector3> pos, List<Vector2> uv, List<Vector3> nrm)
		{
			//IL_00bc: 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_00cf: 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_016a: Expected O, but got Unknown
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			List<Vector3> list = new List<Vector3>();
			List<Vector2> list2 = new List<Vector2>();
			List<Vector3> list3 = new List<Vector3>();
			List<int> list4 = new List<int>();
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			bool flag = true;
			foreach (int[] corner in o.Corners)
			{
				string key = corner[0] + "/" + corner[1] + "/" + corner[2];
				if (!dictionary.TryGetValue(key, out var value))
				{
					value = (dictionary[key] = list.Count);
					list.Add(pos[corner[0]]);
					list2.Add((corner[1] >= 0) ? uv[corner[1]] : Vector2.zero);
					if (corner[2] >= 0)
					{
						list3.Add(nrm[corner[2]]);
					}
					else
					{
						list3.Add(Vector3.up);
						flag = false;
					}
				}
				list4.Add(value);
			}
			if (list.Count == 0)
			{
				return null;
			}
			Mesh val = new Mesh
			{
				name = o.Name
			};
			if (list.Count > 65000)
			{
				val.indexFormat = (IndexFormat)1;
			}
			val.SetVertices(list);
			val.SetUVs(0, list2);
			val.SetTriangles(list4, 0);
			if (flag)
			{
				val.SetNormals(list3);
			}
			else
			{
				val.RecalculateNormals();
			}
			val.RecalculateTangents();
			val.RecalculateBounds();
			return val;
		}

		private static Material MakeMaterial(Material baseMaterial)
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Expected O, but got Unknown
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Expected O, but got Unknown
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0328: Unknown result type (might be due to invalid IL or missing references)
			//IL_0338: Unknown result type (might be due to invalid IL or missing references)
			//IL_0479: Unknown result type (might be due to invalid IL or missing references)
			//IL_0375: Unknown result type (might be due to invalid IL or missing references)
			//IL_0383: Unknown result type (might be due to invalid IL or missing references)
			Shader val = null;
			string[] array = new string[4] { "Standard", "Legacy Shaders/Diffuse", "Diffuse", "Mobile/Diffuse" };
			foreach (string text in array)
			{
				val = Shader.Find(text);
				if ((Object)(object)val != (Object)null)
				{
					break;
				}
			}
			if ((Object)(object)val != (Object)null)
			{
				Material val2 = new Material(val)
				{
					name = "SimpleFishingTrap_Mat"
				};
				Texture2D val3 = LoadTexture(TrapPrefab.FindFile("SimpleFishingTrap_Albedo.png"), linear: false);
				if ((Object)(object)val3 != (Object)null)
				{
					val2.mainTexture = (Texture)(object)val3;
				}
				else
				{
					Plugin.Log.LogWarning((object)"'SimpleFishingTrap_Albedo.png' not found — model will be untextured.");
				}
				val2.color = Color.white;
				if (val2.HasProperty("_Metallic"))
				{
					val2.SetFloat("_Metallic", 0f);
				}
				if (val2.HasProperty("_Glossiness"))
				{
					val2.SetFloat("_Glossiness", 0.1f);
				}
				Texture2D val4 = LoadTexture(TrapPrefab.FindFile("SimpleFishingTrap_Normal.png"), linear: true);
				if ((Object)(object)val4 != (Object)null && val2.HasProperty("_BumpMap"))
				{
					val2.SetTexture("_BumpMap", (Texture)(object)val4);
					val2.EnableKeyword("_NORMALMAP");
				}
				Plugin.Log.LogInfo((object)("Trap material uses shader '" + ((Object)val).name + "'."));
				return val2;
			}
			Plugin.Log.LogWarning((object)"No plain Unity shader found; falling back to a copy of the chest material.");
			Material val5 = (((Object)(object)baseMaterial != (Object)null) ? new Material(baseMaterial) : new Material(Shader.Find("Custom/Piece")));
			((Object)val5).name = "SimpleFishingTrap_Mat";
			if (val5.HasProperty("_Color"))
			{
				val5.SetColor("_Color", Color.white);
			}
			Texture2D val6 = LoadTexture(TrapPrefab.FindFile("SimpleFishingTrap_Albedo.png"), linear: false);
			if ((Object)(object)val6 != (Object)null && val5.HasProperty("_MainTex"))
			{
				val5.SetTexture("_MainTex", (Texture)(object)val6);
			}
			else if ((Object)(object)val6 == (Object)null)
			{
				Plugin.Log.LogWarning((object)"'SimpleFishingTrap_Albedo.png' not found — model will be untextured.");
			}
			Texture2D val7 = LoadTexture(TrapPrefab.FindFile("SimpleFishingTrap_Normal.png"), linear: true);
			if (val5.HasProperty("_BumpMap"))
			{
				val5.SetTexture("_BumpMap", (Texture)(object)val7);
				if ((Object)(object)val7 != (Object)null)
				{
					val5.EnableKeyword("_NORMALMAP");
				}
			}
			string[] array2;
			try
			{
				array2 = val5.GetTexturePropertyNames();
			}
			catch
			{
				array2 = new string[2] { "_MainTex", "_BumpMap" };
			}
			StringBuilder stringBuilder = new StringBuilder();
			string[] array3 = array2;
			foreach (string text2 in array3)
			{
				if (val5.HasProperty(text2))
				{
					Texture texture = val5.GetTexture(text2);
					stringBuilder.Append(string.Format("\n  {0}: {1}, tiling {2}, offset {3}", text2, ((Object)(object)texture != (Object)null) ? ((Object)texture).name : "none", val5.GetTextureScale(text2), val5.GetTextureOffset(text2)));
					if (text2 == "_MainTex" || text2 == "_BumpMap")
					{
						val5.SetTextureScale(text2, Vector2.one);
						val5.SetTextureOffset(text2, Vector2.zero);
					}
				}
			}
			Plugin.Log.LogInfo((object)$"Trap material shader '{((Object)val5.shader).name}', texture slots before reset:{stringBuilder}");
			string[] array4 = new string[4] { "_MetallicGlossMap", "_EmissionMap", "_OcclusionMap", "_DetailAlbedoMap" };
			foreach (string text3 in array4)
			{
				if (val5.HasProperty(text3))
				{
					val5.SetTexture(text3, (Texture)null);
				}
			}
			if (val5.HasProperty("_Metallic"))
			{
				val5.SetFloat("_Metallic", 0f);
			}
			if (val5.HasProperty("_Glossiness"))
			{
				val5.SetFloat("_Glossiness", 0.1f);
			}
			if (val5.HasProperty("_EmissionColor"))
			{
				val5.SetColor("_EmissionColor", Color.black);
			}
			val5.DisableKeyword("_EMISSION");
			val5.DisableKeyword("_METALLICGLOSSMAP");
			return val5;
		}

		private static Texture2D LoadTexture(string path, bool linear)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			if (path == null)
			{
				return null;
			}
			try
			{
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, true, linear);
				if (!LoadImage(val, File.ReadAllBytes(path)))
				{
					Plugin.Log.LogWarning((object)("Couldn't read image '" + path + "'."));
					return null;
				}
				((Object)val).name = Path.GetFileNameWithoutExtension(path);
				((Texture)val).wrapMode = (TextureWrapMode)0;
				((Texture)val).filterMode = (FilterMode)2;
				((Texture)val).anisoLevel = 4;
				return val;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Couldn't load texture '" + path + "': " + ex.Message));
				return null;
			}
		}

		private static bool LoadImage(Texture2D tex, byte[] data)
		{
			if (_loadImage == null)
			{
				_loadImage = Type.GetType("UnityEngine.ImageConversion, UnityEngine.ImageConversionModule")?.GetMethod("LoadImage", new Type[2]
				{
					typeof(Texture2D),
					typeof(byte[])
				});
				if (_loadImage == null)
				{
					Plugin.Log.LogError((object)"UnityEngine.ImageConversion.LoadImage not found — textures can't be loaded.");
					return false;
				}
			}
			return (bool)_loadImage.Invoke(null, new object[2] { tex, data });
		}

		internal static Sprite LoadSprite(string path)
		{
			//IL_0039: 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)
			Texture2D val = LoadTexture(path, linear: false);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			((Texture)val).wrapMode = (TextureWrapMode)1;
			return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
		}
	}
	internal static class Patches
	{
		internal static void ApplyAll(Harmony h)
		{
			TryPatch(h, typeof(ZNetScene), new string[1] { "Awake" }, Hook(typeof(Patches), "ZNetScene_Awake_Prefix"));
			TryPatch(h, typeof(Player), new string[1] { "UpdatePlacementGhost" }, null, Hook(typeof(Placement), "UpdatePlacementGhost_Postfix"));
			TryPatch(h, typeof(Player), new string[2] { "TryPlacePiece", "PlacePiece" }, Hook(typeof(Placement), "TryPlacePiece_Prefix"));
			TryPatch(h, typeof(ZNet), new string[1] { "OnNewConnection" }, null, Hook(typeof(ConfigSync), "OnNewConnection_Postfix"));
			TryPatch(h, typeof(ZNet), new string[1] { "RPC_PeerInfo" }, null, Hook(typeof(ConfigSync), "RPC_PeerInfo_Postfix"));
			TryPatch(h, typeof(ZNet), new string[1] { "OnDestroy" }, null, Hook(typeof(ConfigSync), "ZNet_OnDestroy_Postfix"));
		}

		private static void ZNetScene_Awake_Prefix(ZNetScene __instance)
		{
			try
			{
				TrapPrefab.Register(__instance);
			}
			catch (Exception arg)
			{
				Plugin.Log.LogError((object)$"Failed to register Simple Fishing Trap: {arg}");
			}
		}

		private static HarmonyMethod Hook(Type host, string name)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Expected O, but got Unknown
			return new HarmonyMethod(AccessTools.Method(host, name, (Type[])null, (Type[])null));
		}

		private static void TryPatch(Harmony h, Type type, string[] names, HarmonyMethod prefix = null, HarmonyMethod postfix = null)
		{
			foreach (string text in names)
			{
				MethodInfo methodInfo = AccessTools.Method(type, text, (Type[])null, (Type[])null);
				if (!(methodInfo == null))
				{
					try
					{
						h.Patch((MethodBase)methodInfo, prefix, postfix, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						return;
					}
					catch (Exception ex)
					{
						Plugin.Log.LogError((object)("Failed to patch " + type.Name + "." + text + ": " + ex.Message));
						return;
					}
				}
			}
			Plugin.Log.LogWarning((object)("Could not find " + type.Name + "." + string.Join("/", names) + " — that feature is disabled (a Valheim update probably renamed it)."));
		}
	}
	internal static class Placement
	{
		private static readonly FieldInfo GhostField = AccessTools.Field(typeof(Player), "m_placementGhost");

		private static readonly FieldInfo StatusField = AccessTools.Field(typeof(Player), "m_placementStatus");

		private static readonly MethodInfo SetGhostValid = AccessTools.Method(typeof(Player), "SetPlacementGhostValid", (Type[])null, (Type[])null);

		private static object _invalidStatus;

		private static object _validStatus;

		private const int RingSamples = 16;

		private const float MinWaterDepth = 0.05f;

		internal static bool IsOurs(GameObject go)
		{
			return (Object)(object)go != (Object)null && ((Object)go).name.StartsWith("SimpleFishingTrap", StringComparison.Ordinal);
		}

		internal static bool NearWater(Vector3 pos)
		{
			//IL_0021: 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_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return true;
			}
			float waterLevel = instance.m_waterLevel;
			if (pos.y - waterLevel > ConfigSync.MaxHeightAboveWater)
			{
				return false;
			}
			if (IsWater(instance, pos, waterLevel))
			{
				return true;
			}
			float maxWaterDistance = ConfigSync.MaxWaterDistance;
			if (maxWaterDistance <= 0f)
			{
				return false;
			}
			float[] array = new float[2]
			{
				maxWaterDistance * 0.5f,
				maxWaterDistance
			};
			Vector3 p = default(Vector3);
			foreach (float num in array)
			{
				for (int j = 0; j < 16; j++)
				{
					float num2 = (float)j * (float)Math.PI * 2f / 16f;
					((Vector3)(ref p))..ctor(pos.x + Mathf.Cos(num2) * num, pos.y, pos.z + Mathf.Sin(num2) * num);
					if (IsWater(instance, p, waterLevel))
					{
						return true;
					}
				}
			}
			return false;
		}

		private static bool IsWater(ZoneSystem zs, Vector3 p, float water)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return zs.GetGroundHeight(p) < water - 0.05f;
		}

		private static void EnsureStatusValues()
		{
			if (_invalidStatus == null && !(StatusField == null))
			{
				Type fieldType = StatusField.FieldType;
				_validStatus = Enum.Parse(fieldType, "Valid");
				_invalidStatus = Enum.Parse(fieldType, "Invalid");
			}
		}

		internal static void UpdatePlacementGhost_Postfix(Player __instance)
		{
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (GhostField == null || StatusField == null)
				{
					return;
				}
				object? value = GhostField.GetValue(__instance);
				GameObject val = (GameObject)((value is GameObject) ? value : null);
				if (IsOurs(val))
				{
					EnsureStatusValues();
					if (object.Equals(StatusField.GetValue(__instance), _validStatus) && !NearWater(val.transform.position))
					{
						StatusField.SetValue(__instance, _invalidStatus);
						SetGhostValid?.Invoke(__instance, new object[1] { false });
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogDebug((object)("Placement check error: " + ex.Message));
			}
		}

		internal static bool TryPlacePiece_Prefix(Player __instance, Piece __0, ref bool __result)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__0 == (Object)null || !IsOurs(((Component)__0).gameObject))
			{
				return true;
			}
			object? obj = GhostField?.GetValue(__instance);
			GameObject val = (GameObject)((obj is GameObject) ? obj : null);
			if ((Object)(object)val == (Object)null || NearWater(val.transform.position))
			{
				return true;
			}
			((Character)__instance).Message((MessageType)2, $"Must be placed within {ConfigSync.MaxWaterDistance:0.##}m of water", 0, (Sprite)null, false);
			__result = false;
			return false;
		}
	}
	internal static class Loc
	{
		private static PropertyInfo _instance;

		private static MethodInfo _localize;

		private static bool _looked;

		internal static string Localize(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return text;
			}
			try
			{
				if (!_looked)
				{
					_looked = true;
					Type type = AccessTools.TypeByName("Localization");
					_instance = ((type == null) ? null : AccessTools.Property(type, "instance"));
					_localize = ((type == null) ? null : AccessTools.Method(type, "Localize", new Type[1] { typeof(string) }, (Type[])null));
				}
				object obj = _instance?.GetValue(null, null);
				if (obj != null && _localize != null)
				{
					return (_localize.Invoke(obj, new object[1] { text }) as string) ?? text;
				}
			}
			catch
			{
			}
			return text;
		}
	}
	[BepInPlugin("BhimaJenkins.SimpleFishingTrap", "SimpleFishingTrap", "0.1.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string ModGuid = "BhimaJenkins.SimpleFishingTrap";

		public const string ModName = "SimpleFishingTrap";

		public const string ModVersion = "0.1.0";

		internal static Plugin Instance;

		internal static ManualLogSource Log;

		internal static string PluginDir;

		internal static ConfigEntry<float> SecondsPerFish;

		internal static ConfigEntry<int> MaxFish;

		internal static ConfigEntry<bool> CatchUpWhileAway;

		internal static ConfigEntry<string> ItemPrefab;

		internal static ConfigEntry<float> MaxWaterDistance;

		internal static ConfigEntry<float> MaxHeightAboveWater;

		internal static ConfigEntry<string> Recipe;

		internal static ConfigEntry<string> CraftingStationName;

		internal static ConfigEntry<bool> EnforceServerConfig;

		private Harmony _harmony;

		private FileSystemWatcher _watcher;

		private volatile bool _reloadRequested;

		private void Awake()
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Expected O, but got Unknown
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Expected O, but got Unknown
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Expected O, but got Unknown
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Expected O, but got Unknown
			//IL_0313: Unknown result type (might be due to invalid IL or missing references)
			//IL_031d: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			PluginDir = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
			SecondsPerFish = ((BaseUnityPlugin)this).Config.Bind<float>("1 - Catching", "Seconds Per Fish", 180f, new ConfigDescription("How long each Raw Fish takes to catch (180 = 3 minutes).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 3600f), Array.Empty<object>()));
			MaxFish = ((BaseUnityPlugin)this).Config.Bind<int>("1 - Catching", "Trap Capacity", 10, new ConfigDescription("How many Raw Fish a trap holds. A full trap stops catching until it is emptied.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			CatchUpWhileAway = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - Catching", "Catch While Away", true, "On: traps keep catching while nobody is nearby (like bee hives), and catch up when you return. Off: traps only catch while their area is loaded.");
			ItemPrefab = ((BaseUnityPlugin)this).Config.Bind<string>("1 - Catching", "Item Prefab", "FishRaw", "Prefab name of the item the trap produces. Leave as FishRaw for vanilla Raw Fish.");
			MaxWaterDistance = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Placement", "Max Distance From Water", 0.25f, new ConfigDescription("How close (sideways, in metres) the trap must be to water to be placed.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			MaxHeightAboveWater = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Placement", "Max Height Above Water", 0.25f, new ConfigDescription("How far above the water surface the trap's base may be. Raise this (to ~1.5) to allow placing traps on docks.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			Recipe = ((BaseUnityPlugin)this).Config.Bind<string>("3 - Building", "Recipe", "Wood:6, LeatherScraps:10", "Build cost as ItemPrefab:Amount pairs, comma-separated.");
			CraftingStationName = ((BaseUnityPlugin)this).Config.Bind<string>("3 - Building", "Crafting Station", "piece_workbench", "Prefab name of the station that must be nearby to build the trap. Leave empty for none.");
			EnforceServerConfig = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Server", "Enforce Server Settings", true, "SERVER ONLY. Sends the server's settings to every player who joins, so catch rate, capacity, placement rules and build cost are the same for everyone.");
			Recipe.SettingChanged += delegate
			{
				OnSettingsChanged();
			};
			CraftingStationName.SettingChanged += delegate
			{
				OnSettingsChanged();
			};
			SecondsPerFish.SettingChanged += delegate
			{
				OnSettingsChanged();
			};
			MaxFish.SettingChanged += delegate
			{
				OnSettingsChanged();
			};
			CatchUpWhileAway.SettingChanged += delegate
			{
				OnSettingsChanged();
			};
			ItemPrefab.SettingChanged += delegate
			{
				OnSettingsChanged();
			};
			MaxWaterDistance.SettingChanged += delegate
			{
				OnSettingsChanged();
			};
			MaxHeightAboveWater.SettingChanged += delegate
			{
				OnSettingsChanged();
			};
			_harmony = new Harmony("BhimaJenkins.SimpleFishingTrap");
			Patches.ApplyAll(_harmony);
			SetupConfigWatcher();
			Log.LogInfo((object)"SimpleFishingTrap 0.1.0 loaded.");
		}

		internal static void OnSettingsChanged()
		{
			TrapPrefab.ApplyRecipe();
			ConfigSync.BroadcastIfServer();
		}

		private void Update()
		{
			if (_reloadRequested)
			{
				_reloadRequested = false;
				try
				{
					((BaseUnityPlugin)this).Config.Reload();
					Log.LogInfo((object)"Config reloaded from disk.");
				}
				catch (Exception ex)
				{
					Log.LogWarning((object)("Could not reload config: " + ex.Message));
				}
				OnSettingsChanged();
			}
		}

		private void SetupConfigWatcher()
		{
			try
			{
				_watcher = new FileSystemWatcher(Paths.ConfigPath, Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath));
				_watcher.Changed += delegate
				{
					_reloadRequested = true;
				};
				_watcher.Created += delegate
				{
					_reloadRequested = true;
				};
				_watcher.Renamed += delegate
				{
					_reloadRequested = true;
				};
				_watcher.EnableRaisingEvents = true;
			}
			catch (Exception ex)
			{
				Log.LogWarning((object)("Config watcher unavailable (" + ex.Message + "); edits apply on restart."));
			}
		}

		private void OnDestroy()
		{
			_watcher?.Dispose();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class TrapPrefab
	{
		internal const string PrefabName = "SimpleFishingTrap";

		private const string BasePrefabName = "piece_chest_wood";

		private const string HammerName = "Hammer";

		internal const string BundleFileName = "simplefishingtrap";

		internal const string ModelAssetName = "SimpleFishingTrap_Model";

		internal const string IconAssetName = "SimpleFishingTrap_Icon";

		internal static GameObject Prefab;

		private static GameObject _holder;

		private static AssetBundle _bundle;

		private static bool _bundleTried;

		private static List<GameObject> _sceneList;

		internal static void Register(ZNetScene scene)
		{
			if (scene?.m_prefabs == null)
			{
				return;
			}
			_sceneList = scene.m_prefabs;
			if ((Object)(object)Prefab == (Object)null)
			{
				Prefab = Build();
			}
			if (!((Object)(object)Prefab == (Object)null))
			{
				if (!scene.m_prefabs.Contains(Prefab))
				{
					scene.m_prefabs.Add(Prefab);
				}
				GameObject obj = Find("Hammer");
				PieceTable val = ((obj != null) ? obj.GetComponent<ItemDrop>() : null)?.m_itemData?.m_shared?.m_buildPieces;
				if ((Object)(object)val == (Object)null)
				{
					Plugin.Log.LogError((object)"Couldn't find the Hammer's build table — the trap won't appear in the build menu.");
				}
				else if (!val.m_pieces.Contains(Prefab))
				{
					val.m_pieces.Add(Prefab);
				}
				ApplyRecipe();
			}
		}

		internal static GameObject Find(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return null;
			}
			GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(name) : null);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			if (_sceneList != null)
			{
				foreach (GameObject scene in _sceneList)
				{
					if ((Object)(object)scene != (Object)null && ((Object)scene).name == name)
					{
						return scene;
					}
				}
			}
			return ((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(name) : null;
		}

		private static GameObject Build()
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Find("piece_chest_wood");
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogError((object)"Base prefab 'piece_chest_wood' not found — Simple Fishing Trap disabled.");
				return null;
			}
			if ((Object)(object)_holder == (Object)null)
			{
				_holder = new GameObject("SimpleFishingTrap_Prefabs");
				_holder.SetActive(false);
				Object.DontDestroyOnLoad((Object)(object)_holder);
			}
			GameObject val2 = Object.Instantiate<GameObject>(val, _holder.transform, false);
			((Object)val2).name = "SimpleFishingTrap";
			Container[] componentsInChildren = val2.GetComponentsInChildren<Container>(true);
			foreach (Container val3 in componentsInChildren)
			{
				Object.DestroyImmediate((Object)(object)val3);
			}
			Piece component = val2.GetComponent<Piece>();
			component.m_name = "Simple Fishing Trap";
			component.m_description = "Slowly catches Raw Fish. Must be placed right next to water. Holds a limited number of fish — empty it to keep it catching.";
			component.m_category = (PieceCategory)1;
			component.m_groundOnly = false;
			component.m_noInWater = false;
			WearNTear component2 = val2.GetComponent<WearNTear>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.m_noRoofWear = false;
			}
			if (!TrySwapInModel(val2, component2, component))
			{
				Plugin.Log.LogInfo((object)"No custom model found ('SimpleFishingTrap.obj' next to the DLL) — using placeholder chest model.");
			}
			val2.AddComponent<FishTrap>();
			return val2;
		}

		private static bool TrySwapInModel(GameObject go, WearNTear wnt, Piece piece)
		{
			//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
			Renderer componentInChildren = go.GetComponentInChildren<Renderer>(true);
			Material baseMaterial = (((Object)(object)componentInChildren != (Object)null) ? componentInChildren.sharedMaterial : null);
			GameObject val = null;
			Sprite val2 = null;
			string text = null;
			AssetBundle val3 = LoadBundle();
			if ((Object)(object)val3 != (Object)null)
			{
				val = val3.LoadAsset<GameObject>("SimpleFishingTrap_Model") ?? val3.LoadAllAssets<GameObject>().FirstOrDefault();
				val2 = val3.LoadAsset<Sprite>("SimpleFishingTrap_Icon") ?? val3.LoadAllAssets<Sprite>().FirstOrDefault();
				if ((Object)(object)val != (Object)null)
				{
					text = "AssetBundle";
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				string text2 = FindFile("SimpleFishingTrap.obj");
				if (text2 != null)
				{
					val = ObjModel.Load(text2, baseMaterial, _holder.transform);
					if ((Object)(object)val != (Object)null)
					{
						text = text2;
					}
				}
			}
			if ((Object)(object)val2 == (Object)null)
			{
				val2 = ObjModel.LoadSprite(FindFile("SimpleFishingTrap_Icon.png"));
			}
			if ((Object)(object)val2 != (Object)null)
			{
				piece.m_icon = val2;
			}
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			Plugin.Log.LogInfo((object)("Using custom trap model from " + text + "."));
			LODGroup[] componentsInChildren = go.GetComponentsInChildren<LODGroup>(true);
			foreach (LODGroup val4 in componentsInChildren)
			{
				Object.DestroyImmediate((Object)(object)val4);
			}
			Renderer[] componentsInChildren2 = go.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val5 in componentsInChildren2)
			{
				Object.DestroyImmediate((Object)(object)val5);
			}
			MeshFilter[] componentsInChildren3 = go.GetComponentsInChildren<MeshFilter>(true);
			foreach (MeshFilter val6 in componentsInChildren3)
			{
				Object.DestroyImmediate((Object)(object)val6);
			}
			Collider[] componentsInChildren4 = go.GetComponentsInChildren<Collider>(true);
			foreach (Collider val7 in componentsInChildren4)
			{
				Object.DestroyImmediate((Object)(object)val7);
			}
			List<Transform> list = new List<Transform>();
			if ((Object)(object)wnt != (Object)null)
			{
				GameObject[] array = (GameObject[])(object)new GameObject[3] { wnt.m_new, wnt.m_worn, wnt.m_broken };
				foreach (GameObject val8 in array)
				{
					if ((Object)(object)val8 != (Object)null && !list.Contains(val8.transform))
					{
						list.Add(val8.transform);
					}
				}
			}
			if (list.Count == 0)
			{
				list.Add(go.transform);
			}
			int num = LayerMask.NameToLayer("piece");
			Shader val9 = Shader.Find("Custom/Piece");
			foreach (Transform item in list)
			{
				GameObject val10 = Object.Instantiate<GameObject>(val, item, false);
				((Object)val10).name = "SimpleFishingTrap_Model";
				val10.transform.localPosition = Vector3.zero;
				val10.transform.localRotation = Quaternion.identity;
				Transform[] componentsInChildren5 = val10.GetComponentsInChildren<Transform>(true);
				foreach (Transform val11 in componentsInChildren5)
				{
					if (num >= 0)
					{
						((Component)val11).gameObject.layer = num;
					}
				}
				if ((Object)(object)val9 != (Object)null)
				{
					Renderer[] componentsInChildren6 = val10.GetComponentsInChildren<Renderer>(true);
					foreach (Renderer val12 in componentsInChildren6)
					{
						Material[] sharedMaterials = val12.sharedMaterials;
						foreach (Material val13 in sharedMaterials)
						{
							if ((Object)(object)val13 != (Object)null && (Object)(object)val13.shader != (Object)null && ((Object)val13.shader).name == "Standard")
							{
								val13.shader = val9;
							}
						}
					}
				}
				if ((Object)(object)val10.GetComponentInChildren<Collider>(true) == (Object)null)
				{
					AddFallbackCollider(val10);
				}
			}
			return true;
		}

		private static void AddFallbackCollider(GameObject inst)
		{
			//IL_0033: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			MeshFilter componentInChildren = inst.GetComponentInChildren<MeshFilter>(true);
			BoxCollider val = inst.AddComponent<BoxCollider>();
			if ((Object)(object)componentInChildren != (Object)null && (Object)(object)componentInChildren.sharedMesh != (Object)null)
			{
				Bounds bounds = componentInChildren.sharedMesh.bounds;
				val.center = ((Bounds)(ref bounds)).center;
				val.size = ((Bounds)(ref bounds)).size;
			}
			Plugin.Log.LogWarning((object)"Model has no collider; added a box collider around the first mesh.");
		}

		internal static string FindFile(string fileName)
		{
			List<string> list = new List<string>
			{
				Path.Combine(Plugin.PluginDir, fileName),
				Path.Combine(Path.Combine(Plugin.PluginDir, "Assets"), fileName)
			};
			foreach (string item in list)
			{
				if (File.Exists(item))
				{
					return item;
				}
			}
			try
			{
				string[] files = Directory.GetFiles(Paths.PluginPath, fileName, SearchOption.AllDirectories);
				if (files.Length != 0)
				{
					return files[0];
				}
			}
			catch
			{
			}
			return null;
		}

		private static AssetBundle LoadBundle()
		{
			if (_bundleTried)
			{
				return _bundle;
			}
			_bundleTried = true;
			string text = FindFile("simplefishingtrap");
			if (text == null)
			{
				return null;
			}
			try
			{
				_bundle = AssetBundle.LoadFromFile(text);
				if ((Object)(object)_bundle != (Object)null)
				{
					Plugin.Log.LogInfo((object)("Loaded model bundle: " + text));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Couldn't load bundle '" + text + "': " + ex.Message));
			}
			return _bundle;
		}

		internal static void ApplyRecipe()
		{
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cc: Expected O, but got Unknown
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fd: Expected O, but got Unknown
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0123: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Expected O, but got Unknown
			if ((Object)(object)Prefab == (Object)null)
			{
				return;
			}
			Piece component = Prefab.GetComponent<Piece>();
			if ((Object)(object)component == (Object)null)
			{
				return;
			}
			List<Requirement> list = new List<Requirement>();
			string[] array = (ConfigSync.Recipe ?? "").Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text in array)
			{
				string[] array2 = text.Split(new char[1] { ':' });
				string text2 = array2[0].Trim();
				int result = 1;
				if (array2.Length > 1 && !int.TryParse(array2[1].Trim(), out result))
				{
					result = 1;
				}
				if (text2.Length != 0 && result > 0)
				{
					GameObject obj = Find(text2);
					ItemDrop val = ((obj != null) ? obj.GetComponent<ItemDrop>() : null);
					if ((Object)(object)val == (Object)null)
					{
						Plugin.Log.LogWarning((object)("Recipe item '" + text2 + "' not found (prefab names are case-sensitive)."));
						continue;
					}
					list.Add(new Requirement
					{
						m_resItem = val,
						m_amount = result,
						m_recover = true
					});
				}
			}
			if (list.Count == 0)
			{
				Plugin.Log.LogWarning((object)"Recipe is empty or invalid — falling back to Wood:6, LeatherScraps:10.");
				GameObject obj2 = Find("Wood");
				ItemDrop val2 = ((obj2 != null) ? obj2.GetComponent<ItemDrop>() : null);
				GameObject obj3 = Find("LeatherScraps");
				ItemDrop val3 = ((obj3 != null) ? obj3.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val2 != (Object)null)
				{
					list.Add(new Requirement
					{
						m_resItem = val2,
						m_amount = 6,
						m_recover = true
					});
				}
				if ((Object)(object)val3 != (Object)null)
				{
					list.Add(new Requirement
					{
						m_resItem = val3,
						m_amount = 10,
						m_recover = true
					});
				}
			}
			component.m_resources = list.ToArray();
			string craftingStation = ConfigSync.CraftingStation;
			object craftingStation2;
			if (!string.IsNullOrWhiteSpace(craftingStation))
			{
				GameObject obj4 = Find(craftingStation.Trim());
				craftingStation2 = ((obj4 != null) ? obj4.GetComponent<CraftingStation>() : null);
			}
			else
			{
				craftingStation2 = null;
			}
			component.m_craftingStation = (CraftingStation)craftingStation2;
			if (!string.IsNullOrWhiteSpace(craftingStation) && (Object)(object)component.m_craftingStation == (Object)null)
			{
				Plugin.Log.LogWarning((object)("Crafting station '" + craftingStation + "' not found — trap can be built anywhere."));
			}
		}
	}
}