Decompiled source of SeagullDigestion v1.5.2

BepInEx/plugins/SeagullDigestion/SeagullDigestion.dll

Decompiled 8 hours 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.Object;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: 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 class DigestionRunner : MonoBehaviour
	{
		private struct Meal
		{
			public Item Item;

			public float DueAt;
		}

		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>();

		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;
			foreach (KeyValuePair<Bird, Meal> meal in Meals)
			{
				Bird key = meal.Key;
				Meal value = meal.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 item in Finished)
			{
				if ((Object)(object)item != (Object)null)
				{
					Meals.Remove(item);
				}
			}
			if (Meals.ContainsKey(null))
			{
				Meals.Remove(null);
			}
		}

		private static void Digest(Bird bird, Item item)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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)
			Vector3 position = ((Component)item).transform.position;
			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._bettingMultiplier.Value = PoopMarker.Encode(num3);
			(((Component)item).GetComponent<PoopVisual>() ?? ((Component)item).gameObject.AddComponent<PoopVisual>()).Apply();
			item.ReleasedByBird();
			float value = Plugin.CfgCooldown.Value;
			if (value > 0f)
			{
				FullUntil[bird] = Time.time + value;
			}
			if (Plugin.CfgSound.Value)
			{
				try
				{
					AudioManager.PlayRandomClipAt("Seagull_V", 1, 9, position, false, (AudioDistance)2, 0.5f, 0.1f);
				}
				catch
				{
				}
			}
			Plugin.Log.LogInfo((object)$"Seagull digested '{SafeName(item)}' -> {Plugin.CfgPoopName.Value} worth {num3}");
		}

		private static string SafeName(Item item)
		{
			try
			{
				return item.GetName();
			}
			catch
			{
				return ((Object)(object)item != (Object)null) ? ((Object)item).name : "null";
			}
		}
	}
	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 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)))
				{
					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(Item), "AddBetMultiplier")]
		private static bool Item_AddBetMultiplier(Item __instance)
		{
			return !PoopMarker.IsPoop(__instance);
		}
	}
	[BepInPlugin("erwins.seagulldigestion", "Seagull Digestion", "1.5.2")]
	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<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<int> CfgHungerCost;

		internal static ConfigEntry<bool> CfgDamageOnEmptyStomach;

		internal static ConfigEntry<bool> CfgPoison;

		internal static ConfigEntry<bool> CfgBlockOtherFoodEffects;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Expected O, but got Unknown
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Expected O, but got Unknown
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Expected O, but got Unknown
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Expected O, but got Unknown
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Expected O, but got Unknown
			//IL_0208: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Expected O, but got Unknown
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			//IL_0273: Expected O, but got Unknown
			//IL_02bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c6: Expected O, but got Unknown
			//IL_0331: Unknown result type (might be due to invalid IL or missing references)
			//IL_033b: 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.");
			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.");
			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.");
			_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_00ec: 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_0103: 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)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			if (_applied)
			{
				return;
			}
			_applied = true;
			BirdFood.Forget(_item);
			try
			{
				Renderer[] componentsInChildren = ((Component)this).GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val in componentsInChildren)
				{
					if (!((Object)(object)val == (Object)null) && val.enabled && !(val is ParticleSystemRenderer) && !(val is TrailRenderer) && !(val is LineRenderer) && !((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_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Expected O, but got Unknown
			//IL_00e6: 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 && (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));
			}
		}
	}
}