Decompiled source of MerchantSignposts v0.1.0

BepInEx/plugins/MerchantSignposts/MerchantSignposts.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("0.0.0.0")]
namespace MerchantSignposts;

internal static class Merchants
{
	internal struct Merchant
	{
		public string LocationPrefab;

		public string Label;
	}

	internal static List<Merchant> Parse(string table)
	{
		List<Merchant> list = new List<Merchant>();
		foreach (KeyValuePair<string, string> item in ParsePairs(table, "merchant"))
		{
			list.Add(new Merchant
			{
				LocationPrefab = item.Key,
				Label = item.Value
			});
		}
		return list;
	}

	internal static List<KeyValuePair<string, string>> ParsePairs(string table, string what)
	{
		List<KeyValuePair<string, string>> list = new List<KeyValuePair<string, string>>();
		string[] array = (table ?? "").Split(new char[1] { ',' });
		for (int i = 0; i < array.Length; i++)
		{
			string text = array[i].Trim();
			if (text.Length != 0)
			{
				int num = text.IndexOf('=');
				if (num <= 0 || num == text.Length - 1)
				{
					MerchantSignpostsPlugin.Log.LogWarning((object)("[MerchantSignposts] ignoring malformed " + what + " entry '" + text + "'"));
				}
				else
				{
					list.Add(new KeyValuePair<string, string>(text.Substring(0, num).Trim(), text.Substring(num + 1).Trim()));
				}
			}
		}
		return list;
	}

	private static string Lookup(string table, string what, string locationPrefab)
	{
		foreach (KeyValuePair<string, string> item in ParsePairs(table, what))
		{
			if (string.Equals(item.Key, locationPrefab, StringComparison.Ordinal))
			{
				return item.Value;
			}
		}
		return null;
	}

	internal static string PoleFor(string locationPrefab)
	{
		return Lookup(MerchantSignpostsPlugin.PolePrefabs.Value, "pole", locationPrefab) ?? MerchantSignpostsPlugin.PolePrefab.Value;
	}

	internal static HashSet<string> PolePrefabs()
	{
		HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal) { MerchantSignpostsPlugin.PolePrefab.Value };
		foreach (KeyValuePair<string, string> item in ParsePairs(MerchantSignpostsPlugin.PolePrefabs.Value, "pole"))
		{
			hashSet.Add(item.Value);
		}
		return hashSet;
	}

	internal static string SignText(string locationPrefab, string label)
	{
		string text = MerchantSignpostsPlugin.TextTemplate.Value.Replace("{merchant}", label);
		string text2 = Lookup(MerchantSignpostsPlugin.TextColors.Value, "colour", locationPrefab);
		if (!string.IsNullOrEmpty(text2))
		{
			return "<color=" + text2 + ">" + text + "</color>";
		}
		return text;
	}

	internal static bool TryFind(string nameOrLabel, out Merchant merchant)
	{
		foreach (Merchant item in Parse(MerchantSignpostsPlugin.MerchantTable.Value))
		{
			if (string.Equals(item.LocationPrefab, nameOrLabel, StringComparison.OrdinalIgnoreCase) || string.Equals(item.Label, nameOrLabel, StringComparison.OrdinalIgnoreCase))
			{
				merchant = item;
				return true;
			}
		}
		merchant = default(Merchant);
		return false;
	}

	internal static bool TryNearestInstance(string locationPrefab, Vector3 from, out Vector3 position, out bool placed, out int remaining)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: 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_004b: 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_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: 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_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		position = Vector3.zero;
		placed = false;
		remaining = 0;
		ZoneSystem instance = ZoneSystem.instance;
		if ((Object)(object)instance == (Object)null)
		{
			return false;
		}
		float num = float.MaxValue;
		bool result = false;
		foreach (KeyValuePair<Vector2s, LocationInstance> locationInstance in instance.m_locationInstances)
		{
			LocationInstance value = locationInstance.Value;
			if (value.m_location != null && !(value.m_location.m_prefabName != locationPrefab))
			{
				remaining++;
				float num2 = Vector3.Distance(from, value.m_position);
				if (num2 < num)
				{
					num = num2;
					position = value.m_position;
					placed = value.m_placed;
					result = true;
				}
			}
		}
		return result;
	}
}
[BepInPlugin("com.valheim.merchantsignposts", "MerchantSignposts", "0.1.0")]
public class MerchantSignpostsPlugin : BaseUnityPlugin
{
	public const string PluginGuid = "com.valheim.merchantsignposts";

	public const string PluginName = "MerchantSignposts";

	public const string PluginVersion = "0.1.0";

	internal static ManualLogSource Log;

	internal static ConfigEntry<string> MerchantTable;

	internal static ConfigEntry<string> PolePrefab;

	internal static ConfigEntry<string> PolePrefabs;

	internal static ConfigEntry<string> SignPrefab;

	internal static ConfigEntry<string> TextColors;

	internal static ConfigEntry<float> PoleEmbedDepth;

	internal static ConfigEntry<float> SignInnerEndOffset;

	internal static ConfigEntry<float> SignDropBelowPoleTop;

	internal static ConfigEntry<string> TextTemplate;

	internal static ConfigEntry<bool> FlipFace;

	internal static ConfigEntry<bool> LogGeometry;

	internal static ConfigEntry<string> TriggerPrefix;

	internal static ConfigEntry<bool> RequireAdmin;

	internal static ConfigEntry<float> SweepInterval;

	internal static ConfigEntry<bool> WorldPlacement;

	internal static ConfigEntry<int> Spokes;

	internal static ConfigEntry<float> OuterRadius;

	internal static ConfigEntry<float> InnerRadius;

	internal static ConfigEntry<float> Step;

	internal static ConfigEntry<float> SidewaysJitter;

	internal static ConfigEntry<float> LocationMargin;

	internal static ConfigEntry<float> WaterMargin;

	internal static ConfigEntry<string> WaterMargins;

	internal static ConfigEntry<string> Biomes;

	internal static ConfigEntry<string> Trails;

	private Harmony _harmony;

	private void Awake()
	{
		//IL_023a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0244: Expected O, but got Unknown
		//IL_0393: Unknown result type (might be due to invalid IL or missing references)
		//IL_039d: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		MerchantTable = ((BaseUnityPlugin)this).Config.Bind<string>("Merchants", "Table", "Vendor_BlackForest=Haldor,Hildir_camp=Hildir,BogWitch_Camp=Bog Witch", "Comma-separated 'locationPrefab=label' pairs. The prefab is the vanilla location name in ZoneSystem; the label is written on signs.");
		PolePrefab = ((BaseUnityPlugin)this).Config.Bind<string>("Signpost", "PolePrefab", "wood_pole2", "Vanilla piece used as the vertical post for merchants without an entry in PolePrefabs.");
		PolePrefabs = ((BaseUnityPlugin)this).Config.Bind<string>("Signpost", "PolePrefabs", "Vendor_BlackForest=piece_dvergr_pole,BogWitch_Camp=darkwood_pole", "Per-merchant post override as 'locationPrefab=piece' pairs, so each merchant's signposts are built from a material that suits them. Any vanilla pole with a BoxCollider works; the board is hung from its measured top. Signposts standing on a different post are rebuilt at the next sweep.");
		SignPrefab = ((BaseUnityPlugin)this).Config.Bind<string>("Signpost", "SignPrefab", "sign", "Vanilla piece used as the board. Must carry a Sign component so text renders.");
		PoleEmbedDepth = ((BaseUnityPlugin)this).Config.Bind<float>("Signpost", "PoleEmbedDepth", 0.3f, "Metres the bottom of the post sits below ground level.");
		SignInnerEndOffset = ((BaseUnityPlugin)this).Config.Bind<float>("Signpost", "SignInnerEndOffset", 0.2f, "Horizontal distance from the post's axis to the board's inner end, along the pointing direction. 0.2 puts the inner end on the surface of the 0.4 m post so no letters are hidden inside it; 0 overlaps half the post and hides the last letter.");
		SignDropBelowPoleTop = ((BaseUnityPlugin)this).Config.Bind<float>("Signpost", "SignDropBelowPoleTop", 0f, "Metres the board's top edge sits below the top of the post.");
		TextTemplate = ((BaseUnityPlugin)this).Config.Bind<string>("Signpost", "TextTemplate", "{merchant}", "Sign text. {merchant} is replaced by the label from the merchant table.");
		TextColors = ((BaseUnityPlugin)this).Config.Bind<string>("Signpost", "TextColors", "Vendor_BlackForest=#E3B341,Hildir_camp=#E48BD2,BogWitch_Camp=#8CC63F", "Per-merchant label colour as 'locationPrefab=colour' pairs. The sign text is wrapped in a rich-text <color> tag; use a hex value like #E3B341 or a TextMeshPro colour name. Merchants without an entry keep the vanilla text colour. Signposts whose text differs are rebuilt at the next sweep.");
		FlipFace = ((BaseUnityPlugin)this).Config.Bind<bool>("Signpost", "FlipFace", false, "Rotate the board 180 degrees about the post so its text faces the other side. The board still extends toward the merchant.");
		LogGeometry = ((BaseUnityPlugin)this).Config.Bind<bool>("Signpost", "LogGeometry", false, "Log measured prefab bounds and the computed poses on every placement.");
		TriggerPrefix = ((BaseUnityPlugin)this).Config.Bind<string>("Trigger", "TriggerPrefix", "!signpost", "A player-written sign whose text starts with this, followed by a merchant name, is consumed and replaced by a fingerpost pointing at that merchant.");
		RequireAdmin = ((BaseUnityPlugin)this).Config.Bind<bool>("Trigger", "RequireAdmin", true, "Only honour trigger signs written by players in the server's adminlist.txt. Other players see the sign text change to a refusal.");
		SweepInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Trigger", "SweepInterval", 2f, "Seconds between sweeps of the world's sign objects for triggers.");
		WorldPlacement = ((BaseUnityPlugin)this).Config.Bind<bool>("Placement", "WorldPlacement", true, "Place and reconcile world signposts. Disable to leave existing signposts untouched.");
		Spokes = ((BaseUnityPlugin)this).Config.Bind<int>("Placement", "Spokes", 6, new ConfigDescription("Trails per merchant candidate.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 64), Array.Empty<object>()));
		OuterRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Placement", "OuterRadius", 600f, "Outer trail radius in metres. Restart to apply placement settings.");
		InnerRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Placement", "InnerRadius", 150f, "Inner trail radius in metres; must not exceed OuterRadius.");
		Step = ((BaseUnityPlugin)this).Config.Bind<float>("Placement", "Step", 150f, "Positive distance between trail points in metres.");
		SidewaysJitter = ((BaseUnityPlugin)this).Config.Bind<float>("Placement", "SidewaysJitter", 20f, "Maximum sideways displacement in metres.");
		LocationMargin = ((BaseUnityPlugin)this).Config.Bind<float>("Placement", "LocationMargin", 32f, "Clearance beyond other reserved locations' radii in metres.");
		WaterMargin = ((BaseUnityPlugin)this).Config.Bind<float>("Placement", "WaterMargin", 2f, "Minimum elevation above sea level in metres.");
		WaterMargins = ((BaseUnityPlugin)this).Config.Bind<string>("Placement", "WaterMargins", "BogWitch_Camp=0.25", "Per-merchant minimum elevation above sea level as 'locationPrefab=metres' pairs, overriding WaterMargin. Swamp terrain lies within about two metres of the water line, so the Bog Witch needs a small margin or no trail point survives. Restart to apply.");
		Trails = ((BaseUnityPlugin)this).Config.Bind<string>("Placement", "Trails", "BogWitch_Camp=spokes:12;step:50", "Per-merchant trail density as 'locationPrefab=field:value;field:value' pairs, where the fields are spokes, outer, inner and step and override Spokes, OuterRadius, InnerRadius and Step for that merchant. Most swamp candidates are under water, so the Bog Witch gets five times the candidates to end up with a comparable number of signs. Restart to apply.");
		Biomes = ((BaseUnityPlugin)this).Config.Bind<string>("Placement", "Biomes", "", "Per-merchant trail biomes as 'locationPrefab=Biome|Biome' pairs using Heightmap.Biome names (Meadows, BlackForest, Swamp, Mountain, Plains, Mistlands, AshLands, DeepNorth, Ocean). Merchants without an entry use their location's own biome. Swamps are small, so a Bog Witch trail gains many points from 'BogWitch_Camp=Swamp|BlackForest' at the cost of leaving the swamp. Restart to apply.");
		_harmony = new Harmony("com.valheim.merchantsignposts");
		_harmony.PatchAll(typeof(SignpostCommand));
		_harmony.PatchAll(typeof(ZonePatches));
		Log.LogInfo((object)"[MerchantSignposts] MerchantSignposts 0.1.0 loaded");
	}

	private void Update()
	{
		SignTrigger.Update();
		Reconciler.Update();
	}

	private void OnDestroy()
	{
		Harmony harmony = _harmony;
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
	}
}
internal static class Reconciler
{
	private sealed class Counts
	{
		internal int Existing;

		internal int Destroyed;

		internal int Placed;

		internal int Rebuilt;
	}

	private static readonly FieldRef<ZoneSystem, HashSet<Vector2s>> GeneratedZones = AccessTools.FieldRefAccess<ZoneSystem, HashSet<Vector2s>>("m_generatedZones");

	private static bool _requested = true;

	private static ZNet _session;

	internal static void RequestSweep()
	{
		_requested = true;
	}

	internal static void Update()
	{
		ZNet instance = ZNet.instance;
		if ((Object)(object)instance == (Object)null || instance != _session)
		{
			_session = instance;
			SignpostPlan.Reset();
			_requested = true;
		}
		if (!((Object)(object)instance == (Object)null) && instance.IsServer() && ZDOMan.instance != null && !((Object)(object)ZNetScene.instance == (Object)null) && MerchantSignpostsPlugin.WorldPlacement.Value)
		{
			SignpostPlan signpostPlan = SignpostPlan.Get();
			if (signpostPlan != null && _requested)
			{
				_requested = false;
				Sweep(signpostPlan);
			}
		}
	}

	private static void Sweep(SignpostPlan plan)
	{
		//IL_0155: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0215: Unknown result type (might be due to invalid IL or missing references)
		//IL_021a: Unknown result type (might be due to invalid IL or missing references)
		//IL_021f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0331: Unknown result type (might be due to invalid IL or missing references)
		//IL_0336: Unknown result type (might be due to invalid IL or missing references)
		//IL_0479: Unknown result type (might be due to invalid IL or missing references)
		//IL_04b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_04be: Unknown result type (might be due to invalid IL or missing references)
		List<ZDO> list = new List<ZDO>();
		ZdoSweep.Collect(MerchantSignpostsPlugin.SignPrefab.Value, list);
		HashSet<string> hashSet = Merchants.PolePrefabs();
		foreach (ZDO item in list)
		{
			string text = item.GetString("MerchantSignposts.pole", "");
			if (text.Length > 0)
			{
				hashSet.Add(text);
			}
		}
		List<ZDO> list2 = new List<ZDO>();
		List<ZDO> list3 = new List<ZDO>();
		foreach (string item2 in hashSet)
		{
			ZdoSweep.Collect(item2, list3);
			list2.AddRange(list3);
		}
		Dictionary<string, Counts> dictionary = new Dictionary<string, Counts>();
		foreach (Merchants.Merchant item3 in Merchants.Parse(MerchantSignpostsPlugin.MerchantTable.Value))
		{
			dictionary[item3.LocationPrefab] = new Counts();
		}
		Dictionary<string, List<Vector3>> dictionary2 = new Dictionary<string, List<Vector3>>();
		foreach (SignpostPlan.Planned item4 in plan.All)
		{
			if (!dictionary2.TryGetValue(item4.LocationPrefab, out var value))
			{
				value = (dictionary2[item4.LocationPrefab] = new List<Vector3>());
			}
			value.Add(item4.Position);
		}
		HashSet<ZDOID> removed = new HashSet<ZDOID>();
		HashSet<ZDOID> hashSet2 = new HashSet<ZDOID>();
		Vector3 origin = default(Vector3);
		foreach (ZDO item5 in Combine(list2, list))
		{
			string text2 = item5.GetString("MerchantSignposts.location", "");
			if (text2.Length == 0 || !hashSet2.Add(item5.m_uid))
			{
				continue;
			}
			if (!dictionary.TryGetValue(text2, out var value2))
			{
				value2 = (dictionary[text2] = new Counts());
			}
			value2.Existing++;
			Vector3 target = item5.GetVec3("MerchantSignposts.target", Vector3.zero);
			if (plan.Targets.TryGetValue(text2, out var value3) && value3.Exists((Vector3 p) => Near(p, target)))
			{
				if (item5.GetBool("MerchantSignposts.manual", false) || !item5.GetVec3("MerchantSignposts.origin", ref origin) || (dictionary2.TryGetValue(text2, out var value4) && value4.Exists((Vector3 p) => Near(p, origin))))
				{
					continue;
				}
			}
			Destroy(item5, removed, value2);
		}
		list2.RemoveAll((ZDO zdo) => removed.Contains(zdo.m_uid));
		list.RemoveAll((ZDO zdo) => removed.Contains(zdo.m_uid));
		HashSet<Vector2s> hashSet3 = GeneratedZones.Invoke(ZoneSystem.instance);
		Dictionary<string, string> dictionary3 = new Dictionary<string, string>();
		Vector3 val3 = default(Vector3);
		foreach (SignpostPlan.Planned point in plan.All)
		{
			if (!hashSet3.Contains(ZoneSystem.GetZone(point.Position)))
			{
				continue;
			}
			ZDO val = list2.Find((ZDO zdo) => Matches(zdo, point, pole: true));
			ZDO val2 = list.Find((ZDO zdo) => Matches(zdo, point, pole: false));
			bool flag = false;
			if (val != null)
			{
				if (val2 == null && !val.GetVec3("MerchantSignposts.origin", ref val3))
				{
					continue;
				}
				if (val2 != null)
				{
					if (!dictionary3.TryGetValue(point.LocationPrefab, out var value5))
					{
						value5 = (dictionary3[point.LocationPrefab] = SignpostBuilder.CurrentStamp(point.LocationPrefab, point.Label));
					}
					if (val.GetString("MerchantSignposts.stamp", "") == value5 && val2.GetString("MerchantSignposts.stamp", "") == value5)
					{
						continue;
					}
					flag = true;
				}
			}
			Counts counts2 = dictionary[point.LocationPrefab];
			if (val != null)
			{
				Destroy(val, removed, counts2);
				list2.Remove(val);
			}
			if (val2 != null)
			{
				Destroy(val2, removed, counts2);
				list.Remove(val2);
			}
			float groundY = SignpostPlan.TerrainHeight(WorldGenerator.instance, ZoneSystem.instance, point.Position);
			ZNetView.StartGhostInit();
			try
			{
				string text4 = Merchants.SignText(point.LocationPrefab, point.Label);
				if (SignpostBuilder.TryPlace(point.Position, groundY, point.Target, point.LocationPrefab, text4, manual: false, out var result))
				{
					list2.Add(result.Pole.GetComponent<ZNetView>().GetZDO());
					list.Add(result.Sign.GetComponent<ZNetView>().GetZDO());
					Object.Destroy((Object)(object)result.Pole);
					Object.Destroy((Object)(object)result.Sign);
					counts2.Placed++;
					if (flag)
					{
						counts2.Rebuilt++;
					}
				}
			}
			finally
			{
				ZNetView.FinishGhostInit();
			}
		}
		foreach (KeyValuePair<string, Counts> item6 in dictionary)
		{
			MerchantSignpostsPlugin.Log.LogInfo((object)$"[MerchantSignposts] sweep {item6.Key}: existing={item6.Value.Existing} pieces, destroyed={item6.Value.Destroyed} pieces, placed={item6.Value.Placed} signposts ({item6.Value.Rebuilt} rebuilt under the current stamp)");
		}
	}

	private static IEnumerable<ZDO> Combine(List<ZDO> poles, List<ZDO> signs)
	{
		foreach (ZDO pole in poles)
		{
			yield return pole;
		}
		foreach (ZDO sign in signs)
		{
			yield return sign;
		}
	}

	private static bool Matches(ZDO zdo, SignpostPlan.Planned point, bool pole)
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		if (zdo.GetString("MerchantSignposts.location", "") != point.LocationPrefab || !Near(zdo.GetVec3("MerchantSignposts.target", Vector3.zero), point.Target))
		{
			return false;
		}
		Vector3 a = default(Vector3);
		if (zdo.GetVec3("MerchantSignposts.origin", ref a))
		{
			return Near(a, point.Position);
		}
		if (pole)
		{
			return Near(zdo.GetPosition(), point.Position);
		}
		return false;
	}

	internal static bool Near(Vector3 a, Vector3 b)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		float num = a.x - b.x;
		float num2 = a.z - b.z;
		return num * num + num2 * num2 < 1f;
	}

	private static void Destroy(ZDO zdo, HashSet<ZDOID> removed, Counts count)
	{
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		if (removed.Add(zdo.m_uid))
		{
			zdo.SetOwner(ZDOMan.GetSessionID());
			ZDOMan.instance.DestroyZDO(zdo);
			count.Destroyed++;
		}
	}
}
internal static class SignpostBuilder
{
	internal struct Result
	{
		public GameObject Pole;

		public GameObject Sign;

		public Vector3 PolePosition;

		public Vector3 SignPosition;

		public Quaternion SignRotation;
	}

	internal const string LocationKey = "MerchantSignposts.location";

	internal const string TargetKey = "MerchantSignposts.target";

	internal const string OriginKey = "MerchantSignposts.origin";

	internal const string PoleKey = "MerchantSignposts.pole";

	internal const string StampKey = "MerchantSignposts.stamp";

	internal const string ManualKey = "MerchantSignposts.manual";

	internal const float MinPoleHeight = 1f;

	internal static bool TryMeasure(GameObject prefab, out Bounds local)
	{
		//IL_0001: 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_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: 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_009b: 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_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: 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_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_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)
		local = default(Bounds);
		Transform transform = prefab.transform;
		bool flag = true;
		Collider[] componentsInChildren = prefab.GetComponentsInChildren<Collider>();
		Vector3 val2 = default(Vector3);
		foreach (Collider val in componentsInChildren)
		{
			if (val.isTrigger || !TryLocalBox(val, out var center, out var half))
			{
				continue;
			}
			for (int j = 0; j < 8; j++)
			{
				((Vector3)(ref val2))..ctor(((j & 1) == 0) ? (0f - half.x) : half.x, ((j & 2) == 0) ? (0f - half.y) : half.y, ((j & 4) == 0) ? (0f - half.z) : half.z);
				Vector3 val3 = transform.InverseTransformPoint(((Component)val).transform.TransformPoint(center + val2));
				if (flag)
				{
					local = new Bounds(val3, Vector3.zero);
					flag = false;
				}
				else
				{
					((Bounds)(ref local)).Encapsulate(val3);
				}
			}
		}
		return !flag;
	}

	private static bool TryLocalBox(Collider collider, out Vector3 center, out Vector3 half)
	{
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cf: 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_00ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0140: 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_010f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0114: Unknown result type (might be due to invalid IL or missing references)
		//IL_0118: Unknown result type (might be due to invalid IL or missing references)
		//IL_011d: Unknown result type (might be due to invalid IL or missing references)
		//IL_012a: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0133: Unknown result type (might be due to invalid IL or missing references)
		//IL_0138: Unknown result type (might be due to invalid IL or missing references)
		BoxCollider val = (BoxCollider)(object)((collider is BoxCollider) ? collider : null);
		if (val == null)
		{
			CapsuleCollider val2 = (CapsuleCollider)(object)((collider is CapsuleCollider) ? collider : null);
			if (val2 == null)
			{
				SphereCollider val3 = (SphereCollider)(object)((collider is SphereCollider) ? collider : null);
				if (val3 == null)
				{
					MeshCollider val4 = (MeshCollider)(object)((collider is MeshCollider) ? collider : null);
					if (val4 != null && (Object)(object)val4.sharedMesh != (Object)null)
					{
						Bounds bounds = val4.sharedMesh.bounds;
						center = ((Bounds)(ref bounds)).center;
						bounds = val4.sharedMesh.bounds;
						half = ((Bounds)(ref bounds)).extents;
						return true;
					}
					center = default(Vector3);
					half = default(Vector3);
					return false;
				}
				center = val3.center;
				half = new Vector3(val3.radius, val3.radius, val3.radius);
				return true;
			}
			center = val2.center;
			float num = Mathf.Max(val2.height * 0.5f, val2.radius);
			half = new Vector3(val2.radius, val2.radius, val2.radius);
			if (val2.direction == 0)
			{
				half.x = num;
			}
			else if (val2.direction == 1)
			{
				half.y = num;
			}
			else
			{
				half.z = num;
			}
			return true;
		}
		center = val.center;
		half = val.size * 0.5f;
		return true;
	}

	internal static string Stamp(string poleName, string text)
	{
		return string.Join("|", "0.1.0", poleName, MerchantSignpostsPlugin.SignPrefab.Value, MerchantSignpostsPlugin.PoleEmbedDepth.Value.ToString("R"), MerchantSignpostsPlugin.SignInnerEndOffset.Value.ToString("R"), MerchantSignpostsPlugin.SignDropBelowPoleTop.Value.ToString("R"), MerchantSignpostsPlugin.FlipFace.Value ? "flipped" : "facing", text);
	}

	internal static string CurrentStamp(string locationPrefab, string label)
	{
		return Stamp(Merchants.PoleFor(locationPrefab), Merchants.SignText(locationPrefab, label));
	}

	internal static bool TryPlace(Vector3 origin, float groundY, Vector3 target, string locationPrefab, string text, bool manual, out Result result)
	{
		//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_019f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a6: 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_016f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d4: 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_01de: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_0205: Unknown result type (might be due to invalid IL or missing references)
		//IL_0210: Unknown result type (might be due to invalid IL or missing references)
		//IL_0219: Unknown result type (might be due to invalid IL or missing references)
		//IL_0237: Unknown result type (might be due to invalid IL or missing references)
		//IL_0239: Unknown result type (might be due to invalid IL or missing references)
		//IL_0233: Unknown result type (might be due to invalid IL or missing references)
		//IL_023e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0243: 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_024d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0252: Unknown result type (might be due to invalid IL or missing references)
		//IL_0266: Unknown result type (might be due to invalid IL or missing references)
		//IL_0258: Unknown result type (might be due to invalid IL or missing references)
		//IL_028e: Unknown result type (might be due to invalid IL or missing references)
		//IL_029b: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_02bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_03ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_03bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_03c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_03c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_03df: Unknown result type (might be due to invalid IL or missing references)
		//IL_03e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_0306: Unknown result type (might be due to invalid IL or missing references)
		//IL_033a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0351: Unknown result type (might be due to invalid IL or missing references)
		//IL_0374: Unknown result type (might be due to invalid IL or missing references)
		//IL_037e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0392: Unknown result type (might be due to invalid IL or missing references)
		//IL_039e: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0431: Unknown result type (might be due to invalid IL or missing references)
		//IL_0433: Unknown result type (might be due to invalid IL or missing references)
		//IL_043a: Unknown result type (might be due to invalid IL or missing references)
		//IL_043c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0443: Unknown result type (might be due to invalid IL or missing references)
		//IL_0445: Unknown result type (might be due to invalid IL or missing references)
		result = default(Result);
		ManualLogSource log = MerchantSignpostsPlugin.Log;
		ZNetScene instance = ZNetScene.instance;
		if ((Object)(object)instance == (Object)null)
		{
			log.LogWarning((object)"[MerchantSignposts] no ZNetScene");
			return false;
		}
		string text2 = Merchants.PoleFor(locationPrefab);
		string value = MerchantSignpostsPlugin.SignPrefab.Value;
		GameObject prefab = instance.GetPrefab(text2);
		GameObject prefab2 = instance.GetPrefab(value);
		if ((Object)(object)prefab == (Object)null)
		{
			log.LogError((object)("[MerchantSignposts] pole prefab '" + text2 + "' not found"));
			return false;
		}
		if ((Object)(object)prefab2 == (Object)null)
		{
			log.LogError((object)("[MerchantSignposts] sign prefab '" + value + "' not found"));
			return false;
		}
		if (!CanPersist(prefab) || !CanPersist(prefab2))
		{
			log.LogError((object)"[MerchantSignposts] both prefabs must have a persistent root ZNetView");
			return false;
		}
		if ((Object)(object)prefab2.GetComponent<Sign>() == (Object)null)
		{
			log.LogError((object)("[MerchantSignposts] '" + value + "' has no root Sign component to display text"));
			return false;
		}
		if (!TryMeasure(prefab, out var local))
		{
			log.LogError((object)("[MerchantSignposts] '" + text2 + "' has no collider to measure"));
			return false;
		}
		if (!TryMeasure(prefab2, out var local2))
		{
			log.LogError((object)("[MerchantSignposts] '" + value + "' has no collider to measure"));
			return false;
		}
		float num = ((Bounds)(ref local)).max.y - ((Bounds)(ref local)).min.y;
		if (num < 1f)
		{
			log.LogError((object)($"[MerchantSignposts] '{text2}' measures {num:F2} m tall (bounds min={F(((Bounds)(ref local)).min)} max={F(((Bounds)(ref local)).max)}), " + $"less than the {1f:F1} m a post needs; choose another PolePrefabs entry"));
			return false;
		}
		Vector3 val = target - origin;
		val.y = 0f;
		val = ((((Vector3)(ref val)).sqrMagnitude < 1E-06f) ? Vector3.forward : ((Vector3)(ref val)).normalized);
		Quaternion val2 = Quaternion.LookRotation(val, Vector3.up);
		float value2 = MerchantSignpostsPlugin.PoleEmbedDepth.Value;
		Vector3 val3 = default(Vector3);
		((Vector3)(ref val3))..ctor(origin.x, groundY - value2 - ((Bounds)(ref local)).min.y, origin.z);
		float num2 = val3.y + ((Bounds)(ref local)).max.y;
		bool value3 = MerchantSignpostsPlugin.FlipFace.Value;
		Quaternion val4 = Quaternion.LookRotation(Vector3.Cross(value3 ? (-val) : val, Vector3.up), Vector3.up);
		float num3 = (value3 ? (0f - ((Bounds)(ref local2)).max.x) : ((Bounds)(ref local2)).min.x);
		float num4 = MerchantSignpostsPlugin.SignInnerEndOffset.Value - num3;
		float value4 = MerchantSignpostsPlugin.SignDropBelowPoleTop.Value;
		Vector3 val5 = new Vector3(origin.x, num2 - value4 - ((Bounds)(ref local2)).max.y, origin.z) + val * num4;
		if (MerchantSignpostsPlugin.LogGeometry.Value)
		{
			log.LogInfo((object)("[MerchantSignposts] " + text2 + " local bounds min=" + F(((Bounds)(ref local)).min) + " max=" + F(((Bounds)(ref local)).max)));
			log.LogInfo((object)("[MerchantSignposts] " + value + " local bounds min=" + F(((Bounds)(ref local2)).min) + " max=" + F(((Bounds)(ref local2)).max)));
			log.LogInfo((object)$"[MerchantSignposts] dir={F(val)} pole@{F(val3)} top={num2:F2} sign@{F(val5)} yaw={((Quaternion)(ref val4)).eulerAngles.y:F1}");
		}
		GameObject val6 = Object.Instantiate<GameObject>(prefab, val3, val2);
		GameObject val7 = Object.Instantiate<GameObject>(prefab2, val5, val4);
		string stamp = Stamp(text2, text);
		if (!Tag(val6, locationPrefab, target, origin, text2, stamp, manual, null) || !Tag(val7, locationPrefab, target, origin, text2, stamp, manual, text))
		{
			instance.Destroy(val6);
			instance.Destroy(val7);
			return false;
		}
		result = new Result
		{
			Pole = val6,
			Sign = val7,
			PolePosition = val3,
			SignPosition = val5,
			SignRotation = val4
		};
		return true;
	}

	private static bool CanPersist(GameObject prefab)
	{
		ZNetView component = prefab.GetComponent<ZNetView>();
		if ((Object)(object)component != (Object)null)
		{
			return component.m_persistent;
		}
		return false;
	}

	private static bool Tag(GameObject go, string locationPrefab, Vector3 target, Vector3 origin, string poleName, string stamp, bool manual, string text)
	{
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		ZNetView component = go.GetComponent<ZNetView>();
		ZDO val = (((Object)(object)component != (Object)null) ? component.GetZDO() : null);
		if (val == null)
		{
			MerchantSignpostsPlugin.Log.LogWarning((object)("[MerchantSignposts] " + ((Object)go).name + " has no ZDO; it will not persist"));
			return false;
		}
		val.Set("MerchantSignposts.location", locationPrefab);
		val.Set("MerchantSignposts.target", target);
		val.Set("MerchantSignposts.origin", origin);
		val.Set("MerchantSignposts.pole", poleName);
		val.Set("MerchantSignposts.stamp", stamp);
		if (manual)
		{
			val.Set("MerchantSignposts.manual", true);
		}
		if (text != null)
		{
			val.Set(ZDOVars.s_text, text);
		}
		WearNTear component2 = go.GetComponent<WearNTear>();
		if ((Object)(object)component2 != (Object)null)
		{
			component2.OnPlaced();
		}
		return true;
	}

	private static string F(Vector3 v)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		return $"({v.x:F2}, {v.y:F2}, {v.z:F2})";
	}
}
[HarmonyPatch(typeof(Terminal), "InitTerminal")]
internal static class SignpostCommand
{
	private static bool _registered;

	private static void Postfix()
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Expected O, but got Unknown
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		if (!_registered)
		{
			_registered = true;
			new ConsoleCommand("signpost", "signpost <merchant> [x z] | sweep | plan <merchant> (host only; multi-word names need no quotes)", new ConsoleEvent(Run), true, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}
	}

	private static void Run(ConsoleEventArgs args)
	{
		//IL_032a: Unknown result type (might be due to invalid IL or missing references)
		//IL_032f: Unknown result type (might be due to invalid IL or missing references)
		//IL_037b: Unknown result type (might be due to invalid IL or missing references)
		//IL_010c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0111: Unknown result type (might be due to invalid IL or missing references)
		//IL_0396: Unknown result type (might be due to invalid IL or missing references)
		//IL_03a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_03b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_03ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_0401: Unknown result type (might be due to invalid IL or missing references)
		//IL_0362: Unknown result type (might be due to invalid IL or missing references)
		//IL_043e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0442: Unknown result type (might be due to invalid IL or missing references)
		//IL_041c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0465: Unknown result type (might be due to invalid IL or missing references)
		//IL_0480: Unknown result type (might be due to invalid IL or missing references)
		//IL_0491: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b3: Unknown result type (might be due to invalid IL or missing references)
		Terminal context = args.Context;
		if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
		{
			context.AddString("signpost: only the host can place signposts");
			return;
		}
		if (args.Length >= 2 && args[1] == "sweep")
		{
			Reconciler.RequestSweep();
			context.AddString("signpost: sweep requested (requires WorldPlacement); see server log");
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			context.AddString("signpost: no local player");
			return;
		}
		if (args.Length >= 3 && args[1] == "plan")
		{
			if (!TryParseMerchant(args, 2, out var selected, out var _))
			{
				context.AddString("signpost: unknown merchant '" + Join(args, 2, args.Length) + "'");
				return;
			}
			SignpostPlan signpostPlan = SignpostPlan.Get();
			if (signpostPlan == null)
			{
				context.AddString("signpost: locations not ready");
				return;
			}
			List<SignpostPlan.Planned> list = signpostPlan.All.FindAll((SignpostPlan.Planned p) => p.LocationPrefab == selected.LocationPrefab);
			Vector3 from = ((Component)localPlayer).transform.position;
			list.Sort(delegate(SignpostPlan.Planned a, SignpostPlan.Planned b)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				//IL_000c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_001d: Unknown result type (might be due to invalid IL or missing references)
				//IL_0023: Unknown result type (might be due to invalid IL or missing references)
				//IL_0028: Unknown result type (might be due to invalid IL or missing references)
				//IL_002d: Unknown result type (might be due to invalid IL or missing references)
				Vector3 val = a.Position - from;
				float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
				val = b.Position - from;
				return sqrMagnitude.CompareTo(((Vector3)(ref val)).sqrMagnitude);
			});
			context.AddString($"signpost: seed={WorldGenerator.instance.GetSeed()}, {selected.Label}: {list.Count} planned");
			if (signpostPlan.Tallies.TryGetValue(selected.LocationPrefab, out var value))
			{
				List<string> list2 = new List<string>();
				foreach (KeyValuePair<string, int> biomeName in value.BiomeNames)
				{
					list2.Add($"{biomeName.Key}={biomeName.Value}");
				}
				list2.Sort();
				context.AddString(string.Format("  candidates={0} rejected: biome={1} ({2}) ", value.Candidates, value.Biome, string.Join(", ", list2)) + $"water={value.Water} (submerged={value.Submerged}) near-location={value.Obstructed}; accepted={value.Accepted}");
			}
			else
			{
				context.AddString("  no reserved instance of " + selected.LocationPrefab + " in this world");
			}
			for (int num = 0; num < Math.Min(5, list.Count); num++)
			{
				context.AddString($"  {list[num].Position} -> {list[num].Target}");
			}
			return;
		}
		if (args.Length < 2)
		{
			context.AddString("usage: signpost <merchant> [x z]");
			return;
		}
		if (!TryParseMerchant(args, 1, out var merchant, out var next2))
		{
			context.AddString("signpost: unknown merchant '" + Join(args, 1, args.Length) + "'; see the Merchants table in the config");
			return;
		}
		Vector3 position = ((Component)localPlayer).transform.position;
		Vector3 position2 = default(Vector3);
		if (args.Length >= next2 + 2 && float.TryParse(args[next2], out var result) && float.TryParse(args[next2 + 1], out var result2))
		{
			((Vector3)(ref position2))..ctor(result, position.y, result2);
		}
		else
		{
			if (!Merchants.TryNearestInstance(merchant.LocationPrefab, position, out position2, out var placed, out var remaining))
			{
				context.AddString("signpost: no reserved instance of " + merchant.LocationPrefab + " in this world");
				return;
			}
			context.AddString($"signpost: {merchant.Label} nearest instance at ({position2.x:F0}, {position2.z:F0}), " + $"{Vector3.Distance(position, position2):F0} m away, placed={placed}, instances remaining={remaining}");
		}
		float groundY = position.y;
		float num2 = default(float);
		if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.FindFloor(position, ref num2))
		{
			groundY = num2;
		}
		string text = Merchants.SignText(merchant.LocationPrefab, merchant.Label);
		if (SignpostBuilder.TryPlace(position, groundY, position2, merchant.LocationPrefab, text, manual: true, out var result3))
		{
			context.AddString($"signpost: placed '{text}' pointing {((Quaternion)(ref result3.SignRotation)).eulerAngles.y:F0} deg " + $"(pole {result3.PolePosition.y:F2}, sign {result3.SignPosition.y:F2})");
		}
		else
		{
			context.AddString("signpost: placement failed; see the BepInEx log");
		}
	}

	private static bool TryParseMerchant(ConsoleEventArgs args, int start, out Merchants.Merchant merchant, out int next)
	{
		for (int num = args.Length; num > start; num--)
		{
			if (Merchants.TryFind(Join(args, start, num), out merchant))
			{
				next = num;
				return true;
			}
		}
		merchant = default(Merchants.Merchant);
		next = start;
		return false;
	}

	private static string Join(ConsoleEventArgs args, int start, int end)
	{
		string[] array = new string[end - start];
		for (int i = start; i < end; i++)
		{
			array[i - start] = args[i];
		}
		return string.Join(" ", array);
	}
}
internal sealed class SignpostPlan
{
	internal struct Planned
	{
		internal string LocationPrefab;

		internal string Label;

		internal Vector3 Position;

		internal Vector3 Target;
	}

	internal sealed class Tally
	{
		internal int Candidates;

		internal int Biome;

		internal int Water;

		internal int Submerged;

		internal int Obstructed;

		internal int Accepted;

		internal readonly Dictionary<string, int> BiomeNames = new Dictionary<string, int>(StringComparer.Ordinal);
	}

	internal struct Trail
	{
		internal int Spokes;

		internal float Outer;

		internal float Inner;

		internal float Step;

		internal bool Valid
		{
			get
			{
				if (Spokes > 0 && Positive(Outer) && Positive(Inner) && Positive(Step))
				{
					return Inner <= Outer;
				}
				return false;
			}
		}

		internal double PointsPerSpoke
		{
			get
			{
				if (!Valid)
				{
					return 0.0;
				}
				return Math.Floor(((double)Outer - (double)Inner) / (double)Step) + 1.0;
			}
		}

		internal bool WithinLimit => PointsPerSpoke * (double)Spokes <= 100000.0;
	}

	internal readonly Dictionary<Vector2s, List<Planned>> ByZone = new Dictionary<Vector2s, List<Planned>>();

	internal readonly List<Planned> All = new List<Planned>();

	internal readonly Dictionary<string, List<Vector3>> Targets = new Dictionary<string, List<Vector3>>(StringComparer.Ordinal);

	internal readonly Dictionary<string, Tally> Tallies = new Dictionary<string, Tally>(StringComparer.Ordinal);

	private static SignpostPlan _cached;

	private static object _instances;

	private static int _instanceCount;

	internal static void Reset()
	{
		_cached = null;
		_instances = null;
		_instanceCount = 0;
	}

	internal static SignpostPlan Get()
	{
		if ((Object)(object)ZNet.instance == (Object)null)
		{
			Reset();
			return null;
		}
		ZoneSystem instance = ZoneSystem.instance;
		if (!ZNet.instance.IsServer() || (Object)(object)instance == (Object)null || !instance.LocationsGenerated || WorldGenerator.instance == null)
		{
			return null;
		}
		Dictionary<Vector2s, LocationInstance> locationInstances = instance.m_locationInstances;
		if (_cached != null && _instances == locationInstances && _instanceCount == locationInstances.Count)
		{
			return _cached;
		}
		SignpostPlan result = (_cached = Build(instance, WorldGenerator.instance));
		_instances = locationInstances;
		_instanceCount = locationInstances.Count;
		Reconciler.RequestSweep();
		return result;
	}

	private unsafe static SignpostPlan Build(ZoneSystem zones, WorldGenerator world)
	{
		//IL_0164: Unknown result type (might be due to invalid IL or missing references)
		//IL_0169: Unknown result type (might be due to invalid IL or missing references)
		//IL_016b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0178: Unknown result type (might be due to invalid IL or missing references)
		//IL_0190: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0228: Unknown result type (might be due to invalid IL or missing references)
		//IL_021a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0221: Unknown result type (might be due to invalid IL or missing references)
		//IL_022a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0257: Unknown result type (might be due to invalid IL or missing references)
		//IL_0259: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0304: Unknown result type (might be due to invalid IL or missing references)
		//IL_0309: Unknown result type (might be due to invalid IL or missing references)
		//IL_030e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0318: Unknown result type (might be due to invalid IL or missing references)
		//IL_031d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0322: Unknown result type (might be due to invalid IL or missing references)
		//IL_0334: Unknown result type (might be due to invalid IL or missing references)
		//IL_033b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0348: Unknown result type (might be due to invalid IL or missing references)
		//IL_034d: Unknown result type (might be due to invalid IL or missing references)
		//IL_034f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0351: Unknown result type (might be due to invalid IL or missing references)
		//IL_0353: Unknown result type (might be due to invalid IL or missing references)
		//IL_03a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_03ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_03cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0413: Unknown result type (might be due to invalid IL or missing references)
		//IL_0418: Unknown result type (might be due to invalid IL or missing references)
		//IL_041c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0421: Unknown result type (might be due to invalid IL or missing references)
		//IL_0427: Unknown result type (might be due to invalid IL or missing references)
		//IL_0433: Unknown result type (might be due to invalid IL or missing references)
		//IL_043c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0448: Unknown result type (might be due to invalid IL or missing references)
		//IL_045e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0466: Unknown result type (might be due to invalid IL or missing references)
		//IL_0468: Unknown result type (might be due to invalid IL or missing references)
		//IL_0474: Unknown result type (might be due to invalid IL or missing references)
		//IL_047c: Unknown result type (might be due to invalid IL or missing references)
		//IL_047e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0501: Unknown result type (might be due to invalid IL or missing references)
		//IL_0503: Unknown result type (might be due to invalid IL or missing references)
		//IL_050a: Unknown result type (might be due to invalid IL or missing references)
		//IL_050c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0511: Unknown result type (might be due to invalid IL or missing references)
		//IL_0527: Unknown result type (might be due to invalid IL or missing references)
		//IL_0529: Unknown result type (might be due to invalid IL or missing references)
		//IL_052e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0536: Unknown result type (might be due to invalid IL or missing references)
		//IL_0547: Unknown result type (might be due to invalid IL or missing references)
		SignpostPlan signpostPlan = new SignpostPlan();
		Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
		foreach (Merchants.Merchant item2 in Merchants.Parse(MerchantSignpostsPlugin.MerchantTable.Value))
		{
			dictionary[item2.LocationPrefab] = item2.Label;
		}
		Trail trail = new Trail
		{
			Spokes = MerchantSignpostsPlugin.Spokes.Value,
			Outer = MerchantSignpostsPlugin.OuterRadius.Value,
			Inner = MerchantSignpostsPlugin.InnerRadius.Value,
			Step = MerchantSignpostsPlugin.Step.Value
		};
		float value = MerchantSignpostsPlugin.SidewaysJitter.Value;
		float value2 = MerchantSignpostsPlugin.LocationMargin.Value;
		float value3 = MerchantSignpostsPlugin.WaterMargin.Value;
		Dictionary<string, float> dictionary2 = ParseWaterMargins(MerchantSignpostsPlugin.WaterMargins.Value);
		Dictionary<string, Biome> dictionary3 = ParseBiomes(MerchantSignpostsPlugin.Biomes.Value);
		Dictionary<string, Trail> dictionary4 = ParseTrails(MerchantSignpostsPlugin.Trails.Value, trail);
		bool flag = trail.Valid && trail.WithinLimit && NonNegative(value) && NonNegative(value2) && NonNegative(value3);
		if (!flag)
		{
			MerchantSignpostsPlugin.Log.LogWarning((object)"[MerchantSignposts] invalid Placement configuration: spokes, radii and step must be positive, inner radius must not exceed outer radius, and jitter/margins must be nonnegative; all distances must be finite and candidates per instance must not exceed 100000. No signposts planned.");
		}
		Vector3 val2 = default(Vector3);
		Vector3 val3 = default(Vector3);
		foreach (KeyValuePair<Vector2s, LocationInstance> locationInstance in zones.m_locationInstances)
		{
			LocationInstance value4 = locationInstance.Value;
			if (value4.m_location == null || !dictionary.TryGetValue(value4.m_location.m_prefabName, out var value5))
			{
				continue;
			}
			string prefabName = value4.m_location.m_prefabName;
			if (!signpostPlan.Targets.TryGetValue(prefabName, out var value6))
			{
				value6 = (signpostPlan.Targets[prefabName] = new List<Vector3>());
			}
			value6.Add(value4.m_position);
			if (!signpostPlan.Tallies.TryGetValue(prefabName, out var value7))
			{
				value7 = (signpostPlan.Tallies[prefabName] = new Tally());
			}
			float value8;
			float num = (dictionary2.TryGetValue(prefabName, out value8) ? value8 : value3);
			Biome value9;
			Biome val = (dictionary3.TryGetValue(prefabName, out value9) ? value9 : value4.m_location.m_biome);
			Trail value10;
			Trail trail2 = (dictionary4.TryGetValue(prefabName, out value10) ? value10 : trail);
			int num2 = (int)trail2.PointsPerSpoke;
			if (!flag)
			{
				continue;
			}
			Random random = new Random(Seed(world.GetSeed(), value4.m_position));
			foreach (double item3 in Bearings(random, trail2.Spokes))
			{
				((Vector3)(ref val2))..ctor((float)Math.Cos(item3), 0f, (float)Math.Sin(item3));
				((Vector3)(ref val3))..ctor(0f - val2.z, 0f, val2.x);
				for (int i = 0; i < num2; i++)
				{
					float num3 = (float)((double)trail2.Outer - (double)i * (double)trail2.Step);
					Vector3 val4 = value4.m_position + val2 * (num3 + Offset(random, trail2.Step * 0.25f)) + val3 * Offset(random, value);
					value7.Candidates++;
					Biome biome = world.GetBiome(val4.x, val4.z, 0.02f, false);
					if ((biome & val) == 0)
					{
						value7.Biome++;
						string key = ((object)(*(Biome*)(&biome))/*cast due to .constrained prefix*/).ToString();
						value7.BiomeNames.TryGetValue(key, out var value11);
						value7.BiomeNames[key] = value11 + 1;
						continue;
					}
					val4.y = TerrainHeight(world, zones, val4);
					if (val4.y <= zones.m_waterLevel + num)
					{
						value7.Water++;
						if (val4.y <= zones.m_waterLevel)
						{
							value7.Submerged++;
						}
						continue;
					}
					bool flag2 = false;
					foreach (KeyValuePair<Vector2s, LocationInstance> locationInstance2 in zones.m_locationInstances)
					{
						LocationInstance value12 = locationInstance2.Value;
						Vector2s key2 = locationInstance2.Key;
						if (!((Vector2s)(ref key2)).Equals(locationInstance.Key) && value12.m_location != null)
						{
							float num4 = Math.Max(value12.m_location.m_exteriorRadius, value12.m_location.m_interiorRadius) + value2;
							double num5 = (double)val4.x - (double)value12.m_position.x;
							double num6 = (double)val4.z - (double)value12.m_position.z;
							if (num5 * num5 + num6 * num6 < (double)num4 * (double)num4)
							{
								flag2 = true;
								break;
							}
						}
					}
					if (flag2)
					{
						value7.Obstructed++;
						continue;
					}
					value7.Accepted++;
					Planned item = new Planned
					{
						LocationPrefab = prefabName,
						Label = value5,
						Position = val4,
						Target = value4.m_position
					};
					signpostPlan.All.Add(item);
					Vector2s zone = ZoneSystem.GetZone(val4);
					if (!signpostPlan.ByZone.TryGetValue(zone, out var value13))
					{
						value13 = (signpostPlan.ByZone[zone] = new List<Planned>());
					}
					value13.Add(item);
				}
			}
		}
		return signpostPlan;
	}

	internal static float TerrainHeight(WorldGenerator world, ZoneSystem zones, Vector3 position)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: 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_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: 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_00c2: 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_00d5: 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_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0105: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		//IL_010d: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0080: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		float zoneSize = zones.m_zoneSize;
		Vector3 zonePos = ZoneSystem.GetZonePos(ZoneSystem.GetZone(position));
		float num = zonePos.x - zoneSize * 0.5f;
		float num2 = zonePos.z - zoneSize * 0.5f;
		Biome biome = world.GetBiome(num, num2, 0.02f, false);
		Biome biome2 = world.GetBiome(num + zoneSize, num2, 0.02f, false);
		Biome biome3 = world.GetBiome(num, num2 + zoneSize, 0.02f, false);
		Biome biome4 = world.GetBiome(num + zoneSize, num2 + zoneSize, 0.02f, false);
		Color val = default(Color);
		if (biome == biome2 && biome == biome3 && biome == biome4)
		{
			return world.GetBiomeHeight(biome, position.x, position.z, ref val, false, true);
		}
		float num3 = Mathf.SmoothStep(0f, 1f, (position.x - num) / zoneSize);
		float num4 = Mathf.SmoothStep(0f, 1f, (position.z - num2) / zoneSize);
		float biomeHeight = world.GetBiomeHeight(biome, position.x, position.z, ref val, false, true);
		float biomeHeight2 = world.GetBiomeHeight(biome2, position.x, position.z, ref val, false, true);
		float biomeHeight3 = world.GetBiomeHeight(biome3, position.x, position.z, ref val, false, true);
		float biomeHeight4 = world.GetBiomeHeight(biome4, position.x, position.z, ref val, false, true);
		return Mathf.Lerp(Mathf.Lerp(biomeHeight, biomeHeight2, num3), Mathf.Lerp(biomeHeight3, biomeHeight4, num3), num4);
	}

	internal static Dictionary<string, Trail> ParseTrails(string table, Trail global)
	{
		Dictionary<string, Trail> dictionary = new Dictionary<string, Trail>(StringComparer.Ordinal);
		foreach (KeyValuePair<string, string> item in Merchants.ParsePairs(table, "trail"))
		{
			Trail value = global;
			string[] array = item.Value.Split(new char[1] { ';' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length == 0)
				{
					continue;
				}
				int num = text.IndexOf(':');
				string obj = ((num > 0) ? text.Substring(0, num).Trim().ToLowerInvariant() : "");
				float result;
				bool flag = float.TryParse((num > 0) ? text.Substring(num + 1).Trim() : "", NumberStyles.Float, CultureInfo.InvariantCulture, out result);
				switch (obj)
				{
				case "spokes":
					if (flag)
					{
						value.Spokes = (int)result;
						continue;
					}
					break;
				case "outer":
					if (flag)
					{
						value.Outer = result;
						continue;
					}
					break;
				case "inner":
					if (flag)
					{
						value.Inner = result;
						continue;
					}
					break;
				case "step":
					if (flag)
					{
						value.Step = result;
						continue;
					}
					break;
				}
				MerchantSignpostsPlugin.Log.LogWarning((object)("[MerchantSignposts] ignoring trail field '" + text + "' for " + item.Key + ": expected spokes, outer, inner or step with a number"));
			}
			if (value.Valid && value.WithinLimit)
			{
				dictionary[item.Key] = value;
			}
			else
			{
				MerchantSignpostsPlugin.Log.LogWarning((object)("[MerchantSignposts] ignoring trail override for " + item.Key + ": spokes, radii and step must be positive, inner must not exceed outer, and candidates per instance must not exceed 100000"));
			}
		}
		return dictionary;
	}

	internal static Dictionary<string, float> ParseWaterMargins(string table)
	{
		Dictionary<string, float> dictionary = new Dictionary<string, float>(StringComparer.Ordinal);
		foreach (KeyValuePair<string, string> item in Merchants.ParsePairs(table, "water margin"))
		{
			if (float.TryParse(item.Value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && NonNegative(result))
			{
				dictionary[item.Key] = result;
				continue;
			}
			MerchantSignpostsPlugin.Log.LogWarning((object)("[MerchantSignposts] ignoring water margin '" + item.Value + "' for " + item.Key + ": must be a nonnegative number"));
		}
		return dictionary;
	}

	internal static Dictionary<string, Biome> ParseBiomes(string table)
	{
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_006d: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		Dictionary<string, Biome> dictionary = new Dictionary<string, Biome>(StringComparer.Ordinal);
		foreach (KeyValuePair<string, string> item in Merchants.ParsePairs(table, "biome"))
		{
			Biome val = (Biome)0;
			string[] array = item.Value.Split(new char[1] { '|' });
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length != 0)
				{
					if (Enum.TryParse<Biome>(text, ignoreCase: true, out Biome result))
					{
						val |= result;
					}
					else
					{
						MerchantSignpostsPlugin.Log.LogWarning((object)("[MerchantSignposts] ignoring unknown biome '" + text + "' for " + item.Key));
					}
				}
			}
			if ((int)val != 0)
			{
				dictionary[item.Key] = val;
			}
		}
		return dictionary;
	}

	private static IEnumerable<double> Bearings(Random random, int count)
	{
		double[] weights = new double[count];
		double total = 0.0;
		for (int i = 0; i < count; i++)
		{
			total += (weights[i] = random.NextDouble() + double.Epsilon);
		}
		double angle = random.NextDouble() * Math.PI * 2.0;
		for (int j = 0; j < count; j++)
		{
			yield return angle;
			angle += Math.PI / (double)count + Math.PI * weights[j] / total;
		}
	}

	private static int Seed(int worldSeed, Vector3 position)
	{
		//IL_0007: 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_002b: Unknown result type (might be due to invalid IL or missing references)
		return (((((worldSeed * 397) ^ Mathf.RoundToInt(position.x)) * 397) ^ Mathf.RoundToInt(position.y)) * 397) ^ Mathf.RoundToInt(position.z);
	}

	private static float Offset(Random random, float maximum)
	{
		return (float)((random.NextDouble() * 2.0 - 1.0) * (double)maximum);
	}

	private static bool Positive(float value)
	{
		if (NonNegative(value))
		{
			return value > 0f;
		}
		return false;
	}

	private static bool NonNegative(float value)
	{
		if (!float.IsNaN(value) && !float.IsInfinity(value))
		{
			return value >= 0f;
		}
		return false;
	}
}
internal static class SignTrigger
{
	private const int SignTextLimit = 50;

	private static readonly List<ZDO> Buffer = new List<ZDO>();

	private static readonly Dictionary<ZDOID, uint> Handled = new Dictionary<ZDOID, uint>();

	private static float _nextSweep;

	internal static void Update()
	{
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_0148: 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_0180: Unknown result type (might be due to invalid IL or missing references)
		//IL_0185: Unknown result type (might be due to invalid IL or missing references)
		//IL_018c: Unknown result type (might be due to invalid IL or missing references)
		if (Time.time < _nextSweep)
		{
			return;
		}
		_nextSweep = Time.time + MerchantSignpostsPlugin.SweepInterval.Value;
		ZNet instance = ZNet.instance;
		if ((Object)(object)instance == (Object)null || !instance.IsServer() || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null)
		{
			return;
		}
		ZdoSweep.Collect(MerchantSignpostsPlugin.SignPrefab.Value, Buffer);
		string value = MerchantSignpostsPlugin.TriggerPrefix.Value;
		foreach (ZDO item in Buffer)
		{
			string text = item.GetString(ZDOVars.s_text, "");
			if (!string.IsNullOrEmpty(text) && text.StartsWith(value, StringComparison.OrdinalIgnoreCase) && (!Handled.TryGetValue(item.m_uid, out var value2) || value2 != item.DataRevision))
			{
				Handled[item.m_uid] = item.DataRevision;
				Handle(item, text.Substring(value.Length).Trim());
			}
		}
		if (Handled.Count <= 0)
		{
			return;
		}
		List<ZDOID> list = new List<ZDOID>();
		foreach (ZDOID key in Handled.Keys)
		{
			if (ZDOMan.instance.GetZDO(key) == null)
			{
				list.Add(key);
			}
		}
		foreach (ZDOID item2 in list)
		{
			Handled.Remove(item2);
		}
	}

	private static void Handle(ZDO sign, string argument)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: 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_00ae: 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_0106: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		//IL_011c: 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_0188: Unknown result type (might be due to invalid IL or missing references)
		//IL_018b: 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_01c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
		ManualLogSource log = MerchantSignpostsPlugin.Log;
		string text = sign.GetString(ZDOVars.s_author, "");
		Vector3 position = sign.GetPosition();
		log.LogInfo((object)$"[MerchantSignposts] trigger '{argument}' on sign {sign.m_uid} at {F(position)} by '{text}'");
		if (MerchantSignpostsPlugin.RequireAdmin.Value && !IsAdmin(text))
		{
			Reply(sign, "signpost: admins only");
			return;
		}
		if (argument.Length == 0)
		{
			Reply(sign, "usage: !signpost <merchant>");
			return;
		}
		if (!Merchants.TryFind(argument, out var merchant))
		{
			Reply(sign, "signpost: unknown '" + argument + "'");
			return;
		}
		if (!Merchants.TryNearestInstance(merchant.LocationPrefab, position, out var position2, out var placed, out var remaining))
		{
			Reply(sign, "signpost: no " + merchant.Label + " in world");
			return;
		}
		float num = ((WorldGenerator.instance != null && (Object)(object)ZoneSystem.instance != (Object)null) ? SignpostPlan.TerrainHeight(WorldGenerator.instance, ZoneSystem.instance, position) : position.y);
		float num2 = Vector3.Distance(position, position2);
		log.LogInfo((object)($"[MerchantSignposts] {merchant.Label}: nearest instance at {F(position2)}, {num2:F0} m, " + $"placed={placed}, remaining={remaining}; ground={num:F2} signY={position.y:F2}"));
		string text2 = Merchants.SignText(merchant.LocationPrefab, merchant.Label);
		if (!SignpostBuilder.TryPlace(position, num, position2, merchant.LocationPrefab, text2, manual: true, out var result))
		{
			Reply(sign, "signpost: failed, see server log");
			return;
		}
		log.LogInfo((object)($"[MerchantSignposts] placed '{text2}' yaw={((Quaternion)(ref result.SignRotation)).eulerAngles.y:F0} " + "pole=" + F(result.PolePosition) + " sign=" + F(result.SignPosition)));
		sign.SetOwner(ZDOMan.GetSessionID());
		ZDOMan.instance.DestroyZDO(sign);
	}

	private static bool IsAdmin(string author)
	{
		if (string.IsNullOrEmpty(author))
		{
			return false;
		}
		if (author == "host")
		{
			return true;
		}
		return ZNet.instance.IsAdmin(author);
	}

	private static void Reply(ZDO sign, string message)
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		if (message.Length > 50)
		{
			message = message.Substring(0, 50);
		}
		MerchantSignpostsPlugin.Log.LogInfo((object)$"[MerchantSignposts] reply on sign {sign.m_uid}: {message}");
		sign.SetOwner(ZDOMan.GetSessionID());
		sign.Set(ZDOVars.s_text, message);
	}

	private static string F(Vector3 v)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		return $"({v.x:F1}, {v.y:F1}, {v.z:F1})";
	}
}
internal static class ZdoSweep
{
	internal static void Collect(string prefab, List<ZDO> output)
	{
		output.Clear();
		int num = 0;
		while (!ZDOMan.instance.GetAllZDOsWithPrefabIterative(prefab, output, ref num))
		{
		}
		HashSet<ZDOID> seen = new HashSet<ZDOID>();
		output.RemoveAll((ZDO zdo) => !seen.Add(zdo.m_uid));
	}
}
[HarmonyPatch]
internal static class ZonePatches
{
	[HarmonyPostfix]
	[HarmonyPatch(typeof(ZoneSystem), "PlaceLocations")]
	private static void PlaceLocations(Vector2s zoneID, Heightmap hmap, SpawnMode mode, List<GameObject> spawnedObjects)
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Invalid comparison between Unknown and I4
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: 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: Invalid comparison between Unknown and I4
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: 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_00a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
		if (!MerchantSignpostsPlugin.WorldPlacement.Value || ((int)mode != 2 && (int)mode != 0))
		{
			return;
		}
		SignpostPlan signpostPlan = SignpostPlan.Get();
		if (signpostPlan == null || !signpostPlan.ByZone.TryGetValue(zoneID, out var value))
		{
			return;
		}
		float groundY = default(float);
		foreach (SignpostPlan.Planned item in value)
		{
			if (!hmap.GetWorldHeight(item.Position, ref groundY))
			{
				MerchantSignpostsPlugin.Log.LogWarning((object)$"[MerchantSignposts] no terrain height for {item.Position} in zone {zoneID}");
				continue;
			}
			bool flag = (int)mode == 2;
			if (flag)
			{
				ZNetView.StartGhostInit();
			}
			try
			{
				string text = Merchants.SignText(item.LocationPrefab, item.Label);
				if (SignpostBuilder.TryPlace(item.Position, groundY, item.Target, item.LocationPrefab, text, manual: false, out var result))
				{
					if (flag)
					{
						spawnedObjects.Add(result.Pole);
						spawnedObjects.Add(result.Sign);
					}
					MerchantSignpostsPlugin.Log.LogInfo((object)$"[MerchantSignposts] generated {item.LocationPrefab} signpost at {item.Position}, mode={mode}");
				}
			}
			finally
			{
				if (flag)
				{
					ZNetView.FinishGhostInit();
				}
			}
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(ZoneSystem), "RemoveUnplacedLocations")]
	private static void RemoveUnplacedLocations()
	{
		Reconciler.RequestSweep();
	}
}
internal static class ModBuildVersion
{
	internal const string Value = "0.1.0";
}