Decompiled source of LostCamps v0.1.2

plugins/LostCamps/LostCamps.dll

Decompiled 8 hours ago
using System;
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.Serialization;
using System.Runtime.Serialization.Json;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using LostCamps.Camps;
using LostCamps.Logic;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: InternalsVisibleTo("LostCamps.Harness")]
[assembly: AssemblyCompany("Laoch")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) Laoch")]
[assembly: AssemblyFileVersion("0.1.2.0")]
[assembly: AssemblyInformationalVersion("0.1.2+c5584f3fa38eecb72d912b28193029689d7c67fb")]
[assembly: AssemblyProduct("LostCamps")]
[assembly: AssemblyTitle("LostCamps")]
[assembly: AssemblyVersion("0.1.2.0")]
[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 LostCamps
{
	[BepInPlugin("com.laoch.lostcamps", "Lost Camps", "0.1.2")]
	public class LostCampsPlugin : BaseUnityPlugin
	{
		public const string Guid = "com.laoch.lostcamps";

		public const string Version = "0.1.2";

		internal static ManualLogSource Log;

		private Harmony _harmony;

		private bool _campsFault;

		private void Awake()
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			string directoryName = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location);
			SleepScreens.Bind(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger, directoryName);
			CampSettings.Bind(((BaseUnityPlugin)this).Config);
			CampManager.Init(((BaseUnityPlugin)this).Logger, directoryName);
			_harmony = new Harmony("com.laoch.lostcamps");
			_harmony.PatchAll(typeof(LostCampsPlugin).Assembly);
		}

		private void Update()
		{
			if (_campsFault)
			{
				return;
			}
			try
			{
				CampManager.Update();
			}
			catch (Exception ex)
			{
				_campsFault = true;
				JournalWindow.Hide();
				((BaseUnityPlugin)this).Logger.LogError((object)("Camps stopped after an error: " + ex));
			}
		}

		private void OnGUI()
		{
			if (!_campsFault)
			{
				JournalWindow.Draw();
			}
		}

		private void OnDestroy()
		{
			JournalWindow.Hide();
			Parchment.Release();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	public enum SleepTextMode
	{
		StoicQuotes,
		VanillaDream,
		Both,
		None
	}
	public enum SleepArtFit
	{
		Fit,
		Cover
	}
	internal static class SleepScreens
	{
		private sealed class Source
		{
			public SleepEntry Entry;

			public string Root;
		}

		private static ConfigEntry<bool> _enabled;

		private static ConfigEntry<SleepTextMode> _textMode;

		private static ConfigEntry<SleepArtFit> _fit;

		private static ConfigEntry<float> _fontScale;

		private static ConfigEntry<float> _quoteHeight;

		private static ManualLogSource _log;

		private static string _pluginDir;

		private static string _stateDir;

		private static readonly Dictionary<string, Source> Entries = new Dictionary<string, Source>(StringComparer.OrdinalIgnoreCase);

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

		private static readonly ShuffleBag Bag = new ShuffleBag(new Random());

		private static readonly HashSet<string> Reported = new HashSet<string>();

		private static bool _catalogLoaded;

		private static DateTime _overrideStamp;

		private static readonly MethodInfo LoadImageMethod = typeof(ImageConversion).GetMethod("LoadImage", new Type[3]
		{
			typeof(Texture2D),
			typeof(byte[]),
			typeof(bool)
		});

		private static bool _stateRestored;

		internal static bool Active { get; private set; }

		internal static SleepTextMode TextMode
		{
			get
			{
				if (_textMode != null)
				{
					return _textMode.Value;
				}
				return SleepTextMode.Both;
			}
		}

		internal static bool Enabled
		{
			get
			{
				if (_enabled != null)
				{
					return _enabled.Value;
				}
				return false;
			}
		}

		private static string PackedRoot => Path.Combine(_pluginDir, "sleep");

		private static string OverrideRoot => Path.Combine(_stateDir, "sleep");

		private static string StatePath => Path.Combine(_stateDir, "sleep-state.txt");

		internal static bool KeepVanillaDream
		{
			get
			{
				if (Enabled && TextMode != SleepTextMode.VanillaDream)
				{
					return TextMode == SleepTextMode.Both;
				}
				return true;
			}
		}

		internal static void Bind(ConfigFile c, ManualLogSource log, string pluginDir)
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Expected O, but got Unknown
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Expected O, but got Unknown
			_log = log;
			_pluginDir = pluginDir;
			_stateDir = Path.Combine(Paths.ConfigPath, "LostCamps");
			_enabled = c.Bind<bool>("SleepScreens", "Enabled", true, "Show a painting and a short line over the sleep screen. Local to you; the server and other players are unaffected. Turn off to get the plain game sleep screen back at any time.");
			_textMode = c.Bind<SleepTextMode>("SleepScreens", "TextMode", SleepTextMode.StoicQuotes, "StoicQuotes: the line that goes with the painting. VanillaDream: only the game's own dream text (when the night has one). Both: the line and the game's dream text. None: no text.");
			_fit = c.Bind<SleepArtFit>("SleepScreens", "ImageFit", SleepArtFit.Fit, "Fit shows the whole painting (bars at the sides on ultrawide screens). Cover fills the screen and may crop the edges, including the line.");
			_fontScale = c.Bind<float>("SleepScreens", "QuoteFontScale", 1f, new ConfigDescription("Size of the line. 1 is the default.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 2f), Array.Empty<object>()));
			_quoteHeight = c.Bind<float>("SleepScreens", "QuoteHeight", 0.08f, new ConfigDescription("How far up from the bottom of the painting the line sits, as a fraction of the painting's height.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.4f), Array.Empty<object>()));
		}

		private static void Once(string key, string message)
		{
			if (Reported.Add(key))
			{
				_log.LogWarning((object)("Sleep screens: " + message));
			}
		}

		private static void LoadCatalog()
		{
			DateTime dateTime = SafeStamp(Path.Combine(OverrideRoot, "sleep_screens.json"));
			if (_catalogLoaded && dateTime == _overrideStamp)
			{
				return;
			}
			_catalogLoaded = true;
			_overrideStamp = dateTime;
			Entries.Clear();
			Pool.Clear();
			AddFrom(PackedRoot);
			AddFrom(OverrideRoot);
			foreach (KeyValuePair<string, Source> entry in Entries)
			{
				Pool.Add(entry.Key);
			}
			Pool.Sort(StringComparer.Ordinal);
			_log.LogInfo((object)("Sleep screens: " + Pool.Count + " usable painting(s)."));
		}

		private static DateTime SafeStamp(string p)
		{
			try
			{
				return File.Exists(p) ? File.GetLastWriteTimeUtc(p) : DateTime.MinValue;
			}
			catch (Exception)
			{
				return DateTime.MinValue;
			}
		}

		private static void AddFrom(string root)
		{
			string text = Path.Combine(root, "sleep_screens.json");
			if (!File.Exists(text))
			{
				return;
			}
			SleepCatalog sleepCatalog;
			try
			{
				sleepCatalog = SleepCatalog.Parse(File.ReadAllText(text));
			}
			catch (Exception ex)
			{
				Once("read:" + root, "could not read " + text + " (" + ex.Message + ")");
				return;
			}
			foreach (string problem in sleepCatalog.Problems)
			{
				Once("cat:" + root + problem, problem + " (" + text + ")");
			}
			string text2 = Path.GetFullPath(root).TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
			char directorySeparatorChar = Path.DirectorySeparatorChar;
			string value = text2 + directorySeparatorChar;
			foreach (SleepEntry entry in sleepCatalog.Entries)
			{
				string fullPath;
				try
				{
					fullPath = Path.GetFullPath(Path.Combine(root, entry.Image));
				}
				catch (Exception)
				{
					continue;
				}
				if (!fullPath.StartsWith(value, StringComparison.OrdinalIgnoreCase))
				{
					Once("esc:" + entry.Id, "entry '" + entry.Id + "' points outside its folder; skipped");
				}
				else if (!File.Exists(fullPath))
				{
					Once("miss:" + entry.Id, "entry '" + entry.Id + "' has no image file (" + entry.Image + "); skipped");
				}
				else
				{
					Entries[entry.Id] = new Source
					{
						Entry = entry,
						Root = root
					};
				}
			}
		}

		internal static void Begin(SleepText st)
		{
			if (!Enabled || (Object)(object)st == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null || !Player.m_localPlayer.IsSleeping())
			{
				return;
			}
			try
			{
				if ((Object)(object)((Component)st).transform.parent == (Object)null)
				{
					return;
				}
				Transform val = ((Component)st).transform.parent.Find("DAL_SleepArt");
				if ((Object)(object)val != (Object)null)
				{
					SleepArtView component = ((Component)val).GetComponent<SleepArtView>();
					if ((Object)(object)component != (Object)null && component.IsFor(st))
					{
						return;
					}
					Object.Destroy((Object)(object)((Component)val).gameObject);
				}
				LoadCatalog();
				if (Pool.Count == 0)
				{
					Once("empty", "no usable paintings; using the vanilla sleep screen.");
					return;
				}
				RestoreState();
				for (int i = 0; i < 4; i++)
				{
					string text = Bag.Next(Pool);
					if (text == null)
					{
						break;
					}
					SaveState();
					Texture2D val2 = LoadTexture(Entries[text]);
					if ((Object)(object)val2 == (Object)null)
					{
						Pool.Remove(text);
						if (Pool.Count == 0)
						{
							break;
						}
						continue;
					}
					SleepArtView.Create(st, val2, Entries[text].Entry, _fit.Value, _fontScale.Value, _quoteHeight.Value, TextMode == SleepTextMode.StoicQuotes || TextMode == SleepTextMode.Both);
					Active = true;
					if (_log != null)
					{
						_log.LogDebug((object)("Sleep screens: showing " + text));
					}
					break;
				}
			}
			catch (Exception ex)
			{
				Once("begin:" + ex.GetType().Name, "could not show a painting (" + ex.Message + "); using the vanilla sleep screen.");
			}
		}

		internal static void End()
		{
			Active = false;
		}

		private static Texture2D LoadTexture(Source s)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: 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_009d: Expected O, but got Unknown
			string fullPath = Path.GetFullPath(Path.Combine(s.Root, s.Entry.Image));
			try
			{
				if (new FileInfo(fullPath).Length > 33554432)
				{
					Once("big:" + s.Entry.Id, "'" + s.Entry.Id + "' is larger than 32 MB; skipped");
					return null;
				}
				Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false)
				{
					name = "DAL_Sleep_" + s.Entry.Id,
					wrapMode = (TextureWrapMode)1,
					filterMode = (FilterMode)1
				};
				if (!Decode(val, File.ReadAllBytes(fullPath)))
				{
					Object.Destroy((Object)(object)val);
					Once("img:" + s.Entry.Id, "'" + s.Entry.Id + "' could not be decoded; skipped");
					return null;
				}
				return val;
			}
			catch (Exception ex)
			{
				Once("load:" + s.Entry.Id, "'" + s.Entry.Id + "' could not be loaded (" + ex.Message + "); skipped");
				return null;
			}
		}

		private static bool Decode(Texture2D tex, byte[] bytes)
		{
			if (LoadImageMethod != null)
			{
				return (bool)LoadImageMethod.Invoke(null, new object[3] { tex, bytes, true });
			}
			return false;
		}

		private static void RestoreState()
		{
			if (_stateRestored)
			{
				return;
			}
			_stateRestored = true;
			try
			{
				Bag.Restore(File.Exists(StatePath) ? File.ReadAllText(StatePath) : "");
			}
			catch (Exception)
			{
				Bag.Reset();
			}
		}

		private static void SaveState()
		{
			try
			{
				Directory.CreateDirectory(_stateDir);
				File.WriteAllText(StatePath, Bag.Serialize());
			}
			catch (Exception ex)
			{
				Once("state", "could not save the rotation (" + ex.Message + ")");
			}
		}
	}
	internal sealed class SleepArtView : MonoBehaviour
	{
		internal const string ObjectName = "DAL_SleepArt";

		private RectTransform _root;

		private RectTransform _art;

		private RectTransform _quote;

		private RectTransform _shadow;

		private TextMeshProUGUI _quoteText;

		private TextMeshProUGUI _shadowText;

		private Texture2D _tex;

		private SleepArtFit _fit;

		private float _scale;

		private float _height;

		private Vector2 _lastSize;

		private SleepText _owner;

		internal static void Create(SleepText st, Texture2D tex, SleepEntry entry, SleepArtFit fit, float fontScale, float quoteHeight, bool showQuote)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Expected O, but got Unknown
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Expected O, but got Unknown
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("DAL_SleepArt", new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(((Component)st).transform.parent, false);
			val.transform.SetSiblingIndex(((Component)st).transform.GetSiblingIndex());
			SleepArtView sleepArtView = val.AddComponent<SleepArtView>();
			sleepArtView._owner = st;
			sleepArtView._tex = tex;
			sleepArtView._fit = fit;
			sleepArtView._scale = fontScale;
			sleepArtView._height = quoteHeight;
			sleepArtView._root = (RectTransform)val.transform;
			sleepArtView._root.anchorMin = Vector2.zero;
			sleepArtView._root.anchorMax = Vector2.one;
			RectTransform root = sleepArtView._root;
			Vector2 offsetMin = (sleepArtView._root.offsetMax = Vector2.zero);
			root.offsetMin = offsetMin;
			GameObject val2 = new GameObject("Art", new Type[2]
			{
				typeof(RectTransform),
				typeof(RawImage)
			});
			val2.transform.SetParent(val.transform, false);
			sleepArtView._art = (RectTransform)val2.transform;
			RawImage component = val2.GetComponent<RawImage>();
			component.texture = (Texture)(object)tex;
			((Graphic)component).raycastTarget = false;
			if (showQuote && !string.IsNullOrEmpty(entry.Quote))
			{
				TMP_FontAsset font = (((Object)(object)st.m_dreamField != (Object)null) ? st.m_dreamField.font : null);
				sleepArtView._shadowText = MakeText(val.transform, "QuoteShadow", entry.Quote, font, new Color(0f, 0f, 0f, 0.7f));
				sleepArtView._quoteText = MakeText(val.transform, "Quote", entry.Quote, font, new Color(1f, 0.96f, 0.86f, 1f));
				sleepArtView._shadow = ((TMP_Text)sleepArtView._shadowText).rectTransform;
				sleepArtView._quote = ((TMP_Text)sleepArtView._quoteText).rectTransform;
			}
			sleepArtView.Layout();
		}

		private static TextMeshProUGUI MakeText(Transform parent, string name, string text, TMP_FontAsset font, Color color)
		{
			//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_0051: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name, new Type[2]
			{
				typeof(RectTransform),
				typeof(TextMeshProUGUI)
			});
			val.transform.SetParent(parent, false);
			TextMeshProUGUI component = val.GetComponent<TextMeshProUGUI>();
			if ((Object)(object)font != (Object)null)
			{
				((TMP_Text)component).font = font;
			}
			((TMP_Text)component).text = text;
			((Graphic)component).color = color;
			((Graphic)component).raycastTarget = false;
			((TMP_Text)component).alignment = (TextAlignmentOptions)514;
			((TMP_Text)component).textWrappingMode = (TextWrappingModes)1;
			((TMP_Text)component).enableAutoSizing = true;
			((TMP_Text)component).overflowMode = (TextOverflowModes)1;
			return component;
		}

		internal bool IsFor(SleepText st)
		{
			if ((Object)(object)_owner == (Object)(object)st && (Object)(object)st != (Object)null)
			{
				return ((Component)st).gameObject.activeInHierarchy;
			}
			return false;
		}

		private void LateUpdate()
		{
			//IL_0032: 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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_owner == (Object)null || !((Component)_owner).gameObject.activeInHierarchy)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
				return;
			}
			Rect rect = _root.rect;
			if (((Rect)(ref rect)).size != _lastSize)
			{
				Layout();
			}
		}

		private void Layout()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: 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)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: 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_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			Rect rect = _root.rect;
			Vector2 val = (_lastSize = ((Rect)(ref rect)).size);
			if (!(val.x < 1f) && !(val.y < 1f) && !((Object)(object)_tex == (Object)null))
			{
				float num = (float)((Texture)_tex).width / (float)((Texture)_tex).height;
				float num2;
				float num3;
				if (_fit == SleepArtFit.Fit == val.x / val.y >= num)
				{
					num2 = val.y;
					num3 = num2 * num;
				}
				else
				{
					num3 = val.x;
					num2 = num3 / num;
				}
				if (_fit == SleepArtFit.Fit && num3 > val.x)
				{
					num3 = val.x;
					num2 = num3 / num;
				}
				RectTransform art = _art;
				RectTransform art2 = _art;
				RectTransform art3 = _art;
				Vector2 val2 = default(Vector2);
				((Vector2)(ref val2))..ctor(0.5f, 0.5f);
				art3.pivot = val2;
				Vector2 anchorMin = (art2.anchorMax = val2);
				art.anchorMin = anchorMin;
				_art.sizeDelta = new Vector2(num3, num2);
				_art.anchoredPosition = Vector2.zero;
				if (!((Object)(object)_quote == (Object)null))
				{
					float h = num2 * 0.13f;
					float w = Mathf.Min(num3 * 0.74f, val.x * 0.92f);
					float num4 = (0f - num2) / 2f + num2 * _height;
					float num5 = (0f - val.y) / 2f + val.y * 0.03f;
					num4 = Mathf.Max(num4, num5);
					float num6 = Mathf.Clamp(num2 * 0.04f * _scale, 12f, 90f);
					Place(_quote, _quoteText, w, h, num4, num6, Vector2.zero);
					Place(_shadow, _shadowText, w, h, num4, num6, new Vector2(num6 * 0.06f, (0f - num6) * 0.06f));
				}
			}
		}

		private static void Place(RectTransform rt, TextMeshProUGUI t, float w, float h, float bottom, float size, Vector2 offset)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: 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_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: 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)
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(0.5f, 0.5f);
			rt.anchorMax = val;
			rt.anchorMin = val;
			rt.pivot = new Vector2(0.5f, 0f);
			rt.sizeDelta = new Vector2(w, h);
			rt.anchoredPosition = new Vector2(0f, bottom) + offset;
			((TMP_Text)t).fontSizeMax = size;
			((TMP_Text)t).fontSizeMin = size * 0.55f;
			((TMP_Text)t).fontSize = size;
		}

		private void OnDisable()
		{
			Object.Destroy((Object)(object)((Component)this).gameObject);
		}

		private void OnDestroy()
		{
			SleepScreens.End();
			if ((Object)(object)_tex != (Object)null)
			{
				Object.Destroy((Object)(object)_tex);
				_tex = null;
			}
		}
	}
	[HarmonyPatch(typeof(SleepText), "OnEnable")]
	internal static class SleepTextOnEnable
	{
		private static void Postfix(SleepText __instance)
		{
			SleepScreens.Begin(__instance);
		}
	}
	[HarmonyPatch(typeof(SleepText), "ShowDreamText")]
	internal static class SleepTextShowDream
	{
		private static void Postfix(SleepText __instance)
		{
			if (!SleepScreens.KeepVanillaDream && (Object)(object)__instance != (Object)null && (Object)(object)__instance.m_dreamField != (Object)null)
			{
				((Behaviour)__instance.m_dreamField).enabled = false;
			}
		}
	}
}
namespace LostCamps.Logic
{
	[DataContract]
	public sealed class LorePack
	{
		public const int MaxEntries = 1000;

		public const int MaxTextLength = 6000;

		public const int MaxDay = 1000000;

		[DataMember]
		public int schema = 1;

		[DataMember]
		public string id = "";

		[DataMember]
		public string title = "";

		[DataMember]
		public List<LoreEntry> entries = new List<LoreEntry>();

		public void Validate()
		{
			if (schema != 1 || !Rules.ValidId(id))
			{
				throw new InvalidDataException("Unsupported pack schema or ID.");
			}
			if (entries == null || entries.Count < 1 || entries.Count > 1000)
			{
				throw new InvalidDataException("A pack needs 1.." + 1000 + " entries.");
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			HashSet<string> hashSet2 = new HashSet<string>(StringComparer.Ordinal);
			foreach (LoreEntry entry in entries)
			{
				if (entry == null || !Rules.ValidId(entry.id) || !hashSet.Add(entry.id))
				{
					throw new InvalidDataException("Missing or duplicate entry ID.");
				}
				if (entry.day < 0 || entry.day > 1000000 || string.IsNullOrWhiteSpace(entry.author) || entry.author.Length > 80 || string.IsNullOrWhiteSpace(entry.text) || entry.text.Length > 6000)
				{
					throw new InvalidDataException("Invalid day, author or text: " + entry.id);
				}
				if (entry.targets == null || entry.targets.Count == 0 || entry.targets.Any((string t) => !Rules.ValidId(t)))
				{
					throw new InvalidDataException("Entry needs explicit target location IDs: " + entry.id);
				}
				if (!hashSet2.Add(Regex.Replace(entry.text.Trim(), "\\s+", " ")))
				{
					throw new InvalidDataException("Repeated lore body: " + entry.id);
				}
				foreach (Match item in Regex.Matches(entry.text, "\\{([^{}]+)\\}"))
				{
					if (!Rules.Tokens.Contains(item.Groups[1].Value))
					{
						throw new InvalidDataException("Unknown placeholder: " + item.Value);
					}
				}
				if (Regex.Replace(entry.text, "\\{[^{}]+\\}", "").IndexOfAny(new char[2] { '{', '}' }) >= 0)
				{
					throw new InvalidDataException("Unbalanced placeholders.");
				}
			}
		}
	}
	[DataContract]
	public sealed class LoreEntry
	{
		[DataMember]
		public string id = "";

		[DataMember]
		public int day;

		[DataMember]
		public string author = "";

		[DataMember]
		public string text = "";

		[DataMember]
		public List<string> targets = new List<string>();
	}
	[DataContract]
	public sealed class CampRecord
	{
		public const int MaxNearbyLength = 800;

		[DataMember]
		public string id = "";

		[DataMember]
		public string pack = "";

		[DataMember]
		public string entry = "";

		[DataMember]
		public int day;

		[DataMember]
		public string author = "";

		[DataMember]
		public string text = "";

		[DataMember]
		public string target = "";

		[DataMember]
		public string nearby = "";

		[DataMember]
		public double x;

		[DataMember]
		public double z;

		[DataMember]
		public double targetX;

		[DataMember]
		public double targetZ;

		[DataMember]
		public string status = "reserved";

		public bool IsPlausible()
		{
			if (Rules.ValidId(id) && Rules.ValidId(pack) && Rules.ValidId(entry) && day >= 0 && day <= 1000000 && author != null && author.Length <= 80 && text != null && text.Length <= 6000)
			{
				if (nearby != null)
				{
					return nearby.Length <= 800;
				}
				return true;
			}
			return false;
		}
	}
	public static class CampStatus
	{
		public const string Reserved = "reserved";

		public const string Placed = "placed";

		public const string Removed = "removed";

		public const string Failed = "failed";
	}
	[DataContract]
	public sealed class WorldState
	{
		[DataMember]
		public int schema = 1;

		[DataMember]
		public string world = "";

		[DataMember]
		public List<CampRecord> camps = new List<CampRecord>();

		public void Validate(string expectedWorld)
		{
			if (schema != 1 || world != expectedWorld || camps == null)
			{
				throw new InvalidDataException("World-state identity or schema mismatch.");
			}
			if (camps.Select((CampRecord c) => c.id).Distinct().Count() != camps.Count || camps.Select((CampRecord c) => c.pack + ":" + c.entry).Distinct().Count() != camps.Count)
			{
				throw new InvalidDataException("Duplicate camp or lore assignment.");
			}
			foreach (CampRecord camp in camps)
			{
				if (!camp.IsPlausible() || !Rules.Finite(camp.x) || !Rules.Finite(camp.z) || !Rules.Finite(camp.targetX) || !Rules.Finite(camp.targetZ) || (camp.status != "reserved" && camp.status != "placed" && camp.status != "removed" && camp.status != "failed"))
				{
					throw new InvalidDataException("Invalid camp record.");
				}
			}
		}
	}
	[DataContract]
	public sealed class Journal
	{
		public const int MaxEntries = 1000;

		[DataMember]
		public int schema = 1;

		[DataMember]
		public string world = "";

		[DataMember]
		public string character = "";

		[DataMember]
		public List<CampRecord> entries = new List<CampRecord>();

		public bool Learn(CampRecord c)
		{
			if (entries.Any((CampRecord e) => e.pack == c.pack && e.entry == c.entry))
			{
				return false;
			}
			entries.Add(c);
			return true;
		}

		public List<CampRecord> Ordered()
		{
			return entries.OrderBy((CampRecord c) => c.day).ThenBy<CampRecord, string>((CampRecord c) => c.pack, StringComparer.Ordinal).ThenBy<CampRecord, string>((CampRecord c) => c.entry, StringComparer.Ordinal)
				.ToList();
		}

		public void Validate(string expectedWorld, string expectedCharacter)
		{
			if (schema != 1 || world != expectedWorld || character != expectedCharacter || entries == null || entries.Count > 1000)
			{
				throw new InvalidDataException("Journal identity or schema invalid.");
			}
			if (entries.Any((CampRecord e) => e == null || !e.IsPlausible()))
			{
				throw new InvalidDataException("Journal holds an invalid entry.");
			}
		}
	}
	public static class Rules
	{
		public static readonly HashSet<string> Tokens = new HashSet<string> { "direction", "distance_phrase", "distance_m", "location_name" };

		public static bool ValidId(string s)
		{
			if (s != null)
			{
				return Regex.IsMatch(s, "\\A[a-zA-Z0-9_-]{1,80}\\z");
			}
			return false;
		}

		public static bool Finite(double n)
		{
			if (!double.IsNaN(n))
			{
				return !double.IsInfinity(n);
			}
			return false;
		}

		public static double Distance(double x, double z, double tx, double tz)
		{
			return Math.Sqrt((tx - x) * (tx - x) + (tz - z) * (tz - z));
		}

		public static string Direction(double dx, double dz)
		{
			if (!Finite(dx) || !Finite(dz) || (dx == 0.0 && dz == 0.0))
			{
				throw new ArgumentException("Invalid bearing.");
			}
			return (new string[8] { "north", "northeast", "east", "southeast", "south", "southwest", "west", "northwest" })[(int)Math.Floor((Math.Atan2(dx, dz) * 180.0 / Math.PI + 360.0 + 22.5) / 45.0) % 8];
		}

		public static string Resolve(string text, double x, double z, double tx, double tz, string locationName)
		{
			double num = Distance(x, z, tx, tz);
			if (!Finite(num) || num < 1.0)
			{
				throw new ArgumentException("Invalid target distance.");
			}
			return text.Replace("{direction}", Direction(tx - x, tz - z)).Replace("{distance_phrase}", (num < 200.0) ? "a short walk" : ((num < 500.0) ? "a longer walk" : "a considerable journey")).Replace("{distance_m}", (Math.Round(num / 10.0) * 10.0).ToString(CultureInfo.InvariantCulture))
				.Replace("{location_name}", locationName);
		}

		public static LoreEntry Next(LorePack pack, WorldState state, string target, string stableKey)
		{
			HashSet<string> used = new HashSet<string>(from c in state.camps
				where c.pack == pack.id
				select c.entry);
			return (from e in pack.entries
				where !used.Contains(e.id) && e.targets.Contains(target)
				orderby Hash(stableKey + ":" + e.id)
				select e).ThenBy<LoreEntry, string>((LoreEntry e) => e.id, StringComparer.Ordinal).FirstOrDefault();
		}

		public static uint Hash(string s)
		{
			uint num = 2166136261u;
			foreach (char c in s)
			{
				num ^= c;
				num *= 16777619;
			}
			return num;
		}
	}
	public static class JsonFiles
	{
		public static string Encode<T>(T value)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			using MemoryStream memoryStream = new MemoryStream();
			((XmlObjectSerializer)new DataContractJsonSerializer(typeof(T))).WriteObject((Stream)memoryStream, (object)value);
			return Encoding.UTF8.GetString(memoryStream.ToArray());
		}

		public static T Decode<T>(string json)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			using MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(json));
			return (T)((XmlObjectSerializer)new DataContractJsonSerializer(typeof(T))).ReadObject((Stream)memoryStream);
		}

		public static T Read<T>(string path)
		{
			return Decode<T>(File.ReadAllText(path));
		}

		public static void Save<T>(string path, T value)
		{
			Directory.CreateDirectory(Path.GetDirectoryName(path));
			string text = path + ".tmp";
			using (FileStream fileStream = new FileStream(text, FileMode.Create, FileAccess.Write, FileShare.None))
			{
				byte[] bytes = Encoding.UTF8.GetBytes(Encode(value));
				fileStream.Write(bytes, 0, bytes.Length);
				fileStream.Flush(flushToDisk: true);
			}
			if (File.Exists(path))
			{
				File.Replace(text, path, path + ".bak");
			}
			else
			{
				File.Move(text, path);
			}
		}
	}
	public static class MiniJson
	{
		private const int MaxDepth = 32;

		public static object Parse(string text)
		{
			if (text == null)
			{
				throw new FormatException("no text");
			}
			int i = 0;
			Skip(text, ref i);
			object result = Value(text, ref i, 0);
			Skip(text, ref i);
			if (i != text.Length)
			{
				throw new FormatException("unexpected text after the value at " + i);
			}
			return result;
		}

		private static void Skip(string s, ref int i)
		{
			while (i < s.Length && (s[i] == ' ' || s[i] == '\t' || s[i] == '\r' || s[i] == '\n' || s[i] == '\ufeff'))
			{
				i++;
			}
		}

		private static object Value(string s, ref int i, int depth)
		{
			if (depth > 32)
			{
				throw new FormatException("nested too deeply");
			}
			if (i >= s.Length)
			{
				throw new FormatException("unexpected end");
			}
			switch (s[i])
			{
			case '{':
				return Obj(s, ref i, depth);
			case '[':
				return Arr(s, ref i, depth);
			case '"':
				return Str(s, ref i);
			default:
				if (Match(s, ref i, "true"))
				{
					return true;
				}
				if (Match(s, ref i, "false"))
				{
					return false;
				}
				if (Match(s, ref i, "null"))
				{
					return null;
				}
				return Num(s, ref i);
			}
		}

		private static bool Match(string s, ref int i, string word)
		{
			if (string.CompareOrdinal(s, i, word, 0, word.Length) != 0)
			{
				return false;
			}
			i += word.Length;
			return true;
		}

		private static Dictionary<string, object> Obj(string s, ref int i, int depth)
		{
			Dictionary<string, object> dictionary = new Dictionary<string, object>();
			i++;
			Skip(s, ref i);
			if (i < s.Length && s[i] == '}')
			{
				i++;
				return dictionary;
			}
			while (true)
			{
				Skip(s, ref i);
				if (i >= s.Length || s[i] != '"')
				{
					throw new FormatException("expected a key at " + i);
				}
				string key = Str(s, ref i);
				Skip(s, ref i);
				if (i >= s.Length || s[i] != ':')
				{
					throw new FormatException("expected ':' at " + i);
				}
				i++;
				Skip(s, ref i);
				dictionary[key] = Value(s, ref i, depth + 1);
				Skip(s, ref i);
				if (i >= s.Length)
				{
					throw new FormatException("unterminated object");
				}
				if (s[i] != ',')
				{
					break;
				}
				i++;
			}
			if (s[i] == '}')
			{
				i++;
				return dictionary;
			}
			throw new FormatException("expected ',' or '}' at " + i);
		}

		private static List<object> Arr(string s, ref int i, int depth)
		{
			List<object> list = new List<object>();
			i++;
			Skip(s, ref i);
			if (i < s.Length && s[i] == ']')
			{
				i++;
				return list;
			}
			while (true)
			{
				Skip(s, ref i);
				list.Add(Value(s, ref i, depth + 1));
				Skip(s, ref i);
				if (i >= s.Length)
				{
					throw new FormatException("unterminated array");
				}
				if (s[i] != ',')
				{
					break;
				}
				i++;
			}
			if (s[i] == ']')
			{
				i++;
				return list;
			}
			throw new FormatException("expected ',' or ']' at " + i);
		}

		private static string Str(string s, ref int i)
		{
			StringBuilder stringBuilder = new StringBuilder();
			i++;
			while (true)
			{
				if (i >= s.Length)
				{
					throw new FormatException("unterminated string");
				}
				char c = s[i++];
				if (c == '"')
				{
					return stringBuilder.ToString();
				}
				if (c < ' ')
				{
					throw new FormatException("control character in string");
				}
				if (c != '\\')
				{
					stringBuilder.Append(c);
					continue;
				}
				if (i >= s.Length)
				{
					break;
				}
				char c2 = s[i++];
				switch (c2)
				{
				case '"':
					stringBuilder.Append('"');
					break;
				case '\\':
					stringBuilder.Append('\\');
					break;
				case '/':
					stringBuilder.Append('/');
					break;
				case 'b':
					stringBuilder.Append('\b');
					break;
				case 'f':
					stringBuilder.Append('\f');
					break;
				case 'n':
					stringBuilder.Append('\n');
					break;
				case 'r':
					stringBuilder.Append('\r');
					break;
				case 't':
					stringBuilder.Append('\t');
					break;
				case 'u':
				{
					if (i + 4 > s.Length)
					{
						throw new FormatException("bad \\u escape");
					}
					if (!int.TryParse(s.Substring(i, 4), NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture, out var result))
					{
						throw new FormatException("bad \\u escape");
					}
					stringBuilder.Append((char)result);
					i += 4;
					break;
				}
				default:
					throw new FormatException("bad escape \\" + c2);
				}
			}
			throw new FormatException("unterminated escape");
		}

		private static double Num(string s, ref int i)
		{
			int num = i;
			while (i < s.Length && "+-0123456789.eE".IndexOf(s[i]) >= 0)
			{
				i++;
			}
			if (i == num || !double.TryParse(s.Substring(num, i - num), NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || double.IsNaN(result) || double.IsInfinity(result))
			{
				throw new FormatException("bad value at " + num);
			}
			return result;
		}
	}
	[DataContract]
	public sealed class PoiRule
	{
		[DataMember]
		public string prefix = "";

		[DataMember]
		public string phrase = "";
	}
	[DataContract]
	public sealed class PoiPack
	{
		public const int MaxRules = 500;

		[DataMember]
		public int schema = 1;

		[DataMember]
		public List<PoiRule> rules = new List<PoiRule>();

		public void Validate()
		{
			if (schema != 1 || rules == null || rules.Count > 500)
			{
				throw new InvalidDataException("Unsupported place list.");
			}
			foreach (PoiRule rule in rules)
			{
				if (rule == null || !Rules.ValidId(rule.prefix) || string.IsNullOrWhiteSpace(rule.phrase) || rule.phrase.Length > 60 || rule.phrase.IndexOfAny(new char[3] { '{', '}', '\n' }) >= 0)
				{
					throw new InvalidDataException("A place rule is missing its prefix or phrase, or the phrase is not plain text.");
				}
			}
		}
	}
	public struct Place
	{
		public string Prefab;

		public double X;

		public double Z;

		public Place(string prefab, double x, double z)
		{
			Prefab = prefab;
			X = x;
			Z = z;
		}
	}
	public static class Pois
	{
		public static readonly PoiRule[] Defaults = new PoiRule[31]
		{
			new PoiRule
			{
				prefix = "SunkenCrypt",
				phrase = "a sunken crypt"
			},
			new PoiRule
			{
				prefix = "Crypt",
				phrase = "a burial chamber"
			},
			new PoiRule
			{
				prefix = "MountainCave",
				phrase = "a cave in the mountains"
			},
			new PoiRule
			{
				prefix = "TrollCave",
				phrase = "a troll cave"
			},
			new PoiRule
			{
				prefix = "StoneTowerRuins",
				phrase = "a ruined tower"
			},
			new PoiRule
			{
				prefix = "Ruin",
				phrase = "old ruins"
			},
			new PoiRule
			{
				prefix = "StoneHenge",
				phrase = "a ring of standing stones"
			},
			new PoiRule
			{
				prefix = "WoodVillage",
				phrase = "an old village"
			},
			new PoiRule
			{
				prefix = "WoodHouse",
				phrase = "a tumbledown house"
			},
			new PoiRule
			{
				prefix = "SwampHut",
				phrase = "a hut in the swamp"
			},
			new PoiRule
			{
				prefix = "AbandonedLogCabin",
				phrase = "an abandoned log cabin"
			},
			new PoiRule
			{
				prefix = "GoblinCamp",
				phrase = "a camp of the Fuling"
			},
			new PoiRule
			{
				prefix = "DrakeNest",
				phrase = "a drake's nest"
			},
			new PoiRule
			{
				prefix = "FireHole",
				phrase = "a burning pit"
			},
			new PoiRule
			{
				prefix = "TarPit",
				phrase = "a tar pit"
			},
			new PoiRule
			{
				prefix = "Greydwarf_camp",
				phrase = "a Greydwarf camp"
			},
			new PoiRule
			{
				prefix = "StoneHouse",
				phrase = "an old stone house"
			},
			new PoiRule
			{
				prefix = "CharredRuins",
				phrase = "burnt ruins"
			},
			new PoiRule
			{
				prefix = "Dolmen",
				phrase = "a dolmen"
			},
			new PoiRule
			{
				prefix = "ShipWreck",
				phrase = "a shipwreck"
			},
			new PoiRule
			{
				prefix = "Grave",
				phrase = "a lonely grave"
			},
			new PoiRule
			{
				prefix = "StoneCircle",
				phrase = "a circle of old stones"
			},
			new PoiRule
			{
				prefix = "WoodFarm",
				phrase = "an abandoned farm"
			},
			new PoiRule
			{
				prefix = "BearCave",
				phrase = "a bear's den"
			},
			new PoiRule
			{
				prefix = "MountainGrave",
				phrase = "a high grave"
			},
			new PoiRule
			{
				prefix = "StoneTower",
				phrase = "an old stone tower"
			},
			new PoiRule
			{
				prefix = "CharredTowerRuins",
				phrase = "a burnt tower"
			},
			new PoiRule
			{
				prefix = "LumberCamp",
				phrase = "a lumber camp"
			},
			new PoiRule
			{
				prefix = "FrozenShip",
				phrase = "a ship frozen in the ice"
			},
			new PoiRule
			{
				prefix = "Excavation",
				phrase = "a dig site"
			},
			new PoiRule
			{
				prefix = "Mistlands_Excavation",
				phrase = "a dig site"
			}
		};

		public static string Classify(string prefab, IList<PoiRule> rules)
		{
			if (string.IsNullOrEmpty(prefab) || prefab.IndexOf("_sunk", StringComparison.Ordinal) >= 0)
			{
				return null;
			}
			foreach (PoiRule rule in rules)
			{
				if (prefab.StartsWith(rule.prefix, StringComparison.Ordinal))
				{
					return rule.phrase;
				}
			}
			return null;
		}

		public static string Describe(double x, double z, double targetX, double targetZ, IEnumerable<Place> places, IList<PoiRule> rules, double radius, int maxKinds)
		{
			Dictionary<string, KeyValuePair<double, Place>> dictionary = new Dictionary<string, KeyValuePair<double, Place>>(StringComparer.Ordinal);
			foreach (Place place in places)
			{
				string text = Classify(place.Prefab, rules);
				if (text != null)
				{
					double num = Rules.Distance(x, z, place.X, place.Z);
					if (!(num < 25.0) && !(num > radius) && !(Rules.Distance(targetX, targetZ, place.X, place.Z) < 30.0) && (!dictionary.TryGetValue(text, out var value) || num < value.Key))
					{
						dictionary[text] = new KeyValuePair<double, Place>(num, place);
					}
				}
			}
			List<KeyValuePair<string, KeyValuePair<double, Place>>> list = dictionary.OrderBy((KeyValuePair<string, KeyValuePair<double, Place>> kv) => kv.Value.Key).ThenBy<KeyValuePair<string, KeyValuePair<double, Place>>, string>((KeyValuePair<string, KeyValuePair<double, Place>> kv) => kv.Key, StringComparer.Ordinal).Take(Math.Max(0, maxKinds))
				.ToList();
			if (list.Count == 0)
			{
				return "";
			}
			IEnumerable<string> source = list.Select((KeyValuePair<string, KeyValuePair<double, Place>> kv) => kv.Key + " to the " + Rules.Direction(kv.Value.Value.X - x, kv.Value.Value.Z - z) + ", " + Phrase(kv.Value.Key));
			return "Also seen near this camp: " + string.Join("; ", source.ToArray()) + ".";
		}

		private static string Phrase(double distance)
		{
			if (!(distance < 200.0))
			{
				if (!(distance < 500.0))
				{
					return "a considerable journey";
				}
				return "a longer walk";
			}
			return "a short walk";
		}
	}
	public sealed class ShuffleBag
	{
		private readonly Random _rng;

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

		private string _last;

		public string Last => _last;

		public int Remaining => _bag.Count;

		public ShuffleBag(Random rng)
		{
			_rng = rng ?? new Random();
		}

		public string Next(IList<string> pool)
		{
			if (pool == null || pool.Count == 0)
			{
				_bag.Clear();
				return null;
			}
			HashSet<string> valid = new HashSet<string>(pool);
			_bag.RemoveAll((string id) => !valid.Contains(id));
			if (_bag.Count == 0)
			{
				Refill(pool);
			}
			string text = Take();
			if (text == _last && valid.Count > 1)
			{
				if (_bag.Count == 0)
				{
					Refill(pool);
				}
				else
				{
					_bag.Insert(0, text);
				}
				int index = _bag.FindLastIndex((string id) => id != _last);
				text = _bag[index];
				_bag.RemoveAt(index);
			}
			_last = text;
			return text;
		}

		private string Take()
		{
			string result = _bag[_bag.Count - 1];
			_bag.RemoveAt(_bag.Count - 1);
			return result;
		}

		private void Refill(IList<string> pool)
		{
			_bag.Clear();
			HashSet<string> hashSet = new HashSet<string>();
			foreach (string item in pool)
			{
				if (hashSet.Add(item))
				{
					_bag.Add(item);
				}
			}
			for (int num = _bag.Count - 1; num > 0; num--)
			{
				int index = _rng.Next(num + 1);
				string value = _bag[num];
				_bag[num] = _bag[index];
				_bag[index] = value;
			}
		}

		public void Reset()
		{
			_bag.Clear();
			_last = null;
		}

		public string Serialize()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("last=").Append(_last ?? "").Append('\n');
			foreach (string item in _bag)
			{
				stringBuilder.Append("bag=").Append(item).Append('\n');
			}
			return stringBuilder.ToString();
		}

		public void Restore(string text)
		{
			_bag.Clear();
			_last = null;
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			string[] array = text.Split(new char[1] { '\n' });
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.StartsWith("last=", StringComparison.Ordinal))
				{
					string text3 = text2.Substring(5);
					_last = ((text3.Length == 0 || text3.Length > 64) ? null : text3);
				}
				else if (text2.StartsWith("bag=", StringComparison.Ordinal) && _bag.Count < 500)
				{
					string text4 = text2.Substring(4);
					if (text4.Length > 0 && text4.Length <= 64)
					{
						_bag.Add(text4);
					}
				}
			}
		}
	}
	public sealed class SleepEntry
	{
		public string Id;

		public string Image;

		public string Title;

		public string Quote;

		public string Attribution;

		public string Sha256;

		public int Width;

		public int Height;
	}
	public sealed class SleepCatalog
	{
		public const int MaxEntries = 500;

		public const int MaxQuoteLength = 240;

		public const int MaxTitleLength = 80;

		public const int MaxFileBytes = 1048576;

		private static readonly Regex IdRx = new Regex("^[A-Za-z0-9_\\-]{1,64}$", RegexOptions.Compiled);

		private static readonly Regex ShaRx = new Regex("^[0-9a-fA-F]{64}$", RegexOptions.Compiled);

		public readonly List<SleepEntry> Entries = new List<SleepEntry>();

		public readonly List<string> Problems = new List<string>();

		public static bool IsSafeRelativeImagePath(string p)
		{
			if (string.IsNullOrEmpty(p) || p.Length > 200)
			{
				return false;
			}
			if (p.IndexOf('\0') >= 0 || p.IndexOf(':') >= 0 || p[0] == '/' || p[0] == '\\')
			{
				return false;
			}
			string[] array = p.Split('/', '\\');
			foreach (string text in array)
			{
				if (text.Length == 0 || text == "." || text == "..")
				{
					return false;
				}
			}
			string text2 = p.ToLowerInvariant();
			if (!text2.EndsWith(".png") && !text2.EndsWith(".jpg"))
			{
				return text2.EndsWith(".jpeg");
			}
			return true;
		}

		public static SleepCatalog Parse(string json)
		{
			SleepCatalog sleepCatalog = new SleepCatalog();
			if (json == null)
			{
				sleepCatalog.Problems.Add("catalog is missing");
				return sleepCatalog;
			}
			if (json.Length > 1048576)
			{
				sleepCatalog.Problems.Add("catalog is larger than " + 1048576 + " bytes; ignored");
				return sleepCatalog;
			}
			object obj;
			try
			{
				obj = MiniJson.Parse(json);
			}
			catch (FormatException ex)
			{
				sleepCatalog.Problems.Add("catalog is not valid JSON: " + ex.Message);
				return sleepCatalog;
			}
			List<object> list = ((obj is Dictionary<string, object> dictionary && dictionary.ContainsKey("entries")) ? (dictionary["entries"] as List<object>) : null);
			if (list == null)
			{
				sleepCatalog.Problems.Add("catalog has no \"entries\" list");
				return sleepCatalog;
			}
			if (list.Count > 500)
			{
				sleepCatalog.Problems.Add("catalog has more than " + 500 + " entries; only the first " + 500 + " are read");
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			for (int i = 0; i < list.Count && i < 500; i++)
			{
				Dictionary<string, object> dictionary2 = list[i] as Dictionary<string, object>;
				string text = "entry " + (i + 1);
				if (dictionary2 == null)
				{
					sleepCatalog.Problems.Add(text + " is not an object; skipped");
					continue;
				}
				string text2 = Text(dictionary2, "id");
				if (text2 == null || !IdRx.IsMatch(text2))
				{
					sleepCatalog.Problems.Add(text + " has a missing or invalid id; skipped");
					continue;
				}
				text = "entry '" + text2 + "'";
				if (!hashSet.Add(text2))
				{
					sleepCatalog.Problems.Add(text + " repeats an earlier id; skipped");
					continue;
				}
				string text3 = Text(dictionary2, "image");
				if (!IsSafeRelativeImagePath(text3))
				{
					sleepCatalog.Problems.Add(text + " has an unsafe or missing image path; skipped");
					continue;
				}
				string text4 = Text(dictionary2, "quote") ?? "";
				if (text4.Length > 240)
				{
					sleepCatalog.Problems.Add(text + " quote is longer than " + 240 + " characters; skipped");
					continue;
				}
				string text5 = text4;
				foreach (char c in text5)
				{
					if (c < ' ' && c != '\n')
					{
						sleepCatalog.Problems.Add(text + " quote has control characters; skipped");
						text4 = null;
						break;
					}
				}
				if (text4 == null)
				{
					continue;
				}
				string text6 = Text(dictionary2, "title") ?? "";
				if (text6.Length > 80)
				{
					text6 = text6.Substring(0, 80);
				}
				if (!Dim(dictionary2, "width", out var value) || !Dim(dictionary2, "height", out var value2))
				{
					sleepCatalog.Problems.Add(text + " has an invalid width or height; skipped");
					continue;
				}
				string text7 = Text(dictionary2, "sha256");
				if (text7 != null && !ShaRx.IsMatch(text7))
				{
					sleepCatalog.Problems.Add(text + " has a malformed sha256; skipped");
					continue;
				}
				string text8 = Text(dictionary2, "attribution");
				if (text8 != null && text8.Length > 80)
				{
					text8 = text8.Substring(0, 80);
				}
				sleepCatalog.Entries.Add(new SleepEntry
				{
					Id = text2,
					Image = text3.Replace('\\', '/'),
					Title = text6,
					Quote = text4,
					Attribution = (string.IsNullOrWhiteSpace(text8) ? null : text8),
					Width = value,
					Height = value2,
					Sha256 = text7?.ToLowerInvariant()
				});
			}
			return sleepCatalog;
		}

		private static string Text(Dictionary<string, object> d, string key)
		{
			if (!d.TryGetValue(key, out var value))
			{
				return null;
			}
			return value as string;
		}

		private static bool Dim(Dictionary<string, object> d, string key, out int value)
		{
			value = 0;
			if (!d.TryGetValue(key, out var value2) || !(value2 is double num))
			{
				return false;
			}
			if (num < 1.0 || num > 16384.0 || num != Math.Floor(num))
			{
				return false;
			}
			value = (int)num;
			return true;
		}
	}
}
namespace LostCamps.Camps
{
	internal static class CampCommands
	{
		internal const string Usage = "lostcamps status | journal | reload | cleanup | cleanup confirm TOKEN";

		internal static void Run(ConsoleEventArgs args)
		{
			string text = ((args.Length > 1) ? args[1].ToLowerInvariant() : "status");
			try
			{
				if (text == "journal")
				{
					JournalWindow.Toggle();
					args.Context.AddString(JournalWindow.Ready ? "Journal toggled." : "No journal yet: load into a world first.");
					return;
				}
				if (text == "status")
				{
					args.Context.AddString(CampManager.Status + "; camps on record " + CampManager.AssignedCamps + "; notes in your journal " + JournalWindow.Count);
					return;
				}
				if (!CampWorld.IsServer)
				{
					throw new InvalidOperationException("Host or local game only. On a dedicated server use the Maintenance settings in its config file.");
				}
				if (text == "reload")
				{
					CampSettings.File.Reload();
					CampManager.ReloadLore();
					args.Context.AddString("Reloaded the config and the story pack.");
					return;
				}
				if (text == "cleanup" && args.Length == 2)
				{
					CampManager.PrepareCleanup();
					args.Context.AddString(CampManager.Status);
					return;
				}
				if (text == "cleanup" && args.Length == 4 && args[2].Equals("confirm", StringComparison.OrdinalIgnoreCase))
				{
					CampManager.RunCleanup(args[3]);
					args.Context.AddString(CampManager.Status);
					return;
				}
				throw new ArgumentException("Commands: lostcamps status | journal | reload | cleanup | cleanup confirm TOKEN");
			}
			catch (Exception ex)
			{
				LostCampsPlugin.Log.LogWarning((object)ex.Message);
				args.Context.AddString(ex.Message);
			}
		}
	}
	[HarmonyPatch(typeof(Terminal), "InitTerminal")]
	internal static class RegisterCampCommand
	{
		private static void Postfix()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			new ConsoleCommand("lostcamps", "lostcamps status | journal | reload | cleanup | cleanup confirm TOKEN", new ConsoleEvent(CampCommands.Run), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}
	}
	internal static class CampManager
	{
		private const float CleanupRadius = 18f;

		private static ManualLogSource _log;

		private static string _root;

		private static string _bundledLore;

		private static LorePack _pack;

		private static IList<PoiRule> _poiRules = Pois.Defaults;

		private static List<Place> _places = new List<Place>();

		private static WorldState _state;

		private static string _world = "";

		private static string _statePath = "";

		private static List<Target> _targets = new List<Target>();

		private static bool _targetsBuilt;

		private static bool _placementFault;

		private static int _cursor;

		private static float _nextScan;

		private static DateTime _configStamp;

		private static string _cleanupToken = "";

		private static float _cleanupExpires;

		private static readonly List<GameObject> CleanupObjects = new List<GameObject>();

		private static readonly HashSet<string> Warned = new HashSet<string>();

		internal static string Status = "Starting";

		internal const float NearMeters = 80f;

		private static int _refusalsLogged;

		private static readonly HashSet<int> _failedSlots = new HashSet<int>();

		internal const int Variants = 4;

		internal static int AssignedCamps
		{
			get
			{
				if (_state != null)
				{
					return _state.camps.Count;
				}
				return 0;
			}
		}

		internal static string Root => _root;

		internal static LorePack Pack => _pack;

		internal static IList<Target> CurrentTargets
		{
			get
			{
				EnsureTargets();
				return _targets;
			}
		}

		internal static IList<CampRecord> Camps
		{
			get
			{
				if (_state != null)
				{
					return _state.camps;
				}
				return new List<CampRecord>();
			}
		}

		internal static bool PlacementFaulted => _placementFault;

		internal static string CleanupTokenForTest => _cleanupToken;

		internal static IList<PoiRule> PoiRules => _poiRules;

		internal static IList<Place> AllPlacesForTest
		{
			get
			{
				EnsureTargets();
				return _places;
			}
		}

		internal static void Init(ManualLogSource log, string pluginDir)
		{
			_log = log;
			_root = Path.Combine(Paths.ConfigPath, "LostCamps");
			_bundledLore = Path.Combine(pluginDir, "lore.json");
			Directory.CreateDirectory(_root);
			LoadPoiRules();
			try
			{
				LoadLore();
				Status = "Ready (placement is opt-in)";
			}
			catch (Exception ex)
			{
				Status = "No story pack: " + ex.Message;
				_log.LogError((object)("Lost Camps could not load its story pack, so no camps will be placed: " + ex.Message));
			}
		}

		internal static void LoadLore()
		{
			string text = Path.Combine(_root, "lore.json");
			if (!File.Exists(text))
			{
				if (!File.Exists(_bundledLore))
				{
					throw new FileNotFoundException("lore.json is missing next to LostCamps.dll", _bundledLore);
				}
				File.Copy(_bundledLore, text);
			}
			LorePack lorePack = JsonFiles.Read<LorePack>(text);
			lorePack.Validate();
			_pack = lorePack;
			_targetsBuilt = false;
		}

		private static void WarnOnce(string key, string message)
		{
			if (Warned.Add(key))
			{
				_log.LogWarning((object)message);
			}
		}

		internal static void Update()
		{
			if (!CampSettings.Enabled.Value)
			{
				JournalWindow.Hide();
				return;
			}
			string text = CampWorld.WorldId();
			if (text != _world)
			{
				SwitchWorld(text);
			}
			if (_world.Length == 0)
			{
				JournalWindow.Hide();
				return;
			}
			JournalWindow.Update(_root, _world, _log);
			if (CampWorld.IsServer && !(Time.realtimeSinceStartup < _nextScan))
			{
				_nextScan = Time.realtimeSinceStartup + Mathf.Clamp(CampSettings.ScanIntervalSeconds.Value, 2f, 120f);
				ServerScan();
			}
		}

		private static void SwitchWorld(string id)
		{
			_world = id;
			_state = null;
			_statePath = "";
			_targets = new List<Target>();
			_targetsBuilt = false;
			_cursor = 0;
			_failedSlots.Clear();
			_placementFault = false;
			CleanupObjects.Clear();
			_cleanupToken = "";
			JournalWindow.Reset();
			if (id.Length == 0 || !CampWorld.IsServer)
			{
				return;
			}
			_statePath = Path.Combine(_root, "worlds", "world-" + id + ".json");
			try
			{
				_state = (File.Exists(_statePath) ? JsonFiles.Read<WorldState>(_statePath) : new WorldState
				{
					world = id
				});
				_state.Validate(id);
				_log.LogInfo((object)("World " + id + ": " + _state.camps.Count + " camp assignment(s) on record."));
			}
			catch (Exception ex)
			{
				_state = null;
				Status = "World record unreadable; placement stopped";
				_log.LogError((object)("The camp record for this world is unreadable (" + ex.Message + "). Placement is stopped; the file is left untouched: " + _statePath));
			}
		}

		private static void ServerScan()
		{
			try
			{
				MaybeReloadConfig();
				if (CampSettings.CleanupPreview.Value)
				{
					CampSettings.CleanupPreview.Value = false;
					StampConfig();
					PrepareCleanup();
				}
				if (CampSettings.CleanupConfirm.Value)
				{
					CampSettings.CleanupConfirm.Value = false;
					StampConfig();
					RunCleanup(_cleanupToken);
				}
			}
			catch (Exception ex)
			{
				_log.LogWarning((object)("Cleanup: " + ex.Message));
				Status = "Cleanup: " + ex.Message;
			}
			if (!CampSettings.PlacementEnabled.Value || _state == null || _pack == null || _placementFault)
			{
				return;
			}
			try
			{
				EnsureTargets();
				if (_targets.Count > 0 && _state.camps.Count < CampSettings.MaxCamps.Value)
				{
					PlaceTick();
				}
			}
			catch (Exception ex2)
			{
				_placementFault = true;
				Status = "Placement stopped after an error; see the log";
				_log.LogError((object)("Camp placement stopped for this session: " + ex2));
			}
		}

		private static void EnsureTargets()
		{
			if (!_targetsBuilt && _pack != null && CampWorld.LocationCount != 0)
			{
				string world = _world;
				_targets = (from t in CampWorld.Targets(_pack)
					orderby Rules.Hash(world + ":" + t.Key)
					select t).ToList();
				_targetsBuilt = true;
				_places = CampWorld.AllPlaces();
				_log.LogInfo((object)(_targets.Count + " matching location(s) can host a camp."));
				if (_targets.Count == 0)
				{
					_log.LogWarning((object)"No generated location matches the story pack's targets, so no camp will be placed. Check the target ids in lore.json.");
				}
			}
		}

		private static List<int> NearSlots()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: 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_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			List<int> list = new List<int>();
			List<Vector3> list2 = new List<Vector3>();
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if ((Object)(object)allPlayer != (Object)null)
				{
					list2.Add(((Component)allPlayer).transform.position);
				}
			}
			if (list2.Count == 0)
			{
				return list;
			}
			List<KeyValuePair<float, int>> list3 = new List<KeyValuePair<float, int>>();
			for (int i = 0; i < _targets.Count; i++)
			{
				Target t = _targets[i];
				bool flag = false;
				foreach (Vector3 item in list2)
				{
					if (Rules.Distance(item.x, item.z, t.Position.x, t.Position.z) < 320.0)
					{
						flag = true;
						break;
					}
				}
				if (!flag || _state.camps.Any((CampRecord c) => c.target == t.Key))
				{
					continue;
				}
				for (int num = 0; num < 4; num++)
				{
					uint seed;
					Vector3 val = Propose(t, num, out seed);
					float num2 = float.MaxValue;
					foreach (Vector3 item2 in list2)
					{
						num2 = Math.Min(num2, (float)Rules.Distance(item2.x, item2.z, val.x, val.z));
					}
					if (num2 <= 80f && !_failedSlots.Contains(i + num * _targets.Count))
					{
						list3.Add(new KeyValuePair<float, int>(num2, i + num * _targets.Count));
					}
				}
			}
			list3.Sort((KeyValuePair<float, int> a, KeyValuePair<float, int> b) => a.Key.CompareTo(b.Key));
			for (int num3 = 0; num3 < list3.Count; num3++)
			{
				if (list.Count >= 8)
				{
					break;
				}
				list.Add(list3[num3].Value);
			}
			return list;
		}

		private static void PlaceTick()
		{
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01de: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0240: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_0288: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f5: Unknown result type (might be due to invalid IL or missing references)
			CampWorld.PreflightPrefabs();
			foreach (int item in NearSlots())
			{
				Target t = _targets[item % _targets.Count];
				int variant = item / _targets.Count % 4;
				if (_state.camps.Any((CampRecord c) => c.target == t.Key))
				{
					continue;
				}
				LoreEntry loreEntry = Rules.Next(_pack, _state, t.Prefab, _world + ":" + t.Key);
				if (loreEntry == null)
				{
					continue;
				}
				uint seed;
				Vector3 proposed = Propose(t, variant, out seed);
				if (_state.camps.Any((CampRecord c) => Rules.Distance(c.x, c.z, proposed.x, proposed.z) < (double)CampSettings.CampSpacingMeters.Value))
				{
					continue;
				}
				Vector3 result;
				string text = CampWorld.GroundReason(proposed, out result);
				if (text == null && CampWorld.PlayerConstruction(result, 18f))
				{
					text = "player building nearby";
				}
				if (text != null)
				{
					if (_refusalsLogged++ < 40)
					{
						_log.LogInfo((object)("Camp spot near " + t.Prefab + " skipped: " + text + "."));
					}
					if (text != "terrain not loaded")
					{
						_failedSlots.Add(item);
					}
					continue;
				}
				CampRecord campRecord = new CampRecord
				{
					id = "camp_" + Guid.NewGuid().ToString("N"),
					pack = _pack.id,
					entry = loreEntry.id,
					day = loreEntry.day,
					author = loreEntry.author,
					x = result.x,
					z = result.z,
					targetX = t.Position.x,
					targetZ = t.Position.z,
					target = t.Key,
					text = Rules.Resolve(loreEntry.text, result.x, result.z, t.Position.x, t.Position.z, t.Label),
					nearby = DescribeNearby(result, t)
				};
				_state.camps.Add(campRecord);
				SaveWorld();
				List<GameObject> list = new List<GameObject>();
				try
				{
					Quaternion yaw = Quaternion.Euler(0f, (float)(seed % 360), 0f);
					Part[] array = Layouts.For((int)(seed % 3));
					foreach (Part part in array)
					{
						list.Add(CampWorld.SpawnPart(part, result, yaw, campRecord));
					}
					campRecord.status = "placed";
					SaveWorld();
					Status = "Placed " + _state.camps.Count + " camp(s)";
					_log.LogInfo((object)("Placed " + campRecord.id + " (" + campRecord.entry + ") near " + t.Prefab));
					break;
				}
				catch (Exception)
				{
					campRecord.status = "failed";
					SaveWorld();
					foreach (GameObject item2 in list)
					{
						try
						{
							CampWorld.Destroy(item2);
						}
						catch (Exception ex2)
						{
							_log.LogError((object)("A partial camp piece remains: " + ex2.Message));
						}
					}
					throw;
				}
			}
		}

		internal static Vector3 Propose(Target t, int variant, out uint seed)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			seed = Rules.Hash(_world + ":" + t.Key);
			float num = (float)((uint)((int)seed + variant * 91) % 360u) * ((float)Math.PI / 180f);
			float num2 = 100f + (float)((uint)((int)(seed / 360) + variant * 37) % 100u);
			return t.Position + new Vector3(Mathf.Sin(num) * num2, 0f, Mathf.Cos(num) * num2);
		}

		internal static int ScanFromHarness()
		{
			_nextScan = 0f;
			int count = Camps.Count;
			ServerScan();
			return Camps.Count - count;
		}

		internal static void ClearRecordsForTest()
		{
			if (_state != null)
			{
				_state.camps.Clear();
				_cursor = 0;
				_failedSlots.Clear();
				_placementFault = false;
				SaveWorld();
			}
		}

		private static void SaveWorld()
		{
			_state.Validate(_world);
			JsonFiles.Save(_statePath, _state);
		}

		private static void MaybeReloadConfig()
		{
			try
			{
				DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(CampSettings.File.ConfigFilePath);
				if (!(lastWriteTimeUtc == _configStamp))
				{
					_configStamp = lastWriteTimeUtc;
					CampSettings.File.Reload();
				}
			}
			catch (Exception ex)
			{
				WarnOnce("cfg", "Could not reload the config: " + ex.Message);
			}
		}

		private static void StampConfig()
		{
			try
			{
				_configStamp = File.GetLastWriteTimeUtc(CampSettings.File.ConfigFilePath);
			}
			catch (Exception)
			{
			}
		}

		internal static void PrepareCleanup()
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			if (CampSettings.PlacementEnabled.Value)
			{
				throw new InvalidOperationException("Turn PlacementEnabled off before cleanup.");
			}
			CleanupObjects.Clear();
			int num = 0;
			foreach (IGrouping<string, ZNetView> item in from v in Object.FindObjectsByType<ZNetView>((FindObjectsSortMode)0)
				where CampWorld.OwnerOf(v).StartsWith("camp_", StringComparison.Ordinal)
				group v by CampWorld.OwnerOf(v))
			{
				if (CampWorld.PlayerConstruction(((Component)item.First()).transform.position, 18f))
				{
					num++;
					continue;
				}
				CleanupObjects.AddRange(item.Select((ZNetView v) => ((Component)v).gameObject));
			}
			_cleanupToken = Guid.NewGuid().ToString("N").Substring(0, 8);
			_cleanupExpires = Time.realtimeSinceStartup + 60f;
			Status = "Cleanup preview: " + CleanupObjects.Count + " loaded object(s); " + num + " camp(s) near player buildings skipped. Token " + _cleanupToken;
			_log.LogWarning((object)(Status + ". Camps in unloaded areas are not included. No terrain or player buildings are touched."));
		}

		internal static void RunCleanup(string token)
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			if (CampSettings.PlacementEnabled.Value || _cleanupToken.Length == 0 || token != _cleanupToken || Time.realtimeSinceStartup > _cleanupExpires)
			{
				_log.LogWarning((object)"Cleanup needs placement off and a fresh preview token.");
				Status = "Cleanup refused: needs placement off and a fresh preview";
				return;
			}
			int num = 0;
			foreach (GameObject cleanupObject in CleanupObjects)
			{
				if (!((Object)(object)cleanupObject == (Object)null))
				{
					ZNetView component = cleanupObject.GetComponent<ZNetView>();
					if (!((Object)(object)component == (Object)null) && CampWorld.OwnerOf(component).StartsWith("camp_", StringComparison.Ordinal) && !CampWorld.PlayerConstruction(cleanupObject.transform.position, 18f))
					{
						CampWorld.Destroy(cleanupObject);
						num++;
					}
				}
			}
			CleanupObjects.Clear();
			_cleanupToken = "";
			Status = "Removed " + num + " loaded camp object(s). Visit other areas and repeat before uninstalling.";
			_log.LogWarning((object)Status);
		}

		internal static void ReloadLore()
		{
			LoadLore();
			LoadPoiRules();
			Status = "Story pack reloaded";
		}

		private static void LoadPoiRules()
		{
			_poiRules = Pois.Defaults;
			string path = Path.Combine(_root, "pois.json");
			if (!File.Exists(path))
			{
				return;
			}
			try
			{
				PoiPack poiPack = JsonFiles.Read<PoiPack>(path);
				poiPack.Validate();
				_poiRules = poiPack.rules;
				_log.LogInfo((object)("Using " + poiPack.rules.Count + " place description(s) from pois.json."));
			}
			catch (Exception ex)
			{
				WarnOnce("pois", "pois.json was ignored (" + ex.Message + "); using the built-in list.");
			}
		}

		internal static string DescribeNearby(Vector3 position, Target target)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			if (!CampSettings.NearbyHints.Value)
			{
				return "";
			}
			return Pois.Describe(position.x, position.z, target.Position.x, target.Position.z, _places, _poiRules, CampSettings.NearbyRadiusMeters.Value, CampSettings.NearbyMaxKinds.Value);
		}
	}
	internal static class CampSettings
	{
		internal static ConfigFile File;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> PlacementEnabled;

		internal static ConfigEntry<bool> CleanupPreview;

		internal static ConfigEntry<bool> CleanupConfirm;

		internal static ConfigEntry<bool> NearbyHints;

		internal static ConfigEntry<int> MaxCamps;

		internal static ConfigEntry<int> NearbyMaxKinds;

		internal static ConfigEntry<float> ScanIntervalSeconds;

		internal static ConfigEntry<float> CampSpacingMeters;

		internal static ConfigEntry<float> NearbyRadiusMeters;

		internal static ConfigEntry<KeyboardShortcut> JournalKey;

		internal static void Bind(ConfigFile c)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Expected O, but got Unknown
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Expected O, but got Unknown
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Expected O, but got Unknown
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Expected O, but got Unknown
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Expected O, but got Unknown
			File = c;
			Enabled = c.Bind<bool>("Camps", "Enabled", true, "Turn the camp notes and the journal on or off for you. Does not remove camps that already exist.");
			JournalKey = c.Bind<KeyboardShortcut>("Camps", "JournalKey", new KeyboardShortcut((KeyCode)106, Array.Empty<KeyCode>()), "Opens the journal of camp notes you have read. (F7 to F10 are taken by Safety Net and DAL Compass.)");
			PlacementEnabled = c.Bind<bool>("Server", "PlacementEnabled", false, "Server or host only. Lets this world gain new abandoned camps near real dungeon entrances. Off by default: try it in a disposable world first. No terrain is changed.");
			MaxCamps = c.Bind<int>("Server", "MaxCamps", 12, new ConfigDescription("Most camps this world will ever be given. Camps that failed or were removed still count, so a page is never handed out twice.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000), Array.Empty<object>()));
			ScanIntervalSeconds = c.Bind<float>("Server", "ScanIntervalSeconds", 10f, new ConfigDescription("How often the server tries to place one camp. Each try checks at most eight spots.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 120f), Array.Empty<object>()));
			CampSpacingMeters = c.Bind<float>("Server", "CampSpacingMeters", 500f, new ConfigDescription("Minimum distance between two camps.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(100f, 3000f), Array.Empty<object>()));
			NearbyHints = c.Bind<bool>("Server", "NearbyPlaceHints", true, "Each note also says, in general terms, what other places lie near its camp (for example a troll cave to the west). Worked out when the camp is placed, then saved with it.");
			NearbyRadiusMeters = c.Bind<float>("Server", "NearbyRadiusMeters", 700f, new ConfigDescription("How far from a camp a place can be and still be mentioned.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(200f, 2000f), Array.Empty<object>()));
			NearbyMaxKinds = c.Bind<int>("Server", "NearbyMaxKinds", 3, new ConfigDescription("Most different kinds of place a note will mention.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 5), Array.Empty<object>()));
			CleanupPreview = c.Bind<bool>("Maintenance", "CleanupPreview", false, "Set true to list the loaded camp objects that cleanup could remove (placement must be off). Nothing is removed. Resets itself.");
			CleanupConfirm = c.Bind<bool>("Maintenance", "CleanupConfirm", false, "Within 60 seconds of a preview, set true to remove exactly those loaded objects. Camps near player buildings are skipped. Resets itself.");
		}
	}
	internal static class CampSigns
	{
		private const int MaxPayload = 20000;

		internal static bool TryRead(Sign sign, bool hold, ref bool result)
		{
			if (!CampSettings.Enabled.Value || !JournalWindow.Ready)
			{
				return true;
			}
			ZNetView component = ((Component)sign).GetComponent<ZNetView>();
			string text = CampWorld.OwnerOf(component);
			if (text.Length == 0)
			{
				return true;
			}
			if (hold)
			{
				result = false;
				return false;
			}
			try
			{
				string text2 = CampWorld.NoteOf(component);
				if (text2.Length == 0 || text2.Length > 20000)
				{
					throw new InvalidOperationException("Invalid note size.");
				}
				CampRecord campRecord = JsonFiles.Decode<CampRecord>(text2);
				if (campRecord == null || campRecord.id != text || !campRecord.IsPlausible())
				{
					throw new InvalidOperationException("Invalid note data.");
				}
				JournalWindow.Learn(campRecord, LostCampsPlugin.Log);
				JournalWindow.Open(campRecord);
			}
			catch (Exception ex)
			{
				LostCampsPlugin.Log.LogWarning((object)("Cannot read this camp note: " + ex.Message));
			}
			result = true;
			return false;
		}
	}
	[HarmonyPatch(typeof(Sign), "Interact")]
	internal static class SignInteractPatch
	{
		private static bool Prefix(Sign __instance, bool hold, ref bool __result)
		{
			return CampSigns.TryRead(__instance, hold, ref __result);
		}
	}
	internal sealed class Target
	{
		internal string Prefab = "";

		internal string Label = "";

		internal Vector3 Position;

		internal string Key => Prefab + "_" + Mathf.RoundToInt(Position.x) + "_" + Mathf.RoundToInt(Position.z);
	}
	internal struct Part
	{
		internal string Prefab;

		internal Vector3 Offset;

		internal float Yaw;

		internal Vector3 Lean;

		internal bool IsSign;

		internal Part(string prefab, float x, float y, float z, float yaw = 0f)
		{
			//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_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			Prefab = prefab;
			Offset = new Vector3(x, y, z);
			Yaw = yaw;
			Lean = Vector3.zero;
			IsSign = prefab == "sign";
		}

		internal Part Leaning(float dx, float dy, float dz)
		{
			//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)
			Part result = this;
			result.Lean = new Vector3(dx, dy, dz);
			return result;
		}
	}
	internal static class Layouts
	{
		internal static string[] PrefabNames
		{
			get
			{
				HashSet<string> hashSet = new HashSet<string>();
				for (int i = 0; i < 3; i++)
				{
					Part[] array = For(i);
					for (int j = 0; j < array.Length; j++)
					{
						Part part = array[j];
						hashSet.Add(part.Prefab);
					}
				}
				string[] array2 = new string[hashSet.Count];
				hashSet.CopyTo(array2);
				return array2;
			}
		}

		internal static Part[] For(int kind)
		{
			float num = ((kind == 1) ? (-1f) : 1f);
			List<Part> list = new List<Part>();
			list.Add(new Part("wood_pole2", -1.7f, -0.5f, -1.2f).Leaning(0f, 1f, 0f));
			list.Add(new Part("wood_pole2", 1.7f, -0.5f, -1.2f).Leaning(0f, 1f, 0f));
			list.Add(new Part("wood_pole_log_4_worn", -2f, 1.7f, -1.2f).Leaning(1f, 0f, 0f));
			for (int i = 0; i < 7; i++)
			{
				list.Add(new Part("wood_pole_log", -1.35f + 0.45f * (float)i, -0.1f, -2.5f).Leaning(0f, 1.55f, 1.3f));
			}
			list.Add(new Part("Morkhalla_Bedroll2", -0.4f * num, 0.05f, -1.7f, 80f));
			list.Add(new Part("fire_pit", 0f, 0f, 1.2f));
			list.Add(new Part("Pinetree_01_Stub", -1.5f * num, 0f, 1.9f));
			list.Add(new Part("FirTree_Stub", 1.5f * num, 0f, 1.7f));
			list.Add(new Part("wood_stack", 2.3f * num, 0f, -0.2f, 15f));
			list.Add(new Part("barrell", -2.2f * num, 0f, 0.3f));
			if (kind != 2)
			{
				list.Add(new Part("barrell", -2.3f * num, 0f, -0.7f));
			}
			list.Add(new Part("bucket", 0.9f * num, 0f, 0.2f));
			if (kind == 2)
			{
				list.Add(new Part("stone_pile", -1.9f, 0f, 2.3f, 30f));
			}
			list.Add(new Part("sign", 0.3f * num, 0f, 2.9f, 180f));
			return list.ToArray();
		}
	}
	internal static class CampWorld
	{
		internal const string OwnerKey = "LostCamps.camp";

		internal const string NoteKey = "LostCamps.note";

		private static readonly string[][] Labels = new string[26][]
		{
			new string[2] { "StoneCircle", "circle of standing stones" },
			new string[2] { "StoneHenge", "ring of standing stones" },
			new string[2] { "WoodVillage", "old village" },
			new string[2] { "WoodFarm", "abandoned farm" },
			new string[2] { "TrollCave", "troll cave" },
			new string[2] { "BearCave", "bear's den" },
			new string[2] { "Ruin", "old ruin" },
			new string[2] { "Greydwarf_camp", "Greydwarf camp" },
			new string[2] { "SwampHut", "swamp hut" },
			new string[2] { "SwampRuin", "swamp ruin" },
			new string[2] { "DrakeNest", "drake's nest" },
			new string[2] { "AbandonedLogCabin", "abandoned log cabin" },
			new string[2] { "MountainGrave", "mountain grave" },
			new string[2] { "StoneTowerRuins", "ruined tower" },
			new string[2] { "StoneTower", "old stone tower" },
			new string[2] { "GoblinCamp", "Fuling camp" },
			new string[2] { "GoblinHut", "Fuling hut" },
			new string[2] { "TarPit", "tar pit" },
			new string[2] { "CharredTowerRuins", "burnt tower" },
			new string[2] { "CharredRuins", "burnt ruin" },
			new string[2] { "FortressRuins", "ruined fortress" },
			new string[2] { "SulfurArch", "sulfur arch" },
			new string[2] { "LumberCamp", "lumber camp" },
			new string[2] { "FrozenShip", "ship frozen in the ice" },
			new string[2] { "DN_hut", "hut in the snow" },
			new string[2] { "ShipWreck", "shipwreck" }
		};

		private static readonly Dictionary<string, Vector3> FootCache = new Dictionary<string, Vector3>();

		internal static bool IsServer
		{
			get
			{
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					return ZNet.instance.IsServer();
				}
				return false;
			}
		}

		internal static int LocationCount
		{
			get
			{
				if (!((Object)(object)ZoneSystem.instance == (Object)null))
				{
					return ZoneSystem.instance.m_locationInstances.Count;
				}
				return 0;
			}
		}

		internal static string WorldId()
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null || ZNet.World == null)
			{
				return "";
			}
			long worldUID = instance.GetWorldUID();
			if (worldUID != 0L)
			{
				return worldUID.ToString(CultureInfo.InvariantCulture);
			}
			return "";
		}

		internal static string CharacterId()
		{
			if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)Game.instance == (Object)null)
			{
				return "";
			}
			PlayerProfile playerProfile = Game.instance.GetPlayerProfile();
			if (playerProfile != null)
			{
				return playerProfile.GetPlayerID().ToString(CultureInfo.InvariantCulture);
			}
			return "";
		}

		internal static bool InputBusy()
		{
			if ((!((Object)(object)Chat.instance != (Object)null) || !Chat.instance.HasFocus()) && !Console.IsVisible() && !TextInput.IsVisible() && !InventoryGui.IsVisible() && !Menu.IsVisible() && !Minimap.IsOpen())
			{
				return StoreGui.IsVisible();
			}
			return true;
		}

		internal static List<Target> Targets(LorePack pack)
		{
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			HashSet<string> hashSet = new HashSet<string>();
			foreach (LoreEntry entry in pack.entries)
			{
				foreach (string target in entry.targets)
				{
					hashSet.Add(target);
				}
			}
			List<Target> list = new List<Target>();
			if ((Object)(object)ZoneSystem.instance == (Object)null)
			{
				return list;
			}
			foreach (LocationInstance value in ZoneSystem.instance.m_locationInstances.Values)
			{
				string text = ((value.m_location != null) ? value.m_location.m_prefabName : "");
				if (!string.IsNullOrEmpty(text) && hashSet.Contains(text))
				{
					list.Add(new Target
					{
						Prefab = text,
						Position = value.m_position,
						Label = LabelFor(text)
					});
				}
			}
			return list;
		}

		internal static List<Place> AllPlaces()
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			List<Place> list = new List<Place>();
			if ((Object)(object)ZoneSystem.instance == (Object)null)
			{
				return list;
			}
			foreach (LocationInstance value in ZoneSystem.instance.m_locationInstances.Values)
			{
				string text = ((value.m_location != null) ? value.m_location.m_prefabName : "");
				if (!string.IsNullOrEmpty(text))
				{
					list.Add(new Place(text, value.m_position.x, value.m_position.z));
				}
			}
			return list;
		}

		private static string LabelFor(string prefab)
		{
			if (prefab.StartsWith("Crypt", StringComparison.Ordinal))
			{
				return "burial chamber";
			}
			if (prefab.StartsWith("SunkenCrypt", StringComparison.Ordinal))
			{
				return "sunken crypt";
			}
			if (prefab.StartsWith("MountainCave", StringComparison.Ordinal))
			{
				return "mountain cave";
			}
			string[][] labels = Labels;
			foreach (string[] array in labels)
			{
				if (prefab.StartsWith(array[0], StringComparison.Ordinal))
				{
					return array[1];
				}
			}
			return "dungeon entrance";
		}

		internal static bool Ground(Vector3 proposed, out Vector3 result)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			return GroundReason(proposed, out result) == null;
		}

		internal static string GroundReason(Vector3 proposed, out Vector3 result)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: 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_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0206: Unknown result type (might be due to invalid IL or missing references)
			//IL_020b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			result = proposed;
			int mask = LayerMask.GetMask(new string[1] { "terrain" });
			if (mask == 0)
			{
				throw new InvalidOperationException("The terrain layer is missing.");
			}
			RaycastHit val = default(RaycastHit);
			if (!Physics.Raycast(proposed + Vector3.up * 1000f, Vector3.down, ref val, 2000f, mask, (QueryTriggerInteraction)1))
			{
				return "terrain not loaded";
			}
			if (((RaycastHit)(ref val)).point.y < ZoneSystem.instance.m_waterLevel + 2f)
			{
				return "water or shore";
			}
			if (Vector3.Angle(((RaycastHit)(ref val)).normal, Vector3.up) > 15f)
			{
				return "steep";
			}
			result = ((RaycastHit)(ref val)).point + Vector3.up * 0.08f;
			Vector3[] array = (Vector3[])(object)new Vector3[4]
			{
				new Vector3(-2.7f, 0f, -2.9f),
				new Vector3(2.7f, 0f, -2.9f),
				new Vector3(-2.7f, 0f, 3.1f),
				new Vector3(2.7f, 0f, 3.1f)
			};
			RaycastHit val3 = default(RaycastHit);
			foreach (Vector3 val2 in array)
			{
				if (!Physics.Raycast(result + val2 + Vector3.up * 10f, Vector3.down, ref val3, 20f, mask, (QueryTriggerInteraction)1) || Mathf.Abs(((RaycastHit)(ref val3)).point.y - ((RaycastHit)(ref val)).point.y) > 0.65f)
				{
					return "uneven";
				}
			}
			int mask2 = LayerMask.GetMask(new string[4] { "piece", "static_solid", "Default", "Default_small" });
			if (Physics.OverlapBox(result + new Vector3(0f, 1.7f, 0.1f), new Vector3(2.7f, 1.5f, 3.1f), Quaternion.identity, mask2, (QueryTriggerInteraction)1).Length != 0)
			{
				return "obstructed";
			}
			return null;
		}

		internal static bool PlayerConstruction(Vector3 position, float radius)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			Collider[] array = Physics.OverlapSphere(position, radius, LayerMask.GetMask(new string[2] { "piece", "piece_nonsolid" }));
			for (int i = 0; i < array.Length; i++)
			{
				Piece componentInParent = ((Component)array[i]).GetComponentInParent<Piece>();
				if ((Object)(object)componentInParent != (Object)null && componentInParent.GetCreator() != 0L)
				{
					return true;
				}
			}
			return false;
		}

		internal static void PreflightPrefabs()
		{
			if ((Object)(object)ZNetScene.instance == (Object)null)
			{
				throw new InvalidOperationException("The world's object registry is not ready.");
			}
			string[] prefabNames = Layouts.PrefabNames;
			foreach (string text in prefabNames)
			{
				GameObject prefab = ZNetScene.instance.GetPrefab(text);
				if ((Object)(object)prefab == (Object)null)
				{
					throw new InvalidOperationException("Missing vanilla prefab: " + text);
				}
				if ((Object)(object)prefab.GetComponent<ZNetView>() == (Object)null)
				{
					throw new InvalidOperationException("Prefab lacks ZNetView: " + text);
				}
			}
		}

		private static Vector3 FootOffset(string name)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: 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_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			if (FootCache.TryGetValue(name, out var value))
			{
				return value;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab(name);
			bool flag = false;
			Bounds val = default(Bounds);
			MeshFilter[] componentsInChildren = prefab.GetComponentsInChildren<MeshFilter>(true);
			foreach (MeshFilter val2 in componentsInChildren)
			{
				if ((Object)(object)val2.sharedMesh == (Object)null)
				{
					continue;
				}
				Bounds bounds = val2.sharedMesh.bounds;
				for (int j = 0; j < 8; j++)
				{
					Vector3 val3 = ((Bounds)(ref bounds)).center + Vector3.Scale(((Bounds)(ref bounds)).extents, new Vector3(((j & 1) == 0) ? (-1f) : 1f, ((j & 2) == 0) ? (-1f) : 1f, ((j & 4) == 0) ? (-1f) : 1f));
					Vector3 val4 = prefab.transform.InverseTransformPoint(((Component)val2).transform.TransformPoint(val3));
					if (!flag)
					{
						((Bounds)(ref val))..ctor(val4, Vector3.zero);
						flag = true;
					}
					else
					{
						((Bounds)(ref val)).Encapsulate(val4);
					}
				}
			}
			Vector3 val5 = (Vector3)(flag ? new Vector3(((Bounds)(ref val)).center.x, ((Bounds)(ref val)).min.y, ((Bounds)(ref val)).center.z) : Vector3.zero);
			FootCache[name] = val5;
			return val5;
		}

		internal static float TerrainY(float x, float z, float fallback)
		{
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: 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)
			RaycastHit val = default(RaycastHit);
			if (!Physics.Raycast(new Vector3(x, fallback + 500f, z), Vector3.down, ref val, 1000f, LayerMask.GetMask(new string[1] { "terrain" }), (QueryTriggerInteraction)1))
			{
				return fallback;
			}
			return ((RaycastHit)(ref val)).point.y;
		}

		internal static GameObject SpawnPart(Part part, Vector3 center, Quaternion yaw, CampRecord camp)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: 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_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = yaw * new Vector3(part.Offset.x, 0f, part.Offset.z);
			float num = center.x + val.x;
			float num2 = center.z + val.z;
			Vector3 val2 = default(Vector3);
			((Vector3)(ref val2))..ctor(num, TerrainY(num, num2, center.y) + part.Offset.y, num2);
			Quaternion val4;
			Vector3 val5;
			if (part.Lean != Vector3.zero)
			{
				Vector3 up = Vector3.up;
				Vector3 val3 = yaw * part.Lean;
				val4 = Quaternion.FromToRotation(up, ((Vector3)(ref val3)).normalized) * Quaternion.Euler(0f, part.Yaw, 0f);
				val5 = val2 - val4 * FootOffset(part.Prefab);
			}
			else
			{
				val4 = yaw * Quaternion.Euler(0f, part.Yaw, 0f);
				val5 = val2 - val4 * FootOffset(part.Prefab);
			}
			GameObject val6 = Object.Instantiate<GameObject>(ZNetScene.instance.GetPrefab(part.Prefab), val5, val4);
			try
			{
				ZNetView component = val6.GetComponent<ZNetView>();
				ZDO val7 = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null);
				if (val7 == null)
				{
					throw new InvalidOperationException("No persistent network state for " + part.Prefab);
				}
				val7.Set("LostCamps.camp", camp.id);
				WearNTear component2 = val6.GetComponent<WearNTear>();
				if ((Object)(object)component2 != (Object)null)
				{
					component2.m_noSupportWear = true;
					component2.m_noRoofWear = true;
					if ((Object)(object)component2.m_worn != (Object)null)
					{
						val7.Set(ZDOVars.s_health, component2.m_health * 0.6f);
					}
				}
				if ((Object)(object)val6.GetComponent<Fireplace>() != (O