Decompiled source of ExtraMouths v1.0.0

plugins/ExtraMouths.dll

Decompiled a week ago
using System;
using System.Collections;
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 HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using pworld.Scripts.Extensions;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Arman Ossi Loko")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) 2026 Arman Ossi Loko")]
[assembly: AssemblyDescription("Scales PEAK forage food for lobbies larger than 4 players.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+db658f8820e70303e424f7141b803ff8594bd306")]
[assembly: AssemblyProduct("ExtraMouths")]
[assembly: AssemblyTitle("ExtraMouths")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
[CompilerGenerated]
internal sealed class <>z__ReadOnlyArray<T> : IEnumerable, ICollection, IList, IEnumerable<T>, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T>
{
	int ICollection.Count => _items.Length;

	bool ICollection.IsSynchronized => false;

	object ICollection.SyncRoot => this;

	object IList.this[int index]
	{
		get
		{
			return _items[index];
		}
		set
		{
			throw new NotSupportedException();
		}
	}

	bool IList.IsFixedSize => true;

	bool IList.IsReadOnly => true;

	int IReadOnlyCollection<T>.Count => _items.Length;

	T IReadOnlyList<T>.this[int index] => _items[index];

	int ICollection<T>.Count => _items.Length;

	bool ICollection<T>.IsReadOnly => true;

	T IList<T>.this[int index]
	{
		get
		{
			return _items[index];
		}
		set
		{
			throw new NotSupportedException();
		}
	}

	public <>z__ReadOnlyArray(T[] items)
	{
		_items = items;
	}

	IEnumerator IEnumerable.GetEnumerator()
	{
		return ((IEnumerable)_items).GetEnumerator();
	}

	void ICollection.CopyTo(Array array, int index)
	{
		((ICollection)_items).CopyTo(array, index);
	}

	int IList.Add(object value)
	{
		throw new NotSupportedException();
	}

	void IList.Clear()
	{
		throw new NotSupportedException();
	}

	bool IList.Contains(object value)
	{
		return ((IList)_items).Contains(value);
	}

	int IList.IndexOf(object value)
	{
		return ((IList)_items).IndexOf(value);
	}

	void IList.Insert(int index, object value)
	{
		throw new NotSupportedException();
	}

	void IList.Remove(object value)
	{
		throw new NotSupportedException();
	}

	void IList.RemoveAt(int index)
	{
		throw new NotSupportedException();
	}

	IEnumerator<T> IEnumerable<T>.GetEnumerator()
	{
		return ((IEnumerable<T>)_items).GetEnumerator();
	}

	void ICollection<T>.Add(T item)
	{
		throw new NotSupportedException();
	}

	void ICollection<T>.Clear()
	{
		throw new NotSupportedException();
	}

	bool ICollection<T>.Contains(T item)
	{
		return ((ICollection<T>)_items).Contains(item);
	}

	void ICollection<T>.CopyTo(T[] array, int arrayIndex)
	{
		((ICollection<T>)_items).CopyTo(array, arrayIndex);
	}

	bool ICollection<T>.Remove(T item)
	{
		throw new NotSupportedException();
	}

	int IList<T>.IndexOf(T item)
	{
		return ((IList<T>)_items).IndexOf(item);
	}

	void IList<T>.Insert(int index, T item)
	{
		throw new NotSupportedException();
	}

	void IList<T>.RemoveAt(int index)
	{
		throw new NotSupportedException();
	}
}
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 BepInEx
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class BepInAutoPluginAttribute : Attribute
	{
		public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace BepInEx.Preloader.Core.Patching
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	internal sealed class PatcherAutoPluginAttribute : Attribute
	{
		public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace ExtraMouths
{
	[BepInPlugin("ExtraMouths", "ExtraMouths", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(BerryBush))]
		public static class BerryBushPatch
		{
			[HarmonyPrefix]
			[HarmonyPatch("SpawnItems", new Type[] { typeof(List<Transform>) })]
			public static void SpawnItemsPrefix(BerryBush __instance, ref List<Transform> spawnSpots, out Vector2 __state)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_002e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0033: 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_008a: Unknown result type (might be due to invalid IL or missing references)
				__state = __instance.possibleBerries;
				if (BerryBushes.Value && ShouldScale())
				{
					MultiplySpawnRange(ref __instance.possibleBerries, ref spawnSpots, out __state);
					Vector2 possibleBerries = __instance.possibleBerries;
					Log.LogInfo((object)$"[BerryBush] players={GetConnectedPlayerCount()} mult={GetMultiplier():0.###}x range {__state.x}-{__state.y} → {possibleBerries.x}-{possibleBerries.y}");
				}
			}

			[HarmonyPostfix]
			[HarmonyPatch("SpawnItems", new Type[] { typeof(List<Transform>) })]
			public static void SpawnItemsPostfix(BerryBush __instance, Vector2 __state)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				__instance.possibleBerries = __state;
			}
		}

		[HarmonyPatch(typeof(BerryVine))]
		public static class BerryVinePatch
		{
			[HarmonyPrefix]
			[HarmonyPatch("SpawnItems", new Type[] { typeof(List<Transform>) })]
			public static void SpawnItemsPrefix(BerryVine __instance, ref List<Transform> spawnSpots, out Vector2 __state)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_002e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0033: 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_008a: Unknown result type (might be due to invalid IL or missing references)
				__state = __instance.possibleBerries;
				if (BerryVines.Value && ShouldScale())
				{
					MultiplySpawnRange(ref __instance.possibleBerries, ref spawnSpots, out __state);
					Vector2 possibleBerries = __instance.possibleBerries;
					Log.LogInfo((object)$"[BerryVine] players={GetConnectedPlayerCount()} mult={GetMultiplier():0.###}x range {__state.x}-{__state.y} → {possibleBerries.x}-{possibleBerries.y}");
				}
			}

			[HarmonyPostfix]
			[HarmonyPatch("SpawnItems", new Type[] { typeof(List<Transform>) })]
			public static void SpawnItemsPostfix(BerryVine __instance, Vector2 __state)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				__instance.possibleBerries = __state;
			}
		}

		[HarmonyPatch(typeof(GroundPlaceSpawner))]
		public static class GroundPlaceSpawnerPatch
		{
			[HarmonyPrefix]
			[HarmonyPatch("SpawnItems", new Type[] { typeof(List<Transform>) })]
			public static void SpawnItemsPrefix(GroundPlaceSpawner __instance, ref List<Transform> spawnSpots, out Vector2 __state)
			{
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_0028: Unknown result type (might be due to invalid IL or missing references)
				//IL_002e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0033: Unknown result type (might be due to invalid IL or missing references)
				//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_003f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0045: 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_0058: 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_006f: 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_0096: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b4: 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_00ba: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
				//IL_013e: Unknown result type (might be due to invalid IL or missing references)
				//IL_014c: Unknown result type (might be due to invalid IL or missing references)
				__state = __instance.possibleItems;
				if (GroundSpawns.Value && ShouldScale())
				{
					float multiplier = GetMultiplier();
					__instance.possibleItems *= multiplier;
					Vector2 possibleItems = __instance.possibleItems;
					float num = possibleItems.x - Mathf.Floor(possibleItems.x);
					float num2 = possibleItems.y - Mathf.Floor(possibleItems.y);
					possibleItems.x = ((Random.value < num) ? Mathf.Ceil(possibleItems.x) : Mathf.Floor(possibleItems.x));
					possibleItems.y = ((Random.value < num2) ? Mathf.Ceil(possibleItems.y) : Mathf.Floor(possibleItems.y));
					__instance.possibleItems = possibleItems;
					float num3 = Math.Max(possibleItems.x, possibleItems.y);
					if (num3 > (float)spawnSpots.Count)
					{
						List<Transform> collection = AddMoreSlotsUntil(spawnSpots, Mathf.CeilToInt(num3));
						spawnSpots.Clear();
						spawnSpots.AddRange(collection);
					}
					Log.LogInfo((object)$"[GroundPlace] players={GetConnectedPlayerCount()} mult={multiplier:0.###}x range {__state.x}-{__state.y} → {possibleItems.x}-{possibleItems.y}");
				}
			}

			[HarmonyPostfix]
			[HarmonyPatch("SpawnItems", new Type[] { typeof(List<Transform>) })]
			public static void SpawnItemsPostfix(GroundPlaceSpawner __instance, Vector2 __state)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				__instance.possibleItems = __state;
			}
		}

		[HarmonyPatch(typeof(Spawner))]
		public static class SpawnerPatch
		{
			private static bool IsConfiguredLuggage(Spawner spawner)
			{
				if (luggageSet.Count == 0)
				{
					return false;
				}
				Luggage val = (Luggage)(object)((spawner is Luggage) ? spawner : null);
				if (val != null)
				{
					return luggageSet.Contains(val.displayName);
				}
				return false;
			}

			private static bool IsFoodPoolSpawner(Spawner spawner)
			{
				//IL_003f: Unknown result type (might be due to invalid IL or missing references)
				if (!FoodSpawnPools.Value)
				{
					return false;
				}
				if ((spawner is BerryBush || spawner is BerryVine || spawner is GroundPlaceSpawner || spawner is Luggage) ? true : false)
				{
					return false;
				}
				return FoodPools.Contains(spawner.GetSpawnPool());
			}

			[HarmonyPrefix]
			[HarmonyPatch("SpawnItems", new Type[] { typeof(List<Transform>) })]
			public static void SpawnItemsPrefix(Spawner __instance, ref List<Transform> spawnSpots)
			{
				//IL_005c: Unknown result type (might be due to invalid IL or missing references)
				if (ShouldScale())
				{
					bool flag = IsConfiguredLuggage(__instance);
					bool flag2 = IsFoodPoolSpawner(__instance);
					if (flag || flag2)
					{
						float multiplier = GetMultiplier();
						List<Transform> collection = AddMoreSlots(spawnSpots, multiplier);
						spawnSpots.Clear();
						spawnSpots.AddRange(collection);
						Log.LogInfo((object)$"[Spawner:{((Object)((Component)__instance).gameObject).name}] pool={__instance.spawnPool} players={GetConnectedPlayerCount()} mult={multiplier:0.###}x → {spawnSpots.Count} slots");
					}
				}
			}
		}

		private static HashSet<string> luggageSet = new HashSet<string>();

		private static readonly HashSet<SpawnPool> FoodPools = BuildFoodPools(new <>z__ReadOnlyArray<string>(new string[11]
		{
			"MushroomCluster", "BerryBushBeach", "BerryBushJungle", "SpikyVine", "CoconutTree", "WillowTreeJungle", "JungleVine", "WinterberryTree", "Nest", "Cactus",
			"Redwood"
		}));

		public const string Id = "ExtraMouths";

		internal static ManualLogSource Log { get; private set; } = null;

		internal static ConfigEntry<int> ExtraPlayers { get; private set; } = null;

		internal static ConfigEntry<int> BaselinePlayers { get; private set; } = null;

		internal static ConfigEntry<int> MinPlayersToScale { get; private set; } = null;

		internal static ConfigEntry<string> Luggages { get; private set; } = null;

		internal static ConfigEntry<bool> GroundSpawns { get; private set; } = null;

		internal static ConfigEntry<bool> BerryBushes { get; private set; } = null;

		internal static ConfigEntry<bool> BerryVines { get; private set; } = null;

		internal static ConfigEntry<bool> FoodSpawnPools { get; private set; } = null;

		public static string Name => "ExtraMouths";

		public static string Version => "1.0.0";

		private static HashSet<SpawnPool> BuildFoodPools(IEnumerable<string> names)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			HashSet<SpawnPool> hashSet = new HashSet<SpawnPool>();
			foreach (string name in names)
			{
				if (Enum.TryParse<SpawnPool>(name, ignoreCase: false, out SpawnPool result))
				{
					hashSet.Add(result);
				}
			}
			return hashSet;
		}

		private void Awake()
		{
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			ExtraPlayers = ((BaseUnityPlugin)this).Config.Bind<int>("Scaling", "ExtraPlayers", 2, "Treat the lobby as this many extra mouths to feed. Multiplier = (connectedPlayers + ExtraPlayers) / BaselinePlayers.");
			BaselinePlayers = ((BaseUnityPlugin)this).Config.Bind<int>("Scaling", "BaselinePlayers", 4, "Vanilla balance player count. Food is scaled relative to this.");
			MinPlayersToScale = ((BaseUnityPlugin)this).Config.Bind<int>("Scaling", "MinPlayersToScale", 5, "If connected players are below this, leave spawn rates fully vanilla. Default 5 so 4-player lobbies match the game.");
			BerryBushes = ((BaseUnityPlugin)this).Config.Bind<bool>("Spawners", "BerryBushes", true, "Scale berry bushes.");
			BerryVines = ((BaseUnityPlugin)this).Config.Bind<bool>("Spawners", "BerryVines", true, "Scale berry vines.");
			GroundSpawns = ((BaseUnityPlugin)this).Config.Bind<bool>("Spawners", "GroundSpawns", true, "Scale ground forage (e.g. mushrooms).");
			FoodSpawnPools = ((BaseUnityPlugin)this).Config.Bind<bool>("Spawners", "FoodSpawnPools", true, "Scale base Spawner food pools (coconuts, winterberries, kingberries, clusterberries, nests, cactus fruit, etc.).");
			Luggages = ((BaseUnityPlugin)this).Config.Bind<string>("Spawners", "Luggages", "", "Optional: comma-separated luggage display names to also multiply (e.g. \"Luggage,Big Luggage,Explorer's Luggage\"). Empty = luggage untouched.");
			luggageSet = BuildLuggageSet(Luggages.Value);
			Luggages.SettingChanged += delegate
			{
				luggageSet = BuildLuggageSet(Luggages.Value);
			};
			Harmony val = new Harmony("ExtraMouths");
			try
			{
				val.PatchAll();
				Log.LogInfo((object)$"{Name} v{Version} loaded. Formula: (players + {ExtraPlayers.Value}) / {BaselinePlayers.Value}, skip if players < {MinPlayersToScale.Value}.");
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Failed to patch: {arg}");
			}
		}

		private static HashSet<string> BuildLuggageSet(string raw)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			if (string.IsNullOrWhiteSpace(raw))
			{
				return hashSet;
			}
			string[] array = raw.Split(',');
			foreach (string text in array)
			{
				string text2 = text.Trim();
				if (text2.Length > 0)
				{
					hashSet.Add(text2);
				}
			}
			return hashSet;
		}

		internal static int GetConnectedPlayerCount()
		{
			if (PhotonNetwork.InRoom && PhotonNetwork.CurrentRoom != null)
			{
				return PhotonNetwork.CurrentRoom.PlayerCount;
			}
			Player[] playerList = PhotonNetwork.PlayerList;
			if (playerList == null)
			{
				return 0;
			}
			return playerList.Length;
		}

		internal static bool ShouldScale()
		{
			if (!PhotonNetwork.IsMasterClient)
			{
				return false;
			}
			int connectedPlayerCount = GetConnectedPlayerCount();
			int num = Math.Max(1, MinPlayersToScale.Value);
			return connectedPlayerCount >= num;
		}

		internal static float GetMultiplier()
		{
			if (!ShouldScale())
			{
				return 1f;
			}
			int connectedPlayerCount = GetConnectedPlayerCount();
			int num = Math.Max(1, BaselinePlayers.Value);
			int num2 = Math.Max(0, ExtraPlayers.Value);
			return (float)(connectedPlayerCount + num2) / (float)num;
		}

		internal static List<Transform> AddMoreSlotsUntil(List<Transform> spawnSpots, int target, bool addRandom = false)
		{
			if (spawnSpots.Count == 0 || (target <= 0 && !addRandom))
			{
				return new List<Transform>();
			}
			int count = spawnSpots.Count;
			List<Transform> list = new List<Transform>(Math.Max(0, target) + (addRandom ? 1 : 0));
			while (target >= count)
			{
				list.AddRange(spawnSpots);
				target -= count;
			}
			if (target > 0)
			{
				ExtCollections.Shuffle<Transform>((IList<Transform>)spawnSpots);
				list.AddRange(spawnSpots.GetRange(0, target));
			}
			if (addRandom)
			{
				list.Add(spawnSpots[Random.Range(0, count)]);
			}
			return list;
		}

		internal static List<Transform> AddMoreSlots(List<Transform> spawnSpots, float multiplier)
		{
			if (spawnSpots.Count == 0 || multiplier <= 0f)
			{
				return new List<Transform>();
			}
			float num = (float)spawnSpots.Count * multiplier;
			int num2 = Mathf.FloorToInt(num);
			float num3 = num - (float)num2;
			bool addRandom = Random.value < num3;
			return AddMoreSlotsUntil(spawnSpots, num2, addRandom);
		}

		internal static void MultiplySpawnRange(ref Vector2 range, ref List<Transform> spawnSpots, out Vector2 original)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			original = range;
			float multiplier = GetMultiplier();
			range *= multiplier;
			float num = Math.Max(range.x, range.y);
			if (num > (float)spawnSpots.Count)
			{
				List<Transform> collection = AddMoreSlotsUntil(spawnSpots, Mathf.CeilToInt(num));
				spawnSpots.Clear();
				spawnSpots.AddRange(collection);
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}