Decompiled source of Knarr v1.0.0

plugins/Knarr.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Knarr.Core;
using Knarr.Dev;
using Knarr.Gameplay;
using Microsoft.CodeAnalysis;
using UnityEngine;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Knarr
{
	public static class Cfg
	{
		public static ConfigEntry<string> Recipe;

		public static ConfigEntry<string> Station;

		public static ConfigEntry<string> Hold;

		public static ConfigEntry<float> SpeedBonus;

		public static ConfigEntry<float> EncounterFactor;

		public static ConfigEntry<bool> StirMessage;

		public static void Bind(ConfigFile 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_000d: Expected O, but got Unknown
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Expected O, but got Unknown
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Expected O, but got Unknown
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Expected O, but got Unknown
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Expected O, but got Unknown
			ConfigurationManagerAttributes val = new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			};
			Recipe = c.Bind<string>("1 - Knarr", "Recipe", "FineWood:50,ElderBark:40,IronNails:120,LinenThread:40,LoxPelt:6", new ConfigDescription("Build cost of the knarr, \"Item:amount,Item:amount\" (item prefab names).", (AcceptableValueBase)null, new object[1] { val }));
			Station = c.Bind<string>("1 - Knarr", "Station", "piece_workbench", new ConfigDescription("Crafting station that has to be near when building the knarr (prefab name, empty for none). Ships are built at the workbench in the game.", (AcceptableValueBase)null, new object[1] { val }));
			Hold = c.Bind<string>("1 - Knarr", "Hold", 8 + "x" + 7, new ConfigDescription("Slots of the hold, width x height (up to 8 wide and 12 high; taller holds scroll). 8x7 = 56 is more than any chest in the game. A knarr's hold only ever grows: a bigger value applies to every knarr, a smaller one only to knarrs built afterwards (so no item is ever lost).", (AcceptableValueBase)null, new object[1] { val }));
			SpeedBonus = c.Bind<float>("1 - Knarr", "SpeedBonus", 10f, new ConfigDescription("How much faster than the longship the knarr sails under full sail, in percent (its bigger sail). 0 sails exactly like the longship.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 50f), new object[1] { val }));
			EncounterFactor = c.Bind<float>("1 - Knarr", "EncounterFactor", 4f, new ConfigDescription("While a knarr with someone aboard sails through a stretch of sea, the chance of the sea monsters there (serpents and the like) is multiplied by this. They still keep their own rules: a serpent comes only at night or in rain and storm. 1 is the vanilla chance.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), new object[1] { val }));
			StirMessage = c.Bind<bool>("2 - Client", "StirMessage", true, "Show \"Something stirs beneath the keel …\" when a sea monster rises near the knarr you are on.");
		}

		public static void HoldSize(out int width, out int height)
		{
			KnarrMath.ParseHold(Hold?.Value, out width, out height);
		}
	}
	public static class Lang
	{
		private static readonly (string key, string en, string de)[] Texts = new(string, string, string)[4]
		{
			("knarr", "Knarr", "Knorr"),
			("knarr_desc", "The broad-bellied trading ship of the north: a fifth bigger than the longship, with room on deck for a whole crew and a deep hold that takes more than any chest. Its great linen sail makes it a little faster; it steers like the longship. A ship this big rides loud on the water – what lives below hears it.", "Das bauchige Handelsschiff des Nordens: ein Fünftel größer als das Langschiff, mit Platz an Deck für eine ganze Mannschaft und einem tiefen Laderaum, der mehr fasst als jede Truhe. Das große Leinensegel macht es ein wenig schneller; es steuert sich wie das Langschiff. Ein so großes Schiff liegt laut im Wasser – was darunter lebt, hört es."),
			("knarr_hold", "Hold", "Laderaum"),
			("knarr_msg_stir", "Something stirs beneath the keel …", "Etwas regt sich unter dem Kiel …")
		};

		public static void Register()
		{
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			Dictionary<string, string> dictionary2 = new Dictionary<string, string>();
			(string, string, string)[] texts = Texts;
			for (int i = 0; i < texts.Length; i++)
			{
				(string, string, string) tuple = texts[i];
				string item = tuple.Item1;
				string item2 = tuple.Item2;
				string item3 = tuple.Item3;
				dictionary[item] = item2;
				dictionary2[item] = item3;
			}
			string text = "English";
			localization.AddTranslation(ref text, dictionary);
			text = "German";
			localization.AddTranslation(ref text, dictionary2);
		}

		public static string T(string token, params object[] args)
		{
			string text = ((Localization.instance != null) ? Localization.instance.Localize("$" + token) : token);
			if (args == null || args.Length == 0)
			{
				return text;
			}
			try
			{
				return string.Format(text, args);
			}
			catch (FormatException)
			{
				return text;
			}
		}
	}
	public static class Pieces
	{
		[HarmonyPatch(typeof(Game), "Start")]
		private static class ApplyOnWorldStart
		{
			private static void Postfix()
			{
				ApplyConfig();
			}
		}

		public const string Prefab = "zap_knarr";

		private const string BasePrefab = "VikingShip";

		public static GameObject KnarrPrefab { get; private set; }

		public static void Init()
		{
			PrefabManager.OnVanillaPrefabsAvailable += Add;
			SynchronizationManager.OnConfigurationSynchronized += delegate
			{
				ApplyConfig();
			};
			Cfg.Recipe.SettingChanged += delegate
			{
				ApplyConfig();
			};
			Cfg.Station.SettingChanged += delegate
			{
				ApplyConfig();
			};
			Cfg.Hold.SettingChanged += delegate
			{
				ApplyConfig();
			};
			Cfg.SpeedBonus.SettingChanged += delegate
			{
				ApplyConfig();
			};
		}

		public static void ApplyConfig()
		{
			if ((Object)(object)KnarrPrefab == (Object)null)
			{
				return;
			}
			Apply(KnarrPrefab, Cfg.Recipe.Value, Cfg.Station.Value);
			KnarrShip.Configure(KnarrPrefab);
			foreach (KnarrShip item in KnarrShip.All)
			{
				if ((Object)(object)item != (Object)null)
				{
					KnarrShip.Configure(((Component)item).gameObject);
				}
			}
		}

		internal static GameObject FindPrefab(string name)
		{
			if (string.IsNullOrEmpty(name))
			{
				return null;
			}
			GameObject val = null;
			if ((Object)(object)ObjectDB.instance != (Object)null)
			{
				val = ObjectDB.instance.GetItemPrefab(name);
			}
			if ((Object)(object)val == (Object)null && (Object)(object)ZNetScene.instance != (Object)null)
			{
				val = ZNetScene.instance.GetPrefab(name);
			}
			if ((Object)(object)val == (Object)null)
			{
				try
				{
					val = PrefabManager.Instance.GetPrefab(name);
				}
				catch (Exception)
				{
				}
			}
			return val;
		}

		internal static void Apply(GameObject prefab, string recipe, string station)
		{
			//IL_0088: 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_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Expected O, but got Unknown
			try
			{
				Piece val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<Piece>() : null);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				List<Requirement> list = new List<Requirement>();
				foreach (RequirementConfig item in ParseRecipe(recipe))
				{
					GameObject obj = FindPrefab(item.Item);
					ItemDrop val2 = ((obj != null) ? obj.GetComponent<ItemDrop>() : null);
					if ((Object)(object)val2 == (Object)null)
					{
						KnarrPlugin.Log.LogWarning((object)(((Object)prefab).name + " recipe: unknown item '" + item.Item + "'."));
						continue;
					}
					list.Add(new Requirement
					{
						m_resItem = val2,
						m_amount = item.Amount,
						m_recover = true
					});
				}
				if (list.Count > 0)
				{
					val.m_resources = list.ToArray();
				}
				if (string.IsNullOrWhiteSpace(station))
				{
					val.m_craftingStation = null;
					return;
				}
				GameObject obj2 = FindPrefab(station.Trim());
				CraftingStation val3 = ((obj2 != null) ? obj2.GetComponent<CraftingStation>() : null);
				if ((Object)(object)val3 != (Object)null)
				{
					val.m_craftingStation = val3;
				}
				else
				{
					KnarrPlugin.Log.LogWarning((object)(((Object)prefab).name + " station: unknown crafting station '" + station + "'."));
				}
			}
			catch (Exception ex)
			{
				KnarrPlugin.Log.LogWarning((object)("Could not apply the recipe of " + ((prefab != null) ? ((Object)prefab).name : null) + ": " + ex.Message));
			}
		}

		private static void Add()
		{
			//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_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: 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_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Expected O, but got Unknown
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			try
			{
				PieceConfig val = new PieceConfig
				{
					Name = "$knarr",
					Description = "$knarr_desc",
					PieceTable = "Hammer",
					Category = "Misc",
					CraftingStation = (Cfg.Station.Value ?? "")
				};
				foreach (RequirementConfig item in ParseRecipe(Cfg.Recipe.Value))
				{
					val.AddRequirement(item);
				}
				CustomPiece val2 = new CustomPiece("zap_knarr", "VikingShip", val);
				if ((Object)(object)val2.PiecePrefab == (Object)null)
				{
					KnarrPlugin.Log.LogError((object)"Cloning VikingShip failed; no knarr.");
					return;
				}
				if (string.IsNullOrWhiteSpace(Cfg.Station.Value))
				{
					Piece component = val2.PiecePrefab.GetComponent<Piece>();
					if ((Object)(object)component != (Object)null)
					{
						component.m_craftingStation = null;
					}
				}
				Rebuild(val2.PiecePrefab);
				val2.Piece.m_icon = RenderIcon(val2.PiecePrefab) ?? val2.Piece.m_icon;
				PieceManager.Instance.AddPiece(val2);
				KnarrPrefab = val2.PiecePrefab;
				KnarrShip.Configure(KnarrPrefab);
			}
			catch (Exception ex)
			{
				KnarrPlugin.Log.LogError((object)("Could not register the knarr: " + ex));
			}
			finally
			{
				PrefabManager.OnVanillaPrefabsAvailable -= Add;
			}
		}

		private static void Rebuild(GameObject prefab)
		{
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Expected O, but got Unknown
			//IL_00be: 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_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			Transform transform = prefab.transform;
			MonoBehaviour[] componentsInChildren = prefab.GetComponentsInChildren<MonoBehaviour>(true);
			foreach (MonoBehaviour val in componentsInChildren)
			{
				if (!((Object)(object)val == (Object)null) && FromLongshipMod(((object)val).GetType()))
				{
					KnarrPlugin.Log.LogInfo((object)("Knarr: removing " + ((object)val).GetType().FullName + " on " + ((Object)val).name + ", taken over from the longship."));
					Object.DestroyImmediate((Object)(object)val);
				}
			}
			Ship component = prefab.GetComponent<Ship>();
			BoxCollider floatCollider = component.m_floatCollider;
			AddSeats(transform);
			foreach (Transform item in transform)
			{
				Transform val2 = item;
				val2.localPosition *= 1.2f;
				if (!((Object)(object)floatCollider != (Object)null) || !((Object)(object)val2 == (Object)(object)((Component)floatCollider).transform))
				{
					val2.localScale *= 1.2f;
				}
			}
			if ((Object)(object)floatCollider != (Object)null)
			{
				floatCollider.size *= 1.2f;
				floatCollider.center *= 1.2f;
			}
			LODGroup component2 = prefab.GetComponent<LODGroup>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.size *= 1.2f;
			}
			component.m_waterLevelOffset *= 1.2f;
			component.m_forceDistance *= 1.2f;
			component.m_sailForceOffset *= 1.2f;
			component.m_stearForceOffset *= 1.2f;
			component.m_stearVelForceFactor *= 1.2f;
			component.m_stearForce *= 1.2f;
			Rigidbody component3 = prefab.GetComponent<Rigidbody>();
			if ((Object)(object)component3 != (Object)null)
			{
				component3.mass = 3000f;
			}
			WearNTear component4 = prefab.GetComponent<WearNTear>();
			if ((Object)(object)component4 != (Object)null)
			{
				component4.m_health = 1500f;
			}
			Transform[] componentsInChildren2 = prefab.GetComponentsInChildren<Transform>(true);
			foreach (Transform val3 in componentsInChildren2)
			{
				if (((Object)val3).name == "skull_head")
				{
					((Component)val3).gameObject.SetActive(false);
				}
				else if (((Object)val3).name == "head_collider")
				{
					Object.DestroyImmediate((Object)(object)((Component)val3).gameObject);
				}
			}
			Container componentInChildren = prefab.GetComponentInChildren<Container>(true);
			if ((Object)(object)componentInChildren != (Object)null)
			{
				componentInChildren.m_name = "$knarr_hold";
				Cfg.HoldSize(out componentInChildren.m_width, out componentInChildren.m_height);
			}
			prefab.AddComponent<KnarrShip>();
		}

		private static void AddSeats(Transform root)
		{
			//IL_00a7: 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_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			Transform val = null;
			Chair[] componentsInChildren = ((Component)root).GetComponentsInChildren<Chair>(true);
			foreach (Chair val2 in componentsInChildren)
			{
				if (((Object)val2).name == "sit_box")
				{
					val = ((Component)val2).transform;
					break;
				}
			}
			if ((Object)(object)val == (Object)null)
			{
				KnarrPlugin.Log.LogWarning((object)"Knarr: the longship's seat box was not found; no extra seats.");
				return;
			}
			float[] array = new float[2] { 1.5f, -1.5f };
			foreach (float num in array)
			{
				Transform transform = Object.Instantiate<GameObject>(((Component)val).gameObject, val.parent).transform;
				((Object)transform).name = ((num > 0f) ? "sit_box_knarr_r" : "sit_box_knarr_l");
				transform.localPosition = new Vector3(num, val.localPosition.y, 1.9f);
				transform.localRotation = val.localRotation;
				transform.localScale = val.localScale;
			}
		}

		private static bool FromLongshipMod(Type t)
		{
			try
			{
				return t.Assembly.GetName().Name.IndexOf("LongshipUpgrades", StringComparison.OrdinalIgnoreCase) >= 0;
			}
			catch (Exception)
			{
				return false;
			}
		}

		internal static Sprite RenderIcon(GameObject prefab)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: 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_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			if (GUIManager.IsHeadless())
			{
				return null;
			}
			try
			{
				return RenderManager.Instance.Render(new RenderRequest(prefab)
				{
					Rotation = Quaternion.Euler(-18f, 135f, 0f),
					Width = 128,
					Height = 128,
					ParticleSimulationTime = -1f,
					UseCache = false
				});
			}
			catch (Exception ex)
			{
				KnarrPlugin.Log.LogWarning((object)("Icon rendering failed: " + ex.Message));
				return null;
			}
		}

		public static List<RequirementConfig> ParseRecipe(string recipe)
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Expected O, but got Unknown
			List<RequirementConfig> list = new List<RequirementConfig>();
			if (string.IsNullOrWhiteSpace(recipe))
			{
				return list;
			}
			string[] array = recipe.Split(new char[2] { ',', ';' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text in array)
			{
				string[] array2 = text.Split(new char[1] { ':' });
				if (array2.Length == 2 && int.TryParse(array2[1].Trim(), out var result) && result > 0 && array2[0].Trim().Length > 0)
				{
					list.Add(new RequirementConfig(array2[0].Trim(), result, 0, true));
				}
				else
				{
					KnarrPlugin.Log.LogWarning((object)("Ignoring invalid recipe entry '" + text + "'."));
				}
			}
			return list;
		}
	}
	[BepInPlugin("zap.knarr", "Knarr", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class KnarrPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "zap.knarr";

		public const string PluginName = "Knarr";

		public const string PluginVersion = "1.0.0";

		private Harmony _harmony;

		public static KnarrPlugin Instance { get; private set; }

		public static string PluginDir { get; private set; }

		public static ManualLogSource Log { get; private set; }

		public static string AssetPath(string fileName)
		{
			return Path.Combine(PluginDir ?? "", "Assets", fileName);
		}

		private void Awake()
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			PluginDir = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
			Cfg.Bind(((BaseUnityPlugin)this).Config);
			Lang.Register();
			Pieces.Init();
			_harmony = new Harmony("zap.knarr");
			_harmony.PatchAll(typeof(KnarrPlugin).Assembly);
			SelfTest.TryStart(((Component)this).gameObject);
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
}
namespace Knarr.Gameplay
{
	public static class Encounters
	{
		[HarmonyPatch(typeof(SpawnSystem), "UpdateSpawnList")]
		private static class BoostNearKnarr
		{
			private static void Prefix(SpawnSystem __instance, List<SpawnData> spawners, bool eventSpawners, out List<KeyValuePair<SpawnData, float>> __state)
			{
				__state = null;
				try
				{
					float value = Cfg.EncounterFactor.Value;
					if (eventSpawners || value <= 1f || spawners == null || KnarrShip.All.Count == 0 || !KnarrSailsIn(__instance))
					{
						return;
					}
					foreach (SpawnData d in spawners)
					{
						if (IsSeaMonster(d))
						{
							if (__state == null)
							{
								__state = new List<KeyValuePair<SpawnData, float>>();
							}
							else if (__state.Exists((KeyValuePair<SpawnData, float> kv) => kv.Key == d))
							{
								continue;
							}
							__state.Add(new KeyValuePair<SpawnData, float>(d, d.m_spawnChance));
							d.m_spawnChance = KnarrMath.BoostedChance(d.m_spawnChance, value);
						}
					}
					if (__state != null)
					{
						_boosting = __instance;
					}
				}
				catch (Exception ex)
				{
					KnarrPlugin.Log.LogWarning((object)("Encounter boost failed: " + ex.Message));
				}
			}

			private static Exception Finalizer(Exception __exception, List<KeyValuePair<SpawnData, float>> __state)
			{
				if (__state != null)
				{
					for (int num = __state.Count - 1; num >= 0; num--)
					{
						__state[num].Key.m_spawnChance = __state[num].Value;
					}
				}
				_boosting = null;
				return __exception;
			}
		}

		[HarmonyPatch(typeof(SpawnSystem), "Spawn")]
		private static class WarnTheCrew
		{
			private static void Postfix(SpawnSystem __instance, SpawnData critter, Vector3 spawnPoint)
			{
				//IL_003f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0068: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)_boosting == (Object)null) && !((Object)(object)_boosting != (Object)(object)__instance) && !SpawnSystem.m_nospawn && IsSeaMonster(critter))
				{
					KnarrPlugin.Log.LogInfo((object)$"A {((Object)critter.m_prefab).name} rose at {spawnPoint} near a knarr.");
					ZRoutedRpc instance = ZRoutedRpc.instance;
					if (instance != null)
					{
						instance.InvokeRoutedRPC(0L, "Knarr_Stir", new object[1] { spawnPoint });
					}
				}
			}
		}

		[HarmonyPatch(typeof(Game), "Start")]
		private static class RegisterRpc
		{
			private static void Postfix()
			{
				ZRoutedRpc instance = ZRoutedRpc.instance;
				if (instance != null)
				{
					instance.Register<Vector3>("Knarr_Stir", (Action<long, Vector3>)RPC_Stir);
				}
			}
		}

		private const string RpcStir = "Knarr_Stir";

		private const float StirRange = 120f;

		public static int StirHeard;

		private static SpawnSystem _boosting;

		public static bool IsSeaMonster(SpawnData d)
		{
			if (d != null && d.m_enabled && (Object)(object)d.m_prefab != (Object)null && d.m_maxAltitude <= 0f)
			{
				return (Object)(object)d.m_prefab.GetComponent<MonsterAI>() != (Object)null;
			}
			return false;
		}

		public static bool KnarrSailsIn(SpawnSystem ss)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			foreach (KnarrShip item in KnarrShip.All)
			{
				if ((Object)(object)item != (Object)null && item.Manned() && ss.InsideZone(((Component)item).transform.position, 0f))
				{
					return true;
				}
			}
			return false;
		}

		private static void RPC_Stir(long sender, Vector3 point)
		{
			//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)
			if (Cfg.StirMessage.Value && !((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)MessageHud.instance == (Object)null))
			{
				KnarrShip knarrShip = KnarrShip.Local();
				if (!((Object)(object)knarrShip == (Object)null) && !(Utils.DistanceXZ(((Component)knarrShip).transform.position, point) > 120f))
				{
					MessageHud.instance.ShowMessage((MessageType)2, "$knarr_msg_stir", 0, (Sprite)null, false, true);
					StirHeard++;
				}
			}
		}
	}
	public class KnarrShip : MonoBehaviour
	{
		[HarmonyPatch(typeof(Container), "Awake")]
		private static class HoldSizeOnLoad
		{
			private static void Prefix(Container __instance)
			{
				ZNetView val = (Object.op_Implicit((Object)(object)__instance.m_rootObjectOverride) ? ((Component)__instance.m_rootObjectOverride).GetComponent<ZNetView>() : ((Component)__instance).GetComponent<ZNetView>());
				if (!((Object)(object)val == (Object)null) && !((Object)(object)((Component)val).GetComponent<KnarrShip>() == (Object)null) && val.GetZDO() != null)
				{
					ZDO zDO = val.GetZDO();
					Cfg.HoldSize(out var width, out var height);
					KnarrMath.HoldFor(width, height, zDO.GetInt(HoldWidthKey, 0), zDO.GetInt(HoldHeightKey, 0), out var width2, out var height2);
					__instance.m_width = width2;
					__instance.m_height = height2;
					Remember(val, width2, height2);
				}
			}
		}

		public static readonly List<KnarrShip> All = new List<KnarrShip>();

		private static readonly int HoldWidthKey = StringExtensionMethods.GetStableHashCode("zap_knarr_hold_w");

		private static readonly int HoldHeightKey = StringExtensionMethods.GetStableHashCode("zap_knarr_hold_h");

		public Ship Ship { get; private set; }

		private void Awake()
		{
			Ship = ((Component)this).GetComponent<Ship>();
		}

		private void OnEnable()
		{
			All.Add(this);
		}

		private void OnDisable()
		{
			All.Remove(this);
		}

		public bool Manned()
		{
			if ((Object)(object)Ship == (Object)null)
			{
				return false;
			}
			foreach (Player player in Ship.m_players)
			{
				if ((Object)(object)player != (Object)null && (Object)(object)((Character)player).m_nview != (Object)null && ((Character)player).m_nview.IsValid())
				{
					return true;
				}
			}
			return false;
		}

		public static void Configure(GameObject go)
		{
			if ((Object)(object)go == (Object)null)
			{
				return;
			}
			Ship component = go.GetComponent<Ship>();
			if ((Object)(object)component != (Object)null)
			{
				Rigidbody component2 = go.GetComponent<Rigidbody>();
				component.m_sailForceFactor = KnarrMath.SailForceFactor(Cfg.SpeedBonus.Value / 100f, component.m_dampingForward, component.m_force, ((Object)(object)component2 != (Object)null) ? component2.linearDamping : 0f);
			}
			Container componentInChildren = go.GetComponentInChildren<Container>(true);
			if (!((Object)(object)componentInChildren == (Object)null))
			{
				Inventory inventory = componentInChildren.GetInventory();
				if (inventory == null)
				{
					Cfg.HoldSize(out componentInChildren.m_width, out componentInChildren.m_height);
					return;
				}
				Cfg.HoldSize(out var width, out var height);
				KnarrMath.HoldFor(width, height, inventory.GetWidth(), inventory.GetHeight(), out var width2, out var height2);
				componentInChildren.m_width = (inventory.m_width = width2);
				componentInChildren.m_height = (inventory.m_height = height2);
				Remember(go.GetComponent<ZNetView>(), width2, height2);
			}
		}

		private static void Remember(ZNetView nview, int w, int h)
		{
			if (!((Object)(object)nview == (Object)null) && nview.IsValid() && nview.IsOwner())
			{
				ZDO zDO = nview.GetZDO();
				if (zDO.GetInt(HoldWidthKey, 0) != w)
				{
					zDO.Set(HoldWidthKey, w, false);
				}
				if (zDO.GetInt(HoldHeightKey, 0) != h)
				{
					zDO.Set(HoldHeightKey, h, false);
				}
			}
		}

		public static KnarrShip Local()
		{
			Ship localShip = Ship.GetLocalShip();
			if (!((Object)(object)localShip != (Object)null))
			{
				return null;
			}
			return ((Component)localShip).GetComponent<KnarrShip>();
		}
	}
}
namespace Knarr.Dev
{
	public class SelfTest : MonoBehaviour
	{
		private string _out;

		private readonly List<string> _report = new List<string>();

		private int _pass;

		private int _fail;

		private const string ServerRecipe = "Wood:7";

		private const string ServerHold = "8x9";

		private const float ServerFactor = 5f;

		private static readonly string[] Ships = new string[4] { "Raft", "Karve", "VikingShip", "VikingShip_Ashlands" };

		private float _lastTerminal;

		private float _lastQuarter;

		private float _lastSlow;

		private float _lastBack;

		private Vector3 _sea;

		public static void TryStart(GameObject host)
		{
			string environmentVariable = Environment.GetEnvironmentVariable("KNARR_SELFTEST");
			if (string.IsNullOrEmpty(environmentVariable))
			{
				return;
			}
			Application.runInBackground = true;
			string[] commandLineArgs = Environment.GetCommandLineArgs();
			bool flag = false;
			for (int i = 0; i < commandLineArgs.Length - 1; i++)
			{
				if (commandLineArgs[i] == "-savedir" && !string.IsNullOrEmpty(commandLineArgs[i + 1]))
				{
					Directory.CreateDirectory(commandLineArgs[i + 1]);
					Utils.SetSaveDataPath(commandLineArgs[i + 1]);
					flag = true;
				}
			}
			if (!flag)
			{
				KnarrPlugin.Log.LogError((object)"Self test needs -savedir <scratch folder>; not starting it.");
			}
			else
			{
				host.AddComponent<SelfTest>()._out = environmentVariable;
			}
		}

		private static bool Flag(string name)
		{
			return (Environment.GetEnvironmentVariable("KNARR_SELFTEST_" + name) ?? "") == "1";
		}

		private static string Var(string name)
		{
			return Environment.GetEnvironmentVariable("KNARR_SELFTEST_" + name) ?? "";
		}

		private void Start()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Expected O, but got Unknown
			Directory.CreateDirectory(_out);
			Application.logMessageReceived += new LogCallback(OnLog);
			((MonoBehaviour)this).StartCoroutine(Run());
		}

		private void OnLog(string condition, string stackTrace, LogType type)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			if ((int)type == 4 || (int)type == 0)
			{
				_report.Add($"[{type}] {condition}\n{stackTrace}");
			}
		}

		private IEnumerator Run()
		{
			float timeout = Time.realtimeSinceStartup + 600f;
			while (((Object)(object)FejdStartup.instance == (Object)null || (Object)(object)ObjectDB.instance == (Object)null) && Time.realtimeSinceStartup < timeout)
			{
				yield return null;
			}
			yield return (object)new WaitForSecondsRealtime(4f);
			string text = Var("JOIN");
			if (!string.IsNullOrEmpty(text))
			{
				yield return ((MonoBehaviour)this).StartCoroutine(Safe(EnterServer(text)));
				if ((Object)(object)Player.m_localPlayer != (Object)null)
				{
					yield return ((MonoBehaviour)this).StartCoroutine(Safe(Multiplayer()));
				}
			}
			else if (Flag("WORLD"))
			{
				yield return ((MonoBehaviour)this).StartCoroutine(Safe(EnterWorld()));
				if ((Object)(object)Player.m_localPlayer != (Object)null)
				{
					yield return ((MonoBehaviour)this).StartCoroutine(Safe(WorldTest()));
				}
			}
			_report.Add($"RESULT pass {_pass} fail {_fail}");
			Flush();
			Application.logMessageReceived -= new LogCallback(OnLog);
			yield return (object)new WaitForSecondsRealtime(0.5f);
			Application.Quit();
		}

		private IEnumerator Safe(IEnumerator inner)
		{
			while (true)
			{
				object current;
				try
				{
					if (!inner.MoveNext())
					{
						break;
					}
					current = inner.Current;
				}
				catch (Exception ex)
				{
					_report.Add("step failed: " + ex);
					_fail++;
					break;
				}
				yield return current;
			}
		}

		private void Flush()
		{
			try
			{
				Directory.CreateDirectory(_out);
				string text = Path.Combine(_out, "report.txt");
				File.WriteAllLines(text + ".tmp", _report.ToArray());
				if (File.Exists(text))
				{
					File.Delete(text);
				}
				File.Move(text + ".tmp", text);
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("SelfTest: report not written: " + ex.Message));
			}
		}

		private void Check(bool ok, string what)
		{
			if (ok)
			{
				_pass++;
			}
			else
			{
				_fail++;
			}
			_report.Add((ok ? "PASS " : "FAIL ") + what);
		}

		private IEnumerator EnterWorld()
		{
			FejdStartup instance = FejdStartup.instance;
			if ((Object)(object)instance == (Object)null)
			{
				_report.Add("world: no FejdStartup");
				yield break;
			}
			PlayerProfile val = new PlayerProfile("knarrtester", (FileSource)2);
			val.SetName("Skipper");
			val.m_firstSpawn = false;
			val.Save();
			Game.SetProfile("knarrtester", (FileSource)2);
			World val2 = new World("KnarrTest", "HQ7GnTiegh")
			{
				m_fileSource = (FileSource)2
			};
			val2.SaveWorldFWLData(DateTime.Now);
			instance.m_world = val2;
			instance.m_startingWorld = true;
			ZNet.SetServer(true, false, false, val2.m_name, "", val2);
			ZNet.ResetServerHost();
			instance.TransitionToMainScene();
			_report.Add("world: loading " + val2.m_name + " seed " + val2.m_seedName);
			float timeout = Time.realtimeSinceStartup + 300f;
			while (((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNetScene.instance == (Object)null || ((Character)Player.m_localPlayer).IsTeleporting()) && Time.realtimeSinceStartup < timeout)
			{
				yield return null;
			}
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				_report.Add("world: player never spawned");
				yield break;
			}
			yield return (object)new WaitForSecondsRealtime(6f);
			_report.Add("world: spawned at " + ((object)((Component)Player.m_localPlayer).transform.position/*cast due to .constrained prefix*/).ToString());
		}

		private IEnumerator EnterServer(string address)
		{
			FejdStartup instance = FejdStartup.instance;
			if ((Object)(object)instance == (Object)null)
			{
				_report.Add("join: no FejdStartup");
				yield break;
			}
			PlayerProfile val = new PlayerProfile("knarrmp", (FileSource)2);
			val.SetName("SkipperMP");
			val.m_firstSpawn = false;
			val.Save();
			Game.SetProfile("knarrmp", (FileSource)2);
			instance.SetServerToJoin(new ServerJoinData(new ServerJoinDataDedicated(address)));
			instance.JoinServer();
			_report.Add("join: connecting to " + address);
			float timeout = Time.realtimeSinceStartup + 240f;
			while (((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNetScene.instance == (Object)null || ((Character)Player.m_localPlayer).IsTeleporting()) && Time.realtimeSinceStartup < timeout)
			{
				yield return null;
			}
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				_report.Add("join: player never spawned");
				yield break;
			}
			yield return (object)new WaitForSecondsRealtime(8f);
			_report.Add($"join: spawned at {((Component)Player.m_localPlayer).transform.position}, server {ZNet.instance.IsServer()}, peers {ZNet.instance.GetPeers().Count}");
		}

		private IEnumerator Multiplayer()
		{
			yield return ((MonoBehaviour)this).StartCoroutine(Safe(MpScenes()));
		}

		private IEnumerator WorldTest()
		{
			if (Flag("RESEARCH"))
			{
				yield return ((MonoBehaviour)this).StartCoroutine(Safe(Research()));
			}
			else
			{
				yield return ((MonoBehaviour)this).StartCoroutine(Safe(Scenes()));
			}
		}

		private static void Stand(Player p, Vector3 at)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: 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_0036: Unknown result type (might be due to invalid IL or missing references)
			at.y = ZoneSystem.instance.GetGroundHeight(at) + 0.1f;
			((Component)p).transform.position = at;
			((Character)p).m_body.position = at;
			((Character)p).m_body.linearVelocity = Vector3.zero;
		}

		private IEnumerator Travel(Player p, Vector3 at)
		{
			//IL_000e: 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)
			((Character)p).TeleportTo(at + Vector3.up * 2f, ((Component)p).transform.rotation, true);
			float limit = Time.realtimeSinceStartup + 90f;
			while ((((Character)p).IsTeleporting() || !ZoneSystem.instance.IsZoneLoaded(at)) && Time.realtimeSinceStartup < limit)
			{
				yield return null;
			}
			yield return (object)new WaitForSecondsRealtime(3f);
			Stand(p, at);
			yield return (object)new WaitForSecondsRealtime(1f);
		}

		private void ClearArea(Vector3 c, float r)
		{
			//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)
			int num = 0;
			ZNetView[] array = Object.FindObjectsByType<ZNetView>((FindObjectsSortMode)0);
			foreach (ZNetView val in array)
			{
				if (!((Object)(object)val == (Object)null) && val.IsValid() && !((Object)(object)((Component)val).GetComponent<Player>() != (Object)null) && !(Utils.DistanceXZ(((Component)val).transform.position, c) > r) && (Object.op_Implicit((Object)(object)((Component)val).GetComponent<TreeBase>()) || Object.op_Implicit((Object)(object)((Component)val).GetComponent<TreeLog>()) || Object.op_Implicit((Object)(object)((Component)val).GetComponent<Destructible>()) || Object.op_Implicit((Object)(object)((Component)val).GetComponent<MineRock>()) || Object.op_Implicit((Object)(object)((Component)val).GetComponent<MineRock5>()) || Object.op_Implicit((Object)(object)((Component)val).GetComponent<Pickable>()) || (Object.op_Implicit((Object)(object)((Component)val).GetComponent<Character>()) && !((Component)val).GetComponent<Character>().IsTamed())))
				{
					val.Destroy();
					num++;
				}
			}
			_report.Add("scene: cleared " + num + " objects");
		}

		private static Pose Look(Vector3 target, float bearing, float dist, float height)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: 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_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = target + Quaternion.Euler(0f, bearing, 0f) * Vector3.forward * dist + Vector3.up * height;
			return new Pose(val, Quaternion.LookRotation(target - val));
		}

		private IEnumerator MainShot(string name, Pose pose)
		{
			//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)
			Camera main = Utils.GetMainCamera();
			GameCamera gc = GameCamera.instance;
			if ((Object)(object)main == (Object)null)
			{
				_report.Add("mainshot: no camera");
				yield break;
			}
			if ((Object)(object)gc != (Object)null)
			{
				((Behaviour)gc).enabled = false;
			}
			if ((Object)(object)Hud.instance != (Object)null)
			{
				Hud.instance.m_userHidden = true;
			}
			((Component)main).transform.SetPositionAndRotation(pose.position, pose.rotation);
			float fov = main.fieldOfView;
			main.fieldOfView = 50f;
			yield return null;
			yield return null;
			yield return (object)new WaitForEndOfFrame();
			Directory.CreateDirectory(_out);
			ScreenCapture.CaptureScreenshot(Path.Combine(_out, name + ".png"));
			yield return null;
			yield return null;
			main.fieldOfView = fov;
			if ((Object)(object)gc != (Object)null)
			{
				((Behaviour)gc).enabled = true;
			}
			if ((Object)(object)Hud.instance != (Object)null)
			{
				Hud.instance.m_userHidden = false;
			}
		}

		private IEnumerator Shot(string name)
		{
			yield return (object)new WaitForEndOfFrame();
			Directory.CreateDirectory(_out);
			ScreenCapture.CaptureScreenshot(Path.Combine(_out, name + ".png"));
			yield return null;
			yield return null;
		}

		private static string Flat(string s)
		{
			if (s != null)
			{
				return s.Replace('\n', '|');
			}
			return "null";
		}

		private static Bounds RendererBounds(GameObject go)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			Bounds result = default(Bounds);
			bool flag = false;
			Renderer[] componentsInChildren = go.GetComponentsInChildren<Renderer>();
			foreach (Renderer val in componentsInChildren)
			{
				if (!(val is ParticleSystemRenderer) && val.enabled)
				{
					if (!flag)
					{
						result = val.bounds;
						flag = true;
					}
					else
					{
						((Bounds)(ref result)).Encapsulate(val.bounds);
					}
				}
			}
			return result;
		}

		private IEnumerator MpScenes()
		{
			Player p = Player.m_localPlayer;
			p.SetGodMode(true);
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			Check(serverPeer != null && !ZNet.instance.IsServer(), "mp: connected to a dedicated server");
			if (serverPeer == null)
			{
				yield break;
			}
			Check(Cfg.Recipe.Value == "Wood:7", "mp: recipe from the server " + Cfg.Recipe.Value + " (must be Wood:7)");
			Check(Cfg.Hold.Value == "8x9", "mp: hold from the server " + Cfg.Hold.Value + " (must be 8x9)");
			Check(Math.Abs(Cfg.EncounterFactor.Value - 5f) < 0.01f, $"mp: encounter factor from the server {Cfg.EncounterFactor.Value} (must be {5f})");
			Piece component = Pieces.KnarrPrefab.GetComponent<Piece>();
			string text = string.Join(",", component.m_resources.Select((Requirement r) => ((Object)r.m_resItem).name + ":" + r.m_amount));
			Check(text == "Wood:7", "mp: the hammer asks " + text + " (must be Wood:7)");
			if (!FindOpenSea(((Component)p).transform.position, out _sea))
			{
				Check(ok: false, "mp: open sea found");
				yield break;
			}
			_report.Add("mp: open sea at " + V(_sea));
			EnvMan.instance.m_debugTimeOfDay = true;
			EnvMan.instance.m_debugTime = 0.38f;
			EnvMan.instance.m_debugEnv = "Clear";
			yield return ((MonoBehaviour)this).StartCoroutine(GoToSea(p));
			GameObject knarr = SpawnShip("zap_knarr", _sea);
			yield return (object)new WaitForSeconds(4f);
			ZNetView nview = knarr.GetComponent<ZNetView>();
			Inventory inventory = knarr.GetComponentInChildren<Container>().GetInventory();
			Check(inventory.GetWidth() == 8 && inventory.GetHeight() == 9, $"mp: hold {inventory.GetWidth()}x{inventory.GetHeight()} (must be the server's 8x9)");
			ZDO zdo = nview.GetZDO();
			int num = zdo.GetInt(StringExtensionMethods.GetStableHashCode("zap_knarr_hold_w"), 0);
			int num2 = zdo.GetInt(StringExtensionMethods.GetStableHashCode("zap_knarr_hold_h"), 0);
			Check(num == 8 && num2 == 9, $"mp: the knarr remembers its hold {num}x{num2}");
			inventory.AddItem(ObjectDB.instance.GetItemPrefab("Wood"), 50);
			yield return (object)new WaitForSeconds(1f);
			byte[] byteArray = zdo.GetByteArray(ZDOVars.s_items, (byte[])null);
			Check(byteArray != null && byteArray.Length != 0, $"mp: the hold's items are saved in the ship's state ({((byteArray != null) ? byteArray.Length : 0)} bytes)");
			yield return ((MonoBehaviour)this).StartCoroutine(TakeHelm(p, knarr.GetComponent<Ship>()));
			Check(((Character)p).IsAttached() && (Object)(object)KnarrShip.Local() != (Object)null, "mp: at the knarr's helm");
			KnarrShip component2 = knarr.GetComponent<KnarrShip>();
			Check(component2.Manned(), "mp: the knarr counts its crew");
			EnvMan.instance.SetDebugWind(0f, 1f);
			yield return ((MonoBehaviour)this).StartCoroutine(SetSpeed(knarr.GetComponent<Ship>(), (Speed)4));
			yield return (object)new WaitForSeconds(15f);
			float speed = knarr.GetComponent<Ship>().GetSpeed();
			Check(speed > 8.5f, $"mp: the knarr sails at {F(speed)} m/s under full sail (owner {nview.IsOwner()})");
			yield return ((MonoBehaviour)this).StartCoroutine(SetSpeed(knarr.GetComponent<Ship>(), (Speed)0));
			yield return (object)new WaitForSeconds(4f);
			EnvMan.instance.m_debugTime = 0f;
			yield return (object)new WaitForSeconds(3f);
			SpawnSystem ss = ((IEnumerable<SpawnSystem>)SpawnSystem.m_instances).FirstOrDefault((Func<SpawnSystem, bool>)((SpawnSystem x) => (Object)(object)x != (Object)null && x.InsideZone(knarr.transform.position, 0f)));
			Check((Object)(object)ss != (Object)null, "mp: the zone's spawn system is on this client");
			if ((Object)(object)ss != (Object)null)
			{
				ZNetView ssView = ((Component)ss).GetComponent<ZNetView>();
				List<SpawnData> serpents = (from d in ss.m_spawnLists.SelectMany((SpawnSystemList l) => l.m_spawners)
					where (Object)(object)d.m_prefab != (Object)null && ((Object)d.m_prefab).name == "Serpent"
					select d.Clone()).ToList();
				int spawned = 0;
				int heardBefore = Encounters.StirHeard;
				for (int i = 0; i < 300; i++)
				{
					SpawnSystem.m_tempNearPlayers.Clear();
					SpawnSystem.m_tempNearPlayers.Add(p);
					ss.UpdateSpawnList(serpents, ZNet.instance.GetTime(), false, $"knarrmp{i}_");
					int num3 = 0;
					foreach (Character item in Character.GetAllCharacters().ToList())
					{
						if ((Object)(object)item != (Object)null && ((Object)item).name.StartsWith("Serpent", StringComparison.Ordinal))
						{
							num3++;
							ZNetScene.instance.Destroy(((Component)item).gameObject);
						}
					}
					if (num3 > 0)
					{
						spawned++;
					}
					yield return null;
				}
				yield return (object)new WaitForSeconds(2f);
				float num4 = (float)spawned / 300f;
				int num5 = Encounters.StirHeard - heardBefore;
				_report.Add(string.Format("mp: zone owner {0}; {1} of {2} night rolls brought a serpent ({3} %), stir messages {4}", ssView.IsOwner() ? "this client" : "other", spawned, 300, F(num4 * 100f), num5));
				float num6 = 0.25f;
				Check(Mathf.Abs(num4 - num6) < 0.08f, $"mp: serpent in {F(num4 * 100f)} % of night rolls (server factor {5f}: about {F(num6 * 100f)} %)");
				Check(num5 > 0, $"mp: the stir message came back through the server {num5} times");
			}
			EnvMan.instance.m_debugTime = 0.38f;
			Flush();
			yield return ((MonoBehaviour)this).StartCoroutine(LeaveShip(p, knarr));
			Check(Object.FindObjectsByType<KnarrShip>((FindObjectsSortMode)0).Length == KnarrShip.All.Count, $"mp: the knarr list matches the scene ({KnarrShip.All.Count})");
		}

		private static string F(float v)
		{
			return v.ToString("0.###", CultureInfo.InvariantCulture);
		}

		private static string V(Vector3 v)
		{
			//IL_0010: 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_003c: Unknown result type (might be due to invalid IL or missing references)
			return "(" + F(v.x) + ", " + F(v.y) + ", " + F(v.z) + ")";
		}

		private IEnumerator Research()
		{
			Player localPlayer = Player.m_localPlayer;
			localPlayer.SetGodMode(true);
			SpawnSystem.m_nospawn = true;
			_report.Add($"PHYS gravity {Physics.gravity} fixedDt {Time.fixedDeltaTime} water {F(ZoneSystem.instance.m_waterLevel)}");
			if (Var("ONLY") == "trials")
			{
				yield return ((MonoBehaviour)this).StartCoroutine(Safe(SeaTrials(localPlayer)));
				SpawnSystem.m_nospawn = false;
				Flush();
				yield break;
			}
			string[] ships = Ships;
			foreach (string text in ships)
			{
				GameObject prefab = ZNetScene.instance.GetPrefab(text);
				if ((Object)(object)prefab == (Object)null)
				{
					_report.Add("SHIP " + text + " missing");
				}
				else
				{
					DumpShip(text, prefab);
				}
			}
			ships = new string[2] { "Karve", "VikingShip" };
			foreach (string text2 in ships)
			{
				_report.Add("TREE " + text2);
				Dump(ZNetScene.instance.GetPrefab(text2).transform, 1);
			}
			Flush();
			List<(int, string)> list = new List<(int, string)>();
			foreach (GameObject prefab3 in ZNetScene.instance.m_prefabs)
			{
				if (!((Object)(object)prefab3 == (Object)null))
				{
					Container[] componentsInChildren = prefab3.GetComponentsInChildren<Container>(true);
					foreach (Container val in componentsInChildren)
					{
						list.Add((val.m_width * val.m_height, $"CONTAINER {((Object)prefab3).name} '{val.m_name}' {val.m_width}x{val.m_height} = {val.m_width * val.m_height} piece {(Object)(object)prefab3.GetComponent<Piece>() != (Object)null} ship {(Object)(object)prefab3.GetComponent<Ship>() != (Object)null} wagon {(Object)(object)val.m_wagon != (Object)null}"));
					}
				}
			}
			foreach (var item in list.OrderByDescending<(int, string), int>(((int slots, string line) b) => b.slots))
			{
				_report.Add(item.Item2);
			}
			Flush();
			DumpSpawns();
			ships = new string[4] { "Serpent", "BonemawSerpent", "Leviathan", "Fish3" };
			foreach (string text3 in ships)
			{
				GameObject prefab2 = ZNetScene.instance.GetPrefab(text3);
				if ((Object)(object)prefab2 == (Object)null)
				{
					_report.Add("CREATURE " + text3 + " missing");
					continue;
				}
				Character component = prefab2.GetComponent<Character>();
				MonsterAI component2 = prefab2.GetComponent<MonsterAI>();
				_report.Add($"CREATURE {text3} '{component?.m_name}' hp {F(component?.m_health ?? 0f)} faction {component?.m_faction} boss {component?.m_boss}" + (((Object)(object)component2 != (Object)null) ? $" view {F(((BaseAI)component2).m_viewRange)} alertRange {F(component2.m_alertRange)} huntPlayer {((BaseAI)component2).HuntPlayer()} attackPlayerObjects {component2.m_attackPlayerObjects} avoidLand {component2.m_avoidLand}" : ""));
			}
			Flush();
			ships = new string[17]
			{
				"FineWood", "ElderBark", "RoundLog", "IronNails", "BronzeNails", "DeerHide", "TrollHide", "WolfPelt", "LoxPelt", "LinenThread",
				"Resin", "Tar", "Iron", "Silver", "BlackMetal", "YggdrasilWood", "CeramicPlate"
			};
			foreach (string text4 in ships)
			{
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(text4);
				_report.Add(((Object)(object)itemPrefab == (Object)null) ? ("ITEM " + text4 + " missing") : ("ITEM " + text4 + " '" + itemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared.m_name + "'"));
			}
			Flush();
			yield return ((MonoBehaviour)this).StartCoroutine(Safe(SeaTrials(localPlayer)));
			SpawnSystem.m_nospawn = false;
			Flush();
		}

		private void DumpShip(string name, GameObject go)
		{
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0299: Unknown result type (might be due to invalid IL or missing references)
			//IL_029e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02df: Unknown result type (might be due to invalid IL or missing references)
			//IL_0353: Unknown result type (might be due to invalid IL or missing references)
			//IL_0345: Unknown result type (might be due to invalid IL or missing references)
			//IL_0358: Unknown result type (might be due to invalid IL or missing references)
			//IL_03fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0403: Unknown result type (might be due to invalid IL or missing references)
			//IL_0462: Unknown result type (might be due to invalid IL or missing references)
			//IL_0467: Unknown result type (might be due to invalid IL or missing references)
			//IL_0523: Unknown result type (might be due to invalid IL or missing references)
			//IL_052f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0534: Unknown result type (might be due to invalid IL or missing references)
			//IL_0552: Unknown result type (might be due to invalid IL or missing references)
			//IL_0557: Unknown result type (might be due to invalid IL or missing references)
			//IL_055c: Unknown result type (might be due to invalid IL or missing references)
			//IL_05d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0797: Unknown result type (might be due to invalid IL or missing references)
			//IL_079c: Unknown result type (might be due to invalid IL or missing references)
			//IL_07a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_07af: Unknown result type (might be due to invalid IL or missing references)
			//IL_07b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_07b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_07cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_07e0: Unknown result type (might be due to invalid IL or missing references)
			StringBuilder stringBuilder = new StringBuilder("SHIP " + name + ":");
			Ship component = go.GetComponent<Ship>();
			FieldInfo[] fields = typeof(Ship).GetFields(BindingFlags.Instance | BindingFlags.Public);
			foreach (FieldInfo fieldInfo in fields)
			{
				if (fieldInfo.FieldType == typeof(float) || fieldInfo.FieldType == typeof(int) || fieldInfo.FieldType == typeof(bool))
				{
					object value = fieldInfo.GetValue(component);
					stringBuilder.Append(' ').Append(fieldInfo.Name.Substring(2)).Append('=')
						.Append((value is float v) ? F(v) : value.ToString());
				}
			}
			_report.Add(stringBuilder.ToString());
			Rigidbody component2 = go.GetComponent<Rigidbody>();
			_report.Add($"  rb mass {F(component2.mass)} drag {F(component2.linearDamping)}/{F(component2.angularDamping)} com {V(component2.centerOfMass)} autoCom {component2.automaticCenterOfMass} inertia {V(component2.inertiaTensor)} interp {component2.interpolation} cd {component2.collisionDetectionMode}");
			BoxCollider floatCollider = component.m_floatCollider;
			if ((Object)(object)floatCollider != (Object)null)
			{
				_report.Add($"  float {((Object)floatCollider).name} size {V(floatCollider.size)} center {V(floatCollider.center)} lp {V(((Component)floatCollider).transform.localPosition)} ls {V(((Component)floatCollider).transform.localScale)} trig {((Collider)floatCollider).isTrigger} enabled {((Collider)floatCollider).enabled} layer {LayerMask.LayerToName(((Component)floatCollider).gameObject.layer)}");
			}
			ShipControlls shipControlls = component.m_shipControlls;
			if ((Object)(object)shipControlls != (Object)null)
			{
				_report.Add("  controls " + ((Object)shipControlls).name + " attach " + (((Object)(object)shipControlls.m_attachPoint != (Object)null) ? V(go.transform.InverseTransformPoint(shipControlls.m_attachPoint.position)) : "-") + " anim " + shipControlls.m_attachAnimation + " range " + F(shipControlls.m_maxUseRange) + " detach " + V(shipControlls.m_detachOffset));
			}
			Chair[] componentsInChildren = go.GetComponentsInChildren<Chair>(true);
			foreach (Chair val in componentsInChildren)
			{
				_report.Add($"  chair {((Object)val).name} at {V(go.transform.InverseTransformPoint(((Object)(object)val.m_attachPoint != (Object)null) ? val.m_attachPoint.position : ((Component)val).transform.position))} anim {val.m_attachAnimation} inShip {val.m_inShip}");
			}
			Container[] componentsInChildren2 = go.GetComponentsInChildren<Container>(true);
			foreach (Container val2 in componentsInChildren2)
			{
				_report.Add($"  container {((Object)val2).name} '{val2.m_name}' {val2.m_width}x{val2.m_height} at {V(go.transform.InverseTransformPoint(((Component)val2).transform.position))}");
			}
			Ladder[] componentsInChildren3 = go.GetComponentsInChildren<Ladder>(true);
			foreach (Ladder val3 in componentsInChildren3)
			{
				_report.Add("  ladder " + ((Object)val3).name + " at " + V(go.transform.InverseTransformPoint(((Component)val3).transform.position)));
			}
			Collider[] componentsInChildren4 = go.GetComponentsInChildren<Collider>(true);
			foreach (Collider val4 in componentsInChildren4)
			{
				if (val4.isTrigger)
				{
					List<string> report = _report;
					string[] obj = new string[8]
					{
						"  trigger ",
						((Object)val4).name,
						" ",
						((object)val4).GetType().Name,
						" layer ",
						LayerMask.LayerToName(((Component)val4).gameObject.layer),
						" ",
						null
					};
					BoxCollider val5 = (BoxCollider)(object)((val4 is BoxCollider) ? val4 : null);
					obj[7] = ((val5 != null) ? ("size " + V(Vector3.Scale(val5.size, ((Component)val4).transform.lossyScale)) + " center " + V(go.transform.InverseTransformPoint(((Component)val4).transform.TransformPoint(val5.center)))) : "");
					report.Add(string.Concat(obj));
				}
			}
			WearNTear component3 = go.GetComponent<WearNTear>();
			Piece component4 = go.GetComponent<Piece>();
			List<string> report2 = _report;
			object[] obj2 = new object[6]
			{
				F(component3?.m_health ?? 0f),
				component3?.m_materialType,
				component4?.m_name,
				null,
				null,
				null
			};
			object obj3;
			if (component4 == null)
			{
				obj3 = null;
			}
			else
			{
				CraftingStation craftingStation = component4.m_craftingStation;
				obj3 = ((craftingStation != null) ? ((Object)craftingStation).name : null);
			}
			obj2[3] = obj3;
			obj2[4] = (((Object)(object)component4 == (Object)null) ? "" : string.Join(",", component4.m_resources.Select(delegate(Requirement q)
			{
				ItemDrop resItem = q.m_resItem;
				return ((resItem != null) ? ((Object)resItem).name : null) + ":" + q.m_amount;
			})));
			obj2[5] = component4?.m_waterPiece;
			report2.Add(string.Format("  wnt hp {0} mat {1} | piece '{2}' station {3} res {4} water {5}", obj2));
			ImpactEffect component5 = go.GetComponent<ImpactEffect>();
			if ((Object)(object)component5 != (Object)null)
			{
				_report.Add($"  impact minVel {F(component5.m_minVelocity)} maxVel {F(component5.m_maxVelocity)} dmg blunt {F(component5.m_damages.m_blunt)} dmgToSelf {component5.m_damageToSelf}");
			}
			Renderer[] componentsInChildren5 = go.GetComponentsInChildren<Renderer>(true);
			foreach (Renderer val6 in componentsInChildren5)
			{
				MeshFilter component6 = ((Component)val6).GetComponent<MeshFilter>();
				object obj5;
				if (!((Object)(object)component6 != (Object)null))
				{
					Renderer obj4 = ((val6 is SkinnedMeshRenderer) ? val6 : null);
					obj5 = ((obj4 != null) ? ((SkinnedMeshRenderer)obj4).sharedMesh : null);
				}
				else
				{
					obj5 = component6.sharedMesh;
				}
				Mesh val7 = (Mesh)obj5;
				if (!((Object)(object)val7 == (Object)null))
				{
					List<string> report3 = _report;
					object[] obj6 = new object[10]
					{
						((Object)val6).name,
						((Object)val7).name,
						val7.vertexCount,
						val7.isReadable,
						null,
						null,
						null,
						null,
						null,
						null
					};
					Bounds bounds = val7.bounds;
					obj6[4] = V(((Bounds)(ref bounds)).center);
					bounds = val7.bounds;
					obj6[5] = V(((Bounds)(ref bounds)).extents);
					obj6[6] = V(((Component)val6).transform.localPosition);
					obj6[7] = V(((Component)val6).transform.lossyScale);
					obj6[8] = ((Component)val6).gameObject.activeInHierarchy;
					obj6[9] = string.Join(",", from x in val6.sharedMaterials
						where (Object)(object)x != (Object)null
						select ((Object)x).name);
					report3.Add(string.Format("  mesh {0} '{1}' verts {2} readable {3} bounds c {4} e {5} lp {6} ls {7} active {8} mats {9}", obj6));
				}
			}
		}

		private void DumpSpawns()
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			SpawnSystem component = ZoneSystem.instance.m_zoneCtrlPrefab.GetComponent<SpawnSystem>();
			int num = 0;
			foreach (SpawnSystemList spawnList in component.m_spawnLists)
			{
				foreach (SpawnData spawner in spawnList.m_spawners)
				{
					num++;
					if ((spawner.m_biome & 0x100) != 0 || spawner.m_minOceanDepth > 0f || spawner.m_maxOceanDepth > 0f || ((Object)(object)spawner.m_prefab != (Object)null && ((Object)spawner.m_prefab).name.IndexOf("Serpent", StringComparison.OrdinalIgnoreCase) >= 0))
					{
						List<string> report = _report;
						object[] array = new object[31];
						array[0] = ((Object)spawnList).name;
						array[1] = spawner.m_name;
						GameObject prefab = spawner.m_prefab;
						array[2] = ((prefab != null) ? ((Object)prefab).name : null);
						array[3] = spawner.m_enabled;
						array[4] = spawner.m_biome;
						array[5] = spawner.m_biomeArea;
						array[6] = spawner.m_maxSpawned;
						array[7] = F(spawner.m_spawnInterval);
						array[8] = F(spawner.m_spawnChance);
						array[9] = F(spawner.m_spawnDistance);
						array[10] = F(spawner.m_spawnRadiusMin);
						array[11] = F(spawner.m_spawnRadiusMax);
						array[12] = spawner.m_requiredGlobalKey;
						array[13] = string.Join(",", spawner.m_requiredEnvironments);
						array[14] = spawner.m_groupSizeMin;
						array[15] = spawner.m_groupSizeMax;
						array[16] = F(spawner.m_groupRadius);
						array[17] = spawner.m_spawnAtDay;
						array[18] = spawner.m_spawnAtNight;
						array[19] = F(spawner.m_minAltitude);
						array[20] = F(spawner.m_maxAltitude);
						array[21] = F(spawner.m_minOceanDepth);
						array[22] = F(spawner.m_maxOceanDepth);
						array[23] = spawner.m_huntPlayer;
						array[24] = F(spawner.m_groundOffset);
						array[25] = spawner.m_minLevel;
						array[26] = spawner.m_maxLevel;
						array[27] = F(spawner.m_levelUpMinCenterDistance);
						array[28] = F(spawner.m_overrideLevelupChance);
						array[29] = F(spawner.m_minDistanceFromCenter);
						array[30] = F(spawner.m_maxDistanceFromCenter);
						report.Add(string.Format("SPAWN list {0} '{1}' prefab {2} enabled {3} biome {4} area {5} max {6} interval {7} chance {8} dist {9} radius {10}-{11} key '{12}' env [{13}] group {14}-{15} r {16} day {17} night {18} alt {19}..{20} depth {21}..{22} hunt {23} offset {24} level {25}-{26} levelUpDist {27} levelChance {28} centerDist {29}..{30}", array));
					}
				}
			}
			_report.Add($"SPAWN total entries {num}, lists {component.m_spawnLists.Count}");
		}

		private static bool FindOpenSea(Vector3 from, out Vector3 sea)
		{
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: 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_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			WorldGenerator instance = WorldGenerator.instance;
			float waterLevel = ZoneSystem.instance.m_waterLevel;
			for (float num = 200f; num < 4000f; num += 100f)
			{
				for (int i = 0; i < 36; i++)
				{
					float num2 = from.x + Mathf.Cos((float)i * 10f * ((float)Math.PI / 180f)) * num;
					float num3 = from.z + Mathf.Sin((float)i * 10f * ((float)Math.PI / 180f)) * num;
					Vector2 val = new Vector2(num2, num3);
					if (((Vector2)(ref val)).magnitude > 6000f)
					{
						continue;
					}
					bool flag = true;
					for (int j = 0; j < 16 && flag; j++)
					{
						float[] array = new float[5] { 0f, 100f, 200f, 300f, 400f };
						foreach (float num4 in array)
						{
							if (instance.GetHeight(num2 + Mathf.Cos((float)j * 0.3927f) * num4, num3 + Mathf.Sin((float)j * 0.3927f) * num4) > waterLevel - 4f)
							{
								flag = false;
								break;
							}
						}
					}
					if (flag)
					{
						sea = new Vector3(num2, waterLevel, num3);
						return true;
					}
				}
			}
			sea = Vector3.zero;
			return false;
		}

		private IEnumerator SeaTrials(Player p)
		{
			if (!FindOpenSea(((Component)p).transform.position, out var sea))
			{
				WorldGenerator instance = WorldGenerator.instance;
				_report.Add("TRIAL no open sea found; heights at spawn " + F(instance.GetHeight(((Component)p).transform.position.x, ((Component)p).transform.position.z)) + ", 1000 m east " + F(instance.GetHeight(((Component)p).transform.position.x + 1000f, ((Component)p).transform.position.z)));
				yield break;
			}
			float water = ZoneSystem.instance.m_waterLevel;
			_report.Add("TRIAL open sea at " + V(sea));
			EnvMan.instance.m_debugTimeOfDay = true;
			EnvMan.instance.m_debugTime = 0.4f;
			((Character)p).TeleportTo(sea + Vector3.up * 3f, Quaternion.identity, true);
			float limit = Time.realtimeSinceStartup + 120f;
			while ((((Character)p).IsTeleporting() || !ZoneSystem.instance.IsZoneLoaded(sea)) && Time.realtimeSinceStartup < limit)
			{
				yield return null;
			}
			yield return (object)new WaitForSecondsRealtime(3f);
			string[] array = new string[3] { "VikingShip", "Karve", "VikingShip_Ashlands" };
			foreach (string name in array)
			{
				yield return ((MonoBehaviour)this).StartCoroutine(Safe(Trial(p, name, sea)));
				Flush();
			}
			((Character)p).AttachStop();
			List<GameObject> shown = new List<GameObject>();
			float num = 0f;
			string[] array2 = new string[4] { "Raft", "Karve", "VikingShip", "VikingShip_Ashlands" };
			foreach (string text in array2)
			{
				GameObject item = Object.Instantiate<GameObject>(ZNetScene.instance.GetPrefab(text), sea + new Vector3(num, 0f, 0f), Quaternion.identity);
				shown.Add(item);
				num += ((text == "Raft") ? 10f : ((text == "Karve") ? 16f : 22f));
			}
			((Character)p).TeleportTo(sea + new Vector3(20f, 30f, -40f), Quaternion.identity, true);
			yield return (object)new WaitForSecondsRealtime(8f);
			Vector3 mid = sea + new Vector3(24f, 0f, 0f);
			yield return ((MonoBehaviour)this).StartCoroutine(MainShot("r_ships_side", Look(mid, 200f, 60f, 18f)));
			yield return ((MonoBehaviour)this).StartCoroutine(MainShot("r_ships_top", new Pose(mid + new Vector3(0f, 70f, -10f), Quaternion.LookRotation(Vector3.down + Vector3.forward * 0.15f))));
			foreach (GameObject item2 in shown)
			{
				List<string> report = _report;
				string[] obj = new string[10]
				{
					"PARKED ",
					((Object)item2).name,
					" pos ",
					V(item2.transform.position),
					" draft ",
					F(water - item2.transform.position.y),
					" tilt ",
					F(Vector3.Angle(item2.transform.up, Vector3.up)),
					" bounds ",
					null
				};
				Bounds val = RendererBounds(item2);
				obj[9] = V(((Bounds)(ref val)).size);
				report.Add(string.Concat(obj));
				ZNetScene.instance.Destroy(item2);
			}
		}

		private IEnumerator Trial(Player p, string name, Vector3 start)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			(string, float, float)[] array = new(string, float, float)[7]
			{
				("full", 0f, 1f),
				("full", 0f, 0.5f),
				("full", 90f, 1f),
				("full", 135f, 1f),
				("turn", 0f, 1f),
				("slow", 0f, 1f),
				("back", 0f, 1f)
			};
			for (int i = 0; i < array.Length; i++)
			{
				var (mode, angle, intensity) = array[i];
				yield return ((MonoBehaviour)this).StartCoroutine(Safe(TrialRun(p, name, start, mode, angle, intensity)));
			}
		}

		private IEnumerator TrialRun(Player p, string name, Vector3 start, string mode, float angle, float intensity)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			EnvMan.instance.SetDebugWind(angle, intensity);
			if (Utils.DistanceXZ(((Component)p).transform.position, start) > 60f)
			{
				((Character)p).TeleportTo(start + new Vector3(0f, 3f, -40f), Quaternion.identity, false);
			}
			float tw = Time.time;
			while (((Character)p).IsTeleporting() && Time.time - tw < 20f)
			{
				yield return null;
			}
			yield return (object)new WaitForSeconds(2f);
			GameObject go = Object.Instantiate<GameObject>(ZNetScene.instance.GetPrefab(name), start, Quaternion.identity);
			Ship ship = go.GetComponent<Ship>();
			Rigidbody rb = go.GetComponent<Rigidbody>();
			WearNTear component = go.GetComponent<WearNTear>();
			if ((Object)(object)component != (Object)null)
			{
				component.m_health = 10000000f;
				go.GetComponent<ZNetView>().GetZDO().Set(ZDOVars.s_health, 10000000f);
			}
			yield return (object)new WaitForSeconds(3f);
			yield return ((MonoBehaviour)this).StartCoroutine(TakeHelm(p, ship));
			if (!ship.m_shipControlls.HaveValidUser())
			{
				_report.Add($"TRIAL {name} {mode}: no helmsman (inBoat {ship.IsPlayerInBoat(p)} attached {((Character)p).IsAttached()})");
			}
			yield return (object)new WaitForSeconds(1f);
			switch (mode)
			{
			case "full":
			{
				yield return ((MonoBehaviour)this).StartCoroutine(SetSpeed(ship, (Speed)4));
				List<float> samples = new List<float>();
				float t0 = Time.time;
				float top = 0f;
				float t90 = -1f;
				while (Time.time - t0 < 25f)
				{
					yield return (object)new WaitForSeconds(1f);
					float speed = ship.GetSpeed();
					samples.Add(speed);
					top = Mathf.Max(top, speed);
				}
				float num = samples.Skip(samples.Count - 5).Average();
				for (int i = 0; i < samples.Count; i++)
				{
					if (samples[i] >= num * 0.9f)
					{
						t90 = i + 1;
						break;
					}
				}
				_lastTerminal = num;
				_report.Add($"TRIAL {name} full wind {F(angle)}° x{F(intensity)}: v@3 {F(samples[2])} v@5 {F(samples[4])} v@10 {F(samples[9])} v@15 {F(samples[14])} v@20 {F(samples[19])} terminal {F(num)} top {F(top)} t90 {F(t90)} s heading {F(go.transform.eulerAngles.y)} windFactor {F(ship.GetWindAngleFactor())} side {F(Vector3.Dot(rb.linearVelocity, go.transform.right))} setting {ship.GetSpeedSetting()}");
				break;
			}
			case "turn":
				yield return ((MonoBehaviour)this).StartCoroutine(SetSpeed(ship, (Speed)4));
				yield return (object)new WaitForSeconds(18f);
				if ((Object)(object)go == (Object)null)
				{
					_report.Add("TRIAL " + name + " turn: the ship is gone before the turn");
					Flush();
					yield break;
				}
				_report.Add($"TRIAL {name} turn: start at {V(go.transform.position)} speed {F(ship.GetSpeed())} attached {((Character)p).IsAttached()} health {F(go.GetComponent<WearNTear>().GetHealthPercentage())}");
				yield return ((MonoBehaviour)this).StartCoroutine(Turn(ship, go, name + " full"));
				break;
			case "slow":
				yield return ((MonoBehaviour)this).StartCoroutine(SetSpeed(ship, (Speed)2));
				yield return (object)new WaitForSeconds(15f);
				_lastSlow = ship.GetSpeed();
				_report.Add($"TRIAL {name} slow speed {F(ship.GetSpeed())} setting {ship.GetSpeedSetting()}");
				yield return ((MonoBehaviour)this).StartCoroutine(Turn(ship, go, name + " slow"));
				break;
			default:
				yield return ((MonoBehaviour)this).StartCoroutine(SetSpeed(ship, (Speed)1));
				yield return (object)new WaitForSeconds(12f);
				_lastBack = ship.GetSpeed();
				_report.Add($"TRIAL {name} back speed {F(ship.GetSpeed())} setting {ship.GetSpeedSetting()}");
				break;
			}
			Flush();
			((Character)p).AttachStop();
			yield return (object)new WaitForSeconds(0.5f);
			((Character)p).TeleportTo(start + new Vector3(0f, 3f, -40f), Quaternion.identity, false);
			if ((Object)(object)go != (Object)null)
			{
				ZNetScene.instance.Destroy(go);
			}
			yield return (object)new WaitForSeconds(2f);
		}

		private IEnumerator TakeHelm(Player p, Ship ship)
		{
			Transform seat = ship.m_shipControlls.m_attachPoint;
			for (int attempt = 0; attempt < 4; attempt++)
			{
				if (((Character)p).IsAttached())
				{
					break;
				}
				((Character)p).TeleportTo(seat.position + Vector3.up * 1.2f, ((Component)ship).transform.rotation, false);
				float limit = Time.time + 4f;
				while ((Object)(object)((Character)p).GetStandingOnShip() != (Object)(object)ship && Time.time < limit)
				{
					yield return null;
				}
				ship.m_shipControlls.Interact((Humanoid)(object)p, false, false);
				yield return (object)new WaitForSeconds(1f);
			}
			if (!((Character)p).IsAttached())
			{
				_report.Add("TRIAL helm taken directly (Interact refused)");
				((Component)ship).GetComponent<ZNetView>().GetZDO().Set(ZDOVars.s_user, p.GetPlayerID());
				p.StartDoodadControl((IDoodadController)(object)ship.m_shipControlls);
				((Character)p).AttachStart(seat, (GameObject)null, false, false, true, ship.m_shipControlls.m_attachAnimation, ship.m_shipControlls.m_detachOffset, (Transform)null);
				yield return (object)new WaitForSeconds(1f);
			}
		}

		private IEnumerator Turn(Ship ship, GameObject go, string label)
		{
			float v0 = ship.GetSpeed();
			float y = go.transform.eulerAngles.y;
			float t0 = Time.time;
			float t90 = -1f;
			float total = 0f;
			float last = y;
			float rudderFull = -1f;
			while (Time.time - t0 < 14f)
			{
				if ((Object)(object)go == (Object)null)
				{
					_report.Add("TRIAL " + label + " turn: the ship is gone after " + F(Time.time - t0) + " s");
					yield break;
				}
				ship.ApplyControlls(new Vector3(1f, 0f, 0f));
				if (rudderFull < 0f && ship.GetRudderValue() >= 0.99f)
				{
					rudderFull = Time.time - t0;
				}
				yield return (object)new WaitForFixedUpdate();
				float y2 = go.transform.eulerAngles.y;
				total += Mathf.DeltaAngle(last, y2);
				last = y2;
				if (t90 < 0f && Mathf.Abs(total) >= 90f)
				{
					t90 = Time.time - t0;
				}
			}
			_lastQuarter = t90;
			_report.Add("TRIAL " + label + " turn: speed before " + F(v0) + " after " + F(ship.GetSpeed()) + " turned " + F(total) + "° in 14 s, quarter turn in " + F(t90) + " s, rudder full after " + F(rudderFull) + " s");
			ship.Rudder(0f);
		}

		private IEnumerator SetSpeed(Ship ship, Speed want)
		{
			//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)
			ship.Stop();
			yield return (object)new WaitForFixedUpdate();
			for (int i = 0; i < 5; i++)
			{
				if (ship.GetSpeedSetting() == want)
				{
					break;
				}
				if ((int)want == 1)
				{
					ship.Backward();
				}
				else
				{
					ship.Forward();
				}
				yield return (object)new WaitForFixedUpdate();
				yield return (object)new WaitForFixedUpdate();
			}
			if (ship.GetSpeedSetting() != want)
			{
				_report.Add($"TRIAL speed setting {ship.GetSpeedSetting()} instead of {want}");
			}
		}

		private void Dump(Transform t, int depth)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c8: Expected O, but got Unknown
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			string text = new string(' ', depth * 2);
			_report.Add($"{text}{((Object)t).name} layer {LayerMask.LayerToName(((Component)t).gameObject.layer)} lp {V(t.localPosition)} lr {V(t.localEulerAngles)} ls {V(t.localScale)} active {((Component)t).gameObject.activeSelf}");
			Component[] components = ((Component)t).GetComponents<Component>();
			foreach (Component val in components)
			{
				if ((Object)(object)val == (Object)null || val is Transform)
				{
					continue;
				}
				string text2 = ((object)val).GetType().Name;
				BoxCollider val2 = (BoxCollider)(object)((val is BoxCollider) ? val : null);
				if (val2 == null)
				{
					CapsuleCollider val3 = (CapsuleCollider)(object)((val is CapsuleCollider) ? val : null);
					if (val3 == null)
					{
						MeshCollider val4 = (MeshCollider)(object)((val is MeshCollider) ? val : null);
						if (val4 == null)
						{
							Renderer val5 = (Renderer)(object)((val is Renderer) ? val : null);
							if (val5 == null)
							{
								Container val6 = (Container)(object)((val is Container) ? val : null);
								if (val6 == null)
								{
									Chair val7 = (Chair)(object)((val is Chair) ? val : null);
									if (val7 == null)
									{
										ZSFX val8 = (ZSFX)(object)((val is ZSFX) ? val : null);
										if (val8 == null)
										{
											AudioSource val9 = (AudioSource)(object)((val is AudioSource) ? val : null);
											if (val9 == null)
											{
												if (val is ParticleSystem)
												{
													text2 += " particles";
												}
											}
											else
											{
												string text3 = text2;
												AudioClip clip = val9.clip;
												text2 = text3 + $" clip {((clip != null) ? ((Object)clip).name : null)} loop {val9.loop}";
											}
										}
										else
										{
											text2 = text2 + " clips " + string.Join(",", val8.m_audioClips.Select((AudioClip a) => (a == null) ? null : ((Object)a).name));
										}
									}
									else
									{
										text2 = text2 + " " + val7.m_name + " anim " + val7.m_attachAnimation;
									}
								}
								else
								{
									text2 += $" {val6.m_name} {val6.m_width}x{val6.m_height}";
								}
							}
							else
							{
								text2 = text2 + " mats " + string.Join(", ", from m in val5.sharedMaterials
									where (Object)(object)m != (Object)null
									select ((Object)m).name);
							}
						}
						else
						{
							string text4 = text2;
							object arg = val4.convex;
							Mesh sharedMesh = val4.sharedMesh;
							text2 = text4 + $" convex {arg} mesh {((sharedMesh != null) ? ((Object)sharedMesh).name : null)} trig {((Collider)val4).isTrigger}";
						}
					}
					else
					{
						text2 += $" c {V(val3.center)} r {F(val3.radius)} h {F(val3.height)} dir {val3.direction} trig {((Collider)val3).isTrigger}";
					}
				}
				else
				{
					text2 += $" c {V(val2.center)} s {V(val2.size)} trig {((Collider)val2).isTrigger}";
				}
				_report.Add(text + "  + " + text2);
			}
			foreach (Transform item in t)
			{
				Transform t2 = item;
				Dump(t2, depth + 1);
			}
		}

		private bool Scene(string name)
		{
			string text = Var("ONLY");
			if (!string.IsNullOrEmpty(text))
			{
				return text.Split(new char[1] { ',' }).Contains(name);
			}
			return true;
		}

		private IEnumerator Scenes()
		{
			Player p = Player.m_localPlayer;
			p.SetGodMode(true);
			if (!FindOpenSea(((Component)p).transform.position, out _sea))
			{
				Check(ok: false, "open sea found for the scenes");
				yield break;
			}
			_report.Add("scene: open sea at " + V(_sea));
			EnvMan.instance.m_debugTimeOfDay = true;
			EnvMan.instance.m_debugTime = 0.38f;
			EnvMan.instance.m_debugEnv = "Clear";
			if (Scene("build"))
			{
				yield return ((MonoBehaviour)this).StartCoroutine(Safe(BuildScene()));
			}
			Flush();
			yield return ((MonoBehaviour)this).StartCoroutine(Safe(GoToSea(p)));
			if (Scene("look"))
			{
				yield return ((MonoBehaviour)this).StartCoroutine(Safe(LookScene(p)));
			}
			Flush();
			if (Scene("hold"))
			{
				yield return ((MonoBehaviour)this).StartCoroutine(Safe(HoldScene(p)));
			}
			Flush();
			if (Scene("sail"))
			{
				yield return ((MonoBehaviour)this).StartCoroutine(Safe(SailScene(p)));
			}
			Flush();
			if (Scene("encounter"))
			{
				yield return ((MonoBehaviour)this).StartCoroutine(Safe(EncounterScene(p)));
			}
			Flush();
		}

		private IEnumerator GoToSea(Player p)
		{
			((Character)p).TeleportTo(_sea + Vector3.up * 3f, Quaternion.identity, true);
			float limit = Time.realtimeSinceStartup + 120f;
			while ((((Character)p).IsTeleporting() || !ZoneSystem.instance.IsZoneLoaded(_sea)) && Time.realtimeSinceStartup < limit)
			{
				yield return null;
			}
			yield return (object)new WaitForSecondsRealtime(3f);
		}

		private GameObject SpawnShip(string prefab, Vector3 at, float yaw = 0f)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = Object.Instantiate<GameObject>(ZNetScene.instance.GetPrefab(prefab), at, Quaternion.Euler(0f, yaw, 0f));
			WearNTear component = val.GetComponent<WearNTear>();
			if ((Object)(object)component != (Object)null)
			{
				component.m_health = 10000000f;
				val.GetComponent<ZNetView>().GetZDO().Set(ZDOVars.s_health, 10000000f);
			}
			return val;
		}

		private IEnumerator LeaveShip(Player p, GameObject go)
		{
			if (((Character)p).IsAttached())
			{
				((Character)p).AttachStop();
			}
			yield return (object)new WaitForSeconds(0.5f);
			((Character)p).TeleportTo(_sea + new Vector3(0f, 3f, -60f), Quaternion.identity, false);
			if ((Object)(object)go != (Object)null)
			{
				ZNetScene.instance.Destroy(go);
			}
			yield return (object)new WaitForSeconds(2f);
		}

		private IEnumerator BuildScene()
		{
			GameObject prefab = ZNetScene.instance.GetPrefab("zap_knarr");
			Check((Object)(object)prefab != (Object)null, "knarr prefab zap_knarr registered");
			if (!((Object)(object)prefab == (Object)null))
			{
				Piece component = prefab.GetComponent<Piece>();
				PieceTable buildPieces = ObjectDB.instance.GetItemPrefab("Hammer").GetComponent<ItemDrop>().m_itemData.m_shared.m_buildPieces;
				Check(buildPieces.m_pieces.Contains(prefab), "knarr is in the hammer's pieces");
				string text = string.Join(",", component.m_resources.Select((Requirement r) => ((Object)r.m_resItem).name + ":" + r.m_amount));
				Check(text == "FineWood:50,ElderBark:40,IronNails:120,LinenThread:40,LoxPelt:6", "recipe " + text + " (must be FineWood:50,ElderBark:40,IronNails:120,LinenThread:40,LoxPelt:6)");
				SelfTest selfTest = this;
				bool ok = (Object)(object)component.m_craftingStation != (Object)null && ((Object)component.m_craftingStation).name == "piece_workbench";
				CraftingStation craftingStation = component.m_craftingStation;
				selfTest.Check(ok, "station " + ((craftingStation != null) ? ((Object)craftingStation).name : null) + " (must be piece_workbench)");
				Check(component.m_waterPiece, "placed on water like the longship");
				SelfTest selfTest2 = this;
				bool ok2 = (Object)(object)component.m_icon != (Object)null && ((Object)component.m_icon).name != ((Object)ZNetScene.instance.GetPrefab("VikingShip").GetComponent<Piece>().m_icon).name;
				Sprite icon = component.m_icon;
				selfTest2.Check(ok2, "own icon " + ((icon != null) ? ((Object)icon).name : null));
				_report.Add("piece name " + Localization.instance.Localize(component.m_name) + " desc " + Localization.instance.Localize(component.m_description));
				Check(prefab.GetComponents<MonoBehaviour>().All((MonoBehaviour m) => ((object)m).GetType().Assembly.GetName().Name != "LongshipUpgrades"), "no Longship Upgrades parts on the knarr");
				Ship component2 = prefab.GetComponent<Ship>();
				Ship component3 = ZNetScene.instance.GetPrefab("VikingShip").GetComponent<Ship>();
				_report.Add("sail factor knarr " + F(component2.m_sailForceFactor) + " longship " + F(component3.m_sailForceFactor) + ", steer " + F(component2.m_stearVelForceFactor) + "/" + F(component3.m_stearVelForceFactor) + ", float box " + V(component2.m_floatCollider.size));
				Check(component2.m_sailForceFactor > component3.m_sailForceFactor * 1.1f && component2.m_sailForceFactor < component3.m_sailForceFactor * 1.35f, "sail force a fifth or so above the longship's");
				Container componentInChildren = prefab.GetComponentInChildren<Container>(true);
				Check((Object)(object)componentInChildren != (Object)null && componentInChildren.m_width == 8 && componentInChildren.m_height == 7, $"hold {componentInChildren?.m_width}x{componentInChildren?.m_height} (must be 8x7)");
			}
			yield break;
		}

		private IEnumerator LookScene(Player p)
		{
			GameObject knarr = SpawnShip("zap_knarr", _sea);
			GameObject longship = SpawnShip("VikingShip", _sea + new Vector3(-16f, 0f, -4f));
			((Character)p).TeleportTo(_sea + new Vector3(30f, 25f, -50f), Quaternion.identity, false);
			yield return (object)new WaitForSeconds(12f);
			Bounds val = RendererBounds(knarr);
			Bounds val2 = RendererBounds(longship);
			float waterLevel = ZoneSystem.instance.m_waterLevel;
			_report.Add("look: knarr bounds " + V(((Bounds)(ref val)).size) + " longship " + V(((Bounds)(ref val2)).size) + "; knarr y " + F(knarr.transform.position.y - waterLevel) + " longship y " + F(longship.transform.position.y - waterLevel));
			float num = HullLength(knarr);
			float num2 = HullLength(longship);
			Check(Mathf.Abs(num / num2 - 1.2f) < 0.05f, "knarr hull " + F(num) + " m = longship hull " + F(num2) + " m × " + F(num / num2) + " (must be about " + F(1.2f) + ")");
			Check(Mathf.Abs(((Bounds)(ref val)).size.x / ((Bounds)(ref val2)).size.x - 1.2f) < 0.08f, "knarr beam over all " + F(((Bounds)(ref val)).size.x) + " m = longship " + F(((Bounds)(ref val2)).size.x) + " m × " + F(((Bounds)(ref val)).size.x / ((Bounds)(ref val2)).size.x));
			float num3 = Vector3.Angle(knarr.transform.up, Vector3.up);
			Check(num3 < 6f, "knarr floats upright, tilt " + F(num3) + "° (must be under 6)");
			Check(Mathf.Abs(knarr.transform.position.y - waterLevel) < 4f, "knarr floats at the water, y " + F(knarr.transform.position.y - waterLevel) + " m");
			bool flag = knarr.GetComponentsInChildren<Renderer>().Any((Renderer r) => ((Object)r).name == "skull_head");
			Check(!flag, "no dragon head on the knarr");
			Vector3 mid = (knarr.transform.position + longship.transform.position) * 0.5f;
			yield return ((MonoBehaviour)this).StartCoroutine(MainShot("look_side", Look(mid, 120f, 46f, 12f)));
			yield return ((MonoBehaviour)this).StartCoroutine(MainShot("look_bow", Look(knarr.transform.position, 25f, 36f, 9f)));
			yield return ((MonoBehaviour)this).StartCoroutine(MainShot("look_top", new Pose(mid + new Vector3(0f, 55f, -8f), Quaternion.LookRotation(Vector3.down + Vector3.forward * 0.15f))));
			ZNetScene.instance.Destroy(longship);
			yield return ((MonoBehaviour)this).StartCoroutine(MainShot("look_deck", new Pose(knarr.transform.TransformPoint(new Vector3(0f, 7f, -14f)), Quaternion.LookRotation(knarr.transform.forward * 1f + Vector3.down * 0.45f))));
			yield return ((MonoBehaviour)this).StartCoroutine(TakeHelm(p, knarr.GetComponent<Ship>()));
			Check(((Character)p).IsAttached(), "player at the knarr's helm");
			Check((Object)(object)KnarrShip.Local() != (Object)null, "the game counts the player aboard a knarr");
			((Character)p).SetLookDir(knarr.transform.forward, 0f);
			p.m_lookPitch = 12f;
			((Character)p).UpdateEyeRotation();
			yield return (object)new WaitForSeconds(1f);
			yield return ((MonoBehaviour)this).StartCoroutine(Shot("look_helm"));
			((Character)p).AttachStop();
			yield return (object)new WaitForSeconds(1f);
			Chair[] array = (from c in knarr.GetComponentsInChildren<Chair>()
				where c.m_inShip && c.m_attachAnimation == "attach_sitship"
				select c).ToArray();
			Check(array.Length >= 6, $"seats on the knarr {array.Length} (must be at least 6; the longship has 4)");
			Chair added = ((IEnumerable<Chair>)array).FirstOrDefault((Func<Chair, bool>)((Chair c) => ((Object)c).name == "sit_box_knarr_l"));
			Check((Object)(object)added != (Object)null, "the added seat box is there");
			if ((Object)(object)added != (Object)null)
			{
				((Character)p).TeleportTo(((Component)added).transform.position + knarr.transform.forward * 0.8f + Vector3.up * 0.5f, knarr.transform.rotation, false);
				for (int i = 0; i < 4; i++)
				{
					if (((Character)p).IsAttached())
					{
						break;
					}
					float t = Time.time;
					while (((Character)p).IsTeleporting() && Time.time - t < 10f)
					{
						yield return null;
					}
					yield return (object)new WaitForSeconds(2.5f);
					_report.Add($"sit try {i}: distance to the seat {F(Vector3.Distance(((Component)p).transform.position, added.m_attachPoint.position))} m, teleporting {((Character)p).IsTeleporting()}, encumbered {((Character)p).IsEncumbered()}");
					added.Interact((Humanoid)(object)p, false, false);
					yield return (object)new WaitForSeconds(1.5f);
				}
				Check(((Character)p).IsAttached() && (Object)(object)p.m_attachPoint == (Object)(object)added.m_attachPoint, "player sits on the added seat");
				Vector3 v = knarr.transform.InverseTransformPoint(((Component)p).transform.position);
				_report.Add("seated at local " + V(v));
				((Character)p).SetLookDir(knarr.transform.forward, 0f);
				p.m_lookPitch = 8f;
				((Character)p).UpdateEyeRotation();
				yield return (object)new WaitForSeconds(1f);
				yield return ((MonoBehaviour)this).StartCoroutine(Shot("look_seat"));
				((Character)p).AttachStop();
				yield return (object)new WaitForSeconds(1f);
			}
			yield return ((MonoBehaviour)this).StartCoroutine(MainShot("look_seats", new Pose(knarr.transform.TransformPoint(new Vector3(6f, 9f, 1.5f)), Quaternion.LookRotation(-knarr.transform.right * 0.8f + Vector3.down))));
			yield return ((MonoBehaviour)this).StartCoroutine(LeaveShip(p, knarr));
		}

		private static float HullLength(GameObject ship)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: 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_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			Renderer[] array = (from r in ship.GetComponentsInChildren<Renderer>()
				where ((Object)r).name.StartsWith("hull")
				select r).ToArray();
			if (array.Length == 0)
			{
				Bounds val = RendererBounds(ship);
				return ((Bounds)(ref val)).size.z;
			}
			Bounds bounds = array[0].bounds;
			Renderer[] array2 = array;
			foreach (Renderer val2 in array2)
			{
				((Bounds)(ref bounds)).Encapsulate(val2.bounds);
			}
			return ((Bounds)(ref bounds)).size.z;
		}

		private IEnumerator HoldScene(Player p)
		{
			GameObject knarr = SpawnShip("zap_knarr", _sea);
			yield return (object)new WaitForSeconds(4f);
			Container hold = knarr.GetComponentInChildren<Container>();
			Inventory inv = hold.GetInventory();
			Check(inv.GetWidth() == 8 && inv.GetHeight() == 7, $"hold inventory {inv.GetWidth()}x{inv.GetHeight()} (must be 8x7)");
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Wood");
			int num = 0;
			for (int i = 0; i < 60; i++)
			{
				if (inv.AddItem(itemPrefab, 50))
				{
					num++;
				}
			}
			Check(num == 56, $"stacks of wood that fit in the hold {num} (must be 56)");
			Check(Localization.instance.Localize(hold.m_name) == Lang.T("knarr_hold"), "hold name " + Localization.instance.Localize(hold.m_name));
			((Character)p).TeleportTo(((Component)hold).transform.position + knarr.transform.right * 1.5f + Vector3.up * 1f, knarr.transform.rotation, false);
			yield return (object)new WaitForSeconds(2.5f);
			bool opened = hold.Interact((Humanoid)(object)p, false, false);
			yield return (object)new WaitForSeconds(1.5f);
			Check(InventoryGui.IsVisible() && (Object)(object)InventoryGui.instance.m_currentContainer == (Object)(object)hold, $"hold opens with the use key (interact {opened})");
			yield return ((MonoBehaviour)this).StartCoroutine(Shot("hold_open"));
			InventoryGui.instance.Hide();
			yield return (object)new WaitForSeconds(1f);
			string before = Cfg.Hold.Value;
			Cfg.Hold.Value = "6x10";
			yield return (object)new WaitForSeconds(0.5f);
			Check(inv.GetWidth() == 8 && inv.GetHeight() == 10, $"narrower config 6x10: the hold afloat is {inv.GetWidth()}x{inv.GetHeight()} (must be 8x10, it only grows)");
			ZNetView component = knarr.GetComponent<ZNetView>();
			ZDO zdo = component.GetZDO();
			ZNetScene.instance.m_instances.Remove(zdo);
			zdo.Created = false;
			Object.Destroy((Object)(object)knarr);
			float t0 = Time.time;
			GameObject val = null;
			while ((Object)(object)val == (Object)null && Time.time - t0 < 10f)
			{
				yield return (object)new WaitForSeconds(0.5f);
				ZNetView obj = ZNetScene.instance.FindInstance(zdo);
				val = ((obj != null) ? ((Component)obj).gameObject : null);
			}
			Check((Object)(object)val != (Object)null && (Object)(object)val != (Object)(object)knarr, "the knarr loads again from its saved state");
			if ((Object)(object)val != (Object)null)
			{
				Inventory inventory = val.GetComponentInChildren<Container>().GetInventory();
				int count = inventory.GetAllItems().Count;
				Check(inventory.GetWidth() == 8 && inventory.GetHeight() == 10, $"reloaded hold {inventory.GetWidth()}x{inventory.GetHeight()} (must be 8x10)");
				Check(count == 56, $"stacks in the reloaded hold {count} (must be 56: nothing lost)");
				knarr = val;
			}
			else
			{
				ZDOMan.instance.DestroyZDO(zdo);
				knarr = null;
			}
			GameObject fresh = SpawnShip("zap_knarr", _sea + new Vector3(20f, 0f, 0f));
			yield return (object)new WaitForSeconds(2f);
			Inventory inventory2 = fresh.GetComponentInChildren<Container>().GetInventory();
			Check(inventory2.GetWidth() == 6 && inventory2.GetHeight() == 10, $"a knarr built now has {inventory2.GetWidth()}x{inventory2.GetHeight()} (must be the config's 6x10)");
			ZNetScene.instance.Destroy(fresh);
			Cfg.Hold.Value = before;
			yield return (object)new WaitForSeconds(0.5f);
			yield return ((MonoBehaviour)this).StartCoroutine(LeaveShip(p, knarr));
		}

		private IEnumerator SailScene(Player p)
		{
			EnvMan.instance.m_debugEnv = "";
			float lsTop = 0f;
			float lsQuarter = 0f;
			string[] array = new string[2] { "VikingShip", "zap_knarr" };
			foreach (string name in array)
			{
				yield return ((MonoBehaviour)this).StartCoroutine(Safe(TrialRun(p, name, _sea, "full", 0f, 1f)));
				float top = _lastTerminal;
				yield return ((MonoBehaviour)this).StartCoroutine(Safe(TrialRun(p, name, _sea, "full", 90f, 1f)));
				float beam = _lastTerminal;
				yield return ((MonoBehaviour)this).StartCoroutine(Safe(TrialRun(p, name, _sea, "turn", 0f, 1f)));
				float quarter = _lastQuarter;
				yield return ((MonoBehaviour)this).StartCoroutine(Safe(TrialRun(p, name, _sea, "slow", 0f, 1f)));
				float slow = _lastSlow;
				yield return ((MonoBehaviour)this).StartCoroutine(Safe(TrialRun(p, name, _sea, "back", 0f, 1f)));
				float lastBack = _lastBack;
				if (name == "VikingShip")
				{
					lsTop = top;
					lsQuarter = quarter;
					continue;
				}
				float num = Cfg.SpeedBonus.Value / 100f;
				Check(Mathf.Abs(top / lsTop - (1f + num)) < 0.05f, "knarr top speed " + F(top) + " m/s = longship " + F(lsTop) + " × " + F(top / lsTop) + " (must be about " + F(1f + num) + ")");
				Check(beam > top * 0.9f, "knarr with the wind abeam " + F(beam) + " m/s (about as fast as before the wind)");
				Check(quarter > 0f && Mathf.Abs(quarter - lsQuarter) < 1.5f, "knarr quarter turn " + F(quarter) + " s, longship " + F(lsQuarter) + " s (must be within 1.5 s)");
				Check(slow > 1.2f, "knarr paddles at " + F(slow) + " m/s (must be over 1.2)");
				Check(lastBack < -1.2f, "knarr backs at " + F(lastBack) + " m/s (must be under -1.2)");
			}
			EnvMan.instance.m_debugEnv = "Clear";
			EnvMan.instance.m_debugTimeOfDay = true;
			EnvMan.instance.m_debugTime = 0.62f;
			EnvMan.instance.SetDebugWind(30f, 1f);
			GameObject knarr = SpawnShip("zap_knarr", _sea);
			yield return (object)new WaitForSeconds(3f);
			yield return ((MonoBehaviour)this).StartCoroutine(TakeHelm(p, knarr.GetComponent<Ship>()));
			yield return ((MonoBehaviour)this).StartCoroutine(SetSpeed(knarr.GetComponent<Ship>(), (Speed)4));
			yield return (object)new WaitForSeconds(14f);
			yield return ((MonoBehaviour)this).StartCoroutine(MainShot("sail_side", Look(knarr.transform.position + knarr.transform.forward * 3f + Vector3.up * 4f, knarr.transform.eulerAngles.y + 80f, 24f, 4f)));
			yield return ((MonoBehaviour)this).StartCoroutine(MainShot("sail_bow", Look(knarr.transform.position + Vector3.up * 5f, knarr.transform.eulerAngles.y + 35f, 22f, 3f)));
			yield return ((MonoBehaviour)this).StartCoroutine(MainShot("sail_behind", Look(knarr.transform.position + Vector3.up * 3f, knarr.transform.eulerAngles.y + 200f, 24f, 9f)));
			yield return ((MonoBehaviour)this).StartCoroutine(LeaveShip(p, knarr));
			EnvMan.instance.m_debugEnv = "Clear";
		}

		private IEnumerator EncounterScene(Player p)
		{
			float plainNight = 0f;
			float knarrNight = 0f;
			float knarrDay = 0f;
			int heard = 0;
			(string, float, string)[] array = new(string, float, string)[3]
			{
				("VikingShip", 0f, "longship night"),
				("zap_knarr", 0f, "knarr night"),
				("zap_knarr", 0.5f, "knarr clear day")
			};
			for (int i = 0; i < array.Length; i++)
			{
				(string, float, string) tuple = array[i];
				string item = tuple.Item1;
				float item2 = tuple.Item2;
				string label = tuple.Item3;
				EnvMan.instance.m_debugTime = item2;
				GameObject go = SpawnShip(item, _sea);
				yield return (object)new WaitForSeconds(3f);
				Ship component = go.GetComponent<Ship>();
				yield return ((MonoBehaviour)this).StartCoroutine(TakeHelm(p, component));
				yield return (object)new WaitForSeconds(3f);
				SpawnSystem ss = ((IEnumerable<SpawnSystem>)SpawnSystem.m_instances).FirstOrDefault((Func<SpawnSystem, bool>)((SpawnSystem x) => (Object)(object)x != (Object)null && x.InsideZone(go.transform.position, 0f)));
				if ((Object)(object)ss == (Object)null)
				{
					Check(ok: false, label + ": spawn system of the zone found");
					yield return ((MonoBehaviour)this).StartCoroutine(LeaveShip(p, go));
					continue;
				}
				List<SpawnData> serpents = (from d in ss.m_spawnLists.SelectMany((SpawnSystemList l) => l.m_spawners)
					where (Object)(object)d.m_prefab != (Object)null && ((Object)d.m_prefab).name == "Serpent"
					select d.Clone()).ToList();
				float vanilla = ((serpents.Count > 0) ? serpents[0].m_spawnChance : 0f);
				_report.Add($"encounter {label}: {serpents.Count} serpent entries, chance {F(vanilla)}, knarr in zone {Encounters.KnarrSailsIn(ss)}, night {EnvMan.IsNight()}, env {EnvMan.instance.GetCurrentEnvironment()?.m_name}");
				int spawned = 0;
				int heardBefore = Encounters.StirHeard;
				for (int i2 = 0; i2 < 300; i2++)
				{
					SpawnSystem.m_tempNearPlayers.Clear();
					SpawnSystem.m_tempNearPlayers.Add(p);
					ss.UpdateSpawnList(serpents, ZNet.instance.GetTime(), false, $"knarrtest{label}{i2}_");
					int num = 0;
					foreach (Character item3 in Character.GetAllCharacters().ToList())
					{
						if ((Object)(object)item3 != (Object)null && ((Object)item3).name.StartsWith("Serpent", StringComparison.Ordinal))
						{
							num++;
							ZNetScene.instance.Destroy(((Component)item3).gameObject);
						}
					}
					if (num > 0)
					{
						spawned++;
					}
					yield return null;
				}
				yield return (object)new WaitForSeconds(1f);
				heard += Encounters.StirHeard - heardBefore;
				float num2 = (float)spawned / 300f;
				_report.Add($"encounter {label}: {spawned} of {300} rolls brought a serpent ({F(num2 * 100f)} %), stir messages {Encounters.StirHeard - heardBefore}");
				Check(serpents.All((SpawnData d) => Math.Abs(d.m_spawnChance - vanilla) < 0.001f), label + ": spawn chance back to " + F(vanilla) + " after the rolls");
				if (label == "longship night")
				{
					plainNight = num2;
				}
				else if (label == "knarr night")
				{
					knarrNight = num2;
				}
				else
				{
					knarrDay = num2;
				}
				if (label