Decompiled source of SeagullDigestion v1.7.1

BepInEx/plugins/SeagullDigestion/SeagullDigestion.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FishNet;
using FishNet.Connection;
using FishNet.Object;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace SeagullDigestion
{
	internal static class Chassis
	{
		private static Item _cached;

		private static bool _looked;

		internal static void Reset()
		{
			_looked = false;
			_cached = null;
		}

		internal static Item Find()
		{
			if (_looked)
			{
				return _cached;
			}
			_looked = true;
			try
			{
				string text = (Plugin.CfgChassisItem.Value ?? "").Trim();
				if (text.Length > 0)
				{
					_cached = GameInfo.GetSpawnable(text.Replace(" ", "").ToLowerInvariant());
					if ((Object)(object)_cached != (Object)null)
					{
						Plugin.Log.LogInfo((object)$"Poop chassis set by config: {((Object)((Component)_cached).gameObject).name} (id {_cached.ID})");
						return _cached;
					}
					Plugin.Log.LogWarning((object)("ChassisItem '" + text + "' matched no spawnable item; falling back to automatic choice."));
				}
				_cached = AutoPick();
				if ((Object)(object)_cached != (Object)null)
				{
					Plugin.Log.LogInfo((object)$"Poop chassis chosen automatically: {((Object)((Component)_cached).gameObject).name} (id {_cached.ID})");
				}
				else
				{
					Plugin.Log.LogWarning((object)"No ordinary fish found to build poop from; digested weapons will be transformed in place instead.");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not choose a poop chassis: " + ex.Message));
				_cached = null;
			}
			return _cached;
		}

		private static Item AutoPick()
		{
			if (!(AccessTools.Field(typeof(GameInfo), "_allItems")?.GetValue(null) is Dictionary<byte, Item> { Count: not 0 } dictionary))
			{
				return null;
			}
			Item result = null;
			int num = int.MaxValue;
			foreach (KeyValuePair<byte, Item> item in dictionary)
			{
				Item value = item.Value;
				if (!((Object)(object)value == (Object)null) && IsOrdinaryFish(value) && value.ID < num)
				{
					num = value.ID;
					result = value;
				}
			}
			return result;
		}

		private static bool IsOrdinaryFish(Item prefab)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Invalid comparison between Unknown and I4
			try
			{
				if ((Object)(object)((Component)prefab).GetComponent<Fish>() == (Object)null)
				{
					return false;
				}
				if ((Object)(object)((Component)prefab).GetComponent<Tool>() != (Object)null)
				{
					return false;
				}
				if ((Object)(object)((Component)prefab).GetComponent<Melee>() != (Object)null)
				{
					return false;
				}
				if (prefab.IsQuestItem)
				{
					return false;
				}
				Creature component = ((Component)prefab).GetComponent<Creature>();
				return (Object)(object)component != (Object)null && (int)component.BossType == 0;
			}
			catch
			{
				return false;
			}
		}

		internal static bool NeedsSwap(Item item)
		{
			if ((Object)(object)item == (Object)null || !Plugin.CfgSwapToRealItem.Value)
			{
				return false;
			}
			try
			{
				if ((Object)(object)((Component)item).GetComponent<Tool>() != (Object)null)
				{
					return true;
				}
				if ((Object)(object)((Component)item).GetComponent<Melee>() != (Object)null)
				{
					return true;
				}
				return (Object)(object)((Component)item).GetComponent<Creature>() == (Object)null;
			}
			catch
			{
				return false;
			}
		}

		internal static bool IsTooPreciousToSwap(Item item)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Invalid comparison between Unknown and I4
			if ((Object)(object)item == (Object)null)
			{
				return true;
			}
			try
			{
				if (item.IsQuestItem)
				{
					return true;
				}
				Creature component = ((Component)item).GetComponent<Creature>();
				return (Object)(object)component != (Object)null && (int)component.BossType > 0;
			}
			catch
			{
				return true;
			}
		}
	}
	internal class DigestionRunner : MonoBehaviour
	{
		private struct Meal
		{
			public Item Item;

			public float DueAt;
		}

		private const float MaxDropTravel = 250f;

		private static readonly Dictionary<Bird, Meal> Meals = new Dictionary<Bird, Meal>();

		private static readonly Dictionary<Bird, float> FullUntil = new Dictionary<Bird, float>();

		private static readonly List<Bird> Finished = new List<Bird>();

		private static readonly List<KeyValuePair<Bird, Meal>> Snapshot = new List<KeyValuePair<Bird, Meal>>();

		internal static bool IsFull(Bird bird)
		{
			if ((Object)(object)bird == (Object)null)
			{
				return false;
			}
			if (FullUntil.TryGetValue(bird, out var value))
			{
				return Time.time < value;
			}
			return false;
		}

		internal static void Register(Bird bird, Item item)
		{
			if (!((Object)(object)bird == (Object)null) && !((Object)(object)item == (Object)null))
			{
				float num = Mathf.Min(Plugin.CfgMinSeconds.Value, Plugin.CfgMaxSeconds.Value);
				float num2 = Mathf.Max(Plugin.CfgMinSeconds.Value, Plugin.CfgMaxSeconds.Value);
				float num3 = Random.Range(num, num2);
				Meals[bird] = new Meal
				{
					Item = item,
					DueAt = Time.time + num3
				};
				Plugin.Log.LogInfo((object)$"Seagull grabbed '{SafeName(item)}' - digesting in {num3:0.0}s");
			}
		}

		internal static void Forget(Bird bird)
		{
			if ((Object)(object)bird != (Object)null)
			{
				Meals.Remove(bird);
			}
		}

		private void Update()
		{
			if (Meals.Count == 0)
			{
				return;
			}
			Finished.Clear();
			float time = Time.time;
			Snapshot.Clear();
			Snapshot.AddRange(Meals);
			foreach (KeyValuePair<Bird, Meal> item in Snapshot)
			{
				Bird key = item.Key;
				Meal value = item.Value;
				if ((Object)(object)key == (Object)null || (Object)(object)value.Item == (Object)null || (Object)(object)key.CaughtItem != (Object)(object)value.Item || ((Creature)key).IsDead || !((NetworkBehaviour)key).IsServerInitialized)
				{
					Finished.Add(key);
				}
				else if (!(time < value.DueAt))
				{
					Finished.Add(key);
					try
					{
						Digest(key, value.Item);
					}
					catch (Exception ex)
					{
						Plugin.Log.LogError((object)("Digestion failed: " + ex));
					}
				}
			}
			foreach (Bird item2 in Finished)
			{
				Meals.Remove(item2);
			}
		}

		private static void Digest(Bird bird, Item item)
		{
			//IL_007d: 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_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			string arg = SafeName(item);
			int num = Mathf.Min(Plugin.CfgMinWorth.Value, Plugin.CfgMaxWorth.Value);
			int num2 = Mathf.Max(Plugin.CfgMinWorth.Value, Plugin.CfgMaxWorth.Value);
			int num3 = Random.Range(num, num2 + 1);
			item.ReleasedByBird();
			Item val = (Chassis.NeedsSwap(item) ? TrySwap(item, num3) : null);
			if ((Object)(object)val == (Object)null)
			{
				val = TransformInPlace(item, num3);
			}
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			Vector3 val2 = ((Component)val).transform.position;
			if (Plugin.CfgDropNearPlayer.Value)
			{
				Vector3? val3 = TryDropNearPlayer(bird, val);
				if (val3.HasValue)
				{
					val2 = val3.Value;
				}
			}
			float value = Plugin.CfgCooldown.Value;
			if (value > 0f)
			{
				FullUntil[bird] = Time.time + value;
			}
			if (Plugin.CfgSound.Value)
			{
				try
				{
					AudioManager.PlayRandomClipAt("Seagull_V", 1, 9, val2, false, (AudioDistance)2, 0.5f, 0.1f);
				}
				catch
				{
				}
			}
			Plugin.Log.LogInfo((object)$"Seagull digested '{arg}' -> {Plugin.CfgPoopName.Value} worth {num3}");
		}

		private static Item TransformInPlace(Item item, int worth)
		{
			try
			{
				item._bettingMultiplier.Value = PoopMarker.Encode(worth);
				(((Component)item).GetComponent<PoopVisual>() ?? ((Component)item).gameObject.AddComponent<PoopVisual>()).Apply();
				return item;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Could not transform the digested item: " + ex));
				return null;
			}
		}

		private static Item TrySwap(Item original, int worth)
		{
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			if (Chassis.IsTooPreciousToSwap(original))
			{
				Plugin.Log.LogInfo((object)("Not swapping '" + SafeName(original) + "' - quest or boss item, transforming it in place instead."));
				return null;
			}
			Item val = Chassis.Find();
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			try
			{
				Vector3 position = ((Component)original).transform.position;
				Quaternion rotation = ((Component)original).transform.rotation;
				Item val2 = Object.Instantiate<Item>(val, position, rotation, Server.Instance.DynamicObjectsHolder);
				if ((Object)(object)val2 == (Object)null)
				{
					return null;
				}
				Creature creature = val2.Creature;
				if (creature != null)
				{
					creature.ServerKillOnSpawn();
				}
				float value = PoopMarker.Encode(worth);
				try
				{
					val2._bettingMultiplier.Value = value;
				}
				catch
				{
				}
				InstanceFinder.ServerManager.Spawn(((Component)val2).gameObject, (NetworkConnection)null, default(Scene));
				val2._bettingMultiplier.Value = value;
				(((Component)val2).GetComponent<PoopVisual>() ?? ((Component)val2).gameObject.AddComponent<PoopVisual>()).Apply();
				original.DestroyItem((byte)0, byte.MaxValue);
				return val2;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not swap the digested item for a fresh one, transforming it in place instead: " + ex.Message));
				return null;
			}
		}

		private static Vector3? TryDropNearPlayer(Bird bird, Item item)
		{
			//IL_005e: 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)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: 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_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Player val = PickTarget(bird, item);
				if ((Object)(object)val == (Object)null)
				{
					return null;
				}
				RigidbodySync rigidbodySync = item.RigidbodySync;
				if ((Object)(object)rigidbodySync == (Object)null)
				{
					return null;
				}
				if (!TryGetBody(val, out var position))
				{
					return null;
				}
				Vector2 val2 = Random.insideUnitCircle * Mathf.Max(0f, Plugin.CfgDropRadius.Value);
				Vector3 val3 = position + new Vector3(val2.x, Mathf.Max(0f, Plugin.CfgDropHeight.Value), val2.y);
				float num = Vector3.Distance(((Component)bird).transform.position, val3);
				if (num > 250f)
				{
					Plugin.Log.LogWarning((object)($"Refusing to drop poop {num:0} m from the seagull (target resolved to {val3}). " + "Leaving it where the bird ate it."));
					return null;
				}
				rigidbodySync.StartSimulateLocal(default(Vector3), default(Quaternion));
				rigidbodySync.TeleportToPosRot(val3, ((Component)item).transform.rotation, (float[])null, (Quaternion[])null);
				return val3;
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not drop poop near a player: " + ex.Message));
				return null;
			}
		}

		private static Player PickTarget(Bird bird, Item item)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: 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_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.CfgTargetVictim.Value)
			{
				Player lastHolder = item.LastHolder;
				if ((Object)(object)lastHolder != (Object)null && PlayerManager.AlivePlayers.Contains(lastHolder))
				{
					return lastHolder;
				}
			}
			Player result = null;
			float num = float.MaxValue;
			Vector3 position = ((Component)bird).transform.position;
			foreach (Player alivePlayer in PlayerManager.AlivePlayers)
			{
				if (!((Object)(object)alivePlayer == (Object)null) && TryGetBody(alivePlayer, out var position2))
				{
					Vector3 val = position2 - position;
					float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
					if (sqrMagnitude < num)
					{
						num = sqrMagnitude;
						result = alivePlayer;
					}
				}
			}
			return result;
		}

		private static bool TryGetBody(Player player, out Vector3 position)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			position = Vector3.zero;
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			Transform transform = player.Transform;
			if ((Object)(object)transform == (Object)null)
			{
				return false;
			}
			position = transform.position;
			return true;
		}

		private static string SafeName(Item item)
		{
			try
			{
				return item.GetName();
			}
			catch
			{
				return ((Object)(object)item != (Object)null) ? ((Object)item).name : "null";
			}
		}
	}
	internal static class Disarm
	{
		private static FieldInfo _weaponField;

		private static FieldInfo _meleeField;

		private static bool _looked;

		internal static void Strip(Item item)
		{
			if ((Object)(object)item == (Object)null || !Plugin.CfgDisarmWeapons.Value)
			{
				return;
			}
			try
			{
				if (!_looked)
				{
					_looked = true;
					_weaponField = AccessTools.Field(typeof(Item), "_weapon");
					_meleeField = AccessTools.Field(typeof(Item), "_melee");
					if (_weaponField == null || _meleeField == null)
					{
						Plugin.Log.LogWarning((object)"Item._weapon/_melee not found; digested weapons will stay armed.");
					}
				}
				_weaponField?.SetValue(item, null);
				_meleeField?.SetValue(item, null);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not disarm a digested item: " + ex.Message));
			}
		}

		internal static bool IsHoldingPoop(PlayerHolding holding)
		{
			if (!Plugin.CfgDisarmWeapons.Value || (Object)(object)holding == (Object)null)
			{
				return false;
			}
			return PoopMarker.IsPoop(holding.HeldItem);
		}
	}
	internal static class Eating
	{
		internal static bool SwallowedPoop;

		private static int _fullnessBefore;

		[HarmonyPrefix]
		[HarmonyPriority(800)]
		[HarmonyPatch(typeof(Server), "RpcLogic___FinishEatingCreature___1039939981")]
		private static void BeforeSwallow(Creature __0, Player __1)
		{
			SwallowedPoop = Plugin.CfgEnabled.Value && PoopMarker.IsPoop((Item)(object)__0) && (Object)(object)__1 != (Object)null;
			_fullnessBefore = (SwallowedPoop ? __1.Vitals.Fullness : 0);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Server), "RpcLogic___FinishEatingCreature___1039939981")]
		private static void AfterSwallow(Player __1)
		{
			//IL_007c: 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_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			if (!SwallowedPoop || (Object)(object)__1 == (Object)null)
			{
				return;
			}
			try
			{
				PlayerVitals vitals = __1.Vitals;
				if (!((Object)(object)vitals == (Object)null))
				{
					int num = Mathf.Max(0, Plugin.CfgHungerCost.Value);
					int num2 = Mathf.Min(_fullnessBefore, num);
					int num3 = num - num2;
					int num4 = _fullnessBefore - num2;
					int num5 = num4 - vitals.Fullness;
					if (num5 != 0)
					{
						vitals.RestoreFullness(num5);
					}
					if (num3 > 0 && Plugin.CfgDamageOnEmptyStomach.Value)
					{
						vitals.TakeDamage(num3, default(Vector3), default(Vector3), false);
					}
					if (Plugin.CfgPoison.Value)
					{
						vitals.ApplyNewPoison();
					}
					Plugin.Log.LogInfo((object)($"{SafeName(__1)} swallowed poop: fullness {_fullnessBefore} -> {num4}" + ((num3 > 0) ? $", {num3} damage" : "") + (Plugin.CfgPoison.Value ? ", poisoned" : "")));
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Poop swallow effects failed: " + ex));
			}
		}

		private static string SafeName(Player p)
		{
			try
			{
				return p.SteamName;
			}
			catch
			{
				return "player";
			}
		}
	}
	internal static class ForeignEffects
	{
		private const string EffectPatch = "FishEffects.Plugin+FinishEatingCreaturePatch";

		private const string InspectPatch = "FishEffects.Plugin+UpdateInspectedItemTextPatch";

		internal static void Apply(Harmony harmony)
		{
			if (Plugin.CfgBlockOtherFoodEffects.Value)
			{
				Hook(harmony, "FishEffects.Plugin+FinishEatingCreaturePatch", "Postfix", "SkipIfPoopWasSwallowed");
				Hook(harmony, "FishEffects.Plugin+UpdateInspectedItemTextPatch", "Prefix", "SkipIfInspectingPoop");
			}
		}

		private static void Hook(Harmony harmony, string typeName, string methodName, string ownPrefix)
		{
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			try
			{
				Type type = AccessTools.TypeByName(typeName);
				if (type == null)
				{
					Plugin.Log.LogInfo((object)(typeName + " not present - nothing to suppress."));
					return;
				}
				MethodInfo methodInfo = AccessTools.Method(type, methodName, (Type[])null, (Type[])null);
				if (methodInfo == null)
				{
					Plugin.Log.LogWarning((object)(typeName + "." + methodName + " not found; its layout changed. Poop may grant a food effect."));
					return;
				}
				MethodInfo method = typeof(ForeignEffects).GetMethod(ownPrefix, BindingFlags.Static | BindingFlags.NonPublic);
				harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				Plugin.Log.LogInfo((object)("Suppressing " + typeName + "." + methodName + " for poop."));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not hook " + typeName + "." + methodName + ": " + ex.Message));
			}
		}

		private static bool SkipIfPoopWasSwallowed()
		{
			return !Eating.SwallowedPoop;
		}

		private static bool SkipIfInspectingPoop(object __0)
		{
			return !PoopMarker.IsPoop((Item)((__0 is Item) ? __0 : null));
		}
	}
	internal static class ItemFilter
	{
		internal static bool IsNeverDigest(Item item)
		{
			if ((Object)(object)item == (Object)null)
			{
				return false;
			}
			string value = Plugin.CfgNeverDigest.Value;
			if (string.IsNullOrWhiteSpace(value))
			{
				return false;
			}
			string text = Normalize(PrefabName(item));
			string text2 = Normalize(SafeGetName(item));
			string text3 = item.ID.ToString();
			string[] array = value.Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string text4 = Normalize(array[i]);
				if (text4.Length != 0)
				{
					if (text4 == text3)
					{
						return true;
					}
					if (text.Length > 0 && text4 == text)
					{
						return true;
					}
					if (text2.Length > 0 && text4 == text2)
					{
						return true;
					}
				}
			}
			return false;
		}

		private static string PrefabName(Item item)
		{
			string name = ((Object)((Component)item).gameObject).name;
			if (!name.EndsWith("(Clone)", StringComparison.Ordinal))
			{
				return name;
			}
			return name.Substring(0, name.Length - "(Clone)".Length);
		}

		private static string SafeGetName(Item item)
		{
			try
			{
				return item.GetName();
			}
			catch
			{
				return "";
			}
		}

		private static string Normalize(string s)
		{
			return (s ?? string.Empty).Replace(" ", "").Replace("_", "").ToLowerInvariant();
		}
	}
	internal static class MoneyGuard
	{
		internal struct Pending
		{
			public bool Active;

			public long Before;

			public long Delta;
		}

		private static bool Ready(out MoneyManager manager)
		{
			manager = MoneyManager.Instance;
			if ((Object)(object)manager != (Object)null && ((NetworkBehaviour)manager).IsServerInitialized)
			{
				return manager._money != null;
			}
			return false;
		}

		private static Pending Capture(long delta)
		{
			if (!Plugin.CfgClampBalance.Value || !Ready(out var manager))
			{
				return default(Pending);
			}
			return new Pending
			{
				Active = true,
				Before = manager._money.Value,
				Delta = delta
			};
		}

		private static void Settle(Pending state)
		{
			if (state.Active && Ready(out var manager))
			{
				long num = state.Before + state.Delta;
				int num2 = (int)((num >= 0) ? ((num > int.MaxValue) ? int.MaxValue : num) : 0);
				if (manager._money.Value != num2)
				{
					Plugin.Log.LogWarning((object)(string.Format("Balance ${0} {1} ${2} ", state.Before, (state.Delta < 0) ? "-" : "+", Math.Abs(state.Delta)) + $"= ${num}, outside the money range, and the game does not clamp. " + $"Held at ${num2} instead of letting it land on ${manager._money.Value}."));
					manager._money.Value = num2;
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPriority(800)]
		[HarmonyPatch(typeof(MoneyManager), "SellItem")]
		private static void SellItem_Pre(Item item, out Pending __state)
		{
			__state = (((Object)(object)item == (Object)null) ? default(Pending) : Capture(item.TotalWorth));
		}

		[HarmonyPostfix]
		[HarmonyPriority(0)]
		[HarmonyPatch(typeof(MoneyManager), "SellItem")]
		private static void SellItem_Post(Pending __state)
		{
			Settle(__state);
		}

		[HarmonyPrefix]
		[HarmonyPriority(800)]
		[HarmonyPatch(typeof(MoneyManager), "AddMoney")]
		private static void AddMoney_Pre(int amount, out Pending __state)
		{
			__state = Capture(Math.Abs((long)amount));
		}

		[HarmonyPostfix]
		[HarmonyPriority(0)]
		[HarmonyPatch(typeof(MoneyManager), "AddMoney")]
		private static void AddMoney_Post(Pending __state)
		{
			Settle(__state);
		}
	}
	internal static class Patches
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(Bird), "SetCaughtFood")]
		private static void Bird_SetCaughtFood(Bird __instance)
		{
			if (Plugin.CfgEnabled.Value && !((Object)(object)__instance == (Object)null) && ((NetworkBehaviour)__instance).IsServerInitialized)
			{
				Item caughtItem = __instance.CaughtItem;
				if (!((Object)(object)caughtItem == (Object)null) && !PoopMarker.IsPoop(caughtItem) && (!Plugin.CfgOnlyFish.Value || !((Object)(object)caughtItem.Fish == (Object)null)) && !ItemFilter.IsNeverDigest(caughtItem))
				{
					DigestionRunner.Register(__instance, caughtItem);
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Bird), "RemoveFood")]
		private static void Bird_RemoveFood(Bird __instance)
		{
			DigestionRunner.Forget(__instance);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Bird), "SetAttackingFood")]
		private static void Bird_SetAttackingFood(Bird __instance, ref Item item)
		{
			if (!((Object)(object)item == (Object)null) && (PoopMarker.IsPoop(item) || DigestionRunner.IsFull(__instance)))
			{
				item = null;
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Item), "OnStartClient")]
		private static void Item_OnStartClient(Item __instance)
		{
			if ((Object)(object)__instance != (Object)null && (Object)(object)((Component)__instance).GetComponent<PoopVisual>() == (Object)null)
			{
				((Component)__instance).gameObject.AddComponent<PoopVisual>();
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		private static void Item_TotalWorth(Item __instance, ref int __result)
		{
			if (PoopMarker.IsPoop(__instance))
			{
				__result = PoopMarker.WorthOf(__instance);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Item), "GetName")]
		private static void Item_GetName(Item __instance, ref string __result)
		{
			if (PoopMarker.IsPoop(__instance))
			{
				__result = Plugin.CfgPoopName.Value;
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		private static void Item_Mesh(Item __instance, ref Mesh __result)
		{
			UsePoopMesh(__instance, ref __result);
		}

		[HarmonyPostfix]
		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		private static void Creature_Mesh(Creature __instance, ref Mesh __result)
		{
			UsePoopMesh((Item)(object)__instance, ref __result);
		}

		[HarmonyPostfix]
		[HarmonyPatch(/*Could not decode attribute arguments.*/)]
		private static void Creature_DripMesh(Creature __instance, ref Mesh __result)
		{
			UsePoopMesh((Item)(object)__instance, ref __result);
		}

		private static void UsePoopMesh(Item item, ref Mesh result)
		{
			if (Plugin.CfgReplaceModel.Value && PoopMarker.IsPoop(item))
			{
				PoopVisual component = ((Component)item).GetComponent<PoopVisual>();
				if ((Object)(object)component != (Object)null && (Object)(object)component.IconMesh != (Object)null)
				{
					result = component.IconMesh;
				}
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(PlayerHolding), "PrimaryInput")]
		private static bool PlayerHolding_PrimaryInput(PlayerHolding __instance)
		{
			return !Disarm.IsHoldingPoop(__instance);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(PlayerHolding), "SecondaryInput")]
		private static bool PlayerHolding_SecondaryInput(PlayerHolding __instance)
		{
			return !Disarm.IsHoldingPoop(__instance);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(PlayerHolding), "ReloadInput")]
		private static bool PlayerHolding_ReloadInput(PlayerHolding __instance)
		{
			return !Disarm.IsHoldingPoop(__instance);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(PlayerHolding), "InspectInput")]
		private static bool PlayerHolding_InspectInput(PlayerHolding __instance)
		{
			return !Disarm.IsHoldingPoop(__instance);
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(Item), "AddBetMultiplier")]
		private static bool Item_AddBetMultiplier(Item __instance)
		{
			return !PoopMarker.IsPoop(__instance);
		}
	}
	[BepInPlugin("erwins.seagulldigestion", "Seagull Digestion", "1.7.1")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "erwins.seagulldigestion";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> CfgEnabled;

		internal static ConfigEntry<bool> CfgOnlyFish;

		internal static ConfigEntry<string> CfgNeverDigest;

		internal static ConfigEntry<float> CfgMinSeconds;

		internal static ConfigEntry<float> CfgMaxSeconds;

		internal static ConfigEntry<float> CfgCooldown;

		internal static ConfigEntry<int> CfgMinWorth;

		internal static ConfigEntry<int> CfgMaxWorth;

		internal static ConfigEntry<bool> CfgClampBalance;

		internal static ConfigEntry<string> CfgPoopName;

		internal static ConfigEntry<bool> CfgReplaceModel;

		internal static ConfigEntry<float> CfgPoopScale;

		internal static ConfigEntry<string> CfgPoopColor;

		internal static ConfigEntry<float> CfgPoopCookness;

		internal static ConfigEntry<bool> CfgSound;

		internal static ConfigEntry<bool> CfgDisarmWeapons;

		internal static ConfigEntry<bool> CfgSwapToRealItem;

		internal static ConfigEntry<string> CfgChassisItem;

		internal static ConfigEntry<int> CfgHungerCost;

		internal static ConfigEntry<bool> CfgDamageOnEmptyStomach;

		internal static ConfigEntry<bool> CfgPoison;

		internal static ConfigEntry<bool> CfgBlockOtherFoodEffects;

		internal static ConfigEntry<bool> CfgDropNearPlayer;

		internal static ConfigEntry<bool> CfgTargetVictim;

		internal static ConfigEntry<float> CfgDropRadius;

		internal static ConfigEntry<float> CfgDropHeight;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Expected O, but got Unknown
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Expected O, but got Unknown
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Expected O, but got Unknown
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Expected O, but got Unknown
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Expected O, but got Unknown
			//IL_022c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0236: Expected O, but got Unknown
			//IL_028d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0297: Expected O, but got Unknown
			//IL_0344: Unknown result type (might be due to invalid IL or missing references)
			//IL_034e: Expected O, but got Unknown
			//IL_0421: Unknown result type (might be due to invalid IL or missing references)
			//IL_042b: Expected O, but got Unknown
			//IL_045e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0468: Expected O, but got Unknown
			//IL_049c: Unknown result type (might be due to invalid IL or missing references)
			//IL_04a6: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			CfgEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Master switch. Seagulls digest the food they steal.");
			CfgOnlyFish = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "OnlyFish", false, "Only digest fish. When false, anything a seagull steals can be digested.");
			CfgNeverDigest = ((BaseUnityPlugin)this).Config.Bind<string>("General", "NeverDigest", "", "Comma separated list of things seagulls will never digest, even if OnlyFish is off. Matches the prefab name, which does not change with language, or the display name, or a numeric item id. Spaces and underscores are ignored, and matching is case insensitive.");
			CfgMinSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "MinSeconds", 8f, new ConfigDescription("Shortest time a seagull holds stolen food before eating it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 600f), Array.Empty<object>()));
			CfgMaxSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "MaxSeconds", 25f, new ConfigDescription("Longest time a seagull holds stolen food before eating it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 600f), Array.Empty<object>()));
			CfgCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "CooldownAfterEating", 30f, new ConfigDescription("Seconds a seagull refuses to hunt after a meal. 0 disables the cooldown; a huge value means it never steals again.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100000f), Array.Empty<object>()));
			CfgMinWorth = ((BaseUnityPlugin)this).Config.Bind<int>("Worth", "MinWorth", -60, new ConfigDescription("Low end of the random sell price. Negative costs you money.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(-50000, 50000), Array.Empty<object>()));
			CfgMaxWorth = ((BaseUnityPlugin)this).Config.Bind<int>("Worth", "MaxWorth", 20, new ConfigDescription("High end of the random sell price. Set both to the same number for a fixed price.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(-50000, 50000), Array.Empty<object>()));
			CfgClampBalance = ((BaseUnityPlugin)this).Config.Bind<bool>("Worth", "ClampBalance", true, "Keep the balance between $0 and the $2,147,483,647 limit. Leave this on: a negative balance makes How To Fish - Extended reset you to the limit, and the next sale then overflows to -$2,147,483,648.");
			CfgPoopName = ((BaseUnityPlugin)this).Config.Bind<string>("Poop", "Name", "Seagull Poop", "Display name of the resulting item.");
			CfgReplaceModel = ((BaseUnityPlugin)this).Config.Bind<bool>("Poop", "ReplaceModel", true, "Swap the item's model for a procedurally generated poop. Off = the original model, recoloured.");
			CfgPoopScale = ((BaseUnityPlugin)this).Config.Bind<float>("Poop", "Scale", 1f, new ConfigDescription("Size multiplier. The poop is sized from the item it replaced, so 1 keeps it in proportion to that fish. Capped so it cannot fill the screen when held.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 3f), Array.Empty<object>()));
			CfgPoopColor = ((BaseUnityPlugin)this).Config.Bind<string>("Poop", "Colour", "4A2E17", "Poop colour as an RRGGBB hex string.");
			CfgPoopCookness = ((BaseUnityPlugin)this).Config.Bind<float>("Poop", "Cookness", 0.85f, new ConfigDescription("Feeds the game's own cookness shader input, which darkens the surface.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			CfgSound = ((BaseUnityPlugin)this).Config.Bind<bool>("Poop", "PlaySound", true, "Play a seagull squawk at the moment of digestion.");
			CfgSwapToRealItem = ((BaseUnityPlugin)this).Config.Bind<bool>("Poop", "SwapToRealItem", true, "When a seagull digests a weapon, a tool, or anything that was not already a creature, replace it with a fresh ordinary item and turn that into the poop, instead of dressing up the weapon where it stands. The result was never a weapon, so there is nothing left to fire. Fish are always transformed in place either way. Quest and boss items are never swapped.");
			CfgChassisItem = ((BaseUnityPlugin)this).Config.Bind<string>("Poop", "ChassisItem", "", "Which item the replacement above is built from. Leave empty to pick the lowest numbered ordinary fish automatically; the choice is written to the log on first use. Takes a prefab name, spaces ignored and case insensitive.");
			CfgDisarmWeapons = ((BaseUnityPlugin)this).Config.Bind<bool>("Poop", "DisarmWeapons", true, "Safety net for anything that still ends up transformed in place - a quest item, or a lobby where no ordinary fish could be found. It stops being a weapon: no firing, reloading, swinging or aiming, and the HUD dot goes from weapon orange to ordinary white.");
			CfgHungerCost = ((BaseUnityPlugin)this).Config.Bind<int>("Eating", "HungerCost", 25, new ConfigDescription("Fullness a mouthful of poop costs. Paid out of your stomach first.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
			CfgDamageOnEmptyStomach = ((BaseUnityPlugin)this).Config.Bind<bool>("Eating", "DamageOnEmptyStomach", true, "Whatever fullness you did not have to pay with is dealt as direct damage instead.");
			CfgPoison = ((BaseUnityPlugin)this).Config.Bind<bool>("Eating", "Poison", true, "Apply the game's own poison, exactly like raw pufferfish. Happens regardless of fullness.");
			CfgBlockOtherFoodEffects = ((BaseUnityPlugin)this).Config.Bind<bool>("Eating", "BlockOtherFoodEffects", true, "Stop other food-effect mods (Fish Effects) from granting poop a positive effect.");
			CfgDropNearPlayer = ((BaseUnityPlugin)this).Config.Bind<bool>("Drop", "DropNearPlayer", true, "The instant a seagull finishes digesting, teleport the poop to a random spot near a player and let it fall from there, instead of leaving it to drop wherever the bird happens to be flying (often out over the water or off in the distance, easy to lose). Turn off to go back to that old behaviour.");
			CfgTargetVictim = ((BaseUnityPlugin)this).Config.Bind<bool>("Drop", "TargetVictim", true, "Aim for the player the item was stolen from, if they are still connected and alive. Falls back to whichever player is currently nearest the bird otherwise.");
			CfgDropRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Drop", "DropRadius", 6f, new ConfigDescription("Random horizontal distance from the target player the poop can land within, in metres. 0 always drops it right on top of them.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f), Array.Empty<object>()));
			CfgDropHeight = ((BaseUnityPlugin)this).Config.Bind<float>("Drop", "DropHeight", 6f, new ConfigDescription("Height above the target's feet the poop is placed at before gravity takes it the rest of the way, in metres. Gives it a visible moment of falling instead of popping straight into the grass.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f), Array.Empty<object>()));
			CfgChassisItem.SettingChanged += delegate
			{
				Chassis.Reset();
			};
			_harmony = new Harmony("erwins.seagulldigestion");
			_harmony.PatchAll(typeof(Patches));
			_harmony.PatchAll(typeof(Eating));
			_harmony.PatchAll(typeof(MoneyGuard));
			ForeignEffects.Apply(_harmony);
			((Component)this).gameObject.AddComponent<DigestionRunner>();
			Log.LogInfo((object)"Seagull Digestion loaded. Seagulls now finish what they start.");
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class PoopMarker
	{
		private const float Base = 100000f;

		private const float Threshold = -1000f;

		internal const int MinWorth = -50000;

		internal const int MaxWorth = 50000;

		internal static float Encode(int worth)
		{
			return 0f - (100000f + (float)Mathf.Clamp(worth, -50000, 50000));
		}

		internal static bool IsPoop(Item item)
		{
			if ((Object)(object)item == (Object)null)
			{
				return false;
			}
			try
			{
				return item._bettingMultiplier != null && item._bettingMultiplier.Value < -1000f;
			}
			catch
			{
				return false;
			}
		}

		internal static int WorthOf(Item item)
		{
			try
			{
				return Mathf.RoundToInt(0f - item._bettingMultiplier.Value - 100000f);
			}
			catch
			{
				return 0;
			}
		}
	}
	internal static class PoopMesh
	{
		internal const float UnitHeight = 1.2f;

		private static readonly Dictionary<int, Mesh> Cache = new Dictionary<int, Mesh>();

		internal static Mesh Get(float scale)
		{
			scale = Mathf.Clamp(scale, 0.005f, 5f);
			int num = Mathf.RoundToInt(scale * 1000f);
			if (Cache.TryGetValue(num, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			Mesh val = Build((float)num / 1000f);
			Cache[num] = val;
			return val;
		}

		private static Mesh Build(float scale)
		{
			//IL_0037: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: 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_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			//IL_026e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0279: Unknown result type (might be due to invalid IL or missing references)
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			//IL_0288: Unknown result type (might be due to invalid IL or missing references)
			//IL_0290: Unknown result type (might be due to invalid IL or missing references)
			//IL_0296: Unknown result type (might be due to invalid IL or missing references)
			//IL_029d: Expected O, but got Unknown
			List<Vector3> list = new List<Vector3>(976);
			List<Vector2> list2 = new List<Vector2>(list.Capacity);
			List<int> list3 = new List<int>(5418);
			for (int i = 0; i <= 64; i++)
			{
				float num = (float)i / 64f;
				Vector3 val = PathAt(num);
				Vector3 val2 = PathAt(Mathf.Min(1f, num + 0.008f)) - PathAt(Mathf.Max(0f, num - 0.008f));
				Vector3 val3 = ((Vector3)(ref val2)).normalized;
				if (((Vector3)(ref val3)).sqrMagnitude < 1E-06f)
				{
					val3 = Vector3.up;
				}
				Vector3 val4 = Vector3.Cross(val3, Vector3.up);
				if (((Vector3)(ref val4)).sqrMagnitude < 1E-06f)
				{
					val4 = Vector3.right;
				}
				((Vector3)(ref val4)).Normalize();
				val2 = Vector3.Cross(val4, val3);
				Vector3 normalized = ((Vector3)(ref val2)).normalized;
				float num2 = 0.2f * Mathf.Pow(1f - num, 0.5f);
				for (int j = 0; j <= 14; j++)
				{
					float num3 = (float)j / 14f * MathF.PI * 2f;
					Vector3 val5 = val4 * Mathf.Cos(num3) + normalized * Mathf.Sin(num3);
					list.Add((val + new Vector3(val5.x * num2, val5.y * num2 * 0.85f, val5.z * num2)) * scale);
					list2.Add(new Vector2((float)j / 14f, num));
				}
			}
			int num4 = 15;
			for (int k = 0; k < 64; k++)
			{
				for (int l = 0; l < 14; l++)
				{
					int num5 = k * num4 + l;
					int item = num5 + 1;
					int num6 = num5 + num4;
					int item2 = num6 + 1;
					list3.Add(num5);
					list3.Add(num6);
					list3.Add(item);
					list3.Add(item);
					list3.Add(num6);
					list3.Add(item2);
				}
			}
			int count = list.Count;
			list.Add(PathAt(0f) * scale);
			list2.Add(new Vector2(0.5f, 0f));
			for (int m = 0; m < 14; m++)
			{
				list3.Add(count);
				list3.Add(m);
				list3.Add(m + 1);
			}
			Mesh val6 = new Mesh
			{
				name = "SeagullPoop"
			};
			val6.SetVertices(list);
			val6.SetUVs(0, list2);
			val6.SetTriangles(list3, 0);
			val6.RecalculateNormals();
			val6.RecalculateBounds();
			return val6;
			static Vector3 PathAt(float t)
			{
				//IL_003f: Unknown result type (might be due to invalid IL or missing references)
				float num7 = t * 3f * MathF.PI * 2f;
				float num8 = 0.38f * (1f - t * 0.85f);
				return new Vector3(Mathf.Cos(num7) * num8, 1.2f * t, Mathf.Sin(num7) * num8);
			}
		}
	}
	internal class PoopVisual : MonoBehaviour
	{
		private static readonly int[] ColorIds = new int[2]
		{
			Shader.PropertyToID("_BaseColor"),
			Shader.PropertyToID("_Color")
		};

		private static readonly int[] AlbedoMapIds = new int[2]
		{
			Shader.PropertyToID("_BaseMap"),
			Shader.PropertyToID("_MainTex")
		};

		private static readonly int CooknessId = Shader.PropertyToID("_Cookness");

		private static readonly int UseSkinId = Shader.PropertyToID("_Use_Skin");

		private static readonly int RainbowSkinId = Shader.PropertyToID("_Rainbow_Skin");

		private static Texture2D _flat;

		private static Color _flatColor;

		private const float FallbackLongestSide = 0.5f;

		private const float MinPoopHeight = 0.04f;

		private const float MaxPoopHeight = 0.6f;

		private Item _item;

		private bool _applied;

		private bool _subscribed;

		private readonly List<Renderer> _hidden = new List<Renderer>();

		internal Mesh IconMesh { get; private set; }

		private void Awake()
		{
			_item = ((Component)this).GetComponent<Item>();
			if ((Object)(object)_item == (Object)null)
			{
				((Behaviour)this).enabled = false;
				return;
			}
			try
			{
				if (_item._bettingMultiplier != null)
				{
					_item._bettingMultiplier.OnChange += OnMultiplierChanged;
					_subscribed = true;
					if (PoopMarker.IsPoop(_item))
					{
						Apply();
					}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("PoopVisual subscribe failed: " + ex.Message));
			}
		}

		private void OnDestroy()
		{
			if (!_subscribed || (Object)(object)_item == (Object)null)
			{
				return;
			}
			try
			{
				_item._bettingMultiplier.OnChange -= OnMultiplierChanged;
			}
			catch
			{
			}
		}

		private void OnMultiplierChanged(float prev, float next, bool asServer)
		{
			if (next < -1000f)
			{
				Apply();
			}
		}

		internal void Apply()
		{
			//IL_00f3: 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_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: 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_0127: Unknown result type (might be due to invalid IL or missing references)
			if (_applied)
			{
				return;
			}
			_applied = true;
			BirdFood.Forget(_item);
			Disarm.Strip(_item);
			try
			{
				Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val in componentsInChildren)
				{
					if (!((Object)(object)val == (Object)null) && val.enabled && ((Component)val).gameObject.activeInHierarchy && !(val is ParticleSystemRenderer) && !((Object)(object)((Component)val).GetComponentInParent<Canvas>() != (Object)null))
					{
						_hidden.Add(val);
					}
				}
				if (!Plugin.CfgReplaceModel.Value)
				{
					Recolour();
					return;
				}
				MeasureOriginal(out var localCentre, out var longestSide);
				foreach (Renderer item in _hidden)
				{
					item.enabled = false;
				}
				IconMesh = PoopMesh.Get(FitScale(longestSide));
				GameObject val2 = new GameObject("SeagullPoopModel");
				val2.transform.SetParent(((Component)this).transform, false);
				val2.transform.localPosition = localCentre;
				val2.layer = ((Component)this).gameObject.layer;
				val2.AddComponent<MeshFilter>().sharedMesh = IconMesh;
				((Renderer)val2.AddComponent<MeshRenderer>()).sharedMaterial = MakeMaterial();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Failed to apply poop visuals: " + ex));
			}
		}

		internal static Color PoopColor()
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			string text = (Plugin.CfgPoopColor.Value ?? "").Trim().TrimStart('#');
			Color result = default(Color);
			if (ColorUtility.TryParseHtmlString("#" + text, ref result))
			{
				return result;
			}
			return new Color(0.29f, 0.18f, 0.09f);
		}

		private static Texture2D FlatTexture(Color c)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Expected O, but got Unknown
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: 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)
			if ((Object)(object)_flat != (Object)null && _flatColor == c)
			{
				return _flat;
			}
			_flat = new Texture2D(4, 4, (TextureFormat)4, false)
			{
				name = "SeagullPoopTex"
			};
			Color[] array = (Color[])(object)new Color[16];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = c;
			}
			_flat.SetPixels(array);
			_flat.Apply();
			_flatColor = c;
			return _flat;
		}

		private Material MakeMaterial()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Expected O, but got Unknown
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Expected O, but got Unknown
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			Color color = PoopColor();
			Material val = null;
			foreach (Renderer item in _hidden)
			{
				if ((Object)(object)item != (Object)null && !(item is LineRenderer) && !(item is TrailRenderer) && (Object)(object)item.sharedMaterial != (Object)null)
				{
					val = item.sharedMaterial;
					break;
				}
			}
			Material val2;
			if ((Object)(object)val != (Object)null)
			{
				val2 = new Material(val);
				Plugin.Log.LogInfo((object)("Poop material cloned from shader: " + ((Object)val.shader).name));
			}
			else
			{
				Shader val3 = Shader.Find("Universal Render Pipeline/Lit") ?? Shader.Find("Standard");
				val2 = new Material(val3);
				Plugin.Log.LogWarning((object)("No source material on item; fell back to " + (((Object)(object)val3 != (Object)null) ? ((Object)val3).name : "none")));
			}
			((Object)val2).name = "SeagullPoopMat";
			Paint(val2, color);
			return val2;
		}

		private static void Paint(Material mat, Color color)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			Texture2D val = FlatTexture(color);
			int[] albedoMapIds = AlbedoMapIds;
			foreach (int num in albedoMapIds)
			{
				if (mat.HasProperty(num))
				{
					mat.SetTexture(num, (Texture)(object)val);
				}
			}
			albedoMapIds = ColorIds;
			foreach (int num2 in albedoMapIds)
			{
				if (mat.HasProperty(num2))
				{
					mat.SetColor(num2, color);
				}
			}
			if (mat.HasProperty(UseSkinId))
			{
				mat.SetFloat(UseSkinId, 0f);
			}
			if (mat.HasProperty(RainbowSkinId))
			{
				mat.SetFloat(RainbowSkinId, 0f);
			}
			if (mat.HasProperty(CooknessId))
			{
				mat.SetFloat(CooknessId, Plugin.CfgPoopCookness.Value);
			}
		}

		private void MeasureOriginal(out Vector3 localCentre, out float longestSide)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: 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_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: 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_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			localCentre = Vector3.zero;
			longestSide = 0.5f;
			Bounds? val = null;
			foreach (Renderer item in _hidden)
			{
				if (!((Object)(object)item == (Object)null))
				{
					if (val.HasValue)
					{
						Bounds value = val.Value;
						((Bounds)(ref value)).Encapsulate(item.bounds);
						val = value;
					}
					else
					{
						val = item.bounds;
					}
				}
			}
			if (val.HasValue)
			{
				Bounds value2 = val.Value;
				localCentre = ((Component)this).transform.InverseTransformPoint(((Bounds)(ref value2)).center);
				Vector3 lossyScale = ((Component)this).transform.lossyScale;
				Vector3 val2 = default(Vector3);
				((Vector3)(ref val2))..ctor(((Bounds)(ref value2)).size.x / Mathf.Max(0.0001f, Mathf.Abs(lossyScale.x)), ((Bounds)(ref value2)).size.y / Mathf.Max(0.0001f, Mathf.Abs(lossyScale.y)), ((Bounds)(ref value2)).size.z / Mathf.Max(0.0001f, Mathf.Abs(lossyScale.z)));
				float num = Mathf.Max(val2.x, Mathf.Max(val2.y, val2.z));
				if (num > 0.0001f && !float.IsNaN(num) && !float.IsInfinity(num))
				{
					longestSide = num;
				}
			}
		}

		private static float FitScale(float longestSide)
		{
			return Mathf.Clamp(longestSide * 0.5f * Mathf.Max(0.01f, Plugin.CfgPoopScale.Value), 0.04f, 0.6f) / 1.2f;
		}

		private void Recolour()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			Color color = PoopColor();
			foreach (Renderer item in _hidden)
			{
				if ((Object)(object)item == (Object)null)
				{
					continue;
				}
				Material[] materials = item.materials;
				for (int i = 0; i < materials.Length; i++)
				{
					if ((Object)(object)materials[i] != (Object)null)
					{
						Paint(materials[i], color);
					}
				}
				item.materials = materials;
			}
		}
	}
	internal static class BirdFood
	{
		private static HashSet<Item> _pool;

		private static bool _looked;

		internal static void Forget(Item item)
		{
			if ((Object)(object)item == (Object)null)
			{
				return;
			}
			try
			{
				if (!_looked)
				{
					_looked = true;
					_pool = AccessTools.Field(typeof(ItemManager), "_itemsForBirds")?.GetValue(null) as HashSet<Item>;
				}
				_pool?.Remove(item);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not remove item from the seagull pool: " + ex.Message));
			}
		}
	}
}