Decompiled source of MedkitScatter v0.0.1

MedkitScatter.Planning.dll

Decompiled 10 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("AitServices")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Medkit Scatter")]
[assembly: AssemblyTitle("MedkitScatter.Planning")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
	[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 MedkitScatter.Planning
{
	public interface IRandomSource
	{
		int Next(int minInclusive, int maxExclusive);

		double NextDouble();
	}
	public readonly struct MedkitPlacement
	{
		public RoomKey Room { get; }

		public MedkitTier Tier { get; }

		public MedkitPlacement(RoomKey room, MedkitTier tier)
		{
			Room = room;
			Tier = tier;
		}

		public override string ToString()
		{
			return Room.ToString() + " " + Tier;
		}
	}
	public static class MedkitPlanner
	{
		private readonly struct Weights
		{
			public double Small { get; }

			public double Medium { get; }

			public double Large { get; }

			public double Total => Small + Medium + Large;

			public Weights(double small, double medium, double large)
			{
				Small = small;
				Medium = medium;
				Large = large;
			}
		}

		public const int MaxSupportedMedkits = 128;

		public static PlanResult Plan(PlanInput? input, IRandomSource? rng)
		{
			PlanInput planInput = input ?? new PlanInput();
			IRandomSource rng2 = rng ?? new SystemRandomSource(0);
			List<string> list = new List<string>();
			IReadOnlyList<RoomDescriptor> readOnlyList = planInput.Rooms ?? Array.Empty<RoomDescriptor>();
			int num = 0;
			for (int i = 0; i < readOnlyList.Count; i++)
			{
				if (readOnlyList[i].CountsToTotal)
				{
					num++;
				}
			}
			List<RoomDescriptor> list2 = new List<RoomDescriptor>();
			HashSet<RoomKey> hashSet = new HashSet<RoomKey>();
			foreach (RoomDescriptor item in from r in readOnlyList
				where r.CanHostMedkit && r.CandidateSpots > 0
				orderby r.Key
				select r)
			{
				if (hashSet.Add(item.Key))
				{
					list2.Add(item);
				}
				else
				{
					list.Add("duplicate RoomKey " + item.Key.ToString() + " ignored");
				}
			}
			int num2 = Math.Max(1, planInput.RoomsPerMedkit);
			double num3 = (double)num / (double)num2;
			int num4 = planInput.Rounding switch
			{
				RoundingMode.Ceil => (int)Math.Ceiling(num3), 
				RoundingMode.Round => (int)Math.Round(num3, MidpointRounding.AwayFromZero), 
				_ => (int)Math.Floor(num3), 
			};
			int num5 = Clamp(planInput.MinMedkits, 0, 128);
			int val = ((planInput.MaxMedkits == 0) ? int.MaxValue : planInput.MaxMedkits);
			val = Math.Max(num5, val);
			int num6 = Clamp(num4, num5, val);
			List<RoomDescriptor> list3 = OrderRooms(list2, rng2, planInput.PreferDistantRooms);
			int num7 = Math.Min(num6, list3.Count);
			List<MedkitPlacement> list4 = new List<MedkitPlacement>(num7);
			List<RoomKey> list5 = new List<RoomKey>(Math.Max(0, list3.Count - num7));
			Weights weights = EffectiveWeights(planInput, list);
			List<MedkitTier> list6 = new List<MedkitTier>(num7);
			if (planInput.GuaranteeVariety && num7 >= 3)
			{
				List<MedkitTier> list7 = new List<MedkitTier>
				{
					MedkitTier.Small,
					MedkitTier.Medium,
					MedkitTier.Large
				};
				Shuffle(list7, rng2);
				list6.AddRange(list7);
			}
			while (list6.Count < num7)
			{
				list6.Add(WeightedPick(weights, rng2));
			}
			for (int num8 = 0; num8 < list3.Count; num8++)
			{
				if (num8 < num7)
				{
					list4.Add(new MedkitPlacement(list3[num8].Key, list6[num8]));
				}
				else
				{
					list5.Add(list3[num8].Key);
				}
			}
			string explanation = BuildExplanation(planInput, num, num2, num3, num4, num5, planInput.MaxMedkits, num6, list2.Count, num7, list5.Count, weights, list);
			return new PlanResult(num, num6, list4, list5, explanation, list);
		}

		private static Weights EffectiveWeights(PlanInput cfg, List<string> notes)
		{
			double num = Math.Max(0.0, cfg.WeightSmall);
			double num2 = Math.Max(0.0, cfg.WeightMedium);
			double num3 = Math.Max(0.0, cfg.WeightLarge);
			if (cfg.DifficultyScaling)
			{
				int num4 = Math.Max(0, cfg.LevelsCompleted);
				double num5 = 1.0 + cfg.DifficultyFactor * (double)num4;
				if (double.IsNaN(num5) || double.IsInfinity(num5) || num5 <= 0.0)
				{
					notes.Add("difficulty factor " + Fmt(num5) + " invalid, using 1.0");
					num5 = 1.0;
				}
				num3 *= num5;
				num /= num5;
			}
			if (num + num2 + num3 <= 0.0)
			{
				notes.Add("all tier weights <= 0, degraded to Small-only");
				return new Weights(1.0, 0.0, 0.0);
			}
			return new Weights(num, num2, num3);
		}

		private static MedkitTier WeightedPick(Weights weights, IRandomSource rng)
		{
			double total = weights.Total;
			if (total <= 0.0)
			{
				return MedkitTier.Small;
			}
			double num = rng.NextDouble();
			if (double.IsNaN(num) || num < 0.0)
			{
				num = 0.0;
			}
			else if (num >= 1.0)
			{
				num = 0.999999999;
			}
			double num2 = num * total;
			if (num2 < weights.Small)
			{
				return MedkitTier.Small;
			}
			if (num2 < weights.Small + weights.Medium)
			{
				return MedkitTier.Medium;
			}
			return MedkitTier.Large;
		}

		private static List<RoomDescriptor> OrderRooms(List<RoomDescriptor> sortedByKey, IRandomSource rng, bool preferDistant)
		{
			List<RoomDescriptor> list = new List<RoomDescriptor>(sortedByKey);
			if (!preferDistant)
			{
				Shuffle(list, rng);
				return list;
			}
			List<RoomDescriptor> list2 = new List<RoomDescriptor>(list);
			List<double> list3 = new List<double>(list2.Count);
			foreach (RoomDescriptor item in list2)
			{
				list3.Add(1.0 + (double)Math.Max(0, item.DistanceFromStart));
			}
			List<RoomDescriptor> list4 = new List<RoomDescriptor>(list2.Count);
			while (list2.Count > 0)
			{
				double num = 0.0;
				for (int i = 0; i < list3.Count; i++)
				{
					num += list3[i];
				}
				double num2 = rng.NextDouble();
				if (double.IsNaN(num2) || num2 < 0.0)
				{
					num2 = 0.0;
				}
				else if (num2 >= 1.0)
				{
					num2 = 0.999999999;
				}
				double num3 = num2 * num;
				int index = list2.Count - 1;
				double num4 = 0.0;
				for (int j = 0; j < list2.Count; j++)
				{
					num4 += list3[j];
					if (num3 < num4)
					{
						index = j;
						break;
					}
				}
				list4.Add(list2[index]);
				list2.RemoveAt(index);
				list3.RemoveAt(index);
			}
			return list4;
		}

		private static void Shuffle<T>(IList<T> list, IRandomSource rng)
		{
			for (int num = list.Count - 1; num > 0; num--)
			{
				int num2 = rng.Next(0, num + 1);
				if (num2 < 0 || num2 > num)
				{
					num2 = 0;
				}
				T value = list[num];
				list[num] = list[num2];
				list[num2] = value;
			}
		}

		private static int Clamp(int value, int min, int max)
		{
			if (value < min)
			{
				return min;
			}
			if (value <= max)
			{
				return value;
			}
			return max;
		}

		private static string Fmt(double value)
		{
			return value.ToString("0.##", CultureInfo.InvariantCulture);
		}

		private static string BuildExplanation(PlanInput cfg, int roomCount, int divisor, double raw, int rounded, int effMin, int rawMax, int targetCount, int eligibleCount, int plannedCount, int reserveCount, Weights weights, List<string> notes)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("RoomCount(counted)=").Append(roomCount).Append(" -> ")
				.Append(roomCount)
				.Append(" / ")
				.Append(divisor)
				.Append(" = ")
				.Append(raw.ToString("0.00", CultureInfo.InvariantCulture))
				.Append(" -> ")
				.Append(cfg.Rounding)
				.Append(" -> ")
				.Append(rounded)
				.Append(" -> clamp[")
				.Append(effMin)
				.Append("..")
				.Append((rawMax == 0) ? "inf" : rawMax.ToString(CultureInfo.InvariantCulture))
				.Append("] -> TargetCount=")
				.Append(targetCount);
			stringBuilder.Append(" | eligible=").Append(eligibleCount).Append(" -> PlannedCount=")
				.Append(plannedCount)
				.Append(", Reserve=")
				.Append(reserveCount);
			stringBuilder.Append(" | weights S/M/L=").Append(Fmt(weights.Small)).Append('/')
				.Append(Fmt(weights.Medium))
				.Append('/')
				.Append(Fmt(weights.Large))
				.Append(" variety=")
				.Append(cfg.GuaranteeVariety ? "on" : "off")
				.Append(" distant=")
				.Append(cfg.PreferDistantRooms ? "on" : "off");
			if (notes.Count > 0)
			{
				stringBuilder.Append(" | notes: ").Append(string.Join("; ", notes.ToArray()));
			}
			return stringBuilder.ToString();
		}
	}
	public enum MedkitTier
	{
		Small,
		Medium,
		Large
	}
	public sealed class PlanInput
	{
		public IReadOnlyList<RoomDescriptor> Rooms { get; set; } = Array.Empty<RoomDescriptor>();

		public int RoomsPerMedkit { get; set; } = 4;

		public RoundingMode Rounding { get; set; }

		public int MinMedkits { get; set; } = 1;

		public int MaxMedkits { get; set; }

		public int WeightSmall { get; set; } = 60;

		public int WeightMedium { get; set; } = 30;

		public int WeightLarge { get; set; } = 10;

		public bool GuaranteeVariety { get; set; } = true;

		public bool PreferDistantRooms { get; set; }

		public bool DifficultyScaling { get; set; }

		public double DifficultyFactor { get; set; } = 0.15;

		public int LevelsCompleted { get; set; }
	}
	public sealed class PlanResult
	{
		public int RoomCount { get; }

		public int TargetCount { get; }

		public int PlannedCount => Placements.Count;

		public IReadOnlyList<MedkitPlacement> Placements { get; }

		public IReadOnlyList<RoomKey> Reserve { get; }

		public string Explanation { get; }

		public IReadOnlyList<string> Notes { get; }

		public PlanResult(int roomCount, int targetCount, IReadOnlyList<MedkitPlacement>? placements, IReadOnlyList<RoomKey>? reserve, string? explanation, IReadOnlyList<string>? notes)
		{
			RoomCount = roomCount;
			TargetCount = targetCount;
			Placements = placements ?? Array.Empty<MedkitPlacement>();
			Reserve = reserve ?? Array.Empty<RoomKey>();
			Explanation = explanation ?? string.Empty;
			Notes = notes ?? Array.Empty<string>();
		}
	}
	public readonly struct RoomDescriptor
	{
		public RoomKey Key { get; }

		public RoomKind Kind { get; }

		public bool CountsToTotal { get; }

		public bool CanHostMedkit { get; }

		public int CandidateSpots { get; }

		public int DistanceFromStart { get; }

		public RoomDescriptor(RoomKey key, RoomKind kind, bool countsToTotal, bool canHostMedkit, int candidateSpots, int distanceFromStart)
		{
			Key = key;
			Kind = kind;
			CountsToTotal = countsToTotal;
			CanHostMedkit = canHostMedkit;
			CandidateSpots = candidateSpots;
			DistanceFromStart = distanceFromStart;
		}

		public override string ToString()
		{
			return Key.ToString() + " " + Kind.ToString() + " spots=" + CandidateSpots + " dist=" + DistanceFromStart;
		}
	}
	public readonly struct RoomKey : IEquatable<RoomKey>, IComparable<RoomKey>
	{
		public int GridX { get; }

		public int GridY { get; }

		public RoomKey(int gridX, int gridY)
		{
			GridX = gridX;
			GridY = gridY;
		}

		public int CompareTo(RoomKey other)
		{
			int num = GridY.CompareTo(other.GridY);
			if (num == 0)
			{
				return GridX.CompareTo(other.GridX);
			}
			return num;
		}

		public bool Equals(RoomKey other)
		{
			if (GridX == other.GridX)
			{
				return GridY == other.GridY;
			}
			return false;
		}

		public override bool Equals(object? obj)
		{
			if (obj is RoomKey other)
			{
				return Equals(other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return (GridX * 397) ^ GridY;
		}

		public static bool operator ==(RoomKey left, RoomKey right)
		{
			return left.Equals(right);
		}

		public static bool operator !=(RoomKey left, RoomKey right)
		{
			return !left.Equals(right);
		}

		public static bool operator <(RoomKey left, RoomKey right)
		{
			return left.CompareTo(right) < 0;
		}

		public static bool operator >(RoomKey left, RoomKey right)
		{
			return left.CompareTo(right) > 0;
		}

		public static bool operator <=(RoomKey left, RoomKey right)
		{
			return left.CompareTo(right) <= 0;
		}

		public static bool operator >=(RoomKey left, RoomKey right)
		{
			return left.CompareTo(right) >= 0;
		}

		public override string ToString()
		{
			return "(" + GridX + "," + GridY + ")";
		}
	}
	public enum RoomKind
	{
		Normal,
		Passage,
		DeadEnd,
		Extraction,
		StartRoom,
		Unknown,
		Special
	}
	public enum RoundingMode
	{
		Floor,
		Round,
		Ceil
	}
	public sealed class SystemRandomSource : IRandomSource
	{
		private readonly Random _random;

		public int Seed { get; }

		public SystemRandomSource(int seed)
		{
			Seed = seed;
			_random = new Random(seed);
		}

		public int Next(int minInclusive, int maxExclusive)
		{
			if (maxExclusive <= minInclusive)
			{
				return minInclusive;
			}
			return _random.Next(minInclusive, maxExclusive);
		}

		public double NextDouble()
		{
			return _random.NextDouble();
		}
	}
}

MedkitScatter.dll

Decompiled 10 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using MedkitScatter.Patches;
using MedkitScatter.Planning;
using MedkitScatter.Runtime;
using MedkitScatter.Util;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: IgnoresAccessChecksTo("Autodesk.Fbx")]
[assembly: IgnoresAccessChecksTo("Discord.Sdk")]
[assembly: IgnoresAccessChecksTo("Domain_Reload")]
[assembly: IgnoresAccessChecksTo("Facepunch.Steamworks.Win64")]
[assembly: IgnoresAccessChecksTo("FbxBuildTestAssets")]
[assembly: IgnoresAccessChecksTo("Klattersynth")]
[assembly: IgnoresAccessChecksTo("Photon3Unity3D")]
[assembly: IgnoresAccessChecksTo("PhotonChat")]
[assembly: IgnoresAccessChecksTo("PhotonRealtime")]
[assembly: IgnoresAccessChecksTo("PhotonUnityNetworking")]
[assembly: IgnoresAccessChecksTo("PhotonUnityNetworking.Utilities")]
[assembly: IgnoresAccessChecksTo("PhotonVoice.API")]
[assembly: IgnoresAccessChecksTo("PhotonVoice")]
[assembly: IgnoresAccessChecksTo("PhotonVoice.PUN")]
[assembly: IgnoresAccessChecksTo("SingularityGroup.HotReload.Runtime.Public")]
[assembly: IgnoresAccessChecksTo("Sirenix.OdinInspector.Attributes")]
[assembly: IgnoresAccessChecksTo("Sirenix.OdinInspector.Modules.Unity.Addressables")]
[assembly: IgnoresAccessChecksTo("Sirenix.OdinInspector.Modules.UnityLocalization")]
[assembly: IgnoresAccessChecksTo("Sirenix.Serialization.Config")]
[assembly: IgnoresAccessChecksTo("Sirenix.Serialization")]
[assembly: IgnoresAccessChecksTo("Sirenix.Utilities")]
[assembly: IgnoresAccessChecksTo("Unity.Addressables")]
[assembly: IgnoresAccessChecksTo("Unity.AI.Navigation")]
[assembly: IgnoresAccessChecksTo("Unity.Burst")]
[assembly: IgnoresAccessChecksTo("Unity.Burst.Unsafe")]
[assembly: IgnoresAccessChecksTo("Unity.Collections")]
[assembly: IgnoresAccessChecksTo("Unity.Collections.LowLevel.ILSupport")]
[assembly: IgnoresAccessChecksTo("Unity.Formats.Fbx.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.InputSystem")]
[assembly: IgnoresAccessChecksTo("Unity.InputSystem.ForUI")]
[assembly: IgnoresAccessChecksTo("Unity.InternalAPIEngineBridge.013")]
[assembly: IgnoresAccessChecksTo("Unity.Localization")]
[assembly: IgnoresAccessChecksTo("Unity.Mathematics")]
[assembly: IgnoresAccessChecksTo("Unity.MemoryProfiler")]
[assembly: IgnoresAccessChecksTo("Unity.Postprocessing.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.Profiling.Core")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.Core.ShaderLibrary")]
[assembly: IgnoresAccessChecksTo("Unity.RenderPipelines.ShaderGraph.ShaderGraphLibrary")]
[assembly: IgnoresAccessChecksTo("Unity.ResourceManager")]
[assembly: IgnoresAccessChecksTo("Unity.ScriptableBuildPipeline")]
[assembly: IgnoresAccessChecksTo("Unity.Splines")]
[assembly: IgnoresAccessChecksTo("Unity.TextMeshPro")]
[assembly: IgnoresAccessChecksTo("Unity.Timeline")]
[assembly: IgnoresAccessChecksTo("Unity.VisualScripting.Antlr3.Runtime")]
[assembly: IgnoresAccessChecksTo("Unity.VisualScripting.Core")]
[assembly: IgnoresAccessChecksTo("Unity.VisualScripting.Flow")]
[assembly: IgnoresAccessChecksTo("Unity.VisualScripting.State")]
[assembly: IgnoresAccessChecksTo("UnityEngine.ARModule")]
[assembly: IgnoresAccessChecksTo("UnityEngine.NVIDIAModule")]
[assembly: IgnoresAccessChecksTo("UnityEngine.UI")]
[assembly: IgnoresAccessChecksTo("websocket-sharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("AitServices")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Medkit Scatter")]
[assembly: AssemblyTitle("MedkitScatter")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.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.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 MedkitScatter
{
	internal sealed class ModConfig
	{
		private const string SectionGeneral = "General";

		private const string SectionAmount = "Amount";

		private const string SectionRooms = "Rooms";

		private const string SectionTiers = "Tiers";

		private const string SectionPlacement = "Placement";

		private readonly ConfigEntry<string> _allowedVolumeTypes;

		private List<Type>? _parsedVolumeTypes;

		private string _parsedFrom = string.Empty;

		internal ConfigEntry<bool> Enabled { get; }

		internal ConfigEntry<bool> ExtendedLogging { get; }

		internal ConfigEntry<int> FixedSeed { get; }

		internal ConfigEntry<int> MaxSpawnFailuresPerLevel { get; }

		internal ConfigEntry<int> MaxExceptionsPerSession { get; }

		internal ConfigEntry<int> RoomsPerMedkit { get; }

		internal ConfigEntry<RoundingMode> Rounding { get; }

		internal ConfigEntry<int> MinMedkits { get; }

		internal ConfigEntry<int> MaxMedkits { get; }

		internal ConfigEntry<bool> CountNormal { get; }

		internal ConfigEntry<bool> CountPassages { get; }

		internal ConfigEntry<bool> CountDeadEnds { get; }

		internal ConfigEntry<bool> CountExtraction { get; }

		internal ConfigEntry<bool> CountSpecial { get; }

		internal ConfigEntry<bool> SpawnInNormal { get; }

		internal ConfigEntry<bool> SpawnInPassages { get; }

		internal ConfigEntry<bool> SpawnInDeadEnds { get; }

		internal ConfigEntry<bool> SpawnInSpecial { get; }

		internal ConfigEntry<bool> PreferDistantRooms { get; }

		internal ConfigEntry<int> WeightSmall { get; }

		internal ConfigEntry<int> WeightMedium { get; }

		internal ConfigEntry<int> WeightLarge { get; }

		internal ConfigEntry<bool> GuaranteeVariety { get; }

		internal ConfigEntry<bool> DifficultyScaling { get; }

		internal ConfigEntry<float> DifficultyScalingFactor { get; }

		internal ConfigEntry<bool> AllowLevelPointFallback { get; }

		internal ConfigEntry<float> LevelPointOffset { get; }

		internal ConfigEntry<float> RaycastUp { get; }

		internal ConfigEntry<float> RaycastDistance { get; }

		internal ConfigEntry<int> GroundRaycastMask { get; }

		internal ConfigEntry<float> GroundOffset { get; }

		internal ConfigEntry<float> ClearanceRadius { get; }

		internal ConfigEntry<int> SpawnDelayFrames { get; }

		internal IReadOnlyList<Type> AllowedVolumeTypes
		{
			get
			{
				//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
				//IL_00a6: 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_00b3: Unknown result type (might be due to invalid IL or missing references)
				string text = _allowedVolumeTypes.Value ?? string.Empty;
				if (_parsedVolumeTypes != null && string.Equals(text, _parsedFrom, StringComparison.Ordinal))
				{
					return _parsedVolumeTypes;
				}
				List<Type> list = new List<Type>();
				string[] array = text.Split(',');
				foreach (string text2 in array)
				{
					string text3 = text2.Trim();
					if (text3.Length == 0)
					{
						continue;
					}
					bool flag = false;
					string[] names = Enum.GetNames(typeof(Type));
					foreach (string text4 in names)
					{
						if (string.Equals(text4, text3, StringComparison.OrdinalIgnoreCase))
						{
							Type item = (Type)Enum.Parse(typeof(Type), text4);
							if (!list.Contains(item))
							{
								list.Add(item);
							}
							flag = true;
							break;
						}
					}
					if (!flag)
					{
						ModLog.Warning("AllowedVolumeTypes: значение \"" + text3 + "\" не является членом ValuableVolume.Type и проигнорировано.");
					}
				}
				if (list.Count == 0)
				{
					ModLog.Warning("AllowedVolumeTypes пуст или полностью нераспознан — объёмы использоваться не будут, останется только LevelPoint.");
				}
				_parsedFrom = text;
				_parsedVolumeTypes = list;
				return list;
			}
		}

		internal ModConfig(ConfigFile file)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: 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_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Expected O, but got Unknown
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Expected O, but got Unknown
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Expected O, but got Unknown
			//IL_02a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b3: Expected O, but got Unknown
			//IL_02db: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e5: Expected O, but got Unknown
			//IL_030d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0317: Expected O, but got Unknown
			//IL_037e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0388: Expected O, but got Unknown
			//IL_03f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fd: Expected O, but got Unknown
			//IL_042c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0436: Expected O, but got Unknown
			//IL_0465: Unknown result type (might be due to invalid IL or missing references)
			//IL_046f: Expected O, but got Unknown
			//IL_04ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c4: Expected O, but got Unknown
			//IL_04f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04fd: Expected O, but got Unknown
			//IL_0521: Unknown result type (might be due to invalid IL or missing references)
			//IL_052b: Expected O, but got Unknown
			Enabled = file.Bind<bool>("General", "Enabled", true, "Полностью включить или выключить разброс аптечек.");
			ExtendedLogging = file.Bind<bool>("General", "ExtendedLogging", false, "Подробный лог плана и каждого спавна. Пишется уровнем Info, поэтому виден и в консоли, и в LogOutput.log.");
			FixedSeed = file.Bind<int>("General", "FixedSeed", 0, "0 = сид выводится из забега и имени уровня. Любое другое значение — фиксированный сид: одинаковая раскладка при каждом прогоне.");
			MaxSpawnFailuresPerLevel = file.Bind<int>("General", "MaxSpawnFailuresPerLevel", 3, new ConfigDescription("Сколько неудачных попыток размещения терпим за уровень, прежде чем прекратить расстановку на этом уровне. Счётчик сбрасывается каждый уровень.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 64), Array.Empty<object>()));
			MaxExceptionsPerSession = file.Bind<int>("General", "MaxExceptionsPerSession", 10, new ConfigDescription("После стольких пойманных исключений плагин выключается до перезапуска игры.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000), Array.Empty<object>()));
			RoomsPerMedkit = file.Bind<int>("Amount", "RoomsPerMedkit", 4, new ConfigDescription("Делитель формулы «комнат / N». Значение по умолчанию 4 — прямое прочтение требования заказчика.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 64), Array.Empty<object>()));
			Rounding = file.Bind<RoundingMode>("Amount", "RoundingMode", (RoundingMode)0, "Округление результата деления: Floor (вниз), Round (к ближайшему, половина от нуля), Ceil (вверх).");
			MinMedkits = file.Bind<int>("Amount", "MinMedkits", 1, new ConfigDescription("Нижняя граница количества аптечек на уровне.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 64), Array.Empty<object>()));
			MaxMedkits = file.Bind<int>("Amount", "MaxMedkits", 0, new ConfigDescription("Верхняя граница количества. 0 = без лимита. Если Min больше Max, побеждает Min.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 128), Array.Empty<object>()));
			CountNormal = file.Bind<bool>("Rooms", "CountNormal", true, "Учитывать обычные комнаты в знаменателе формулы.");
			CountPassages = file.Bind<bool>("Rooms", "CountPassages", true, "Учитывать коридоры в знаменателе. По умолчанию да: коридоры формируют размер уровня и должны влиять на количество аптечек.");
			CountDeadEnds = file.Bind<bool>("Rooms", "CountDeadEnds", true, "Учитывать тупики в знаменателе.");
			CountExtraction = file.Bind<bool>("Rooms", "CountExtraction", false, "Учитывать комнаты извлечения в знаменателе.");
			CountSpecial = file.Bind<bool>("Rooms", "CountSpecial", false, "Учитывать модули типа Special в знаменателе. Этот тип появился в игре после версии 0.1.2 и в исходной спецификации не описан, поэтому по умолчанию он не считается и не заселяется.");
			SpawnInNormal = file.Bind<bool>("Rooms", "SpawnInNormal", true, "Разрешить размещение в обычных комнатах.");
			SpawnInPassages = file.Bind<bool>("Rooms", "SpawnInPassages", false, "Разрешить размещение в коридорах. По умолчанию нет: лут в проходном коридоре плохо читается игроком.");
			SpawnInDeadEnds = file.Bind<bool>("Rooms", "SpawnInDeadEnds", true, "Разрешить размещение в тупиках.");
			SpawnInSpecial = file.Bind<bool>("Rooms", "SpawnInSpecial", false, "Разрешить размещение в модулях типа Special.");
			PreferDistantRooms = file.Bind<bool>("Rooms", "PreferDistantRooms", false, "Смещать выбор к комнатам подальше от стартовой.");
			WeightSmall = file.Bind<int>("Tiers", "WeightSmall", 60, new ConfigDescription("Вес аптечки на 25 HP.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000), Array.Empty<object>()));
			WeightMedium = file.Bind<int>("Tiers", "WeightMedium", 30, new ConfigDescription("Вес аптечки на 50 HP.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000), Array.Empty<object>()));
			WeightLarge = file.Bind<int>("Tiers", "WeightLarge", 10, new ConfigDescription("Вес аптечки на 100 HP.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000), Array.Empty<object>()));
			GuaranteeVariety = file.Bind<bool>("Tiers", "GuaranteeVariety", true, "При трёх и более аптечках гарантировать все три тира. ВНИМАНИЕ: это осознанно переопределяет веса для первых трёх позиций — при 5 аптечках фактическая доля Large будет не меньше 20 %, а не 10 %. Нужна честная пропорция — выключите.");
			DifficultyScaling = file.Bind<bool>("Tiers", "DifficultyScaling", false, "Смещать веса к крупным аптечкам с ростом номера уровня в забеге.");
			DifficultyScalingFactor = file.Bind<float>("Tiers", "DifficultyScalingFactor", 0.15f, new ConfigDescription("Коэффициент k: вес Large умножается на (1 + k * пройденных уровней), вес Small на столько же делится.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			_allowedVolumeTypes = file.Bind<string>("Placement", "AllowedVolumeTypes", "Small,Medium,Tiny,Big", "Типы ValuableVolume, которые можно занимать, через запятую. ПОРЯДОК ЗАДАЁТ ПРИОРИТЕТ: чем левее, тем охотнее используется. Доступные значения: Tiny, Small, Medium, Big, Wide, Tall, VeryTall.");
			AllowLevelPointFallback = file.Bind<bool>("Placement", "AllowLevelPointFallback", true, "Использовать навигационные точки уровня, если свободных объёмов в комнате нет.");
			LevelPointOffset = file.Bind<float>("Placement", "LevelPointOffset", 0.35f, new ConfigDescription("Горизонтальный сдвиг от узла навигации, метры — чтобы аптечка не стояла ровно в точке маршрута.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>()));
			RaycastUp = file.Bind<float>("Placement", "RaycastUp", 1f, new ConfigDescription("Высота начала луча над кандидатом, метры.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), Array.Empty<object>()));
			RaycastDistance = file.Bind<float>("Placement", "RaycastDistance", 3f, new ConfigDescription("Длина луча вниз, метры.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 30f), Array.Empty<object>()));
			GroundRaycastMask = file.Bind<int>("Placement", "GroundRaycastMask", -1, "LayerMask для поиска пола и проверки пересечений. -1 = все слои.");
			GroundOffset = file.Bind<float>("Placement", "GroundOffset", 0.08f, new ConfigDescription("Подъём над полом после луча, метры — чтобы аптечка не проваливалась.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			ClearanceRadius = file.Bind<float>("Placement", "ClearanceRadius", 0.45f, new ConfigDescription("Радиус проверки свободного места, метры.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			SpawnDelayFrames = file.Bind<int>("Placement", "SpawnDelayFrames", 2, new ConfigDescription("Задержка после завершения генерации, кадры. Нужна, чтобы PhysGrabObject ванильных предметов успел настроить свои коллайдеры.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 120), Array.Empty<object>()));
		}

		internal bool ShouldCount(RoomKind kind)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected I4, but got Unknown
			return (int)kind switch
			{
				0 => CountNormal.Value, 
				1 => CountPassages.Value, 
				2 => CountDeadEnds.Value, 
				3 => CountExtraction.Value, 
				6 => CountSpecial.Value, 
				_ => false, 
			};
		}

		internal bool ShouldHost(RoomKind kind)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Expected I4, but got Unknown
			return (int)kind switch
			{
				0 => SpawnInNormal.Value, 
				1 => SpawnInPassages.Value, 
				2 => SpawnInDeadEnds.Value, 
				6 => SpawnInSpecial.Value, 
				_ => false, 
			};
		}
	}
	[BepInPlugin("AitServices.MedkitScatter", "Medkit Scatter", "1.0.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "AitServices.MedkitScatter";

		public const string PluginName = "Medkit Scatter";

		public const string PluginVersion = "1.0.0";

		private const string GenerateDoneMethod = "GenerateDone";

		private const string SpawnValuableMethod = "SpawnValuable";

		private const string LegacySpawnMethod = "Spawn";

		private Harmony? _harmony;

		internal static Plugin? Instance { get; private set; }

		private void Awake()
		{
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Expected O, but got Unknown
			Instance = this;
			ModConfig config = new ModConfig(((BaseUnityPlugin)this).Config);
			ModLog.Initialize(((BaseUnityPlugin)this).Logger, config.MaxExceptionsPerSession.Value);
			ModLog.Extended = config.ExtendedLogging.Value;
			config.ExtendedLogging.SettingChanged += delegate
			{
				ModLog.Extended = config.ExtendedLogging.Value;
			};
			GameApi.Initialize();
			ModLog.Info(GameApi.Describe());
			MedkitScatterService.Initialize(config);
			_harmony = new Harmony("AitServices.MedkitScatter");
			PatchLevelGenerator(_harmony);
			PatchValuableDirector(_harmony);
			ModLog.Info("Medkit Scatter 1.0.0 загружен (host-only, game v0.4.4).");
		}

		private static void PatchLevelGenerator(Harmony harmony)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			try
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(LevelGenerator), "GenerateDone", (Type[])null, (Type[])null);
				if (methodInfo == null)
				{
					ModLog.Error("LevelGenerator.GenerateDone не найден — плагин не сможет расставлять аптечки. Версия игры не 0.4.4?");
					return;
				}
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(AccessTools.Method(typeof(LevelGeneratorPatch), "GenerateDonePostfix", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				ModLog.Detail("Патч LevelGenerator.GenerateDone применён.");
			}
			catch (Exception exception)
			{
				ModLog.CountedError("PatchLevelGenerator", exception);
			}
		}

		private static void PatchValuableDirector(Harmony harmony)
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Expected O, but got Unknown
			try
			{
				string text = "SpawnValuable";
				MethodInfo methodInfo = AccessTools.Method(typeof(ValuableDirector), "SpawnValuable", (Type[])null, (Type[])null);
				if (methodInfo == null)
				{
					text = "Spawn";
					methodInfo = AccessTools.Method(typeof(ValuableDirector), "Spawn", (Type[])null, (Type[])null);
				}
				if (methodInfo == null)
				{
					ModLog.Warning("Метод спавна ценностей у ValuableDirector не найден — работаем без маркеров занятости объёмов.");
					return;
				}
				harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(AccessTools.Method(typeof(ValuableDirectorPatch), "MarkVolume", (Type[])null, (Type[])null)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				ModLog.Detail("Патч ValuableDirector." + text + " применён.");
			}
			catch (Exception exception)
			{
				ModLog.CountedError("PatchValuableDirector", exception);
			}
		}
	}
}
namespace MedkitScatter.Util
{
	internal static class GameApi
	{
		private const BindingFlags AnyInstance = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		private static FieldInfo? _moduleTypeField;

		private static PropertyInfo? _moduleTypeProperty;

		private static FieldInfo? _levelGridField;

		private static FieldInfo? _tileTypeField;

		private static FieldInfo? _runSeedField;

		private static FieldInfo? _levelsCompletedField;

		private static FieldInfo? _levelCurrentField;

		private static Func<int>? _levelsCompletedFunc;

		private static Func<bool>? _runIsShop;

		private static Func<bool>? _runIsLobby;

		private static Func<bool>? _runIsLobbyMenu;

		private static Func<bool>? _runIsArena;

		private static Func<bool>? _runIsTutorial;

		private static Type? _itemHealthPackType;

		private static FieldInfo? _healAmountField;

		private static readonly List<string> Report = new List<string>();

		internal static string RunSeedFieldName { get; private set; } = string.Empty;

		internal static string ModuleTypeSource { get; private set; } = "none";

		internal static void Initialize()
		{
			Report.Clear();
			ResolveModuleType();
			ResolveRunState();
			ResolveRunPredicates();
			ResolveHealthPack();
		}

		internal static string Describe()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("Game API: moduleType=").Append(ModuleTypeSource);
			stringBuilder.Append(", runSeed=").Append((RunSeedFieldName.Length == 0) ? "none" : RunSeedFieldName);
			stringBuilder.Append(", levelsCompleted=").Append((_levelsCompletedField != null) ? "RunManager.levelsCompleted" : ((_levelsCompletedFunc != null) ? "SemiFunc.RunGetLevelsCompleted" : "none"));
			stringBuilder.Append(", healAmount=").Append((_healAmountField != null) ? "ok" : "none");
			stringBuilder.Append(", predicates=[").Append((_runIsShop != null) ? "Shop " : string.Empty).Append((_runIsLobby != null) ? "Lobby " : string.Empty)
				.Append((_runIsLobbyMenu != null) ? "LobbyMenu " : string.Empty)
				.Append((_runIsArena != null) ? "Arena " : string.Empty)
				.Append((_runIsTutorial != null) ? "Tutorial" : string.Empty)
				.Append(']');
			if (Report.Count > 0)
			{
				stringBuilder.Append(" | ").Append(string.Join("; ", Report.ToArray()));
			}
			return stringBuilder.ToString();
		}

		private static void ResolveModuleType()
		{
			Type typeFromHandle = typeof(Type);
			FieldInfo[] fields = typeof(Module).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (FieldInfo fieldInfo in fields)
			{
				if (fieldInfo.FieldType == typeFromHandle)
				{
					_moduleTypeField = fieldInfo;
					ModuleTypeSource = "Module." + fieldInfo.Name;
					return;
				}
			}
			PropertyInfo[] properties = typeof(Module).GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (PropertyInfo propertyInfo in properties)
			{
				if (propertyInfo.PropertyType == typeFromHandle && propertyInfo.CanRead)
				{
					_moduleTypeProperty = propertyInfo;
					ModuleTypeSource = "Module." + propertyInfo.Name + " (property)";
					return;
				}
			}
			_levelGridField = AccessTools.Field(typeof(LevelGenerator), "LevelGrid");
			if (_levelGridField == null)
			{
				Report.Add("LevelGenerator.LevelGrid не найден");
				ModuleTypeSource = "name heuristic";
				return;
			}
			Type elementType = _levelGridField.FieldType.GetElementType();
			if (elementType == null)
			{
				Report.Add("LevelGrid не массив: " + _levelGridField.FieldType.Name);
				ModuleTypeSource = "name heuristic";
				return;
			}
			_tileTypeField = AccessTools.Field(elementType, "type");
			if (_tileTypeField == null)
			{
				Report.Add(elementType.Name + ".type не найден");
				ModuleTypeSource = "name heuristic";
			}
			else
			{
				ModuleTypeSource = "LevelGrid[x,y]." + _tileTypeField.Name;
			}
		}

		internal static string? TryGetModuleTypeName(Module module, int gridX, int gridY)
		{
			if ((Object)(object)module == (Object)null)
			{
				return null;
			}
			FieldInfo moduleTypeField = _moduleTypeField;
			if (moduleTypeField != null)
			{
				return moduleTypeField.GetValue(module)?.ToString();
			}
			PropertyInfo moduleTypeProperty = _moduleTypeProperty;
			if (moduleTypeProperty != null)
			{
				return moduleTypeProperty.GetValue(module, null)?.ToString();
			}
			FieldInfo levelGridField = _levelGridField;
			FieldInfo tileTypeField = _tileTypeField;
			if (levelGridField == null || tileTypeField == null)
			{
				return null;
			}
			LevelGenerator instance = LevelGenerator.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return null;
			}
			if (!(levelGridField.GetValue(instance) is Array { Rank: 2 } array))
			{
				return null;
			}
			if (gridX < array.GetLowerBound(0) || gridX > array.GetUpperBound(0) || gridY < array.GetLowerBound(1) || gridY > array.GetUpperBound(1))
			{
				return null;
			}
			object value = array.GetValue(gridX, gridY);
			if (value != null)
			{
				return tileTypeField.GetValue(value)?.ToString();
			}
			return null;
		}

		private static void ResolveRunState()
		{
			_levelsCompletedField = AccessTools.Field(typeof(RunManager), "levelsCompleted");
			if (_levelsCompletedField != null && _levelsCompletedField.FieldType != typeof(int))
			{
				Report.Add("RunManager.levelsCompleted не int: " + _levelsCompletedField.FieldType.Name);
				_levelsCompletedField = null;
			}
			_levelsCompletedFunc = ResolveIntFunc("RunGetLevelsCompleted");
			if (_levelsCompletedField == null && _levelsCompletedFunc == null)
			{
				Report.Add("число пройденных уровней недоступно, считаем 0");
			}
			_levelCurrentField = AccessTools.Field(typeof(RunManager), "levelCurrent");
			Dictionary<string, FieldInfo> dictionary = new Dictionary<string, FieldInfo>(StringComparer.Ordinal);
			FieldInfo[] fields = typeof(RunManager).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (FieldInfo fieldInfo in fields)
			{
				if (fieldInfo.FieldType == typeof(int) && !dictionary.ContainsKey(fieldInfo.Name))
				{
					dictionary[fieldInfo.Name] = fieldInfo;
				}
			}
			string[] array = new string[5] { "runSeed", "levelSeed", "saveLevelSeed", "seed", "RandomSeed" };
			string[] array2 = array;
			foreach (string text in array2)
			{
				if (dictionary.TryGetValue(text, out var value))
				{
					_runSeedField = value;
					RunSeedFieldName = text;
					break;
				}
			}
		}

		internal static int GetLevelsCompleted()
		{
			FieldInfo levelsCompletedField = _levelsCompletedField;
			if (levelsCompletedField != null)
			{
				RunManager instance = RunManager.instance;
				if ((Object)(object)instance != (Object)null)
				{
					object value = levelsCompletedField.GetValue(instance);
					if (value is int)
					{
						return (int)value;
					}
				}
			}
			return _levelsCompletedFunc?.Invoke() ?? 0;
		}

		internal static string GetLevelName()
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			FieldInfo levelCurrentField = _levelCurrentField;
			if (levelCurrentField != null)
			{
				RunManager instance = RunManager.instance;
				if ((Object)(object)instance != (Object)null)
				{
					object? value = levelCurrentField.GetValue(instance);
					Object val = (Object)((value is Object) ? value : null);
					if (val != null && val != (Object)null)
					{
						return val.name;
					}
				}
			}
			Scene activeScene = SceneManager.GetActiveScene();
			return ((Scene)(ref activeScene)).name;
		}

		internal static int? TryGetRunSeed()
		{
			FieldInfo runSeedField = _runSeedField;
			if (runSeedField == null)
			{
				return null;
			}
			RunManager instance = RunManager.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return null;
			}
			object value = runSeedField.GetValue(instance);
			if (!(value is int))
			{
				return null;
			}
			return (int)value;
		}

		private static void ResolveRunPredicates()
		{
			_runIsShop = ResolveBoolFunc("RunIsShop");
			_runIsLobby = ResolveBoolFunc("RunIsLobby");
			_runIsLobbyMenu = ResolveBoolFunc("RunIsLobbyMenu");
			_runIsArena = ResolveBoolFunc("RunIsArena");
			_runIsTutorial = ResolveBoolFunc("RunIsTutorial");
		}

		internal static bool IsNonLevelContext()
		{
			if (!Invoke(_runIsShop) && !Invoke(_runIsLobby) && !Invoke(_runIsLobbyMenu) && !Invoke(_runIsArena))
			{
				return Invoke(_runIsTutorial);
			}
			return true;
		}

		private static bool Invoke(Func<bool>? predicate)
		{
			return predicate?.Invoke() ?? false;
		}

		private static Func<bool>? ResolveBoolFunc(string name)
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(SemiFunc), name, (Type[])null, (Type[])null);
			if (methodInfo == null || !methodInfo.IsStatic || methodInfo.ReturnType != typeof(bool) || methodInfo.GetParameters().Length != 0)
			{
				Report.Add("SemiFunc." + name + " отсутствует");
				return null;
			}
			return (Func<bool>)Delegate.CreateDelegate(typeof(Func<bool>), methodInfo);
		}

		private static Func<int>? ResolveIntFunc(string name)
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(SemiFunc), name, (Type[])null, (Type[])null);
			if (methodInfo == null || !methodInfo.IsStatic || methodInfo.ReturnType != typeof(int) || methodInfo.GetParameters().Length != 0)
			{
				return null;
			}
			return (Func<int>)Delegate.CreateDelegate(typeof(Func<int>), methodInfo);
		}

		private static void ResolveHealthPack()
		{
			_itemHealthPackType = AccessTools.TypeByName("ItemHealthPack");
			if (_itemHealthPackType == null)
			{
				Report.Add("тип ItemHealthPack не найден");
				return;
			}
			_healAmountField = AccessTools.Field(_itemHealthPackType, "healAmount");
			if (_healAmountField == null)
			{
				Report.Add("ItemHealthPack.healAmount не найден");
			}
		}

		internal static double? TryGetHealAmount(GameObject prefab)
		{
			Type itemHealthPackType = _itemHealthPackType;
			FieldInfo healAmountField = _healAmountField;
			if ((Object)(object)prefab == (Object)null || itemHealthPackType == null || healAmountField == null)
			{
				return null;
			}
			Component component = prefab.GetComponent(itemHealthPackType);
			if ((Object)(object)component == (Object)null)
			{
				return null;
			}
			object value = healAmountField.GetValue(component);
			if (value is int num)
			{
				return num;
			}
			if (value is float num2)
			{
				return num2;
			}
			if (value is double)
			{
				return (double)value;
			}
			return null;
		}
	}
	internal static class ModLog
	{
		private static ManualLogSource? _source;

		private static int _maxExceptionsPerSession = 10;

		private static int _exceptionCount;

		internal static bool Extended { get; set; }

		internal static bool SessionDisabled { get; private set; }

		internal static void Initialize(ManualLogSource source, int maxExceptionsPerSession)
		{
			_source = source;
			_maxExceptionsPerSession = Math.Max(1, maxExceptionsPerSession);
		}

		internal static void Info(string message)
		{
			ManualLogSource? source = _source;
			if (source != null)
			{
				source.LogInfo((object)message);
			}
		}

		internal static void Detail(string message)
		{
			if (Extended)
			{
				ManualLogSource? source = _source;
				if (source != null)
				{
					source.LogInfo((object)message);
				}
			}
		}

		internal static void Warning(string message)
		{
			ManualLogSource? source = _source;
			if (source != null)
			{
				source.LogWarning((object)message);
			}
		}

		internal static void Error(string message)
		{
			ManualLogSource? source = _source;
			if (source != null)
			{
				source.LogError((object)message);
			}
		}

		internal static void Debug(string message)
		{
			ManualLogSource? source = _source;
			if (source != null)
			{
				source.LogDebug((object)message);
			}
		}

		internal static void CountedError(string where, Exception exception)
		{
			_exceptionCount++;
			ManualLogSource? source = _source;
			if (source != null)
			{
				source.LogError((object)("[" + where + "] " + exception));
			}
			if (_exceptionCount >= _maxExceptionsPerSession && !SessionDisabled)
			{
				DisableForSession("поймано исключений: " + _exceptionCount + " (лимит " + _maxExceptionsPerSession + ")");
			}
		}

		internal static void DisableForSession(string reason)
		{
			SessionDisabled = true;
			ManualLogSource? source = _source;
			if (source != null)
			{
				source.LogError((object)("MedkitScatter выключен до перезапуска игры: " + reason));
			}
		}
	}
	internal static class SeedFactory
	{
		private const uint FnvOffsetBasis = 2166136261u;

		private const uint FnvPrime = 16777619u;

		internal static int Create(int fixedSeed, string levelName, int levelsCompleted, out string saltSource)
		{
			if (fixedSeed != 0)
			{
				saltSource = "FixedSeed";
				return fixedSeed;
			}
			int? num = GameApi.TryGetRunSeed();
			int value;
			if (num.HasValue)
			{
				value = num.Value;
				saltSource = "RunManager." + GameApi.RunSeedFieldName;
			}
			else
			{
				Room currentRoom = PhotonNetwork.CurrentRoom;
				string value2 = ((currentRoom != null) ? currentRoom.Name : null);
				if (string.IsNullOrEmpty(value2))
				{
					value = 0;
					saltSource = "singleplayer";
				}
				else
				{
					value = (int)StableHash(value2);
					saltSource = "photonRoom";
				}
			}
			uint hash = 2166136261u;
			hash = Mix(hash, StableHash(levelName ?? string.Empty));
			hash = Mix(hash, (uint)levelsCompleted);
			return (int)Mix(hash, (uint)value);
		}

		private static uint StableHash(string value)
		{
			uint num = 2166136261u;
			for (int i = 0; i < value.Length; i++)
			{
				num ^= value[i];
				num *= 16777619;
			}
			return num;
		}

		private static uint Mix(uint hash, uint value)
		{
			hash ^= value;
			hash *= 16777619;
			return hash;
		}
	}
}
namespace MedkitScatter.Runtime
{
	internal enum CandidateSource
	{
		ValuableVolume,
		LevelPoint
	}
	internal readonly struct Candidate
	{
		internal RoomKey Room { get; }

		internal CandidateSource Source { get; }

		internal Vector3 Position { get; }

		internal Quaternion Rotation { get; }

		internal int Priority { get; }

		internal Candidate(RoomKey room, CandidateSource source, Vector3 position, Quaternion rotation, int priority)
		{
			//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)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			Room = room;
			Source = source;
			Position = position;
			Rotation = rotation;
			Priority = priority;
		}
	}
	internal sealed class CoroutineRunner : MonoBehaviour
	{
		private static CoroutineRunner? _instance;

		internal static bool TryRun(IEnumerator routine)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			if (routine == null)
			{
				return false;
			}
			if ((Object)(object)_instance == (Object)null)
			{
				GameObject val = new GameObject("MedkitScatter_CoroutineRunner");
				((Object)val).hideFlags = (HideFlags)61;
				Object.DontDestroyOnLoad((Object)(object)val);
				_instance = val.AddComponent<CoroutineRunner>();
			}
			((MonoBehaviour)_instance).StartCoroutine(routine);
			return true;
		}
	}
	internal sealed class MedkitCatalog
	{
		private const string SmallItemName = "Item Health Pack Small";

		private const string MediumItemName = "Item Health Pack Medium";

		private const string LargeItemName = "Item Health Pack Large";

		private const string HealthPackItemTypeName = "healthPack";

		private const double ExpectedSmallHeal = 25.0;

		private const double ExpectedMediumHeal = 50.0;

		private const double ExpectedLargeHeal = 100.0;

		private Dictionary<MedkitTier, Item>? _cache;

		private StatsManager? _boundTo;

		internal Dictionary<MedkitTier, Item>? Resolve()
		{
			StatsManager instance = StatsManager.instance;
			if ((Object)(object)instance == (Object)null)
			{
				ModLog.Warning("StatsManager.instance отсутствует — уровень пропущен.");
				return null;
			}
			if (_cache != null && (Object)(object)_boundTo != (Object)null && (Object)(object)_boundTo == (Object)(object)instance)
			{
				return _cache;
			}
			Dictionary<string, Item> itemDictionary = instance.itemDictionary;
			if (itemDictionary == null || itemDictionary.Count == 0)
			{
				ModLog.Warning("StatsManager.itemDictionary пуст — уровень пропущен.");
				return null;
			}
			Dictionary<MedkitTier, Item> dictionary = ResolveByExactNames(itemDictionary) ?? ResolveByItemType(itemDictionary);
			if (dictionary == null)
			{
				return null;
			}
			ValidateHealAmounts(dictionary);
			_cache = dictionary;
			_boundTo = instance;
			return dictionary;
		}

		private static Dictionary<MedkitTier, Item>? ResolveByExactNames(Dictionary<string, Item> dictionary)
		{
			if (!dictionary.TryGetValue("Item Health Pack Small", out Item value) || (Object)(object)value == (Object)null)
			{
				return null;
			}
			if (!dictionary.TryGetValue("Item Health Pack Medium", out Item value2) || (Object)(object)value2 == (Object)null)
			{
				return null;
			}
			if (!dictionary.TryGetValue("Item Health Pack Large", out Item value3) || (Object)(object)value3 == (Object)null)
			{
				return null;
			}
			ModLog.Detail("Каталог: три аптечки найдены по точным именам.");
			return new Dictionary<MedkitTier, Item>
			{
				{
					(MedkitTier)0,
					value
				},
				{
					(MedkitTier)1,
					value2
				},
				{
					(MedkitTier)2,
					value3
				}
			};
		}

		private static Dictionary<MedkitTier, Item>? ResolveByItemType(Dictionary<string, Item> dictionary)
		{
			List<Item> list = new List<Item>();
			foreach (Item value in dictionary.Values)
			{
				if ((Object)(object)value != (Object)null && IsHealthPack(value))
				{
					list.Add(value);
				}
			}
			if (list.Count != 3)
			{
				ModLog.DisableForSession("ожидалось ровно 3 ванильных аптечки, найдено " + list.Count + " (вероятно, установлен мод, добавляющий свои). Автоопределение отключено.");
				return null;
			}
			List<KeyValuePair<double, Item>> list2 = new List<KeyValuePair<double, Item>>(3);
			foreach (Item item in list)
			{
				if (item.prefab == null || !((PrefabRef<GameObject>)(object)item.prefab).IsValid())
				{
					ModLog.DisableForSession("у аптечки \"" + ((Object)item).name + "\" невалидный PrefabRef.");
					return null;
				}
				GameObject prefab = ((PrefabRef<GameObject>)(object)item.prefab).Prefab;
				if ((Object)(object)prefab == (Object)null)
				{
					ModLog.DisableForSession("не удалось получить префаб аптечки \"" + ((Object)item).name + "\".");
					return null;
				}
				double? num = GameApi.TryGetHealAmount(prefab);
				if (!num.HasValue)
				{
					ModLog.DisableForSession("не удалось прочитать healAmount у \"" + ((Object)item).name + "\" — порядок тиров определить невозможно.");
					return null;
				}
				list2.Add(new KeyValuePair<double, Item>(num.Value, item));
			}
			list2.Sort((KeyValuePair<double, Item> a, KeyValuePair<double, Item> b) => a.Key.CompareTo(b.Key));
			ModLog.Warning("Каталог собран автоопределением по типу предмета: " + ((Object)list2[0].Value).name + "=" + Fmt(list2[0].Key) + ", " + ((Object)list2[1].Value).name + "=" + Fmt(list2[1].Key) + ", " + ((Object)list2[2].Value).name + "=" + Fmt(list2[2].Key) + ".");
			return new Dictionary<MedkitTier, Item>
			{
				{
					(MedkitTier)0,
					list2[0].Value
				},
				{
					(MedkitTier)1,
					list2[1].Value
				},
				{
					(MedkitTier)2,
					list2[2].Value
				}
			};
		}

		private static bool IsHealthPack(Item item)
		{
			return string.Equals(((object)Unsafe.As<itemType, itemType>(ref item.itemType)/*cast due to .constrained prefix*/).ToString(), "healthPack", StringComparison.Ordinal);
		}

		private static void ValidateHealAmounts(Dictionary<MedkitTier, Item> items)
		{
			Check(items, (MedkitTier)0, 25.0);
			Check(items, (MedkitTier)1, 50.0);
			Check(items, (MedkitTier)2, 100.0);
		}

		private unsafe static void Check(Dictionary<MedkitTier, Item> items, MedkitTier tier, double expected)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			if (!items.TryGetValue(tier, out Item value) || (Object)(object)value == (Object)null || value.prefab == null || !((PrefabRef<GameObject>)(object)value.prefab).IsValid())
			{
				return;
			}
			GameObject prefab = ((PrefabRef<GameObject>)(object)value.prefab).Prefab;
			if ((Object)(object)prefab == (Object)null)
			{
				return;
			}
			double? num = GameApi.TryGetHealAmount(prefab);
			if (num.HasValue)
			{
				if (Math.Abs(num.Value - expected) > 0.01)
				{
					ModLog.Warning("Аптечка " + ((object)(*(MedkitTier*)(&tier))/*cast due to .constrained prefix*/).ToString() + " (\"" + ((Object)value).name + "\") лечит на " + Fmt(num.Value) + ", а не на " + Fmt(expected) + ". Логика не меняется, но README устарел.");
				}
				else
				{
					ModLog.Detail("Аптечка " + ((object)(*(MedkitTier*)(&tier))/*cast due to .constrained prefix*/).ToString() + " = " + Fmt(num.Value) + " HP.");
				}
			}
		}

		private static string Fmt(double value)
		{
			return value.ToString("0.##", CultureInfo.InvariantCulture);
		}
	}
	internal static class MedkitScatterService
	{
		private static readonly MedkitCatalog Catalog = new MedkitCatalog();

		private static ModConfig? _config;

		private static bool _running;

		internal static void Initialize(ModConfig config)
		{
			_config = config;
		}

		internal static void OnLevelGenerated()
		{
			if (ModLog.SessionDisabled)
			{
				return;
			}
			ModConfig config = _config;
			if (config == null || !config.Enabled.Value || !SemiFunc.IsMasterClientOrSingleplayer() || !SemiFunc.RunIsLevel() || GameApi.IsNonLevelContext() || (Object)(object)LevelGenerator.Instance == (Object)null)
			{
				return;
			}
			if ((Object)(object)StatsManager.instance == (Object)null)
			{
				ModLog.Warning("StatsManager.instance отсутствует — уровень пропущен.");
				return;
			}
			if (_running)
			{
				ModLog.Detail("Размещение уже идёт, повторный вызов GenerateDone проигнорирован.");
				return;
			}
			_running = true;
			try
			{
				if (!CoroutineRunner.TryRun(Run(config)))
				{
					_running = false;
					ModLog.Warning("Не удалось запустить корутину размещения — уровень пропущен.");
				}
			}
			catch (Exception exception)
			{
				_running = false;
				ModLog.CountedError("MedkitScatterService.StartCoroutine", exception);
			}
		}

		private static IEnumerator Run(ModConfig config)
		{
			int frames = Math.Max(0, config.SpawnDelayFrames.Value);
			for (int i = 0; i < frames; i++)
			{
				yield return null;
			}
			try
			{
				Execute(config);
			}
			catch (Exception exception)
			{
				ModLog.CountedError("MedkitScatterService.Execute", exception);
			}
			finally
			{
				_running = false;
			}
		}

		private static void Execute(ModConfig config)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_017e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Unknown result type (might be due to invalid IL or missing references)
			//IL_0229: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Expected O, but got Unknown
			Stopwatch stopwatch = Stopwatch.StartNew();
			LevelGenerator instance = LevelGenerator.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			if (!instance.Generated)
			{
				ModLog.Warning("LevelGenerator.Generated == false после GenerateDone — продолжаем.");
			}
			int levelsCompleted = GameApi.GetLevelsCompleted();
			string levelName = GameApi.GetLevelName();
			string saltSource;
			int num = SeedFactory.Create(config.FixedSeed.Value, levelName, levelsCompleted, out saltSource);
			SystemRandomSource val = new SystemRandomSource(num);
			ModLog.Info("Level generated: \"" + levelName + "\" (levelsCompleted=" + levelsCompleted + ", seed=" + num + " via " + saltSource + ")");
			RoomKey? startRoomKey;
			List<ModuleEntry> list = RoomIndexBuilder.CollectModules(out startRoomKey);
			if (list.Count == 0)
			{
				ModLog.Info("Модулей на сцене не найдено — 0 аптечек размещено.");
				return;
			}
			ModLog.Detail("Rooms: " + list.Count + " modules | " + RoomIndexBuilder.Histogram(list));
			SpawnPointResolver spawnPointResolver = new SpawnPointResolver(config);
			Dictionary<RoomKey, List<Candidate>> candidates = spawnPointResolver.Prescan(list);
			ModLog.Detail("Candidates: volumes=" + spawnPointResolver.VolumeCandidates + " free, levelPoints=" + spawnPointResolver.LevelPointCandidates + " fallback");
			List<RoomDescriptor> rooms = RoomIndexBuilder.BuildDescriptors(list, candidates, config, startRoomKey);
			PlanInput val2 = new PlanInput
			{
				Rooms = rooms,
				RoomsPerMedkit = config.RoomsPerMedkit.Value,
				Rounding = config.Rounding.Value,
				MinMedkits = config.MinMedkits.Value,
				MaxMedkits = config.MaxMedkits.Value,
				WeightSmall = config.WeightSmall.Value,
				WeightMedium = config.WeightMedium.Value,
				WeightLarge = config.WeightLarge.Value,
				GuaranteeVariety = config.GuaranteeVariety.Value,
				PreferDistantRooms = config.PreferDistantRooms.Value,
				DifficultyScaling = config.DifficultyScaling.Value,
				DifficultyFactor = config.DifficultyScalingFactor.Value,
				LevelsCompleted = levelsCompleted
			};
			PlanResult val3 = MedkitPlanner.Plan(val2, (IRandomSource)(object)val);
			ModLog.Detail(val3.Explanation);
			foreach (string note in val3.Notes)
			{
				ModLog.Warning("Планирование: " + note);
			}
			if (val3.PlannedCount == 0)
			{
				ModLog.Info("Spawned 0/" + val3.TargetCount + " medkits: подходящих комнат нет.");
				return;
			}
			ModLog.Detail("Plan: " + Describe(val3));
			Dictionary<MedkitTier, Item> dictionary = Catalog.Resolve();
			if (dictionary == null)
			{
				ModLog.Info("Spawned 0/" + val3.TargetCount + " medkits: каталог аптечек недоступен.");
			}
			else
			{
				PlaceAll(config, val3, spawnPointResolver, dictionary, (IRandomSource)(object)val, stopwatch);
			}
		}

		private unsafe static void PlaceAll(ModConfig config, PlanResult plan, SpawnPointResolver resolver, Dictionary<MedkitTier, Item> items, IRandomSource rng, Stopwatch stopwatch)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: 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_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_020f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0211: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Invalid comparison between Unknown and I4
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			Queue<RoomKey> queue = new Queue<RoomKey>(plan.Reserve);
			int value = config.MaxSpawnFailuresPerLevel.Value;
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			int num5 = 0;
			bool flag = false;
			bool flag2 = false;
			foreach (MedkitPlacement placement in plan.Placements)
			{
				MedkitPlacement current = placement;
				RoomKey room = ((MedkitPlacement)(ref current)).Room;
				MedkitTier tier = ((MedkitPlacement)(ref current)).Tier;
				while (true)
				{
					bool flag3 = false;
					if (resolver.TryResolvePosition(room, rng, out var position, out var rotation, out var source))
					{
						if (!items.TryGetValue(tier, out Item value2) || (Object)(object)value2 == (Object)null)
						{
							if (!flag2)
							{
								flag2 = true;
								ModLog.Warning("Нет предмета для тира " + ((object)(*(MedkitTier*)(&tier))/*cast due to .constrained prefix*/).ToString() + " — позиции пропускаются.");
							}
							break;
						}
						GameObject val = NetworkSpawner.Spawn(value2, position, rotation);
						if ((Object)(object)val != (Object)null)
						{
							num++;
							flag3 = true;
							if ((int)tier != 0)
							{
								if ((int)tier == 1)
								{
									num4++;
								}
								else
								{
									num5++;
								}
							}
							else
							{
								num3++;
							}
							ModLog.Detail("  room " + ((object)(*(RoomKey*)(&room))/*cast due to .constrained prefix*/).ToString() + " -> " + source.ToString() + " " + ((object)(*(MedkitTier*)(&tier))/*cast due to .constrained prefix*/).ToString() + " at " + Format(position));
						}
					}
					if (flag3)
					{
						break;
					}
					num2++;
					if (num2 > value)
					{
						ModLog.Warning("Превышен MaxSpawnFailuresPerLevel (" + value + ") — расстановка на этом уровне прекращена.");
						flag = true;
						break;
					}
					if (queue.Count == 0)
					{
						ModLog.Warning("Комната " + ((object)(*(RoomKey*)(&room))/*cast due to .constrained prefix*/).ToString() + ": валидной позиции нет, резерв исчерпан.");
						break;
					}
					RoomKey val2 = queue.Dequeue();
					ModLog.Warning("Комната " + ((object)(*(RoomKey*)(&room))/*cast due to .constrained prefix*/).ToString() + ": валидной позиции нет, переходим к резервной " + ((object)(*(RoomKey*)(&val2))/*cast due to .constrained prefix*/).ToString() + ".");
					room = val2;
				}
				if (flag)
				{
					break;
				}
			}
			stopwatch.Stop();
			ModLog.Info("Spawned " + num + "/" + plan.TargetCount + " medkits (Small=" + num3 + " Medium=" + num4 + " Large=" + num5 + ") in " + stopwatch.ElapsedMilliseconds + " ms");
		}

		private static string Describe(PlanResult plan)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: 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_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_004f: Unknown result type (might be due to invalid IL or missing references)
			StringBuilder stringBuilder = new StringBuilder();
			for (int i = 0; i < plan.Placements.Count; i++)
			{
				if (i > 0)
				{
					stringBuilder.Append(" | ");
				}
				MedkitPlacement val = plan.Placements[i];
				StringBuilder stringBuilder2 = stringBuilder.Append(((MedkitPlacement)(ref val)).Room).Append(' ');
				val = plan.Placements[i];
				stringBuilder2.Append(((MedkitPlacement)(ref val)).Tier);
			}
			return stringBuilder.ToString();
		}

		private static string Format(Vector3 position)
		{
			return "(" + position.x.ToString("0.00", CultureInfo.InvariantCulture) + ", " + position.y.ToString("0.00", CultureInfo.InvariantCulture) + ", " + position.z.ToString("0.00", CultureInfo.InvariantCulture) + ")";
		}
	}
	internal static class NetworkSpawner
	{
		internal static GameObject? Spawn(Item item, Vector3 position, Quaternion rotation)
		{
			//IL_0057: 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_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)item == (Object)null)
			{
				return null;
			}
			if (item.prefab == null || !((PrefabRef<GameObject>)(object)item.prefab).IsValid())
			{
				ModLog.Warning("PrefabRef невалиден у предмета \"" + ((Object)item).name + "\", пропускаем.");
				return null;
			}
			if (!SemiFunc.IsMasterClientOrSingleplayer())
			{
				return null;
			}
			if (SemiFunc.IsMultiplayer())
			{
				return PhotonNetwork.InstantiateRoomObject(((PrefabRef<GameObject>)(object)item.prefab).ResourcePath, position, rotation, (byte)0, (object[])null);
			}
			GameObject prefab = ((PrefabRef<GameObject>)(object)item.prefab).Prefab;
			if ((Object)(object)prefab == (Object)null)
			{
				ModLog.Warning("Префаб не резолвится у предмета \"" + ((Object)item).name + "\", пропускаем.");
				return null;
			}
			return Object.Instantiate<GameObject>(prefab, position, rotation);
		}
	}
	internal readonly struct ModuleEntry
	{
		internal RoomKey Key { get; }

		internal Module ModuleRef { get; }

		internal RoomKind Kind { get; }

		internal ModuleEntry(RoomKey key, Module moduleRef, RoomKind kind)
		{
			//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)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			Key = key;
			ModuleRef = moduleRef;
			Kind = kind;
		}
	}
	internal static class RoomIndexBuilder
	{
		private const string TypeNormal = "Normal";

		private const string TypePassage = "Passage";

		private const string TypeDeadEnd = "DeadEnd";

		private const string TypeExtraction = "Extraction";

		private const string TypeSpecial = "Special";

		private const string HintPassage = "Passage";

		private const string HintDeadEndSpaced = "Dead End";

		private const string HintDeadEnd = "DeadEnd";

		private const string HintExtraction = "Extraction";

		private const string HintStartRoom = "Start Room";

		internal static List<ModuleEntry> CollectModules(out RoomKey? startRoomKey)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: 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_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Invalid comparison between Unknown and I4
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			startRoomKey = null;
			Module[] array = Object.FindObjectsOfType<Module>();
			List<ModuleEntry> list = new List<ModuleEntry>(array.Length);
			HashSet<RoomKey> hashSet = new HashSet<RoomKey>();
			bool heuristicWarned = false;
			int num = 0;
			Module[] array2 = array;
			RoomKey val2 = default(RoomKey);
			foreach (Module val in array2)
			{
				if (!((Object)(object)val == (Object)null))
				{
					((RoomKey)(ref val2))..ctor(val.GridX, val.GridY);
					if (!hashSet.Add(val2))
					{
						num++;
					}
					else
					{
						list.Add(new ModuleEntry(val2, val, Classify(val, val2, ref heuristicWarned)));
					}
				}
			}
			if (num > 0)
			{
				ModLog.Warning("Пропущено модулей с дублирующимся RoomKey: " + num + ".");
			}
			list.Sort(delegate(ModuleEntry a, ModuleEntry b)
			{
				//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_000c: Unknown result type (might be due to invalid IL or missing references)
				RoomKey key = a.Key;
				return ((RoomKey)(ref key)).CompareTo(b.Key);
			});
			foreach (ModuleEntry item in list)
			{
				if ((int)item.Kind == 4)
				{
					startRoomKey = item.Key;
					break;
				}
			}
			return list;
		}

		private static RoomKind Classify(Module module, RoomKey key, ref bool heuristicWarned)
		{
			if (!module.StartRoom)
			{
				string text = GameApi.TryGetModuleTypeName(module, ((RoomKey)(ref key)).GridX, ((RoomKey)(ref key)).GridY);
				if (!string.IsNullOrEmpty(text))
				{
					switch (text)
					{
					case "Normal":
						return (RoomKind)0;
					case "Passage":
						return (RoomKind)1;
					case "DeadEnd":
						return (RoomKind)2;
					case "Extraction":
						return (RoomKind)3;
					case "Special":
						return (RoomKind)6;
					default:
						if (!heuristicWarned)
						{
							heuristicWarned = true;
							ModLog.Warning("Неизвестный член Module.Type: \"" + text + "\". Такие комнаты не считаются и не заселяются.");
						}
						return (RoomKind)5;
					}
				}
				if (!heuristicWarned)
				{
					heuristicWarned = true;
					ModLog.Warning("Тип модуля не читается через игровой API (" + GameApi.ModuleTypeSource + "), включена эвристика по имени объекта. Точность раскладки снижена.");
				}
				string name = ((Object)((Component)module).gameObject).name;
				if (name.IndexOf("Start Room", StringComparison.OrdinalIgnoreCase) < 0)
				{
					if (name.IndexOf("Extraction", StringComparison.OrdinalIgnoreCase) < 0)
					{
						if (name.IndexOf("Dead End", StringComparison.OrdinalIgnoreCase) < 0 && name.IndexOf("DeadEnd", StringComparison.OrdinalIgnoreCase) < 0)
						{
							if (name.IndexOf("Passage", StringComparison.OrdinalIgnoreCase) < 0)
							{
								return (RoomKind)5;
							}
							return (RoomKind)1;
						}
						return (RoomKind)2;
					}
					return (RoomKind)3;
				}
				return (RoomKind)4;
			}
			return (RoomKind)4;
		}

		internal static List<RoomDescriptor> BuildDescriptors(List<ModuleEntry> modules, Dictionary<RoomKey, List<Candidate>> candidates, ModConfig config, RoomKey? startRoomKey)
		{
			//IL_0020: 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_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: 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_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			List<RoomDescriptor> list = new List<RoomDescriptor>(modules.Count);
			foreach (ModuleEntry module in modules)
			{
				List<Candidate> value;
				int num = (candidates.TryGetValue(module.Key, out value) ? value.Count : 0);
				list.Add(new RoomDescriptor(module.Key, module.Kind, config.ShouldCount(module.Kind), config.ShouldHost(module.Kind), num, GridDistanceToStart(module.Key, startRoomKey)));
			}
			return list;
		}

		private static int GridDistanceToStart(RoomKey key, RoomKey? startRoomKey)
		{
			//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)
			if (!startRoomKey.HasValue)
			{
				return 0;
			}
			RoomKey value = startRoomKey.Value;
			return Math.Abs(((RoomKey)(ref key)).GridX - ((RoomKey)(ref value)).GridX) + Math.Abs(((RoomKey)(ref key)).GridY - ((RoomKey)(ref value)).GridY);
		}

		internal unsafe static string Histogram(List<ModuleEntry> modules)
		{
			//IL_001a: 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_0077: 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)
			Dictionary<RoomKind, int> dictionary = new Dictionary<RoomKind, int>();
			foreach (ModuleEntry module in modules)
			{
				dictionary.TryGetValue(module.Kind, out var value);
				dictionary[module.Kind] = value + 1;
			}
			List<string> list = new List<string>();
			RoomKind[] array = (RoomKind[])Enum.GetValues(typeof(RoomKind));
			for (int i = 0; i < array.Length; i++)
			{
				RoomKind key = array[i];
				if (dictionary.TryGetValue(key, out var value2) && value2 > 0)
				{
					list.Add(((object)(*(RoomKind*)(&key))/*cast due to .constrained prefix*/).ToString() + "=" + value2);
				}
			}
			return string.Join(" ", list.ToArray());
		}
	}
	internal sealed class SpawnPointResolver
	{
		private const int LevelPointPriority = 1000;

		private readonly ModConfig _config;

		private readonly Dictionary<RoomKey, List<Candidate>> _byRoom = new Dictionary<RoomKey, List<Candidate>>();

		internal int VolumeCandidates { get; private set; }

		internal int LevelPointCandidates { get; private set; }

		internal SpawnPointResolver(ModConfig config)
		{
			_config = config;
		}

		internal Dictionary<RoomKey, List<Candidate>> Prescan(List<ModuleEntry> modules)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			_byRoom.Clear();
			VolumeCandidates = 0;
			LevelPointCandidates = 0;
			Dictionary<int, RoomKey> dictionary = new Dictionary<int, RoomKey>(modules.Count);
			foreach (ModuleEntry module in modules)
			{
				if ((Object)(object)module.ModuleRef != (Object)null)
				{
					dictionary[((Object)module.ModuleRef).GetInstanceID()] = module.Key;
				}
			}
			CollectVolumes(dictionary);
			if (_config.AllowLevelPointFallback.Value)
			{
				CollectLevelPoints(dictionary);
			}
			foreach (List<Candidate> value in _byRoom.Values)
			{
				value.Sort(CompareCandidates);
			}
			return _byRoom;
		}

		private void CollectVolumes(Dictionary<int, RoomKey> moduleKeys)
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			ValuableVolume[] array = Object.FindObjectsOfType<ValuableVolume>();
			IReadOnlyList<Type> allowedVolumeTypes = _config.AllowedVolumeTypes;
			ValuableVolume[] array2 = array;
			foreach (ValuableVolume val in array2)
			{
				if ((Object)(object)val == (Object)null || !((Behaviour)val).isActiveAndEnabled || !((Component)val).gameObject.activeInHierarchy || (Object)(object)((Component)val).gameObject.GetComponent<UsedVolumeMarker>() != (Object)null || (Object)(object)((Component)((Component)val).transform).GetComponentInParent<ValuablePropSwitch>() != (Object)null)
				{
					continue;
				}
				int num = IndexOf(allowedVolumeTypes, val.VolumeType);
				if (num >= 0)
				{
					Module componentInParent = ((Component)val).GetComponentInParent<Module>();
					if (!((Object)(object)componentInParent == (Object)null) && moduleKeys.TryGetValue(((Object)componentInParent).GetInstanceID(), out var value))
					{
						Transform transform = ((Component)val).transform;
						Add(value, new Candidate(value, CandidateSource.ValuableVolume, transform.position, transform.rotation, num));
						VolumeCandidates++;
					}
				}
			}
		}

		private void CollectLevelPoints(Dictionary<int, RoomKey> moduleKeys)
		{
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: 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)
			List<LevelPoint> list = SemiFunc.LevelPointsGetAll();
			if (list == null)
			{
				return;
			}
			foreach (LevelPoint item in list)
			{
				if (!((Object)(object)item == (Object)null) && !item.Truck && !item.inStartRoom && !item.ModuleConnect)
				{
					RoomVolume room = item.Room;
					Module val = (((Object)(object)room != (Object)null) ? room.Module : ((Component)item).GetComponentInParent<Module>());
					if ((Object)(object)val == (Object)null)
					{
						val = ((Component)item).GetComponentInParent<Module>();
					}
					if (!((Object)(object)val == (Object)null) && moduleKeys.TryGetValue(((Object)val).GetInstanceID(), out var value))
					{
						Add(value, new Candidate(value, CandidateSource.LevelPoint, ((Component)item).transform.position, Quaternion.identity, 1000));
						LevelPointCandidates++;
					}
				}
			}
		}

		internal bool TryResolvePosition(RoomKey room, IRandomSource rng, out Vector3 position, out Quaternion rotation, out CandidateSource source)
		{
			//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_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_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: 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_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: 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_00ae: 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_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: 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)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			position = Vector3.zero;
			rotation = Quaternion.identity;
			source = CandidateSource.ValuableVolume;
			if (!_byRoom.TryGetValue(room, out List<Candidate> value))
			{
				return false;
			}
			foreach (Candidate item in value)
			{
				Vector3 val = item.Position;
				if (item.Source == CandidateSource.LevelPoint && _config.LevelPointOffset.Value > 0f)
				{
					double num = rng.NextDouble() * Math.PI * 2.0;
					float value2 = _config.LevelPointOffset.Value;
					val += new Vector3((float)Math.Cos(num), 0f, (float)Math.Sin(num)) * value2;
				}
				if (TryFindGround(val, out var grounded) && IsClear(grounded))
				{
					position = grounded;
					rotation = Quaternion.Euler(0f, (float)rng.Next(0, 360), 0f);
					source = item.Source;
					return true;
				}
			}
			return false;
		}

		private bool TryFindGround(Vector3 candidate, out Vector3 grounded)
		{
			//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_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: 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_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: 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)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			grounded = Vector3.zero;
			Vector3 val = candidate + Vector3.up * _config.RaycastUp.Value;
			RaycastHit[] array = Physics.RaycastAll(val, Vector3.down, _config.RaycastDistance.Value, _config.GroundRaycastMask.Value, (QueryTriggerInteraction)1);
			if (array.Length == 0)
			{
				return false;
			}
			Array.Sort(array, (RaycastHit a, RaycastHit b) => ((RaycastHit)(ref a)).distance.CompareTo(((RaycastHit)(ref b)).distance));
			RaycastHit[] array2 = array;
			for (int num = 0; num < array2.Length; num++)
			{
				RaycastHit val2 = array2[num];
				Collider collider = ((RaycastHit)(ref val2)).collider;
				if (!((Object)(object)collider == (Object)null) && !collider.isTrigger && !((Object)(object)((Component)collider).GetComponentInParent<PhysGrabObject>() != (Object)null) && !((Object)(object)((Component)collider).GetComponentInParent<ValuableObject>() != (Object)null) && !((Object)(object)((Component)collider).GetComponentInParent<PlayerAvatar>() != (Object)null))
				{
					grounded = ((RaycastHit)(ref val2)).point + Vector3.up * _config.GroundOffset.Value;
					return true;
				}
			}
			return false;
		}

		private bool IsClear(Vector3 position)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			float value = _config.ClearanceRadius.Value;
			if (value <= 0f)
			{
				return true;
			}
			Collider[] array = Physics.OverlapSphere(position, value, _config.GroundRaycastMask.Value, (QueryTriggerInteraction)1);
			Collider[] array2 = array;
			foreach (Collider val in array2)
			{
				if (!((Object)(object)val == (Object)null) && !val.isTrigger)
				{
					if ((Object)(object)((Component)val).GetComponentInParent<ValuableObject>() != (Object)null)
					{
						return false;
					}
					if ((Object)(object)((Component)val).GetComponentInParent<ItemAttributes>() != (Object)null)
					{
						return false;
					}
					if ((Object)(object)((Component)val).GetComponentInParent<PlayerAvatar>() != (Object)null)
					{
						return false;
					}
				}
			}
			return true;
		}

		private void Add(RoomKey key, Candidate candidate)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			if (!_byRoom.TryGetValue(key, out List<Candidate> value))
			{
				value = new List<Candidate>();
				_byRoom[key] = value;
			}
			value.Add(candidate);
		}

		private static int IndexOf(IReadOnlyList<Type> allowed, Type value)
		{
			//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)
			for (int i = 0; i < allowed.Count; i++)
			{
				if (allowed[i] == value)
				{
					return i;
				}
			}
			return -1;
		}

		private static int CompareCandidates(Candidate left, Candidate right)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: 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_004a: 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)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			int num = left.Priority.CompareTo(right.Priority);
			if (num != 0)
			{
				return num;
			}
			int num2 = left.Position.x.CompareTo(right.Position.x);
			if (num2 != 0)
			{
				return num2;
			}
			int num3 = left.Position.y.CompareTo(right.Position.y);
			if (num3 == 0)
			{
				return left.Position.z.CompareTo(right.Position.z);
			}
			return num3;
		}
	}
	internal sealed class UsedVolumeMarker : MonoBehaviour
	{
	}
}
namespace MedkitScatter.Patches
{
	internal static class LevelGeneratorPatch
	{
		internal static void GenerateDonePostfix()
		{
			try
			{
				MedkitScatterService.OnLevelGenerated();
			}
			catch (Exception exception)
			{
				ModLog.CountedError("LevelGeneratorPatch.GenerateDonePostfix", exception);
			}
		}
	}
	internal static class ValuableDirectorPatch
	{
		internal static void MarkVolume(object[] __args)
		{
			try
			{
				if (__args == null)
				{
					return;
				}
				foreach (object obj in __args)
				{
					ValuableVolume val = (ValuableVolume)((obj is ValuableVolume) ? obj : null);
					if (val != null && !((Object)(object)val == (Object)null))
					{
						if ((Object)(object)((Component)val).gameObject.GetComponent<UsedVolumeMarker>() == (Object)null)
						{
							((Component)val).gameObject.AddComponent<UsedVolumeMarker>();
						}
						break;
					}
				}
			}
			catch (Exception exception)
			{
				ModLog.CountedError("ValuableDirectorPatch.MarkVolume", exception);
			}
		}
	}
}