Decompiled source of WaveRun v0.5.7

plugins/MuckWaveRun.dll

Decompiled 2 hours ago
using System;
using System.Collections;
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 BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using MuckWaveRun.Core;
using MuckWaveRun.Game;
using MuckWaveRun.Patches;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.SceneManagement;

[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("MuckWaveRun")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("A Wave Run lobby game mode: an endless Crab Champions style run with Risk of Rain 2 scaling, a boss every 5th round, an island that regrows each round, and a new themed island every 5 rounds. Every player needs it.")]
[assembly: AssemblyFileVersion("0.5.7.0")]
[assembly: AssemblyInformationalVersion("0.5.7+c5beb045c5fc22e60a856738e4f715e40948986f")]
[assembly: AssemblyProduct("Wave Run")]
[assembly: AssemblyTitle("MuckWaveRun")]
[assembly: InternalsVisibleTo("MuckWaveRun.Tests")]
[assembly: AssemblyVersion("0.5.7.0")]
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;
		}
	}
	internal static class IsExternalInit
	{
	}
}
namespace MuckWaveRun
{
	internal static class RetiredClearKeys
	{
		public static List<(string Key, string Raw)> Drop(ConfigFile cfg)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			List<(string, string)> list = new List<(string, string)>();
			try
			{
				if (!(AccessTools.Property(typeof(ConfigFile), "OrphanedEntries")?.GetValue(cfg, null) is Dictionary<ConfigDefinition, string> dictionary))
				{
					return list;
				}
				(string, string, float)[] retiredClearKeys = LegacyConfig.RetiredClearKeys;
				for (int i = 0; i < retiredClearKeys.Length; i++)
				{
					(string, string, float) tuple = retiredClearKeys[i];
					string item = tuple.Item1;
					string item2 = tuple.Item2;
					ConfigDefinition key = new ConfigDefinition(item, item2);
					if (dictionary.TryGetValue(key, out var value))
					{
						dictionary.Remove(key);
						list.Add((item2, value));
					}
				}
				if (list.Count > 0)
				{
					cfg.Save();
				}
				return list;
			}
			catch (Exception)
			{
				return new List<(string, string)>();
			}
		}
	}
	internal sealed class ConfigurationManagerAttributes
	{
		public bool? Browsable;
	}
	internal sealed class WaveRunConfig
	{
		private const string HostOnly = " Only the host's value is used.";

		public readonly ConfigEntry<int> LegacyWaveCount;

		public readonly ConfigEntry<int> RoundCount;

		public readonly ConfigEntry<int> BossEvery;

		public readonly ConfigEntry<float> FirstPrepSeconds;

		public readonly ConfigEntry<float> PrepSeconds;

		public readonly ConfigEntry<int> MaxAliveBase;

		public readonly ConfigEntry<int> MaxAlivePerPlayer;

		public readonly ConfigEntry<int> MaxAliveCap;

		public readonly ConfigEntry<int> HuntBelow;

		public readonly ConfigEntry<float> HuntDelaySeconds;

		public readonly ConfigEntry<float> WarpAfterSeconds;

		public readonly ConfigEntry<float> WarpMinDistance;

		public readonly ConfigEntry<float> WarpMaxDistance;

		public readonly ConfigEntry<int> WarpAttempts;

		public readonly ConfigEntry<bool> WarpViewRequired;

		public readonly ConfigEntry<float> LookSyncSeconds;

		private readonly List<(string Key, string Raw)> _retired;

		public readonly ConfigEntry<int> DaysPerWave;

		public readonly ConfigEntry<int> VanillaDayCap;

		public readonly ConfigEntry<float> TimeScaling;

		public readonly ConfigEntry<float> RoundGrowth;

		public readonly ConfigEntry<float> HpPerLevel;

		public readonly ConfigEntry<float> DamagePerLevel;

		public readonly ConfigEntry<int> MaxLevel;

		public readonly ConfigEntry<int> MobBase;

		public readonly ConfigEntry<float> MobGrowth;

		public readonly ConfigEntry<int> MaxMobsPerPlayer;

		public readonly ConfigEntry<int> LegacyBaseMobs;

		public readonly ConfigEntry<float> LegacyMobsPerCoefficient;

		public readonly ConfigEntry<float> LegacyExtraPlayerMobFactor;

		public readonly ConfigEntry<int> LegacyMaxMobsPerRound;

		public readonly ConfigEntry<bool> BossBuff;

		public readonly ConfigEntry<bool> BossArena;

		public readonly ConfigEntry<float> ArenaBossDistance;

		public readonly ConfigEntry<bool> ResetResources;

		public readonly ConfigEntry<bool> ResetPickups;

		public readonly ConfigEntry<bool> WaveClearPowerup;

		public readonly ConfigEntry<bool> BossPowerup;

		public readonly ConfigEntry<float> HudScale;

		public readonly ConfigEntry<HudAnchor> HudPosition;

		public readonly ConfigEntry<HudAnchor> LegacyHudAnchor;

		public readonly ConfigEntry<float> HudTop;

		public readonly ConfigEntry<bool> EnemyMarkers;

		public readonly ConfigEntry<bool> MapRotation;

		public readonly ConfigEntry<int> RoundsPerMap;

		public readonly ConfigEntry<string> MapOrder;

		public readonly ConfigEntry<bool> ShuffleMaps;

		public readonly ConfigEntry<float> SwitchDelaySeconds;

		public readonly ConfigEntry<float> MapPrepSeconds;

		public readonly ConfigEntry<float> AckTimeoutSeconds;

		public readonly ConfigEntry<bool> VerboseLogging;

		public readonly ConfigEntry<KeyCode> DebugRegrowKey;

		public readonly ConfigEntry<KeyCode> DebugMapKey;

		public readonly ConfigEntry<bool> LogMapTimings;

		public readonly ConfigEntry<string> FailMapStep;

		public WaveRunConfig(ConfigFile cfg)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Expected O, but got Unknown
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Expected O, but got Unknown
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Expected O, but got Unknown
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Expected O, but got Unknown
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Expected O, but got Unknown
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Expected O, but got Unknown
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Expected O, but got Unknown
			//IL_01e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Expected O, but got Unknown
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Expected O, but got Unknown
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Expected O, but got Unknown
			//IL_0290: Unknown result type (might be due to invalid IL or missing references)
			//IL_029a: Expected O, but got Unknown
			//IL_02ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d4: Expected O, but got Unknown
			//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0307: Expected O, but got Unknown
			//IL_0357: Unknown result type (might be due to invalid IL or missing references)
			//IL_0361: Expected O, but got Unknown
			//IL_0389: Unknown result type (might be due to invalid IL or missing references)
			//IL_0393: Expected O, but got Unknown
			//IL_03bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c6: Expected O, but got Unknown
			//IL_03f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0400: Expected O, but got Unknown
			//IL_0430: Unknown result type (might be due to invalid IL or missing references)
			//IL_043a: Expected O, but got Unknown
			//IL_046a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0474: Expected O, but got Unknown
			//IL_04a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ae: Expected O, but got Unknown
			//IL_04d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e1: Expected O, but got Unknown
			//IL_050a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0514: Expected O, but got Unknown
			//IL_0544: Unknown result type (might be due to invalid IL or missing references)
			//IL_054e: Expected O, but got Unknown
			//IL_057b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0585: Expected O, but got Unknown
			//IL_065f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0669: Expected O, but got Unknown
			//IL_0712: Unknown result type (might be due to invalid IL or missing references)
			//IL_071c: Expected O, but got Unknown
			//IL_076a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0774: Expected O, but got Unknown
			//IL_07a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_07ad: Expected O, but got Unknown
			//IL_0813: Unknown result type (might be due to invalid IL or missing references)
			//IL_081d: Expected O, but got Unknown
			//IL_089d: Unknown result type (might be due to invalid IL or missing references)
			//IL_08a7: Expected O, but got Unknown
			//IL_08d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_08e1: Expected O, but got Unknown
			//IL_0911: Unknown result type (might be due to invalid IL or missing references)
			//IL_091b: Expected O, but got Unknown
			RunRules runRules = new RunRules();
			LegacyWaveCount = cfg.Bind<int>("Waves", "WaveCount", 5, new ConfigDescription("Removed in 0.2.0 and not used. Set RoundCount instead (0 is endless). A value other than 5 logs a warning at launch.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Browsable = false
				}
			}));
			RoundCount = cfg.Bind<int>("Waves", "RoundCount", runRules.RoundCount, new ConfigDescription("0 is endless: rounds go on until everyone is dead. Above 0, the run has this many rounds, the last is a boss, and killing it wins. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
			BossEvery = cfg.Bind<int>("Waves", "BossEvery", runRules.BossEvery, new ConfigDescription("Every round divisible by this is a boss round: one random vanilla boss (never the Dragon), no other mobs. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(2, 50), Array.Empty<object>()));
			FirstPrepSeconds = cfg.Bind<float>("Waves", "FirstPrepSeconds", runRules.FirstPrepSeconds, new ConfigDescription("Daytime before round 1, for gathering and crafting on the fresh island. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(15f, 600f), Array.Empty<object>()));
			PrepSeconds = cfg.Bind<float>("Waves", "PrepSeconds", runRules.PrepSeconds, new ConfigDescription("Daytime before every later round, after the island regrows. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 600f), Array.Empty<object>()));
			MaxAliveBase = cfg.Bind<int>("Waves", "MaxAliveBase", runRules.MaxAliveBase, new ConfigDescription("Round mobs alive at once are this plus MaxAlivePerPlayer per player, at most MaxAliveCap. The rest of the round waits its turn. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			MaxAlivePerPlayer = cfg.Bind<int>("Waves", "MaxAlivePerPlayer", runRules.MaxAlivePerPlayer, new ConfigDescription("Round mobs alive at once added per player alive when the round's combat starts. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 50), Array.Empty<object>()));
			MaxAliveCap = cfg.Bind<int>("Waves", "MaxAliveCap", runRules.MaxAliveCap, new ConfigDescription("Most round mobs alive at once, whatever the lobby. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 200), Array.Empty<object>()));
			HuntBelow = cfg.Bind<int>("Clear", "HuntBelow", runRules.HuntBelow, new ConfigDescription("A round has no time limit: it ends when every enemy it spawned is dead. Once all have spawned and at most this many are left for HuntDelaySeconds, every player sees a marker on each one. 0 turns the markers and the straggler warp off. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 20), Array.Empty<object>()));
			HuntDelaySeconds = cfg.Bind<float>("Clear", "HuntDelaySeconds", runRules.HuntDelaySeconds, new ConfigDescription("Seconds with at most HuntBelow enemies left before they are marked. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 300f), Array.Empty<object>()));
			WarpAfterSeconds = cfg.Bind<float>("Clear", "WarpAfterSeconds", runRules.WarpAfterSeconds, new ConfigDescription("While the last enemies are marked, each time this long passes without anyone hurting one, those more than 40 m from every player are moved onto a player's screen, WarpMinDistance to WarpMaxDistance away. 0 turns this off. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 600f), Array.Empty<object>()));
			WarpMinDistance = cfg.Bind<float>("Warp", "MinDistance", runRules.WarpMinMetres, new ConfigDescription("A warped enemy (a straggler, or one stuck on a cliff or far away) lands at least this many metres from every player. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f), Array.Empty<object>()));
			WarpMaxDistance = cfg.Bind<float>("Warp", "MaxDistance", runRules.WarpMaxMetres, new ConfigDescription("First try: up to this many metres from a player's camera. If no spot fits, the host tries up to 1.5 times as far. At least MinDistance + 5. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 200f), Array.Empty<object>()));
			WarpAttempts = cfg.Bind<int>("Warp", "Attempts", runRules.WarpAttempts, new ConfigDescription("Spots tried per pass (in view; in view and further; then anywhere unless ViewRequired). With none found the enemy stays and is tried again later. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 50), Array.Empty<object>()));
			WarpViewRequired = cfg.Bind<bool>("Warp", "ViewRequired", runRules.WarpViewRequired, "On: an enemy is only warped to a dry spot some player can see. Off: when no such spot is found, a dry spot out of view is used. Only the host's value is used.");
			LookSyncSeconds = cfg.Bind<float>("Warp", "LookSyncSeconds", 1f, new ConfigDescription("Local, clients. How often your camera's position, direction and field of view go to the host during a Wave Run, so warped enemies land on your screen. 0 sends nothing; the host then uses the direction the game already sends.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			DaysPerWave = cfg.Bind<int>("Scaling", "DaysPerWave", runRules.DaysPerRound, new ConfigDescription("Vanilla days per round. Round n plays at vanilla day (n-1)*this: the mob table and buff chance follow it, and mob HP, damage and chest prices follow it up to VanillaDayCap. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 5), Array.Empty<object>()));
			VanillaDayCap = cfg.Bind<int>("Scaling", "VanillaDayCap", runRules.VanillaDayCap, new ConfigDescription("Highest vanilla day used for mob HP, damage and chest prices. The difficulty coefficient scales on top of it. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 30), Array.Empty<object>()));
			TimeScaling = cfg.Bind<float>("Scaling", "TimeScaling", runRules.TimeScaling, new ConfigDescription("Risk of Rain 2 difficulty value: how fast run time raises the coefficient (0.0506 x this per minute). Drizzle is 1, Rainstorm 2, Monsoon 3. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f), Array.Empty<object>()));
			RoundGrowth = cfg.Bind<float>("Scaling", "RoundGrowth", runRules.RoundGrowth, new ConfigDescription("The coefficient is multiplied by this for every round played, like RoR2's 1.15 per stage. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 1.2f), Array.Empty<object>()));
			HpPerLevel = cfg.Bind<float>("Scaling", "HpPerLevel", runRules.HpPerLevel, new ConfigDescription("Mob HP added per monster level, as a fraction of vanilla. RoR2: 0.3. A level is 0.33 of coefficient. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			DamagePerLevel = cfg.Bind<float>("Scaling", "DamagePerLevel", runRules.DamagePerLevel, new ConfigDescription("Mob damage added per monster level, as a fraction of vanilla. RoR2: 0.2. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			MaxLevel = cfg.Bind<int>("Scaling", "MaxLevel", runRules.MaxLevel, new ConfigDescription("Monster level cap, which also caps the coefficient and the mob count growth. RoR2: 99. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 99), Array.Empty<object>()));
			MobBase = cfg.Bind<int>("Scaling", "MobBase", runRules.MobBase, new ConfigDescription("Mobs one player gets at the start of the run. A round brings the solo count times the players alive when its combat starts. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 50), Array.Empty<object>()));
			MobGrowth = cfg.Bind<float>("Scaling", "MobGrowth", runRules.MobGrowth, new ConfigDescription("Each point of solo coefficient above 1 adds this many times MobBase to the solo count. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 5f), Array.Empty<object>()));
			MaxMobsPerPlayer = cfg.Bind<int>("Scaling", "MaxMobsPerPlayer", runRules.MaxMobsPerPlayer, new ConfigDescription("Soft cap on the solo count, so a round's cap is this times the players. Counts bend toward it from half of it and never pass it. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(10, 500), Array.Empty<object>()));
			LegacyBaseMobs = BindRemoved(cfg, "BaseMobs", 6, "Set MobBase instead.");
			LegacyMobsPerCoefficient = BindRemoved(cfg, "MobsPerCoefficient", 1.5f, "Set MobGrowth instead.");
			LegacyExtraPlayerMobFactor = BindRemoved(cfg, "ExtraPlayerMobFactor", 0.5f, "A round now brings the solo count times the players alive when it starts.");
			LegacyMaxMobsPerRound = BindRemoved(cfg, "MaxMobsPerRound", 150, "Set MaxMobsPerPlayer instead.");
			BossBuff = cfg.Bind<bool>("Boss", "BossBuff", runRules.BossBuff, "When true, every boss spawns buffed (multiplier 1.5), like a boss shrine boss. Only the host's value is used.");
			BossArena = cfg.Bind<bool>("Boss", "Arena", runRules.BossArena, "When a boss round's fight starts, every living player is moved to one flat, dry, open spot near the team, a few metres apart, and the boss spawns ArenaBossDistance away. A player who joins or comes back to life during the fight is moved there too. Only the host's value is used.");
			ArenaBossDistance = cfg.Bind<float>("Boss", "ArenaBossDistance", runRules.ArenaBossMetres, new ConfigDescription("Metres from the arena's centre to where the boss spawns. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 60f), Array.Empty<object>()));
			ResetResources = cfg.Bind<bool>("Reset", "ResetResources", true, "Regrow trees, rocks, ores, bushes and cave ores before each round after the first. Only the host's value is used.");
			ResetPickups = cfg.Bind<bool>("Reset", "ResetPickups", true, "Respawn the ground pickups from world generation before each round after the first. Only the host's value is used.");
			WaveClearPowerup = cfg.Bind<bool>("Rewards", "WaveClearPowerup", runRules.WaveClearPowerup, "Clearing a round drops one powerup per player in front of them. Only the host's value is used.");
			BossPowerup = cfg.Bind<bool>("Rewards", "BossPowerup", runRules.BossPowerup, "Killing a round's boss drops one powerup per player, weighted toward Blue and Orange. Only the host's value is used.");
			HudScale = cfg.Bind<float>("HUD", "HudScale", 1f, new ConfigDescription("Size of the round HUD. 1 is sized for 1080p and scales with screen height. Local.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 3f), Array.Empty<object>()));
			HudPosition = cfg.Bind<HudAnchor>("HUD", "HudPosition", HudAnchor.TopCenter, "Where the round HUD sits. The HUD moves below the boss bar, an \"Unlocked\" popup or the day banner when one would sit under it. The end-screen summary uses the top right when this is TopCenter. Local.");
			LegacyHudAnchor = cfg.Bind<HudAnchor>("HUD", "HudAnchor", HudAnchor.TopRight, new ConfigDescription("Removed in 0.3.0 and not used. Set HudPosition instead. A value other than TopRight logs a warning at launch.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Browsable = false
				}
			}));
			HudTop = cfg.Bind<float>("HUD", "HudTop", 0.025f, new ConfigDescription("Top of the round HUD, as a fraction of screen height. The HUD moves below the boss bar, an \"Unlocked\" popup or the day banner when one would sit under it. Local.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.5f), Array.Empty<object>()));
			EnemyMarkers = cfg.Bind<bool>("HUD", "EnemyMarkers", true, "When the host hunts a round's last enemies, draw an amber marker with its distance over each one, at the screen edge when it is off screen. Local.");
			MapRotation = cfg.Bind<bool>("Maps", "Rotation", runRules.MapRotation, "After every RoundsPerMap-th round the island sinks and the run goes on on a new themed island. Inventories, powerups and HP stay; builds, ground items and mobs go, and storage contents drop at the new spawn. Only the host's value is used.");
			RoundsPerMap = cfg.Bind<int>("Maps", "RoundsPerMap", runRules.RoundsPerMap, new ConfigDescription("Rounds on each island. At the default 5 the island switches right after each boss round. A value that is not a multiple of BossEvery switches after regular rounds too. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			MapOrder = cfg.Bind<string>("Maps", "MapOrder", "SaltpanMesas,FrostbiteAtoll", "The islands after the first, comma-separated, cycled in this order. Known maps: " + ThemeTable.Keys() + ". HomeIsle is the vanilla look on a new seed. Unknown names are logged and skipped; an empty list turns Rotation off. Only the host's value is used.");
			ShuffleMaps = cfg.Bind<bool>("Maps", "Shuffle", runRules.ShuffleMaps, "Shuffle MapOrder once per run, from the lobby seed, so the same seed gives the same order. Only the host's value is used.");
			SwitchDelaySeconds = cfg.Bind<float>("Maps", "SwitchDelaySeconds", runRules.SwitchDelaySeconds, new ConfigDescription("Seconds after a map round ends before the island sinks, to collect the boss loot. 0 switches at once. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 120f), Array.Empty<object>()));
			MapPrepSeconds = cfg.Bind<float>("Maps", "MapPrepSeconds", runRules.MapPrepSeconds, new ConfigDescription("Daytime on a new island before its first round, counted from the moment every player has it. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(15f, 600f), Array.Empty<object>()));
			AckTimeoutSeconds = cfg.Bind<float>("Maps", "AckTimeoutSeconds", runRules.AckTimeoutSeconds, new ConfigDescription("Seconds the host waits for every player's island checksum after its own rebuild. A player whose island differs, or who has not answered by then, rebuilds it once more with the same wait; only if that also fails is it marked out of sync and the run goes on. Only the host's value is used.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 30f), Array.Empty<object>()));
			VerboseLogging = cfg.Bind<bool>("Debug", "VerboseLogging", false, "Log every snapshot, spawn batch, warp and reset. On the host, DebugRegrowKey also regrows the island at once and DebugMapKey switches the island. Local.");
			DebugRegrowKey = cfg.Bind<KeyCode>("Debug", "DebugRegrowKey", (KeyCode)265, "With VerboseLogging on, the host presses this to regrow the island at once, to test the reset. Local.");
			DebugMapKey = cfg.Bind<KeyCode>("Debug", "DebugMapKey", (KeyCode)264, "With VerboseLogging on, the host presses this in prep to switch to the next island in MapOrder at once, to test the rebuild. Local.");
			LogMapTimings = cfg.Bind<bool>("Debug", "LogMapTimings", true, "Log one line per map switch with each rebuild step's time, the object count and the island checksum, even without VerboseLogging. Local.");
			FailMapStep = cfg.Bind<string>("Debug", "FailMapStep", "", "Test aid. With VerboseLogging on, the host's first rebuild attempt throws in this step (cover, park, clear, counters, theme, terrain, minimap, resources, chunks, navmesh, capture), to test the recovery. Empty does nothing. Local.");
			_retired = RetiredClearKeys.Drop(cfg);
		}

		private static ConfigEntry<T> BindRemoved<T>(ConfigFile cfg, string key, T oldDefault, string instead)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			return cfg.Bind<T>("Scaling", key, oldDefault, new ConfigDescription("Removed in 0.2.1 and not used. " + instead + " A value other than " + Convert.ToString(oldDefault, CultureInfo.InvariantCulture) + " logs a warning at launch.", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Browsable = false
				}
			}));
		}

		public IEnumerable<string> LegacyWarnings()
		{
			string[] array = new string[6]
			{
				LegacyConfig.WaveCountWarning(LegacyWaveCount.Value),
				LegacyConfig.BaseMobsWarning(LegacyBaseMobs.Value),
				LegacyConfig.MobsPerCoefficientWarning(LegacyMobsPerCoefficient.Value),
				LegacyConfig.ExtraPlayerMobFactorWarning(LegacyExtraPlayerMobFactor.Value),
				LegacyConfig.MaxMobsPerRoundWarning(LegacyMaxMobsPerRound.Value),
				LegacyConfig.HudAnchorWarning(LegacyHudAnchor.Value.ToString())
			};
			string[] array2 = array;
			foreach (string text in array2)
			{
				if (text != null)
				{
					yield return text;
				}
			}
			foreach (var item3 in _retired)
			{
				string item = item3.Key;
				string item2 = item3.Raw;
				string text2 = LegacyConfig.RetiredClearKey(item, item2);
				if (text2 != null)
				{
					yield return text2;
				}
			}
		}

		public RunRules BuildRules()
		{
			ResetMask resetMask = ResetMask.None;
			if (ResetResources.Value)
			{
				resetMask |= ResetMask.Resources;
			}
			if (ResetPickups.Value)
			{
				resetMask |= ResetMask.Pickups;
			}
			List<string> unknown;
			return new RunRules
			{
				RoundCount = RoundCount.Value,
				BossEvery = BossEvery.Value,
				FirstPrepSeconds = FirstPrepSeconds.Value,
				PrepSeconds = PrepSeconds.Value,
				MaxAliveBase = MaxAliveBase.Value,
				MaxAlivePerPlayer = MaxAlivePerPlayer.Value,
				MaxAliveCap = MaxAliveCap.Value,
				HuntBelow = HuntBelow.Value,
				HuntDelaySeconds = HuntDelaySeconds.Value,
				WarpAfterSeconds = WarpAfterSeconds.Value,
				WarpMinMetres = WarpMinDistance.Value,
				WarpMaxMetres = WarpMaxDistance.Value,
				WarpAttempts = WarpAttempts.Value,
				WarpViewRequired = WarpViewRequired.Value,
				DaysPerRound = DaysPerWave.Value,
				VanillaDayCap = VanillaDayCap.Value,
				TimeScaling = TimeScaling.Value,
				RoundGrowth = RoundGrowth.Value,
				HpPerLevel = HpPerLevel.Value,
				DamagePerLevel = DamagePerLevel.Value,
				MaxLevel = MaxLevel.Value,
				MobBase = MobBase.Value,
				MobGrowth = MobGrowth.Value,
				MaxMobsPerPlayer = MaxMobsPerPlayer.Value,
				BossBuff = BossBuff.Value,
				BossArena = BossArena.Value,
				ArenaBossMetres = ArenaBossDistance.Value,
				WaveClearPowerup = WaveClearPowerup.Value,
				BossPowerup = BossPowerup.Value,
				Reset = resetMask,
				MapRotation = MapRotation.Value,
				RoundsPerMap = RoundsPerMap.Value,
				MapOrder = MapSchedule.ParseOrder(MapOrder.Value, ThemeTable.IdByKey, out unknown).ToArray(),
				ShuffleMaps = ShuffleMaps.Value,
				SwitchDelaySeconds = SwitchDelaySeconds.Value,
				MapPrepSeconds = MapPrepSeconds.Value,
				AckTimeoutSeconds = AckTimeoutSeconds.Value
			}.Sanitized();
		}
	}
	internal static class WaveRunHooks
	{
		private static readonly Type[] CorePatches = new Type[10]
		{
			typeof(GameLoopUpdatePatch),
			typeof(ChatBoxAppendPatch),
			typeof(FinishedLoadingPatch),
			typeof(CheckIfGameOverPatch),
			typeof(ClientStartGamePatch),
			typeof(MakeSettingsPatch),
			typeof(StartGameSendPatch),
			typeof(SendTcpDataPatch),
			typeof(GenerateMapRootsPatch),
			typeof(GameModeButtonPatch)
		};

		private static readonly (Type Patch, string Lost)[] OptionalPatches = new(Type, string)[14]
		{
			(typeof(CaveResourcesPatch), "cave ores will not regrow"),
			(typeof(AchievementsPatch), "a finished run can count toward Steam wins and achievements"),
			(typeof(DayUiPatch), "the vanilla DAY banner shows on top of the round banners"),
			(typeof(ExtraUiPatch), "the day counter can show the vanilla day instead of the round"),
			(typeof(GameoverHeaderPatch), "the end screen says Victory! or Defeat.. instead of the run's result"),
			(typeof(GameOverPatch), "a Dragon ending is only noticed once its end screen shows"),
			(typeof(MobHpPatch), "mob HP does not grow past the vanilla day cap"),
			(typeof(HitableMobStartPatch), "neutral animals get the round's HP factor too"),
			(typeof(MobDamagePatch), "mob damage does not grow past the vanilla day cap"),
			(typeof(ClientNewDayPatch), "a client's clock flashes to midnight when the vanilla day changes"),
			(typeof(GraveDayPatch), "a grave can be revived during combat, not only in prep"),
			(typeof(DayCycleUpdatePatch), "themed maps keep vanilla fog density and grey ambient light"),
			(typeof(ServerMobSpawnPatch), "a mob sent again to a client that lacks it is rebuilt from its live values, not its spawn packet"),
			(typeof(ClientMobPacketPatch), "a client that misses a mob throws on every packet for it, as in vanilla, and never asks the host for it")
		};

		internal static bool ApplyCore(Harmony harmony)
		{
			string arg = null;
			try
			{
				arg = "ServerSend.SendTCPDataToAll(Packet)";
				RunChannel.Bind();
				arg = "DrawChunks and PickupInteract fields";
				IslandSnapshot.Bind();
				arg = "map switch fields (MapGenerator.falloffMap, BuildDestruction.otherBuilds, ChestManager.chestId, MobZoneManager.zoneId)";
				ThemeApplier.Bind();
				IslandBuilder.Bind();
				Type[] corePatches = CorePatches;
				foreach (Type type in corePatches)
				{
					arg = type.Name;
					harmony.CreateClassProcessor(type).Patch();
					WaveRunPlugin.Log.LogInfo((object)("Hook ok: " + type.Name));
				}
				return true;
			}
			catch (Exception arg2)
			{
				harmony.UnpatchSelf();
				WaveRunPlugin.Log.LogError((object)$"Wave Run disabled: {arg} failed: {arg2}");
				return false;
			}
		}

		internal static void ApplyOptional(Harmony harmony)
		{
			(Type, string)[] optionalPatches = OptionalPatches;
			for (int i = 0; i < optionalPatches.Length; i++)
			{
				var (type, arg) = optionalPatches[i];
				try
				{
					harmony.CreateClassProcessor(type).Patch();
					WaveRunPlugin.Log.LogInfo((object)("Hook ok: " + type.Name));
				}
				catch (Exception arg2)
				{
					WaveRunPlugin.Log.LogWarning((object)$"Wave Run: {type.Name} failed, so {arg}: {arg2}");
				}
			}
		}
	}
	[BepInPlugin("evan.muck.waverun", "Wave Run", "0.5.7")]
	public class WaveRunPlugin : BaseUnityPlugin
	{
		public const string Guid = "evan.muck.waverun";

		public const string Name = "Wave Run";

		public const string Version = "0.5.7";

		internal static ManualLogSource Log;

		internal static WaveRunConfig Cfg;

		private void Awake()
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Cfg = new WaveRunConfig(((BaseUnityPlugin)this).Config);
			foreach (string item in Cfg.LegacyWarnings())
			{
				Log.LogWarning((object)item);
			}
			Harmony harmony = new Harmony("evan.muck.waverun");
			if (WaveRunHooks.ApplyCore(harmony))
			{
				WaveRunHooks.ApplyOptional(harmony);
				WaveRunner.Ensure();
				Log.LogInfo((object)"Wave Run 0.5.7 loaded. Pick Wave Run in the lobby's game mode row to play it. Every player needs this mod.");
			}
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "MuckWaveRun";

		public const string PLUGIN_NAME = "Wave Run";

		public const string PLUGIN_VERSION = "0.5.7";
	}
}
namespace MuckWaveRun.Patches
{
	[HarmonyPatch(typeof(AchievementManager), "CheckGameOverAchievements", new Type[] { typeof(int) })]
	internal static class AchievementsPatch
	{
		private static bool Prefix()
		{
			try
			{
				if (!WaveRunner.RunActive)
				{
					return true;
				}
				WaveRunner.Verbose("Skipped the vanilla game-over achievements and win stats for a Wave Run.");
				return false;
			}
			catch (Exception e)
			{
				WaveRunner.LogOnce("AchievementManager.CheckGameOverAchievements", e);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(SpawnResourcesInLocations), "SetResources", new Type[] { typeof(ConsistentRandom) })]
	internal static class CaveResourcesPatch
	{
		private static void Prefix(out int __state)
		{
			__state = ResourceManager.globalId;
		}

		private static void Postfix(int __state)
		{
			try
			{
				if (RunMode.Picked)
				{
					for (int i = __state; i < ResourceManager.globalId; i++)
					{
						IslandSnapshot.CaveIds.Add(i);
					}
				}
			}
			catch (Exception e)
			{
				WaveRunner.LogOnce("SpawnResourcesInLocations.SetResources", e);
			}
		}
	}
	[HarmonyPatch(typeof(ChatBox), "AppendMessage", new Type[]
	{
		typeof(int),
		typeof(string),
		typeof(string)
	})]
	internal static class ChatBoxAppendPatch
	{
		[HarmonyPriority(800)]
		private static bool Prefix(int __0, string __1, string __2)
		{
			bool flag;
			try
			{
				if (RunWire.Classify(__1) == WireKind.NotOurs)
				{
					return true;
				}
				flag = RunWire.IsSystemSender(__0, __2);
			}
			catch (Exception e)
			{
				WaveRunner.LogOnce("ChatBox.AppendMessage", e);
				return true;
			}
			try
			{
				if (flag)
				{
					WaveRunner.OnWire(__1);
				}
				else if (__0 >= 0)
				{
					WaveRunner.OnPlayerWire(__0, __1);
				}
			}
			catch (Exception e2)
			{
				WaveRunner.LogOnce("Wave Run message", e2);
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(GameManager), "CheckIfGameOver", new Type[] { })]
	internal static class CheckIfGameOverPatch
	{
		private static bool Prefix()
		{
			try
			{
				return !WaveRunner.OwnsVanilla;
			}
			catch (Exception e)
			{
				WaveRunner.LogOnce("GameManager.CheckIfGameOver", e);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(ClientHandle), "NewDay", new Type[] { typeof(Packet) })]
	internal static class ClientNewDayPatch
	{
		private static void Prefix(out float __state)
		{
			__state = DayCycle.time;
		}

		private static void Postfix(float __state)
		{
			try
			{
				if (WaveRunner.RunActive)
				{
					DayCycle.time = __state;
				}
			}
			catch (Exception e)
			{
				WaveRunner.LogOnce("ClientHandle.NewDay", e);
			}
		}
	}
	[HarmonyPatch(typeof(DayCycle), "Update", new Type[] { })]
	internal static class DayCycleUpdatePatch
	{
		private static Color _ambient;

		private static Theme _for;

		private static void Postfix(DayCycle __instance)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Invalid comparison between Unknown and I4
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: 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_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Theme current = ThemeApplier.Current;
				if (current == null || current.IsVanilla)
				{
					_for = null;
					return;
				}
				if (_for != current)
				{
					_for = current;
					_ambient = RenderSettings.ambientSkyColor;
				}
				if ((int)GameManager.state == 1 && (!((Object)(object)MoveCamera.Instance != (Object)null) || !((Object)(object)World.Instance != (Object)null) || !((Object)(object)World.Instance.water != (Object)null) || !(((Component)MoveCamera.Instance).transform.position.y < World.Instance.water.position.y)))
				{
					RenderSettings.fogDensity = current.FogDensity;
					if (__instance.waterGraph != null)
					{
						float num = __instance.waterGraph.Evaluate((DayCycle.time + 0.75f) % 1f);
						float num2 = 0.75f - num * 0.5f;
						_ambient = Color.Lerp(_ambient, ThemeApplier.ToColor(current.Ambient) * num2, Time.deltaTime * 0.5f);
						RenderSettings.ambientSkyColor = _ambient;
					}
				}
			}
			catch (Exception e)
			{
				WaveRunner.LogOnce("DayCycle.Update", e);
			}
		}
	}
	[HarmonyPatch(typeof(DayUi), "SetDay", new Type[] { typeof(int) })]
	internal static class DayUiPatch
	{
		private static bool Prefix()
		{
			try
			{
				return !WaveRunner.RunActive;
			}
			catch (Exception e)
			{
				WaveRunner.LogOnce("DayUi.SetDay", e);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(ExtraUI), "UpdateDay", new Type[] { typeof(int) })]
	internal static class ExtraUiPatch
	{
		private static void Postfix(ExtraUI __instance)
		{
			try
			{
				if (WaveRunner.RunActive)
				{
					int round = WaveRunner.LastSnapshot.Round;
					TmpText.Set(__instance, "dayText", round.ToString(CultureInfo.InvariantCulture));
				}
			}
			catch (Exception e)
			{
				WaveRunner.LogOnce("ExtraUI.UpdateDay", e);
			}
		}
	}
	[HarmonyPatch(typeof(ClientSend), "PlayerFinishedLoading", new Type[] { })]
	internal static class FinishedLoadingPatch
	{
		private static void Postfix()
		{
			try
			{
				if (!RunMode.Picked)
				{
					return;
				}
				WorldSet.RecordGenerated();
			}
			catch (Exception arg)
			{
				WaveRunPlugin.Log.LogError((object)$"Wave Run world set failed. The island cannot switch maps cleanly: {arg}");
			}
			try
			{
				if (RunMode.Picked)
				{
					IslandSnapshot.Capture();
				}
			}
			catch (Exception arg2)
			{
				WaveRunPlugin.Log.LogError((object)$"Wave Run island snapshot failed. Resources will not regrow: {arg2}");
			}
		}
	}
	[HarmonyPatch(typeof(GameLoop), "Update", new Type[] { })]
	internal static class GameLoopUpdatePatch
	{
		private static bool Prefix()
		{
			try
			{
				return !WaveRunner.OwnsVanilla;
			}
			catch (Exception e)
			{
				WaveRunner.LogOnce("GameLoop.Update", e);
				return true;
			}
		}
	}
	[HarmonyPatch(typeof(UiSettings), "AddSettings", new Type[]
	{
		typeof(int),
		typeof(string[])
	})]
	internal static class GameModeButtonPatch
	{
		private static void Prefix(UiSettings __instance, ref string[] __1)
		{
			try
			{
				LobbySettings instance = LobbySettings.Instance;
				if (!((Object)(object)instance == (Object)null) && !((Object)(object)__instance != (Object)(object)instance.gamemodeSetting))
				{
					string[] array = ModePick.WithWaveRun(__1);
					RunMode.WaveRunIndex = array.Length - 1;
					__1 = array;
				}
			}
			catch (Exception e)
			{
				RunMode.WaveRunIndex = -1;
				WaveRunner.LogOnce("UiSettings.AddSettings", e);
			}
		}
	}
	[HarmonyPatch(typeof(GameoverUI), "HeaderText", new Type[] { })]
	internal static class GameoverHeaderPatch
	{
		private static void Postfix(GameoverUI __instance)
		{
			try
			{
				if (WaveRunner.RunActive)
				{
					RunSnapshot lastSnapshot = WaveRunner.LastSnapshot;
					if (lastSnapshot.Phase == Phase.Victory || lastSnapshot.Phase == Phase.Defeat)
					{
						TmpText.Set(__instance, "header", HudText.EndHeader(lastSnapshot));
					}
				}
			}
			catch (Exception e)
			{
				WaveRunner.LogOnce("GameoverUI.HeaderText", e);
			}
		}
	}
	[HarmonyPatch(typeof(GameManager), "GameOver", new Type[]
	{
		typeof(int),
		typeof(float)
	})]
	internal static class GameOverPatch
	{
		private static void Postfix(int __0)
		{
			try
			{
				WaveRunner.OnGameOver(__0);
			}
			catch (Exception e)
			{
				WaveRunner.LogOnce("GameManager.GameOver", e);
			}
		}
	}
	[HarmonyPatch(typeof(MapGenerator), "GenerateMap", new Type[] { typeof(int) })]
	internal static class GenerateMapRootsPatch
	{
		private static void Prefix()
		{
			try
			{
				if (RunMode.Picked && !WorldSet.Rebuilding)
				{
					if (!SpawnerTemplates.Ready)
					{
						CloneTemplates();
					}
					if (!WorldSet.HasBaseline)
					{
						WorldSet.RecordBaseline();
					}
				}
			}
			catch (Exception e)
			{
				WaveRunner.LogOnce("MapGenerator.GenerateMap", e);
			}
		}

		private static void CloneTemplates()
		{
			try
			{
				SpawnerTemplates.Clone(((Object)(object)GameManager.instance != (Object)null) ? GameManager.instance.resourceGen : null);
			}
			catch (Exception arg)
			{
				WaveRunPlugin.Log.LogError((object)$"Wave Run templates FAILED, so this game's island cannot switch maps: {arg}");
			}
		}
	}
	[HarmonyPatch(typeof(GraveInteract), "IsDay")]
	internal static class GraveDayPatch
	{
		private static void Postfix(ref bool __result)
		{
			try
			{
				if (__result && WaveRunner.RunActive)
				{
					__result = DayClock.RevivesAllowed(vanillaIsDay: true, runActive: true, WaveRunner.LastSnapshot.Phase);
				}
			}
			catch (Exception e)
			{
				WaveRunner.LogOnce("GraveInteract.IsDay", e);
			}
		}
	}
	[HarmonyPatch(typeof(HitableMob), "Start")]
	internal static class HitableMobStartPatch
	{
		private static void Prefix(HitableMob __instance)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Invalid comparison between Unknown and I4
			try
			{
				if (WaveRunner.RunActive)
				{
					Mob component = ((Component)__instance).GetComponent<Mob>();
					MobHpPatch.StartingNeutral = (Object)(object)component != (Object)null && (Object)(object)component.mobType != (Object)null && (int)component.mobType.behaviour == 0;
				}
			}
			catch (Exception e)
			{
				MobHpPatch.StartingNeutral = false;
				WaveRunner.LogOnce("HitableMob.Start", e);
			}
		}

		private static void Finalizer()
		{
			MobHpPatch.StartingNeutral = false;
		}
	}
	[HarmonyPatch(typeof(SteamLobby), "MakeSettings", new Type[] { })]
	internal static class MakeSettingsPatch
	{
		private static void Postfix(GameSettings __result)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Invalid comparison between Unknown and I4
			try
			{
				int setting = LobbySettings.Instance.gamemodeSetting.setting;
				RunMode.HostPick = ModePick.Choice(setting, RunMode.WaveRunIndex);
				__result.gameMode = (GameMode)ModePick.VanillaMode(setting, RunMode.WaveRunIndex);
			}
			catch (Exception e)
			{
				RunMode.HostPick = GameModeChoice.Vanilla;
				WaveRunner.LogOnce("SteamLobby.MakeSettings", e);
				if (__result != null && (int)__result.gameMode > 2)
				{
					__result.gameMode = (GameMode)0;
				}
			}
		}
	}
	[HarmonyPatch(typeof(ServerSend), "StartGame", new Type[]
	{
		typeof(int),
		typeof(GameSettings)
	})]
	internal static class StartGameSendPatch
	{
		internal static readonly MarkerLatch Marker = new MarkerLatch();

		private static void Prefix()
		{
			Marker.Begin(RunMode.HostPick);
		}

		private static void Finalizer()
		{
			Marker.End();
		}
	}
	[HarmonyPatch(typeof(ServerSend), "SendTCPData", new Type[]
	{
		typeof(int),
		typeof(Packet)
	})]
	internal static class SendTcpDataPatch
	{
		private static void Prefix(Packet __1)
		{
			if (!StartGameSendPatch.Marker.Take())
			{
				return;
			}
			try
			{
				__1.Write(1464991745);
				WaveRunner.Verbose("Wave Run: marked packet 13 as a Wave Run game.");
			}
			catch (Exception e)
			{
				WaveRunner.LogOnce("ServerSend.SendTCPData", e);
			}
		}
	}
	[HarmonyPatch(typeof(ClientHandle), "StartGame", new Type[] { typeof(Packet) })]
	internal static class ClientStartGamePatch
	{
		private static void Postfix(Packet __0)
		{
			try
			{
				RunMode.Current = ModeWire.Parse(__0.ToArray());
				WaveRunner.Verbose($"Wave Run: game mode from the host is {RunMode.Current}.");
			}
			catch (Exception e)
			{
				RunMode.Current = GameModeChoice.Vanilla;
				WaveRunner.LogOnce("ClientHandle.StartGame", e);
			}
		}
	}
	[HarmonyPatch(typeof(GameManager), "MobDamageMultiplier", new Type[] { })]
	internal static class MobDamagePatch
	{
		private static void Postfix(ref float __result)
		{
			try
			{
				__result = WaveRunner.ScaleMobDamage(__result);
			}
			catch (Exception e)
			{
				WaveRunner.LogOnce("GameManager.MobDamageMultiplier", e);
			}
		}
	}
	[HarmonyPatch(typeof(GameManager), "MobHpMultiplier", new Type[] { })]
	internal static class MobHpPatch
	{
		internal static bool StartingNeutral;

		private static void Postfix(ref float __result)
		{
			try
			{
				__result = WaveRunner.ScaleMobHp(__result, StartingNeutral);
			}
			catch (Exception e)
			{
				WaveRunner.LogOnce("GameManager.MobHpMultiplier", e);
			}
		}
	}
	[HarmonyPatch]
	internal static class ClientMobPacketPatch
	{
		private static bool Prepare()
		{
			MobResyncNet.Bind();
			return true;
		}

		private static IEnumerable<MethodBase> TargetMethods()
		{
			foreach (MobPacket item in MobPackets.All)
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(ClientHandle), item.Handler, new Type[1] { typeof(Packet) }, (Type[])null);
				if (methodInfo == null)
				{
					throw new MissingMethodException("ClientHandle", item.Handler);
				}
				yield return methodInfo;
			}
		}

		[HarmonyPriority(800)]
		private static bool Prefix(MethodBase __originalMethod, Packet __0)
		{
			try
			{
				return MobResyncNet.OnClientPacket(__originalMethod.Name, __0);
			}
			catch (Exception e)
			{
				WaveRunner.LogOnce("Wave Run mob packet guard", e);
				return true;
			}
		}
	}
	[HarmonyPatch]
	internal static class ServerMobSpawnPatch
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(ServerSend), "MobSpawn")]
		private static void MobSpawn(int mobType, int mobId, float multiplier, float bossMultiplier, int guardianType)
		{
			try
			{
				MobResyncNet.RecordSpawn(mobType, mobId, multiplier, bossMultiplier, guardianType);
			}
			catch (Exception e)
			{
				WaveRunner.LogOnce("Wave Run spawn record", e);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(ServerSend), "MobZoneSpawn")]
		private static void MobZoneSpawn(int mobType, int mobId, int mobZoneId)
		{
			try
			{
				MobResyncNet.RecordZoneSpawn(mobType, mobId, mobZoneId);
			}
			catch (Exception e)
			{
				WaveRunner.LogOnce("Wave Run zone spawn record", e);
			}
		}
	}
}
namespace MuckWaveRun.Game
{
	internal sealed class IslandBuildResult
	{
		public MapSwitch Switch;

		public int Attempt;

		public bool Ok;

		public string FailedStep;

		public bool FreshBaseline;

		public int Count;

		public string Checksum;
	}
	internal static class IslandBuilder
	{
		public static readonly Vector3 Park = new Vector3(3000f, 300f, 3000f);

		private static FieldRef<BuildDestruction, List<BuildDestruction>> _otherBuilds;

		private static FieldRef<ChestManager, int> _chestId;

		private static FieldRef<int> _zoneId;

		private static readonly Dictionary<int, GameObject> Graves = new Dictionary<int, GameObject>();

		public static readonly List<(int ItemId, int Amount)> ChestContents = new List<(int, int)>();

		private static bool _inputWasActive = true;

		private static bool _wasKinematic;

		private static bool _hasParkedFrom;

		private static Vector3 _parkedFrom;

		public static bool Running { get; private set; }

		public static bool Parked { get; private set; }

		public static void Bind()
		{
			FieldInfo fieldInfo = AccessTools.Field(typeof(BuildDestruction), "otherBuilds");
			if (fieldInfo == null || fieldInfo.FieldType != typeof(List<BuildDestruction>))
			{
				throw new MissingFieldException("BuildDestruction", "otherBuilds");
			}
			_otherBuilds = AccessTools.FieldRefAccess<BuildDestruction, List<BuildDestruction>>(fieldInfo);
			FieldInfo fieldInfo2 = AccessTools.Field(typeof(ChestManager), "chestId");
			if (fieldInfo2 == null || fieldInfo2.FieldType != typeof(int))
			{
				throw new MissingFieldException("ChestManager", "chestId");
			}
			_chestId = AccessTools.FieldRefAccess<ChestManager, int>(fieldInfo2);
			FieldInfo fieldInfo3 = AccessTools.Field(typeof(MobZoneManager), "zoneId");
			if (fieldInfo3 == null || fieldInfo3.FieldType != typeof(int) || !fieldInfo3.IsStatic)
			{
				throw new MissingFieldException("MobZoneManager", "zoneId");
			}
			_zoneId = AccessTools.StaticFieldRefAccess<int>(fieldInfo3);
		}

		public static IEnumerator Rebuild(MapSwitch sw, int attempt, bool isHost, string failStep, Action<IslandBuildResult> done)
		{
			Running = true;
			Theme theme = ThemeTable.ById(sw.ThemeId);
			IslandBuildResult result = new IslandBuildResult
			{
				Switch = sw,
				Attempt = attempt,
				Ok = false
			};
			List<(string, double)> timings = new List<(string, double)>();
			int resourceObjects = 0;
			MapCover.Show(ThemeTable.Name(sw.ThemeId));
			foreach (string step in RebuildSteps.Names)
			{
				MapCover.Step(step);
				Stopwatch watch = Stopwatch.StartNew();
				IEnumerator body = null;
				string error = null;
				try
				{
					if (step == failStep)
					{
						throw new InvalidOperationException("FailMapStep = " + failStep + " (debug)");
					}
					body = Body(step, sw, theme, isHost, result, delegate(int n)
					{
						resourceObjects = n;
					});
				}
				catch (Exception ex)
				{
					error = ex.ToString();
				}
				while (error == null && body != null)
				{
					bool flag;
					try
					{
						flag = body.MoveNext();
					}
					catch (Exception ex2)
					{
						error = ex2.ToString();
						break;
					}
					if (!flag)
					{
						break;
					}
					yield return body.Current;
				}
				timings.Add((step, watch.Elapsed.TotalSeconds));
				if (error != null)
				{
					result.FailedStep = step;
					WaveRunPlugin.Log.LogError((object)("Wave Run map switch step " + step + " FAILED: " + error));
					break;
				}
				if (StepYields(step))
				{
					yield return null;
				}
			}
			WorldSet.Rebuilding = false;
			result.Ok = result.FailedStep == null;
			if (result.Ok && WaveRunPlugin.Cfg.LogMapTimings.Value)
			{
				WaveRunPlugin.Log.LogInfo((object)RebuildSteps.TimingLine(sw.MapIndex + 1, ThemeTable.Name(sw.ThemeId), sw.Seed, sw.IdBase, timings, resourceObjects, result.Checksum, result.Count, attempt));
			}
			if (!result.Ok)
			{
				Recover(sw, result.FailedStep);
			}
			Running = false;
			try
			{
				done?.Invoke(result);
			}
			catch (Exception arg)
			{
				WaveRunPlugin.Log.LogError((object)$"Wave Run map {sw.MapIndex + 1}: handling the rebuild result failed: {arg}");
			}
		}

		private static bool StepYields(string step)
		{
			switch (step)
			{
			default:
				return step == "navmesh";
			case "terrain":
			case "minimap":
			case "chunks":
				return true;
			}
		}

		private static IEnumerator Body(string step, MapSwitch sw, Theme theme, bool isHost, IslandBuildResult result, Action<int> resources)
		{
			switch (step)
			{
			case "cover":
				CloseContainerUi();
				return null;
			case "park":
				ParkAll();
				return null;
			case "clear":
				Clear(isHost);
				return null;
			case "counters":
				ResetCounters(sw);
				return null;
			case "theme":
				if (theme == null)
				{
					throw new InvalidOperationException($"theme {sw.ThemeId} is not in this peer's ThemeTable");
				}
				ThemeApplier.Apply(theme);
				return null;
			case "terrain":
				Terrain(sw, theme);
				return null;
			case "minimap":
				Minimap();
				return null;
			case "resources":
				return SpawnResources(theme, result, resources);
			case "chunks":
				foreach (GameObject item in SpawnerTemplates.Live)
				{
					if ((Object)(object)item != (Object)null)
					{
						DrawChunks[] componentsInChildren = item.GetComponentsInChildren<DrawChunks>(true);
						for (int i = 0; i < componentsInChildren.Length; i++)
						{
							IslandSnapshot.ZeroChunks(componentsInChildren[i]);
						}
					}
				}
				return null;
			case "navmesh":
				GameManager.instance.generateNavmesh.GenerateNavMesh();
				return null;
			case "capture":
				Capture(result);
				return null;
			default:
				throw new InvalidOperationException("unknown rebuild step " + step);
			}
		}

		private static void CloseContainerUi()
		{
			OtherInput instance = OtherInput.Instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)InventoryUI.Instance != (Object)null && ((Component)InventoryUI.Instance).gameObject.activeInHierarchy && (Object)(object)instance.currentChest != (Object)null)
			{
				instance.ToggleInventory((CraftingState)0);
			}
		}

		private static void ParkAll()
		{
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			PlayerMovement instance = PlayerMovement.Instance;
			if (!Parked)
			{
				_inputWasActive = (Object)(object)PlayerInput.Instance == (Object)null || PlayerInput.Instance.active;
				_wasKinematic = (Object)(object)instance != (Object)null && (Object)(object)instance.GetRb() != (Object)null && instance.GetRb().isKinematic;
				_hasParkedFrom = (Object)(object)instance != (Object)null;
				if ((Object)(object)instance != (Object)null)
				{
					_parkedFrom = ((Component)instance).transform.position;
				}
			}
			if ((Object)(object)PlayerInput.Instance != (Object)null)
			{
				PlayerInput.Instance.active = false;
			}
			if ((Object)(object)instance != (Object)null)
			{
				Rigidbody rb = instance.GetRb();
				if ((Object)(object)rb != (Object)null)
				{
					rb.velocity = Vector3.zero;
					rb.isKinematic = true;
				}
				((Component)instance).transform.position = Park;
			}
			Parked = true;
			int num = (((Object)(object)LocalClient.instance != (Object)null) ? LocalClient.instance.myId : (-1));
			if (GameManager.players != null)
			{
				foreach (PlayerManager value in GameManager.players.Values)
				{
					if (!((Object)(object)value == (Object)null) && value.id != num)
					{
						((Component)value).transform.position = Park;
						value.SetDesiredPosition(Park);
					}
				}
			}
			CollectGraves();
			foreach (GameObject value2 in Graves.Values)
			{
				value2.transform.position = Park;
			}
		}

		private static void CollectGraves()
		{
			Graves.Clear();
			ResourceManager instance = ResourceManager.Instance;
			if ((Object)(object)instance == (Object)null || instance.list == null)
			{
				return;
			}
			foreach (KeyValuePair<int, GameObject> item in instance.list)
			{
				if ((Object)(object)item.Value != (Object)null && (Object)(object)item.Value.GetComponentInChildren<GraveInteract>(true) != (Object)null)
				{
					Graves[item.Key] = item.Value;
				}
			}
		}

		private static void Clear(bool isHost)
		{
			BuildDestruction[] array = Object.FindObjectsOfType<BuildDestruction>();
			foreach (BuildDestruction val in array)
			{
				if ((Object)(object)val != (Object)null)
				{
					_otherBuilds.Invoke(val)?.Clear();
				}
			}
			ChestManager instance = ChestManager.Instance;
			if (isHost)
			{
				SaveChestContents(instance);
			}
			CollectGraves();
			HashSet<int> keep = new HashSet<int>();
			foreach (GameObject value in Graves.Values)
			{
				keep.Add(((Object)value).GetInstanceID());
			}
			WorldSet.Sweep((GameObject go) => keep.Contains(((Object)go).GetInstanceID()) || (Object)(object)go.GetComponentInChildren<PlayerManager>(true) != (Object)null);
			ResourceManager instance2 = ResourceManager.Instance;
			if ((Object)(object)instance2 != (Object)null)
			{
				DestroyValues(instance2.list, keep);
				DestroyValues(instance2.builds, keep);
				instance2.list?.Clear();
				instance2.builds?.Clear();
				if (instance2.list != null)
				{
					foreach (KeyValuePair<int, GameObject> grafe in Graves)
					{
						instance2.list[grafe.Key] = grafe.Value;
					}
				}
			}
			if ((Object)(object)instance != (Object)null && instance.chests != null)
			{
				foreach (Chest value2 in instance.chests.Values)
				{
					if ((Object)(object)value2 != (Object)null)
					{
						Object.Destroy((Object)(object)(((Object)(object)((Component)value2).transform.parent != (Object)null) ? ((Component)((Component)value2).transform.parent).gameObject : ((Component)value2).gameObject));
					}
				}
				instance.chests.Clear();
			}
			MobZoneManager instance3 = MobZoneManager.Instance;
			if ((Object)(object)instance3 != (Object)null && instance3.zones != null)
			{
				foreach (SpawnZone value3 in instance3.zones.Values)
				{
					if ((Object)(object)value3 != (Object)null)
					{
						Object.Destroy((Object)(object)((Component)value3).gameObject);
					}
				}
				instance3.zones.Clear();
			}
			MobManager instance4 = MobManager.Instance;
			if ((Object)(object)instance4 != (Object)null && instance4.mobs != null)
			{
				foreach (Mob value4 in instance4.mobs.Values)
				{
					if ((Object)(object)value4 != (Object)null)
					{
						Object.Destroy((Object)(object)((Component)value4).gameObject);
					}
				}
				instance4.mobs.Clear();
			}
			ItemManager instance5 = ItemManager.Instance;
			if (instance5 != null && instance5.list != null)
			{
				int count = instance5.list.Count;
				foreach (GameObject value5 in instance5.list.Values)
				{
					if ((Object)(object)value5 != (Object)null)
					{
						Object.Destroy((Object)(object)value5);
					}
				}
				instance5.list.Clear();
				WaveRunPlugin.Log.LogInfo((object)$"Wave Run map switch: {count} ground items removed.");
			}
			IslandSnapshot.Forget();
		}

		private static void SaveChestContents(ChestManager chests)
		{
			if ((Object)(object)chests == (Object)null || chests.chests == null)
			{
				return;
			}
			foreach (Chest value in chests.chests.Values)
			{
				if ((Object)(object)value == (Object)null || value.cells == null || (Object)(object)((Component)value).GetComponentInParent<BuildInfo>() == (Object)null)
				{
					continue;
				}
				InventoryItem[] cells = value.cells;
				foreach (InventoryItem val in cells)
				{
					if ((Object)(object)val != (Object)null && val.amount > 0)
					{
						ChestContents.Add((val.id, val.amount));
					}
				}
			}
		}

		private static void DestroyValues(Dictionary<int, GameObject> dict, HashSet<int> keep)
		{
			if (dict == null)
			{
				return;
			}
			foreach (GameObject value in dict.Values)
			{
				if ((Object)(object)value != (Object)null && !keep.Contains(((Object)value).GetInstanceID()))
				{
					Object.Destroy((Object)(object)value);
				}
			}
		}

		private static void ResetCounters(MapSwitch sw)
		{
			ResourceManager.generatorSeedOffset = 0;
			ResourceManager.globalId = sw.IdBase;
			_zoneId.Invoke() = 0;
			if ((Object)(object)ChestManager.Instance != (Object)null)
			{
				_chestId.Invoke(ChestManager.Instance) = (int)IdOffsets.chestIdRange.x;
			}
			GameManager.gameSettings.Seed = sw.Seed;
		}

		private static void Terrain(MapSwitch sw, Theme theme)
		{
			ThemeApplier.SetFalloff(theme);
			MapDisplay val = Object.FindObjectOfType<MapDisplay>();
			Mesh val2 = (((Object)(object)val != (Object)null && (Object)(object)val.meshFilter != (Object)null) ? val.meshFilter.sharedMesh : null);
			WorldSet.Rebuilding = true;
			try
			{
				GameManager.instance.mapGenerator.GenerateMap(sw.Seed);
			}
			finally
			{
				WorldSet.Rebuilding = false;
			}
			if ((Object)(object)val2 != (Object)null && (Object)(object)val.meshFilter.sharedMesh != (Object)(object)val2)
			{
				Object.Destroy((Object)(object)val2);
			}
			Physics.SyncTransforms();
		}

		private static void Minimap()
		{
			Map instance = Map.Instance;
			if (!((Object)(object)instance == (Object)null))
			{
				Texture val = (((Object)(object)instance.mapTextureMaterial != (Object)null) ? instance.mapTextureMaterial.mainTexture : null);
				instance.GenerateMap();
				if ((Object)(object)val != (Object)null && (Object)(object)instance.mapTextureMaterial.mainTexture != (Object)(object)val)
				{
					Object.Destroy((Object)(object)val);
				}
			}
		}

		private static IEnumerator SpawnResources(Theme theme, IslandBuildResult result, Action<int> resources)
		{
			WorldSet.RecordBaseline();
			result.FreshBaseline = true;
			int before = (((Object)(object)ResourceManager.Instance != (Object)null && ResourceManager.Instance.list != null) ? ResourceManager.Instance.list.Count : 0);
			GenerateAllResources originals = (((Object)(object)GameManager.instance.resourceGen != (Object)null) ? GameManager.instance.resourceGen.GetComponent<GenerateAllResources>() : null);
			IEnumerator spawn = SpawnerTemplates.Spawn(theme, originals);
			while (spawn.MoveNext())
			{
				yield return spawn.Current;
			}
			int num = (((Object)(object)ResourceManager.Instance != (Object)null && ResourceManager.Instance.list != null) ? ResourceManager.Instance.list.Count : 0);
			resources(num - before);
		}

		private static void Capture(IslandBuildResult result)
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: 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_009a: Unknown result type (might be due to invalid IL or missing references)
			WorldSet.RecordGenerated();
			IslandSnapshot.Recapture();
			List<SlotEntry> list = new List<SlotEntry>();
			ResourceManager instance = ResourceManager.Instance;
			if ((Object)(object)instance != (Object)null && instance.list != null)
			{
				foreach (KeyValuePair<int, GameObject> item in instance.list)
				{
					if (!((Object)(object)item.Value == (Object)null) && !Graves.ContainsKey(item.Key))
					{
						Vector3 position = item.Value.transform.position;
						list.Add(new SlotEntry(item.Key, ((Object)item.Value).name, position.x, position.y, position.z));
					}
				}
			}
			result.Count = list.Count;
			result.Checksum = IslandChecksum.Compute(list);
		}

		public static void Unpark(Vector3 spot, Vector3 graveSpot)
		{
			//IL_0015: 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_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				PlayerMovement instance = PlayerMovement.Instance;
				if ((Object)(object)instance != (Object)null)
				{
					((Component)instance).transform.position = spot + Vector3.up * 3f;
					Rigidbody rb = instance.GetRb();
					if ((Object)(object)rb != (Object)null)
					{
						rb.velocity = Vector3.zero;
						rb.isKinematic = _wasKinematic;
					}
				}
				if ((Object)(object)PlayerInput.Instance != (Object)null)
				{
					PlayerInput.Instance.active = _inputWasActive;
				}
			}
			finally
			{
				Parked = false;
				MapCover.Hide();
			}
			MoveGraves(graveSpot);
		}

		private static void MoveGraves(Vector3 at)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			foreach (GameObject value in Graves.Values)
			{
				try
				{
					if (!((Object)(object)value == (Object)null))
					{
						Vector3 at2 = at + new Vector3(2.5f * (float)num++, 0f, 0f);
						value.transform.position = SpawnFinder.Ground(at2);
					}
				}
				catch (Exception arg)
				{
					WaveRunPlugin.Log.LogError((object)$"Wave Run grave move failed: {arg}");
				}
			}
		}

		public static Vector3 Recover(MapSwitch sw, string failedStep)
		{
			//IL_0025: 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_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: 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_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				MapCover.Hide();
			}
			catch (Exception arg)
			{
				WaveRunPlugin.Log.LogError((object)$"Wave Run recover: cover failed: {arg}");
			}
			WorldSet.Rebuilding = false;
			Vector3 val = SpawnFinder.VanillaFallback;
			try
			{
				List<Vector3> list = new List<Vector3>();
				val = ((!RebuildSteps.AtOrAfterClear(failedStep) && _hasParkedFrom) ? (_parkedFrom - Vector3.up * 3f) : ((!RebuildSteps.TerrainBuilt(failedStep) || !SpawnFinder.Find(sw.Seed, 1, list, out var _)) ? SpawnFinder.Ground(SpawnFinder.VanillaFallback) : list[0]));
			}
			catch (Exception arg2)
			{
				WaveRunPlugin.Log.LogError((object)$"Wave Run recover: spot search failed: {arg2}");
			}
			try
			{
				Unpark(val, val);
			}
			catch (Exception arg3)
			{
				WaveRunPlugin.Log.LogError((object)$"Wave Run recover: unpark failed: {arg3}");
			}
			WaveRunPlugin.Log.LogInfo((object)$"Wave Run map {sw.MapIndex + 1}: recovered after step {failedStep} at {val}");
			return val;
		}

		public static void Abort()
		{
			Running = false;
			Parked = false;
			Graves.Clear();
			ChestContents.Clear();
			_inputWasActive = true;
			_wasKinematic = false;
			_hasParkedFrom = false;
			WorldSet.Rebuilding = false;
			MapCover.Hide();
		}
	}
	internal static class IslandSnapshot
	{
		private sealed class Record
		{
			public int Id;

			public string Name;

			public Vector3 Position;

			public Quaternion Rotation;

			public Vector3 Scale;

			public bool WasActive;

			public DrawChunks Chunks;

			public int Chunk;

			public int Index;

			public SlotKind Kind;
		}

		public static readonly HashSet<int> CaveIds = new HashSet<int>();

		private static readonly List<Slot> Slots = new List<Slot>();

		private static readonly List<Record> Records = new List<Record>();

		private static readonly Dictionary<(string, SlotKind), GameObject> Templates = new Dictionary<(string, SlotKind), GameObject>();

		private static GameObject _holder;

		private static FieldRef<DrawChunks, bool[]> _visibleChunks;

		private static FieldRef<DrawChunks, int[]> _chunkLod;

		private static FieldRef<PickupInteract, Vector3> _pickupDefaultScale;

		public static bool Ready { get; private set; }

		public static bool Loaded { get; private set; }

		public static int SlotCount => Slots.Count;

		public static void Bind()
		{
			_visibleChunks = FieldRef<DrawChunks, bool[]>("visibleChunks");
			_chunkLod = FieldRef<DrawChunks, int[]>("chunkLOD");
			_pickupDefaultScale = FieldRef<PickupInteract, Vector3>("defaultScale");
		}

		public static void ZeroChunks(DrawChunks dc)
		{
			bool[] array = _visibleChunks.Invoke(dc);
			int[] array2 = _chunkLod.Invoke(dc);
			if (array != null)
			{
				Array.Clear(array, 0, array.Length);
			}
			if (array2 != null)
			{
				Array.Clear(array2, 0, array2.Length);
			}
		}

		private static FieldRef<T, F> FieldRef<T, F>(string name)
		{
			FieldInfo fieldInfo = AccessTools.Field(typeof(T), name);
			if (fieldInfo == null || fieldInfo.FieldType != typeof(F))
			{
				throw new MissingFieldException(typeof(T).Name, name);
			}
			return AccessTools.FieldRefAccess<T, F>(fieldInfo);
		}

		public static void Capture()
		{
			Loaded = true;
			if (!Ready)
			{
				Build();
			}
		}

		public static void Recapture()
		{
			Loaded = true;
			Build();
		}

		public static void Forget()
		{
			ClearState();
			CaveIds.Clear();
			Loaded = true;
		}

		private static void Build()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			ResourceManager instance = ResourceManager.Instance;
			if ((Object)(object)instance == (Object)null || instance.list == null)
			{
				return;
			}
			ClearState();
			_holder = new GameObject("WaveRunTemplates");
			_holder.SetActive(false);
			HashSet<int> hashSet = new HashSet<int>();
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			DrawChunks[] array = Object.FindObjectsOfType<DrawChunks>();
			foreach (DrawChunks val in array)
			{
				if ((Object)(object)val == (Object)null || val.chunks == null)
				{
					continue;
				}
				for (int j = 0; j < val.chunks.Length; j++)
				{
					List<GameObject> list = val.chunks[j];
					if (list == null)
					{
						continue;
					}
					for (int k = 0; k < list.Count; k++)
					{
						GameObject val2 = list[k];
						if ((Object)(object)val2 == (Object)null || !TryKind(val2, out var kind))
						{
							continue;
						}
						int id = val2.GetComponentInChildren<SharedObject>(true).GetId();
						if (hashSet.Add(id))
						{
							Add(val2, id, kind, val, j, k);
							if (kind == SlotKind.Resource)
							{
								num++;
							}
							else
							{
								num2++;
							}
						}
					}
				}
			}
			List<int> list2 = new List<int>(CaveIds);
			list2.Sort();
			foreach (int item in list2)
			{
				if (!instance.list.TryGetValue(item, out var value) || (Object)(object)value == (Object)null)
				{
					continue;
				}
				HitableResource component = value.GetComponent<HitableResource>();
				if (!((Object)(object)component == (Object)null) && !(component is HitableChest))
				{
					SharedObject componentInChildren = value.GetComponentInChildren<SharedObject>(true);
					if (componentInChildren != null && componentInChildren.GetId() == item && hashSet.Add(item))
					{
						Add(value, item, SlotKind.Resource, null, -1, -1);
						num3++;
					}
				}
			}
			Ready = true;
			WaveRunPlugin.Log.LogInfo((object)$"Snapshot: {num} resources, {num2} pickups, {num3} cave ores, {Templates.Count} templates, list={instance.list.Count}");
		}

		private static bool TryKind(GameObject go, out SlotKind kind)
		{
			kind = SlotKind.Resource;
			HitableResource component = go.GetComponent<HitableResource>();
			if ((Object)(object)component != (Object)null)
			{
				return !(component is HitableChest);
			}
			if ((Object)(object)go.GetComponentInChildren<PickupInteract>(true) == (Object)null)
			{
				return false;
			}
			kind = SlotKind.Pickup;
			return go.GetComponentInChildren<SharedObject>(true) != null;
		}

		private static void Add(GameObject go, int id, SlotKind kind, DrawChunks dc, int chunk, int index)
		{
			//IL_0008: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: 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_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: 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_0041: Unknown result type (might be due to invalid IL or missing references)
			Transform transform = go.transform;
			Vector3 scale = transform.localScale;
			if (kind == SlotKind.Pickup)
			{
				PickupInteract componentInChildren = go.GetComponentInChildren<PickupInteract>(true);
				if ((Object)(object)componentInChildren != (Object)null && (Object)(object)((Component)componentInChildren).transform == (Object)(object)transform)
				{
					scale = _pickupDefaultScale.Invoke(componentInChildren);
				}
			}
			Records.Add(new Record
			{
				Id = id,
				Name = ((Object)go).name,
				Position = transform.position,
				Rotation = transform.rotation,
				Scale = scale,
				WasActive = go.activeSelf,
				Chunks = dc,
				Chunk = chunk,
				Index = index,
				Kind = kind
			});
			Slots.Add(new Slot(id, kind, transform.position.x, transform.position.y, transform.position.z));
			if (!Templates.ContainsKey((((Object)go).name, kind)))
			{
				GameObject val = Object.Instantiate<GameObject>(go, _holder.transform);
				val.SetActive(false);
				Templates.Add((((Object)go).name, kind), val);
			}
		}

		public static List<int> PlanSkips(ResetMask mask, IReadOnlyList<Spot> occupants)
		{
			ResourceManager instance = ResourceManager.Instance;
			if (!Ready || (Object)(object)instance == (Object)null || instance.list == null)
			{
				return new List<int>();
			}
			List<Box> list = new List<Box>();
			if (instance.builds != null)
			{
				Physics.SyncTransforms();
				foreach (GameObject value in instance.builds.Values)
				{
					if ((Object)(object)value != (Object)null && TryBuildBox(value, out var box))
					{
						list.Add(box);
					}
				}
			}
			int underBuilds;
			int occupied;
			List<int> list2 = ResetPlan.Skips(Slots, new HashSet<int>(instance.list.Keys), list, occupants, mask, out underBuilds, out occupied);
			WaveRunPlugin.Log.LogInfo((object)$"Reset plan: {list2.Count} slots stay empty ({underBuilds} under builds, {occupied} next to a player or mob)");
			return list2;
		}

		public static void Reset(ResetMask mask, ISet<int> skip, int resetSeq)
		{
			ResourceManager instance = ResourceManager.Instance;
			if (!Ready || (Object)(object)instance == (Object)null || instance.list == null)
			{
				return;
			}
			HashSet<int> present = new HashSet<int>(instance.list.Keys);
			IReadOnlyList<int> readOnlyList = ResetPlan.Select(Slots, present, skip, mask);
			int num = 0;
			int num2 = 0;
			foreach (int item in readOnlyList)
			{
				try
				{
					if (Respawn(instance, Records[item]))
					{
						num++;
					}
					else
					{
						num2++;
					}
				}
				catch (Exception arg)
				{
					num2++;
					WaveRunPlugin.Log.LogError((object)$"Wave Run reset failed for id {Records[item].Id} ({Records[item].Name}): {arg}");
				}
			}
			string text = ((num2 > 0) ? $", {num2} failed" : "");
			int num3 = skip?.Count ?? 0;
			WaveRunPlugin.Log.LogInfo((object)$"Reset {resetSeq}: respawned {num} of {Slots.Count} ({num3} kept empty by the host{text})");
		}

		private static bool Respawn(ResourceManager rm, Record r)
		{
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			if (!Templates.TryGetValue((r.Name, r.Kind), out var value) || (Object)(object)value == (Object)null)
			{
				return false;
			}
			int num;
			if ((Object)(object)r.Chunks != (Object)null && r.Chunks.chunks != null && r.Chunk < r.Chunks.chunks.Length && r.Chunks.chunks[r.Chunk] != null)
			{
				num = ((r.Index < r.Chunks.chunks[r.Chunk].Count) ? 1 : 0);
				if (num != 0)
				{
					GameObject val = r.Chunks.chunks[r.Chunk][r.Index];
					if ((Object)(object)val != (Object)null)
					{
						Object.Destroy((Object)(object)val);
					}
				}
			}
			else
			{
				num = 0;
			}
			GameObject val2 = Object.Instantiate<GameObject>(value, r.Position, r.Rotation);
			((Object)val2).name = r.Name;
			val2.transform.localScale = r.Scale;
			val2.GetComponentInChildren<SharedObject>(true).SetId(r.Id);
			HitableResource component = val2.GetComponent<HitableResource>();
			if ((Object)(object)component != (Object)null)
			{
				component.SetDefaultScale(r.Scale);
				component.PopIn();
			}
			rm.AddObject(r.Id, val2);
			if (num != 0)
			{
				r.Chunks.chunks[r.Chunk][r.Index] = val2;
				bool[] array = _visibleChunks.Invoke(r.Chunks);
				int[] array2 = _chunkLod.Invoke(r.Chunks);
				bool active = array != null && array2 != null && r.Chunk < array.Length && r.Chunk < array2.Length && array[r.Chunk] && array2[r.Chunk] > 0 && r.Index % array2[r.Chunk] == 0;
				val2.SetActive(active);
			}
			else
			{
				val2.SetActive(r.WasActive);
			}
			return true;
		}

		private static bool TryBuildBox(GameObject build, out Box box)
		{
			//IL_0017: 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)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			box = default(Box);
			Collider[] componentsInChildren = build.GetComponentsInChildren<Collider>();
			if (componentsInChildren.Length == 0)
			{
				return false;
			}
			Bounds bounds = componentsInChildren[0].bounds;
			for (int i = 1; i < componentsInChildren.Length; i++)
			{
				((Bounds)(ref bounds)).Encapsulate(componentsInChildren[i].bounds);
			}
			((Bounds)(ref bounds)).Expand(1f);
			box = new Box(((Bounds)(ref bounds)).min.x, ((Bounds)(ref bounds)).min.y, ((Bounds)(ref bounds)).min.z, ((Bounds)(ref bounds)).max.x, ((Bounds)(ref bounds)).max.y, ((Bounds)(ref bounds)).max.z);
			return true;
		}

		private static void ClearState()
		{
			Ready = false;
			Slots.Clear();
			Records.Clear();
			Templates.Clear();
			if ((Object)(object)_holder != (Object)null)
			{
				Object.Destroy((Object)(object)_holder);
			}
			_holder = null;
		}

		public static void Clear()
		{
			ClearState();
			Loaded = false;
			CaveIds.Clear();
		}
	}
	internal static class MapCover
	{
		private static readonly Color Backing = new Color(0f, 0f, 0f, 0.92f);

		private static readonly Color BarBack = new Color(1f, 1f, 1f, 0.15f);

		private static readonly Color BarFill = new Color(1f, 1f, 1f, 0.85f);

		private static readonly Color Shadow = new Color(0f, 0f, 0f, 0.9f);

		private static Texture2D _white;

		private static GUIStyle _title;

		private static GUIStyle _step;

		private static int _builtHeight = -1;

		private static float _builtScale = -1f;

		private static float _scale = 1f;

		private static string _heading = "Switching island";

		private static string _line = "";

		private static float _progress;

		public static bool Showing { get; private set; }

		public static void Show(string mapName)
		{
			Showing = true;
			_heading = (string.IsNullOrEmpty(mapName) ? "Switching island" : ("Switching island: " + mapName));
			_line = "";
			_progress = 0f;
		}

		public static void Step(string step)
		{
			_line = RebuildSteps.Label(step);
			int num = RebuildSteps.IndexOf(step);
			_progress = ((num < 0) ? _progress : (((float)num + 1f) / ((float)RebuildSteps.Names.Count + 1f)));
		}

		public static void Waiting(string line)
		{
			_line = line;
			_progress = (float)RebuildSteps.Names.Count / ((float)RebuildSteps.Names.Count + 1f);
		}

		public static void Hide()
		{
			Showing = false;
		}

		public static void Draw(float hudScale)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected O, but got Unknown
			//IL_0042: 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_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			//IL_0057: 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_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: 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_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			if (Showing)
			{
				EnsureStyles(hudScale);
				Fill(new Rect(0f, 0f, (float)Screen.width, (float)Screen.height), Backing);
				Vector2 val = _title.CalcSize(new GUIContent(_heading));
				Vector2 val2 = _step.CalcSize(new GUIContent(_line));
				float num = (float)Screen.width * 0.4f;
				float num2 = Mathf.Max(4f, 12f * _scale);
				float num3 = 16f * _scale;
				float num4 = val.y + num3 + val2.y + num3 + num2;
				float num5 = ((float)Screen.height - num4) / 2f;
				Label(new Rect(((float)Screen.width - val.x) / 2f, num5, val.x, val.y), _heading, _title);
				num5 += val.y + num3;
				Label(new Rect(((float)Screen.width - val2.x) / 2f, num5, val2.x, val2.y), _line, _step);
				num5 += val2.y + num3;
				Rect r = default(Rect);
				((Rect)(ref r))..ctor(((float)Screen.width - num) / 2f, num5, num, num2);
				Fill(r, BarBack);
				Fill(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y, ((Rect)(ref r)).width * Mathf.Clamp01(_progress), ((Rect)(ref r)).height), BarFill);
			}
		}

		private static void EnsureStyles(float hudScale)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Expected O, but got Unknown
			//IL_0028: 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_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Expected O, but got Unknown
			//IL_00d3: 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_00e2: Expected O, but got Unknown
			//IL_00e7: Expected O, but got Unknown
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Expected O, but got Unknown
			if ((Object)(object)_white == (Object)null)
			{
				_white = new Texture2D(1, 1)
				{
					hideFlags = (HideFlags)61
				};
				_white.SetPixel(0, 0, Color.white);
				_white.Apply();
			}
			if (Screen.height != _builtHeight || hudScale != _builtScale || _title == null)
			{
				_builtHeight = Screen.height;
				_builtScale = hudScale;
				_scale = Mathf.Clamp((float)Screen.height / 1080f * hudScale, 0.5f, 3f);
				_title = new GUIStyle(GUI.skin.label)
				{
					fontSize = Mathf.RoundToInt(56f * _scale),
					fontStyle = (FontStyle)1,
					alignment = (TextAnchor)4,
					wordWrap = false,
					padding = new RectOffset(0, 0, 0, 0),
					margin = new RectOffset(0, 0, 0, 0)
				};
				_title.normal.textColor = Color.white;
				_step = new GUIStyle(_title)
				{
					fontSize = Mathf.RoundToInt(30f * _scale),
					fontStyle = (FontStyle)0
				};
			}
		}

		private static void Fill(Rect r, Color c)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			Color color = GUI.color;
			GUI.color = c;
			GUI.DrawTexture(r, (Texture)(object)_white);
			GUI.color = color;
		}

		private static void Label(Rect r, string text, GUIStyle style)
		{
			//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_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Max(1f, Mathf.Round(2f * _scale));
			Color textColor = style.normal.textColor;
			style.normal.textColor = Shadow;
			GUI.Label(new Rect(((Rect)(ref r)).x + num, ((Rect)(ref r)).y + num, ((Rect)(ref r)).width, ((Rect)(ref r)).height), text, style);
			style.normal.textColor = Color.white;
			GUI.Label(r, text, style);
			style.normal.textColor = textColor;
		}

		public static void Dispose()
		{
			if ((Object)(object)_white != (Object)null)
			{
				Object.Destroy((Object)(object)_white);
			}
			_white = null;
		}
	}
	internal static class MobMover
	{
		internal static readonly FieldInfo[] StaleOffsetFields = new FieldInfo[2]
		{
			typeof(Mob).GetField("offsetPosition", BindingFlags.Instance | BindingFlags.NonPublic),
			typeof(Mob).GetField("offsetDir", BindingFlags.Instance | BindingFlags.NonPublic)
		};

		public static void Warp(Mob mob, Vector3 pos)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			NavMeshAgent agent = mob.agent;
			if ((Object)(object)agent == (Object)null || !((Behaviour)agent).enabled || !agent.Warp(pos))
			{
				((Component)mob).transform.position = pos;
			}
			FieldInfo[] staleOffsetFields = StaleOffsetFields;
			foreach (FieldInfo fieldInfo in staleOffsetFields)
			{
				if (fieldInfo != null && fieldInfo.FieldType == typeof(Vector3))
				{
					fieldInfo.SetValue(mob, Vector3.zero);
				}
			}
		}
	}
	internal static class MobResyncNet
	{
		private readonly struct SpawnArgs
		{
			public readonly int MobType;

			public readonly float Multiplier;

			public readonly float BossMultiplier;

			public readonly int GuardianType;

			public readonly int ZoneId;

			public SpawnArgs(int mobType, float multiplier, float bossMultiplier, int guardianType, int zoneId)
			{
				MobType = mobType;
				Multiplier = multiplier;
				BossMultiplier = bossMultiplier;
				GuardianType = guardianType;
				ZoneId = zoneId;
			}
		}

		private const int MobSpawnPacket = 30;

		private const int MobZoneSpawnPacket = 37;

		private static FieldRef<Packet, int> _readPos;

		private static Action<int, Packet> _sendTo;

		private static readonly MissingMobs Missing = new MissingMobs();

		private static readonly ResendBook Resent = new ResendBook();

		private static readonly Dictionary<int, SpawnArgs> Spawns = new Dictionary<int, SpawnArgs>();

		private static float _sinceAsk;

		private static bool ClientGuards
		{
			get
			{
				if (RunMode.Current == GameModeChoice.WaveRun)
				{
					return !LocalClient.serverOwner;
				}
				return false;
			}
		}

		private static bool HostAnswers
		{
			get
			{
				if (RunMode.Current == GameModeChoice.WaveRun)
				{
					return LocalClient.serverOwner;
				}
				return false;
			}
		}

		public static void Bind()
		{
			_readPos = AccessTools.FieldRefAccess<Packet, int>("readPos");
			MethodInfo methodInfo = AccessTools.Method(typeof(ServerSend), "SendTCPData", new Type[2]
			{
				typeof(int),
				typeof(Packet)
			}, (Type[])null);
			if (methodInfo == null)
			{
				throw new MissingMethodException("ServerSend", "SendTCPData(int, Packet)");
			}
			_sendTo = (Action<int, Packet>)Delegate.CreateDelegate(typeof(Action<int, Packet>), methodInfo);
		}

		public static bool OnClientPacket(string handler, Packet packet)
		{
			if (!ClientGuards || !MobPackets.TryGet(handler, out var packet2))
			{
				return true;
			}
			MobManager instance = MobManager.Instance;
			MobZoneManager instance2 = MobZoneManager.Instance;
			if ((Object)(object)instance == (Object)null || instance.mobs == null)
			{
				return true;
			}
			int num = _readPos.Invoke(packet);
			int num2 = ((packet2.MobIdAt >= 0) ? PeekInt(packet, num + packet2.MobIdAt) : (-1));
			int num3 = ((packet2.ZoneIdAt >= 0) ? PeekInt(packet, num + packet2.ZoneIdAt) : (-1));
			if (num2 >= 0 && instance.mobs.TryGetValue(num2, out var value) && (Object)(object)value == (Object)null)
			{
				instance.mobs.Remove(num2);
			}
			Mob value2;
			bool mobKnown = num2 >= 0 && instance.mobs.TryGetValue(num2, out value2) && (Object)(object)value2 != (Object)null;
			SpawnZone value3;
			bool zoneKnown = (Object)(object)instance2 != (Object)null && instance2.zones != null && instance2.zones.TryGetValue(num3, out value3) && (Object)(object)value3 != (Object)null;
			switch (MobPackets.Decide(packet2, mobKnown, zoneKnown))
			{
			case MobPacketVerdict.SkipAndAsk:
				if (Missing.Note(num2))
				{
					WaveRunPlugin.Log.LogInfo((object)$"Wave Run: {handler} names mob {num2}, which this game does not have. Asking the host for it.");
				}
				return false;
			case MobPacketVerdict.Skip:
				WaveRunner.Verbose($"Wave Run: dropped {handler} for mob {num2}, zone {num3}.");
				return false;
			case MobPacketVerdict.SpawnWithoutZone:
				SpawnWithoutZone(packet, num3);
				return false;
			case MobPacketVerdict.FireWithoutMob:
				FireWithoutMob(packet);
				return false;
			default:
				return true;
			}
		}

		private static int PeekInt(Packet packet, int at)
		{
			int num = _readPos.Invoke(packet);
			try
			{
				_readPos.Invoke(packet) = at;
				return packet.ReadInt(false);
			}
			finally
			{
				_readPos.Invoke(packet) = num;
			}
		}

		private static void SpawnWithoutZone(Packet packet, int zoneId)
		{
			//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_0029: 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)
			Vector3 val = packet.ReadVector3(true);
			int num = packet.ReadInt(true);
			int num2 = packet.ReadInt(true);
			Mob component = Object.Instantiate<GameObject>(MobSpawner.Instance.allMobs[num].mobPrefab, val, Quaternion.identity).GetComponent<Mob>();
			((Component)component).tag = "DontCount";
			MobManager.Instance.AddMob(component, num2);
			WaveRunPlugin.Log.LogWarning((object)$"Wave Run: zone {zoneId} is not on this island yet, so mob {num2} spawned without it.");
		}

		private static void FireWithoutMob(Packet packet)
		{
			//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_000a: 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_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = packet.ReadVector3(true);
			Vector3 val2 = packet.ReadVector3(true);
			float num = packet.ReadFloat(true);
			int key = packet.ReadInt(true);
			packet.ReadInt(true);
			InventoryItem val3 = ItemManager.Instance.allItems[key];
			GameObject obj = Object.Instantiate<GameObject>(val3.prefab, val, Quaternion.LookRotation(val2));
			Rigidbody component = obj.GetComponent<Rigidbody>();
			if ((Object)(object)component != (Object)null)
			{
				component.AddForce(val2 * num * val3.bowComponent.projectileSpeed);
				component.angularVelocity = val3.rotationOffset;
			}
			EnemyProjectile component2 = obj.GetComponent<EnemyProjectile>();
			component2.DisableCollider(val3.bowComponent.colliderDisabledTime);
			component2.damage = val3.attackDamage;
		}

		public static void Tick(float dt)
		{
			if (!ClientGuards)
			{
				return;
			}
			_sinceAsk += dt;
			if (!(_sinceAsk < 1f))
			{
				_sinceAsk = 0f;
				List<int> list = Missing.TakeDue(Time.time);
				if (list.Count != 0)
				{
					ClientSend.SendChatMessage(MobNeedWire.Encode(list));
					WaveRunner.Verbose($"Wave Run: asked the host for {list.Count} missing mobs.");
				}
			}
		}

		public static void RecordSpawn(int mobType, int mobId, float multiplier, float bossMultiplier, int guardianType)
		{
			if (HostAnswers)
			{
				Spawns[mobId] = new SpawnArgs(mobType, multiplier, bossMultiplier, guardianType, -1);
			}
		}

		public static void RecordZoneSpawn(int mobType, int mobId, int zoneId)
		{
			if (HostAnswers)
			{
				Spawns[mobId] = new SpawnArgs(mobType, 1f, 1f, -1, zoneId);
			}
		}

		public static void Answer(int clientId, List<int> mobIds)
		{
			if (!HostAnswers || (Object)(object)MobManager.Instance == (Object)null || MobManager.Instance.mobs == null)
			{
				return;
			}
			int num = 0;
			foreach (int mobId in mobIds)
			{
				if (MobManager.Instance.mobs.TryGetValue(mobId, out var value) && !((Object)(object)value == (Object)null) && !((Object)(object)value.mobType == (Object)null) && Resent.Allow(clientId, mobId, Time.time))
				{
					SendMob(clientId, mobId, value);
					num++;
				}
			}
			if (num > 0)
			{
				WaveRunPlugin.Log.LogInfo((object)$"Wave Run: sent {num} of {mobIds.Count} asked mobs again to client {clientId}.");
			}
		}

		public static void SendAllTo(int clientId)
		{
			if (!HostAnswers || (Object)(object)MobManager.Instance == (Object)null || MobManager.Instance.mobs == null)
			{
				return;
			}
			int num = 0;
			foreach (KeyValuePair<int, Mob> mob in MobManager.Instance.mobs)
			{
				if (!((Object)(object)mob.Value == (Object)null) && !((Object)(object)mob.Value.mobType == (Object)null))
				{
					SendMob(clientId, mob.Key, mob.Value);
					num++;
				}
			}
			WaveRunPlugin.Log.LogInfo((object)$"Wave Run: sent all {num} live mobs to client {clientId}.");
		}

		private static void SendMob(int clientId, int id, Mob mob)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Expected O, but got Unknown
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			SpawnArgs value;
			SpawnArgs spawnArgs = (Spawns.TryGetValue(id, out value) ? value : new SpawnArgs(mob.mobType.id, mob.multiplier, mob.bossMultiplier, -1, -1));
			Packet val = new Packet((spawnArgs.ZoneId >= 0) ? 37 : 30);
			try
			{
				val.Write(((Component)mob).transform.position);
				val.Write(spawnArgs.MobType);
				val.Write(id);
				if (spawnArgs.ZoneId >= 0)
				{
					val.Write(spawnArgs.ZoneId);
				}
				else
				{
					val.Write(spawnArgs.Multiplier);
					val.Write(spawnArgs.BossMultiplier);
					val.Write(spawnArgs.GuardianType);
				}
				_sendTo(clientId, val);
			}
			finally
			{
				((IDisposable)val)?.Dispose();
			}
		}

		public static void Clear()
		{
			Missing.Clear();
			Resent.Clear();
			Spawns.Clear();
			_sinceAsk = 0f;
		}
	}
	internal static class RunChannel
	{
		public const int ChatPacketId = 40;

		private static Action<Packet> _sendToAll;

		public static void Bind()
		{
			MethodInfo methodInfo = AccessTools.Method(ty