Decompiled source of GaS QuestSpawner v1.0.7

plugins/GaS-QuestSpawner/GaS-QuestSpawner.dll

Decompiled 2 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using GaS;
using GaSQuestSpawner.Core;
using GearAndStorage;
using GearAndStorage.Core;
using HarmonyLib;
using Newtonsoft.Json;
using Splatform;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("GaS-QuestSpawner")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.7.0")]
[assembly: AssemblyInformationalVersion("1.0.7")]
[assembly: AssemblyProduct("GaS-QuestSpawner")]
[assembly: AssemblyTitle("GaS-QuestSpawner")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.7.0")]
[module: UnverifiableCode]
namespace GaS
{
	internal static class L
	{
		internal static Func<string> Language = () => "English";

		internal static readonly Dictionary<string, string[]> Catalog = Load();

		internal static string Code
		{
			get
			{
				switch (Language()?.ToLowerInvariant() ?? "")
				{
				default:
					return "en";
				case "german":
				case "deutsch":
				case "de":
					return "de";
				case "czech":
				case "čeština":
				case "cs":
					return "cs";
				}
			}
		}

		private static Dictionary<string, string[]> Load()
		{
			Dictionary<string, string[]> dictionary = new Dictionary<string, string[]>(StringComparer.Ordinal);
			using StreamReader streamReader = new StreamReader(Assembly.GetExecutingAssembly().GetManifestResourceStream("GaS.Translations.tsv"));
			string text;
			while ((text = streamReader.ReadLine()) != null)
			{
				if (text.Length != 0 && !text.StartsWith("#"))
				{
					string[] array = text.Split('\t');
					if (array.Length != 4)
					{
						throw new InvalidDataException("Invalid localization row: " + array[0]);
					}
					dictionary.Add(array[0], new string[3]
					{
						array[1],
						array[2],
						array[3]
					});
				}
			}
			return dictionary;
		}

		internal static string T(string source)
		{
			if (source == null)
			{
				return "";
			}
			if (!Catalog.TryGetValue(source, out var value))
			{
				return source;
			}
			return value[(Code == "cs") ? 1 : ((Code == "de") ? 2 : 0)];
		}

		internal static string F(string source, params object[] args)
		{
			return string.Format(CultureInfo.CurrentCulture, T(source), args);
		}

		internal static string Notice(string text)
		{
			if (string.IsNullOrEmpty(text))
			{
				return text;
			}
			foreach (KeyValuePair<string, string[]> item in Catalog)
			{
				string[] value = item.Value;
				foreach (string text2 in value)
				{
					if (text == text2)
					{
						return T(item.Key);
					}
					if (item.Key.EndsWith(": ", StringComparison.Ordinal) && text.StartsWith(text2, StringComparison.Ordinal))
					{
						return T(item.Key) + text.Substring(text2.Length);
					}
				}
			}
			return text;
		}
	}
	internal static class LanguageRuntime
	{
		private sealed class ConfigurationManagerAttributes
		{
			public string DispName;

			public string Category;

			public string Description;

			public Action<ConfigEntryBase> CustomDrawer;

			private bool expanded;

			public ConfigurationManagerAttributes(ConfigEntryBase entry)
			{
				DispName = Label(entry.Definition.Key);
				Category = Label(entry.Definition.Section);
				Description = Describe(entry.Description.Description);
				if (entry.SettingType.IsEnum && entry.Definition.Key == "Difficulty")
				{
					CustomDrawer = DrawDifficulty;
				}
			}

			private void DrawDifficulty(ConfigEntryBase entry)
			{
				GUILayout.BeginVertical(Array.Empty<GUILayoutOption>());
				if (GUILayout.Button(L.T(entry.BoxedValue.ToString()) + " ▾", Array.Empty<GUILayoutOption>()))
				{
					expanded = !expanded;
				}
				if (expanded)
				{
					foreach (object value in Enum.GetValues(entry.SettingType))
					{
						if (GUILayout.Button(L.T(value.ToString()), Array.Empty<GUILayoutOption>()))
						{
							entry.BoxedValue = value;
							expanded = false;
						}
					}
				}
				GUILayout.EndVertical();
			}
		}

		private static string language;

		private static readonly List<ConfigFile> files = new List<ConfigFile>();

		internal static void Register(ConfigFile file)
		{
			if (!files.Contains(file))
			{
				files.Add(file);
			}
		}

		internal static void Tick()
		{
			L.Language = () => (Localization.instance != null) ? Localization.instance.GetSelectedLanguage() : "English";
			if (language == L.Code)
			{
				return;
			}
			language = L.Code;
			foreach (ConfigFile file in files)
			{
				foreach (KeyValuePair<ConfigDefinition, ConfigEntryBase> item in file)
				{
					Attach(item.Value);
				}
			}
			foreach (PluginInfo value in Chainloader.PluginInfos.Values)
			{
				BaseUnityPlugin instance = value.Instance;
				if (!((Object)(object)instance == (Object)null) && !(((object)instance).GetType().FullName != "ConfigurationManager.ConfigurationManager"))
				{
					((object)instance).GetType().GetMethod("BuildSettingList", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.Invoke(instance, null);
				}
			}
		}

		private static void Attach(ConfigEntryBase entry)
		{
			object[] tags = entry.Description.Tags;
			tags = tags.Where((object t) => !(t is ConfigurationManagerAttributes)).ToArray();
			typeof(ConfigDescription).GetField("<Tags>k__BackingField", BindingFlags.Instance | BindingFlags.NonPublic)?.SetValue(entry.Description, tags.Concat(new object[1]
			{
				new ConfigurationManagerAttributes(entry)
			}).ToArray());
		}

		internal static string Label(string key)
		{
			if (key.StartsWith("Storage."))
			{
				return L.T("Storage") + ": " + L.T(key.Substring(8));
			}
			if (key.StartsWith("Allow") && key.Length > 5)
			{
				return L.T("Allow") + ": " + L.T(key.Substring(5));
			}
			if (key.StartsWith("Hotkey"))
			{
				return L.T("Hotkey") + " " + key.Substring(6);
			}
			return L.T(key);
		}

		internal static string Describe(string source)
		{
			if (source.StartsWith("Additionally allow restricted items of ") && source.EndsWith(" even before the trophy unlock. False does not revoke an altar unlock."))
			{
				return L.F("portal.allow.description", L.T(source.Substring("Additionally allow restricted items of ".Length, source.Length - "Additionally allow restricted items of ".Length - " even before the trophy unlock. False does not revoke an altar unlock.".Length)));
			}
			return L.T(source);
		}

		internal static string Creature(string prefab)
		{
			ZNetScene instance = ZNetScene.instance;
			object obj;
			if (instance == null)
			{
				obj = null;
			}
			else
			{
				GameObject prefab2 = instance.GetPrefab(prefab);
				obj = ((prefab2 == null) ? null : prefab2.GetComponent<Character>()?.m_name);
			}
			if (obj == null)
			{
				obj = prefab;
			}
			string text = (string)obj;
			if (Localization.instance != null)
			{
				return Localization.instance.Localize(text);
			}
			return text;
		}
	}
}
namespace GaSQuestSpawner
{
	internal static class Network
	{
		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		private static class Connection
		{
			private static void Prefix(ZNetPeer peer)
			{
				peer.m_rpc.Register<string>("GaSQuestSpawner.PolicyRequest", (Action<ZRpc, string>)delegate(ZRpc rpc, string version)
				{
					if (ZNet.instance.IsServer() && version == "1.0.7")
					{
						rpc.Invoke("GaSQuestSpawner.Policy", new object[1] { JsonConvert.SerializeObject((object)Settings.Local()) });
					}
				});
				peer.m_rpc.Register<string>("GaSQuestSpawner.Policy", (Action<ZRpc, string>)delegate(ZRpc rpc, string json)
				{
					if (ZNet.instance.IsServer() || rpc != ZNet.instance.GetServerRPC())
					{
						return;
					}
					try
					{
						if (json.Length <= 100000)
						{
							Settings settings = JsonConvert.DeserializeObject<Settings>(json);
							if (settings != null && settings.Valid())
							{
								settings.Families = new Dictionary<string, Family>(settings.Families, StringComparer.OrdinalIgnoreCase);
								settings.BiomeOverrides = new Dictionary<string, string>(settings.BiomeOverrides, StringComparer.OrdinalIgnoreCase);
								settings.LargeCreatures = new HashSet<string>(settings.LargeCreatures, StringComparer.OrdinalIgnoreCase);
								settings.AquaticCreatures = new HashSet<string>(settings.AquaticCreatures, StringComparer.OrdinalIgnoreCase);
								Remote = settings;
								Settings.ApplyServerView(settings);
								Plugin.Log.LogInfo((object)$"Quest zone server policy received: difficulty={settings.Difficulty}, radius={settings.Radius}, distance={settings.MinDistance}-{settings.MaxDistance}.");
							}
						}
					}
					catch (Exception ex)
					{
						Plugin.Log.LogWarning((object)("Zone policy rejected: " + ex.Message));
					}
				});
			}
		}

		private const string Request = "GaSQuestSpawner.PolicyRequest";

		private const string Response = "GaSQuestSpawner.Policy";

		internal static Settings Remote;

		private static ZRpc server;

		private static float nextRequest;

		private static string lastPolicyLog;

		internal static Settings Current
		{
			get
			{
				if ((Object)(object)ZNet.instance == (Object)null)
				{
					return null;
				}
				if (ZNet.instance.IsServer())
				{
					return Settings.Local();
				}
				ZRpc serverRPC = ZNet.instance.GetServerRPC();
				if (serverRPC != server)
				{
					Remote = null;
					server = serverRPC;
					nextRequest = 0f;
				}
				if (serverRPC != null && Time.realtimeSinceStartup >= nextRequest)
				{
					nextRequest = Time.realtimeSinceStartup + 5f;
					serverRPC.Invoke("GaSQuestSpawner.PolicyRequest", new object[1] { "1.0.7" });
				}
				if (Remote == null)
				{
					return null;
				}
				Settings.ApplyServerView(Remote);
				string text = $"server-authoritative difficulty={Remote.Difficulty}, radius={Remote.Radius}, distance={Remote.MinDistance}-{Remote.MaxDistance}";
				if (text != lastPolicyLog)
				{
					lastPolicyLog = text;
					Plugin.Log.LogInfo((object)("Quest zone policy: " + text + "."));
				}
				return new Settings
				{
					Enabled = Remote.Enabled,
					Difficulty = Rules.EffectiveDifficulty(Remote.Difficulty, Settings.DifficultyEntry.Value),
					Radius = Remote.Radius,
					MinDistance = Remote.MinDistance,
					MaxDistance = Remote.MaxDistance,
					Families = Remote.Families,
					BiomeOverrides = Remote.BiomeOverrides,
					LargeCreatures = Remote.LargeCreatures,
					AquaticCreatures = Remote.AquaticCreatures
				};
			}
		}
	}
	[BepInPlugin("mkova.GaSQuestSpawner", "GaS-QuestSpawner", "1.0.7")]
	[BepInDependency("mkova.GearAndStorage", "0.5.17")]
	public sealed class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(Player), "Save")]
		private static class Persist
		{
			private static void Prefix(Player __instance)
			{
				if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
				{
					Save();
				}
			}
		}

		public const string Id = "mkova.GaSQuestSpawner";

		public const string Version = "1.0.7";

		private const string SaveKey = "GaSQuestSpawner.state.v1";

		internal static ManualLogSource Log;

		internal static HuntState State;

		internal static Plugin Instance;

		private Harmony harmony;

		private Player loaded;

		private long world;

		private IEnumerator search;

		private float nextTick;

		private float nextSave;

		private string policyNotice;

		private void Awake()
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			Settings.Bind(((BaseUnityPlugin)this).Config);
			LanguageRuntime.Register(((BaseUnityPlugin)this).Config);
			LanguageRuntime.Tick();
			harmony = new Harmony("mkova.GaSQuestSpawner");
			harmony.PatchAll();
			QuestIntegration.ResolveKill += TaggedCreature.ResolveKill;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"GaS-QuestSpawner 1.0.7 loaded.");
		}

		private void Update()
		{
			LanguageRuntime.Tick();
			if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ZNet.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null || WorldGenerator.instance == null)
			{
				loaded = null;
				State = null;
				search = null;
				ZoneMap.Clear();
				return;
			}
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if (localPlayer.m_isLoading || !QuestIntegration.IsLoaded)
				{
					return;
				}
				if ((Object)(object)loaded != (Object)(object)localPlayer || world != ZNet.World.m_uid)
				{
					loaded = localPlayer;
					world = ZNet.World.m_uid;
					State = null;
					search = null;
					ZoneMap.Clear();
					if (localPlayer.m_customData.TryGetValue("GaSQuestSpawner.state.v1", out var value))
					{
						try
						{
							HuntState huntState = JsonConvert.DeserializeObject<HuntState>(value);
							if (huntState?.World == world && huntState.Owner == localPlayer.GetPlayerID() && huntState.Zones != null)
							{
								State = huntState;
							}
						}
						catch (Exception ex)
						{
							Log.LogWarning((object)("Cannot restore hunt zones: " + ex.Message));
						}
					}
				}
				if (search != null && !search.MoveNext())
				{
					search = null;
				}
				if (!(Time.realtimeSinceStartup < nextTick))
				{
					nextTick = Time.realtimeSinceStartup + 1f;
					Tick(localPlayer);
				}
			}
			catch (Exception ex2)
			{
				search = null;
				nextTick = Time.realtimeSinceStartup + 5f;
				Log.LogError((object)("Hunt zone update: " + ex2));
			}
		}

		private void Tick(Player player)
		{
			//IL_0155: 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_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0222: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0235: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_023c: 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_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b8: Invalid comparison between I4 and Unknown
			//IL_02c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c8: Expected I4, but got Unknown
			QuestDefinition active = QuestIntegration.Active;
			Settings current = Network.Current;
			if (active == null)
			{
				ClearState();
				return;
			}
			if (State != null && (State.Quest != active.ID || State.Attempt != QuestIntegration.AttemptID))
			{
				ClearState();
			}
			if (current == null)
			{
				if (policyNotice != active.ID)
				{
					Notice(L.F("zone.wait", "1.0.7"));
					policyNotice = active.ID;
				}
				ZoneMap.Clear();
				return;
			}
			if (!current.Enabled || ((Character)player).IsDead())
			{
				search = null;
				ZoneMap.Clear();
				ZNetView nview = ((Character)player).m_nview;
				if (nview != null)
				{
					ZDO zDO = nview.GetZDO();
					if (zDO != null)
					{
						zDO.Set("gqs.activeUntil", 0L);
					}
				}
				return;
			}
			if (active.KillReqs.Count == 0 || active.KillReqs.Any((QuestObjective o) => IsBoss(o.Prefab)))
			{
				ClearState();
				return;
			}
			if (State == null)
			{
				State = new HuntState
				{
					World = world,
					Owner = player.GetPlayerID(),
					Quest = active.ID,
					Attempt = QuestIntegration.AttemptID,
					OriginX = ((Component)player).transform.position.x,
					OriginZ = ((Component)player).transform.position.z
				};
				Save();
			}
			HuntZone[] array = State.Zones.ToArray();
			foreach (HuntZone zone in array)
			{
				QuestObjective val = ((IEnumerable<QuestObjective>)active.KillReqs).FirstOrDefault((Func<QuestObjective, bool>)((QuestObjective o) => o.Prefab == zone.Target));
				if (val == null || QuestIntegration.Count(val) >= val.Amount)
				{
					State.Zones.Remove(zone);
					Save();
					continue;
				}
				List<SpawnData> rules = SpawnRules(zone.Target);
				Biome val2 = AllowedBiomes(zone.Target, current, rules);
				Biome biome = WorldGenerator.instance.GetBiome(zone.Center);
				if ((int)val2 == 0 || (val2 & biome) == 0)
				{
					State.Zones.Remove(zone);
					State.RetryAt.Remove(zone.Target);
					Save();
					Notice(L.F("zone.relocated", LanguageRuntime.Creature(zone.Target)));
				}
				else if (zone.Biomes != (int)val2)
				{
					zone.Biomes = (int)val2;
					Save();
				}
			}
			if (search == null)
			{
				float value;
				QuestObjective val3 = ((IEnumerable<QuestObjective>)active.KillReqs).FirstOrDefault((Func<QuestObjective, bool>)((QuestObjective o) => QuestIntegration.Count(o) < o.Amount && !State.Zones.Any((HuntZone z) => z.Target == o.Prefab) && (!State.RetryAt.TryGetValue(o.Prefab, out value) || Time.realtimeSinceStartup >= value)));
				if (val3 != null)
				{
					search = FindZone(State, val3.Prefab, current, current.Radius, current.MinDistance, current.MaxDistance);
				}
			}
			((Character)player).m_nview.GetZDO().Set("gqs.activeZones", string.Join("|", State.Zones.Select((HuntZone z) => z.ID)));
			((Character)player).m_nview.GetZDO().Set("gqs.activeUntil", 0L);
			foreach (HuntZone zone2 in State.Zones)
			{
				SpawnController.Tick(player, zone2, current);
			}
			ZoneMap.Update(State.Zones, (active.ID == "gearandstorage_first_supplies" && active.Title == "První zásoby") ? L.T(active.Title) : active.Title);
			if (Time.realtimeSinceStartup >= nextSave)
			{
				nextSave = Time.realtimeSinceStartup + 5f;
				Save();
			}
		}

		internal static bool IsBoss(string prefab)
		{
			GameObject prefab2 = ZNetScene.instance.GetPrefab(prefab);
			if (prefab2 == null)
			{
				return false;
			}
			return prefab2.GetComponent<Character>()?.m_boss == true;
		}

		internal static List<SpawnData> SpawnRules(string prefab)
		{
			return (from s in (from s in SpawnSystem.m_instances.SelectMany((SpawnSystem s) => s.m_spawnLists)
					where (Object)(object)s != (Object)null
					select s).SelectMany((SpawnSystemList s) => s.m_spawners)
				where s.m_enabled && !s.m_devDisabled && (Object)(object)s.m_prefab != (Object)null && ((Object)s.m_prefab).name == prefab
				select s).ToList();
		}

		internal static Biome AllowedBiomes(string prefab, Settings policy, List<SpawnData> rules)
		{
			//IL_0018: 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_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			if (policy.BiomeOverrides.TryGetValue(prefab, out var value))
			{
				return Settings.ParseBiomes(value);
			}
			Biome val = (Biome)0;
			foreach (SpawnData rule in rules)
			{
				val |= rule.m_biome;
			}
			return val;
		}

		private IEnumerator FindZone(HuntState state, string target, Settings policy, float radius, float minDistance, float maxDistance)
		{
			List<SpawnData> rules = SpawnRules(target);
			if (rules.Count == 0)
			{
				state.RetryAt[target] = Time.realtimeSinceStartup + 60f;
				Notice(L.F("zone.rules", LanguageRuntime.Creature(target)));
				yield break;
			}
			Biome allowedBiomes = AllowedBiomes(target, policy, rules);
			if ((int)allowedBiomes == 0)
			{
				state.RetryAt[target] = Time.realtimeSinceStartup + 60f;
				Notice(L.F("zone.rules", LanguageRuntime.Creature(target)));
				yield break;
			}
			bool aquatic = policy.AquaticCreatures.Contains(target) || rules.Any(IsWaterRule);
			Log.LogInfo((object)$"Quest zone search: target={target}, distance={minDistance:0.#}-{maxDistance:0.#}m, biomes={allowedBiomes}, aquatic={aquatic}.");
			int budget = 0;
			double phase = (double)(uint)StringExtensionMethods.GetStableHashCode(target) / 4294967295.0 * Math.PI * 2.0;
			Vector3 val = default(Vector3);
			foreach (var item in Rules.Candidates(minDistance, maxDistance, phase))
			{
				if (state != State || QuestIntegration.AttemptID != state.Attempt)
				{
					yield break;
				}
				((Vector3)(ref val))..ctor(state.OriginX + item.x, 0f, state.OriginZ + item.z);
				if (SuitableSeed(val, rules, allowedBiomes, aquatic))
				{
					val.y = WorldGenerator.instance.GetHeight(val);
					int num = 0;
					for (int i = 0; i < 8; i++)
					{
						float num2 = (float)i * (float)Math.PI / 4f;
						if (SuitableSeed(val + new Vector3(Mathf.Cos(num2), 0f, Mathf.Sin(num2)) * radius * 0.6f, rules, allowedBiomes, aquatic))
						{
							num++;
						}
					}
					if (num >= (aquatic ? 2 : 5))
					{
						state.Zones.Add(new HuntZone
						{
							ID = Guid.NewGuid().ToString("N"),
							Target = target,
							X = val.x,
							Y = val.y,
							Z = val.z,
							Radius = radius,
							Biomes = (int)allowedBiomes
						});
						Save();
						Notice(L.F("zone.found", LanguageRuntime.Creature(target)));
						yield break;
					}
				}
				int num3 = budget + 1;
				budget = num3;
				if (num3 >= 48)
				{
					budget = 0;
					yield return null;
				}
			}
			state.RetryAt[target] = Time.realtimeSinceStartup + 60f;
			Notice(L.F("zone.missing", LanguageRuntime.Creature(target), Mathf.RoundToInt(minDistance), Mathf.RoundToInt(maxDistance)));
		}

		internal static bool IsWaterRule(SpawnData rule)
		{
			if (rule != null && Math.Abs(rule.m_minOceanDepth - rule.m_maxOceanDepth) > 0.001f)
			{
				return rule.m_maxOceanDepth > 0f;
			}
			return false;
		}

		private static bool SuitableSeed(Vector3 pos, List<SpawnData> rules, Biome allowedBiomes, bool aquatic)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: 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_0057: 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_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: 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_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: 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_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: 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)
			WorldGenerator instance = WorldGenerator.instance;
			Biome biome = instance.GetBiome(pos);
			float height = instance.GetHeight(pos);
			float altitude = height - ZoneSystem.instance.m_waterLevel;
			float waterDepth = Mathf.Max(0f, ZoneSystem.instance.m_waterLevel - height);
			Vector2 val = new Vector2(pos.x, pos.z);
			float distance = ((Vector2)(ref val)).magnitude;
			bool forest = WorldGenerator.InForest(pos);
			BiomeArea area = instance.GetBiomeArea(pos);
			if ((allowedBiomes & biome) == 0 || !rules.Any(delegate(SpawnData r)
			{
				//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_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_001e: Unknown result type (might be due to invalid IL or missing references)
				if ((r.m_biome & biome) != 0 && (r.m_biomeArea & area) != 0 && altitude >= r.m_minAltitude && altitude <= r.m_maxAltitude && (forest ? r.m_inForest : r.m_outsideForest) && (r.m_minDistanceFromCenter <= 0f || distance >= r.m_minDistanceFromCenter) && (r.m_maxDistanceFromCenter <= 0f || distance <= r.m_maxDistanceFromCenter))
				{
					if (!aquatic)
					{
						return altitude >= 0.5f;
					}
					if (altitude < 0.5f)
					{
						if (IsWaterRule(r))
						{
							if (waterDepth >= r.m_minOceanDepth)
							{
								return waterDepth <= r.m_maxOceanDepth;
							}
							return false;
						}
						return true;
					}
					return false;
				}
				return false;
			}))
			{
				return false;
			}
			if ((Object)(object)EffectArea.IsPointInsideArea(new Vector3(pos.x, height, pos.z), (Type)4, 30f) != (Object)null)
			{
				return false;
			}
			if (Mathf.Abs(instance.GetHeight(pos + Vector3.right * 8f) - height) < 7f)
			{
				return Mathf.Abs(instance.GetHeight(pos + Vector3.forward * 8f) - height) < 7f;
			}
			return false;
		}

		internal static void Notice(string text)
		{
			Log.LogInfo((object)text);
			Player localPlayer = Player.m_localPlayer;
			if (localPlayer != null)
			{
				((Character)localPlayer).Message((MessageType)2, text, 0, (Sprite)null, false);
			}
		}

		internal static void Save()
		{
			if ((Object)(object)Instance?.loaded != (Object)null && State != null)
			{
				Instance.loaded.m_customData["GaSQuestSpawner.state.v1"] = JsonConvert.SerializeObject((object)State);
			}
		}

		private void ClearState()
		{
			search = null;
			State = null;
			loaded?.m_customData.Remove("GaSQuestSpawner.state.v1");
			Player obj = loaded;
			if (obj != null)
			{
				ZNetView nview = ((Character)obj).m_nview;
				if (nview != null)
				{
					ZDO zDO = nview.GetZDO();
					if (zDO != null)
					{
						zDO.Set("gqs.activeUntil", 0L);
					}
				}
			}
			ZoneMap.Clear();
		}

		private void OnDestroy()
		{
			QuestIntegration.ResolveKill -= TaggedCreature.ResolveKill;
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
			ZoneMap.Clear();
			Instance = null;
		}
	}
	internal sealed class HuntState
	{
		public long World;

		public long Owner;

		public string Quest;

		public string Attempt;

		public float OriginX;

		public float OriginZ;

		public List<HuntZone> Zones = new List<HuntZone>();

		[JsonIgnore]
		public Dictionary<string, float> RetryAt = new Dictionary<string, float>();
	}
	internal sealed class HuntZone
	{
		public string ID;

		public string Target;

		public float X;

		public float Y;

		public float Z;

		public float Radius;

		public int Biomes;

		public bool BossSpawned;

		public List<SpawnRecord> Creatures = new List<SpawnRecord>();

		[JsonIgnore]
		public float NextSpawn;

		[JsonIgnore]
		public float EnteredAt;

		[JsonIgnore]
		public float LastBlockedNotice;

		[JsonIgnore]
		public bool Inside;

		[JsonIgnore]
		public Vector3 Center => new Vector3(X, Y, Z);

		internal bool Contains(Vector3 p)
		{
			//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)
			return Rules.Inside(p.x, p.z, X, Z, Radius);
		}
	}
	internal sealed class SpawnRecord
	{
		public string Token;

		public long Expires;

		public bool Boss;

		public bool Large;
	}
	internal sealed class Settings
	{
		private static bool applyingServerView;

		public bool Enabled = true;

		public Difficulty Difficulty = Difficulty.Normal;

		public float Radius = 70f;

		public float MinDistance = 500f;

		public float MaxDistance = 1000f;

		public Dictionary<string, Family> Families = new Dictionary<string, Family>(StringComparer.OrdinalIgnoreCase);

		public Dictionary<string, string> BiomeOverrides = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		public HashSet<string> LargeCreatures = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		public HashSet<string> AquaticCreatures = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static ConfigEntry<bool> EnabledEntry;

		internal static ConfigEntry<Difficulty> DifficultyEntry;

		internal static ConfigEntry<float> RadiusEntry;

		internal static ConfigEntry<float> MinDistanceEntry;

		internal static ConfigEntry<float> MaxDistanceEntry;

		internal static ConfigEntry<string> FamiliesEntry;

		internal static ConfigEntry<string> BiomeOverridesEntry;

		internal static ConfigEntry<string> LargeCreaturesEntry;

		internal static ConfigEntry<string> AquaticCreaturesEntry;

		internal static void Bind(ConfigFile file)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Expected O, but got Unknown
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Expected O, but got Unknown
			EnabledEntry = file.Bind<bool>("General", "Enabled", true, "Enable personal quest hunting zones. Server controls this setting in multiplayer.");
			DifficultyEntry = file.Bind<Difficulty>("General", "Difficulty", Difficulty.Normal, "Easy: basic mobs with 0 stars. Normal: 0-1 stars. Hard: 1-2 stars. VeryHard: 2-4 stars, mostly elites and one miniboss per zone. In multiplayer every gameplay setting is controlled by the server. Quest rewards never change.");
			RadiusEntry = file.Bind<float>("General", "ZoneRadius", 70f, new ConfigDescription("Hunting circle radius in metres. New zones only. Configure the search distance separately.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(40f, 100f), Array.Empty<object>()));
			MinDistanceEntry = file.Bind<float>("General", "SpawnDistanceMin", 500f, new ConfigDescription("Minimum distance in metres from quest acceptance to a newly selected hunting zone.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(50f, 5000f), Array.Empty<object>()));
			MaxDistanceEntry = file.Bind<float>("General", "SpawnDistanceMax", 1000f, new ConfigDescription("Maximum distance in metres from quest acceptance to a newly selected hunting zone. Must be at least SpawnDistanceMin.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(100f, 10000f), Array.Empty<object>()));
			Dictionary<string, Family> dictionary = new Dictionary<string, Family>
			{
				["Greyling"] = new Family("Greydwarf_Shaman", "Greydwarf_Elite", "Greydwarf_Elite"),
				["Greydwarf"] = new Family("Greydwarf_Shaman", "Greydwarf_Elite", "Greydwarf_Elite"),
				["Greydwarf_Shaman"] = new Family("Greydwarf_Elite", "Troll", "Greydwarf_Elite"),
				["Greydwarf_Elite"] = new Family("Greydwarf_Shaman", "Troll", "Greydwarf_Elite"),
				["Skeleton"] = new Family("Skeleton_Poison", "Skeleton_Poison", "Skeleton_Poison"),
				["Draugr"] = new Family("Draugr_Ranged", "Draugr_Elite", "Draugr_Elite"),
				["Goblin"] = new Family("GoblinShaman", "GoblinBrute", "GoblinBrute"),
				["Wolf"] = new Family("Wolf", "Fenring", "Fenring"),
				["Seeker"] = new Family("Seeker", "SeekerBrute", "SeekerBrute")
			};
			FamiliesEntry = file.Bind<string>("Creatures", "FamiliesJson", JsonConvert.SerializeObject((object)dictionary), "Target prefab -> Strong, Large and Boss prefab. Missing family uses stronger target variants. Boss means a named ordinary creature; true game bosses are always rejected. Stars follow difficulty. Restart after editing configuration files.");
			BiomeOverridesEntry = file.Bind<string>("Creatures", "BiomeOverridesJson", JsonConvert.SerializeObject((object)BiomeCatalog.Defaults()), "Target prefab -> allowed Valheim biome names. Vanilla defaults are strict. Separate multiple legitimate biomes with commas. Creatures not listed use their loaded natural SpawnSystem rules. Existing zones are checked and relocated after a change. Restart after editing configuration files.");
			LargeCreaturesEntry = file.Bind<string>("Creatures", "LargeCreatures", "Troll,Abomination,StoneGolem,Lox,SeekerBrute,Gjall,Morgen_NonSleeping,FallenValkyrie,TrollFrost,JotunWarrior", "Comma-separated prefab names that use reduced spawn caps, smaller batches and longer intervals. Applies both to quest targets and stronger family variants.");
			AquaticCreaturesEntry = file.Bind<string>("Creatures", "AquaticCreatures", string.Join(",", BiomeCatalog.AquaticDefaults()), "Comma-separated aquatic target prefabs. Their zones search water inside the allowed biome instead of rejecting submerged terrain. Native water-depth rules still apply.");
			file.SettingChanged += delegate
			{
				if (!applyingServerView && Network.Remote != null && (Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer())
				{
					ApplyServerView(Network.Remote);
				}
			};
		}

		internal static void ApplyServerView(Settings server)
		{
			if (server == null || applyingServerView)
			{
				return;
			}
			applyingServerView = true;
			try
			{
				EnabledEntry.Value = server.Enabled;
				DifficultyEntry.Value = server.Difficulty;
				RadiusEntry.Value = server.Radius;
				MinDistanceEntry.Value = server.MinDistance;
				MaxDistanceEntry.Value = server.MaxDistance;
				FamiliesEntry.Value = JsonConvert.SerializeObject((object)server.Families);
				BiomeOverridesEntry.Value = JsonConvert.SerializeObject((object)server.BiomeOverrides);
				LargeCreaturesEntry.Value = string.Join(",", server.LargeCreatures.OrderBy<string, string>((string x) => x, StringComparer.OrdinalIgnoreCase));
				AquaticCreaturesEntry.Value = string.Join(",", server.AquaticCreatures.OrderBy<string, string>((string x) => x, StringComparer.OrdinalIgnoreCase));
			}
			finally
			{
				applyingServerView = false;
			}
		}

		internal static Settings Local()
		{
			Dictionary<string, Family> dictionary = JsonConvert.DeserializeObject<Dictionary<string, Family>>(FamiliesEntry.Value);
			Dictionary<string, string> dictionary2 = JsonConvert.DeserializeObject<Dictionary<string, string>>(BiomeOverridesEntry.Value);
			HashSet<string> largeCreatures = new HashSet<string>(from x in (LargeCreaturesEntry.Value ?? "").Split(',')
				select x.Trim() into x
				where x.Length > 0
				select x, StringComparer.OrdinalIgnoreCase);
			HashSet<string> aquaticCreatures = new HashSet<string>(from x in (AquaticCreaturesEntry.Value ?? "").Split(',')
				select x.Trim() into x
				where x.Length > 0
				select x, StringComparer.OrdinalIgnoreCase);
			float value = MinDistanceEntry.Value;
			float value2 = MaxDistanceEntry.Value;
			return new Settings
			{
				Enabled = EnabledEntry.Value,
				Difficulty = DifficultyEntry.Value,
				Radius = RadiusEntry.Value,
				MinDistance = Math.Min(value, value2),
				MaxDistance = Math.Max(value, value2),
				Families = new Dictionary<string, Family>(dictionary ?? new Dictionary<string, Family>(), StringComparer.OrdinalIgnoreCase),
				BiomeOverrides = new Dictionary<string, string>(dictionary2 ?? new Dictionary<string, string>(), StringComparer.OrdinalIgnoreCase),
				LargeCreatures = largeCreatures,
				AquaticCreatures = aquaticCreatures
			};
		}

		internal bool Valid()
		{
			if (Enum.IsDefined(typeof(Difficulty), Difficulty) && Radius >= 40f && Radius <= 100f && MinDistance >= 50f && MaxDistance >= MinDistance && MaxDistance <= 10000f && Families != null && Families.Count <= 500 && BiomeOverrides != null && BiomeOverrides.Count <= 1000 && BiomeOverrides.All((KeyValuePair<string, string> x) => !string.IsNullOrWhiteSpace(x.Key) && (int)ParseBiomes(x.Value) > 0) && LargeCreatures != null && LargeCreatures.Count <= 500 && AquaticCreatures != null)
			{
				return AquaticCreatures.Count <= 500;
			}
			return false;
		}

		internal static Biome ParseBiomes(string text)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			Biome val = (Biome)0;
			string[] array = (text ?? "").Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				if (!Enum.TryParse<Biome>(array[i].Trim(), ignoreCase: true, out Biome result) || !Enum.IsDefined(typeof(Biome), result))
				{
					return (Biome)0;
				}
				val |= result;
			}
			return val;
		}
	}
	internal sealed class Family
	{
		public string Strong;

		public string Large;

		public string Boss;

		public Family()
		{
		}

		public Family(string strong, string large, string boss)
		{
			Strong = strong;
			Large = large;
			Boss = boss;
		}
	}
	internal static class SpawnController
	{
		internal static void Tick(Player player, HuntZone zone, Settings policy)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_027c: Unknown result type (might be due to invalid IL or missing references)
			//IL_027e: 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_0421: Unknown result type (might be due to invalid IL or missing references)
			//IL_0432: Unknown result type (might be due to invalid IL or missing references)
			//IL_0437: 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_0441: Unknown result type (might be due to invalid IL or missing references)
			//IL_044d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0454: Unknown result type (might be due to invalid IL or missing references)
			//IL_0459: 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_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_0485: Unknown result type (might be due to invalid IL or missing references)
			//IL_048a: Unknown result type (might be due to invalid IL or missing references)
			//IL_048c: Unknown result type (might be due to invalid IL or missing references)
			//IL_058f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0594: Unknown result type (might be due to invalid IL or missing references)
			//IL_059e: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_05be: Unknown result type (might be due to invalid IL or missing references)
			//IL_0494: Unknown result type (might be due to invalid IL or missing references)
			//IL_069b: Unknown result type (might be due to invalid IL or missing references)
			//IL_04db: Unknown result type (might be due to invalid IL or missing references)
			//IL_050e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0528: Unknown result type (might be due to invalid IL or missing references)
			//IL_052d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0537: Unknown result type (might be due to invalid IL or missing references)
			//IL_053c: Unknown result type (might be due to invalid IL or missing references)
			bool flag = zone.Contains(((Component)player).transform.position) && !((Character)player).IsTeleporting() && !((Character)player).InCutscene();
			if (flag && !zone.Inside)
			{
				zone.EnteredAt = Time.realtimeSinceStartup;
			}
			zone.Inside = flag;
			if (!flag || Time.realtimeSinceStartup - zone.EnteredAt < 5f)
			{
				return;
			}
			long now = ZNet.instance.GetTime().Ticks;
			foreach (Character allCharacter in Character.GetAllCharacters())
			{
				ZNetView nview = allCharacter.m_nview;
				ZDO val = ((nview != null) ? nview.GetZDO() : null);
				if (val != null && !(val.GetString("gqs.zone", "") != zone.ID) && !allCharacter.IsDead())
				{
					string token = val.GetString("gqs.token", "");
					SpawnRecord spawnRecord = zone.Creatures.FirstOrDefault((SpawnRecord r) => r.Token == token);
					if (spawnRecord != null)
					{
						spawnRecord.Expires = now + TimeSpan.FromSeconds(90.0).Ticks;
					}
					if (allCharacter.m_nview.IsOwner())
					{
						val.Set("gqs.expires", now + TimeSpan.FromSeconds(60.0).Ticks);
					}
				}
			}
			zone.Creatures.RemoveAll((SpawnRecord r) => r.Expires < now);
			((Character)player).m_nview.GetZDO().Set("gqs.activeAttempt", Plugin.State.Attempt);
			((Character)player).m_nview.GetZDO().Set("gqs.activeUntil", now + TimeSpan.FromSeconds(4.0).Ticks);
			if (Time.realtimeSinceStartup < zone.NextSpawn)
			{
				return;
			}
			bool large = policy.LargeCreatures.Contains(zone.Target);
			zone.NextSpawn = Time.realtimeSinceStartup + Rules.Interval(policy.Difficulty, large);
			List<SpawnData> list = Plugin.SpawnRules(zone.Target);
			if (list.Count == 0)
			{
				return;
			}
			bool flag2 = policy.AquaticCreatures.Contains(zone.Target) || list.Any(Plugin.IsWaterRule);
			Biome val2 = (Biome)zone.Biomes;
			if ((int)val2 == 0)
			{
				return;
			}
			SpawnSystem spawnSystem = SpawnSystem.m_instances.FirstOrDefault();
			if ((Object)(object)spawnSystem == (Object)null)
			{
				return;
			}
			MethodInfo clone = AccessTools.Method(typeof(object), "MemberwiseClone", (Type[])null, (Type[])null);
			list = ((IEnumerable<SpawnData>)list).Select((Func<SpawnData, SpawnData>)((SpawnData r) => (SpawnData)clone.Invoke(r, null))).ToList();
			foreach (SpawnData item in list)
			{
				item.m_canSpawnCloseToPlayer = true;
			}
			int num = Rules.Limit(policy.Difficulty, large);
			int num2 = Math.Max(0, Math.Min(Rules.Batch(policy.Difficulty, large), num - zone.Creatures.Count));
			int num3 = zone.Creatures.Count((SpawnRecord r) => r.Large);
			int num4 = 0;
			int num5 = 0;
			for (int num6 = 0; num6 < num2; num6++)
			{
				Role role = ((policy.Difficulty == Difficulty.VeryHard && !zone.BossSpawned) ? Role.Boss : Rules.Pick(policy.Difficulty, Random.value));
				GameObject val3 = SelectPrefab(zone.Target, role, policy);
				if ((Object)(object)val3 == (Object)null)
				{
					continue;
				}
				bool flag3 = policy.LargeCreatures.Contains(((Object)val3).name);
				if (flag3 && (num3 + num4 >= Rules.Limit(policy.Difficulty, large: true) || num4 >= Rules.Batch(policy.Difficulty, large: true)))
				{
					continue;
				}
				bool flag4 = false;
				Vector3 position = default(Vector3);
				for (int num7 = 0; num7 < 32; num7++)
				{
					Vector2 val4 = Random.insideUnitCircle * (zone.Radius - 5f);
					position = zone.Center + new Vector3(val4.x, 0f, val4.y);
					if (!Player.IsPlayerInRange(position, 15f) && (WorldGenerator.instance.GetBiome(position) & val2) != 0 && !((Object)(object)Heightmap.FindHeightmap(position) == (Object)null) && list.Any((SpawnData rule) => spawnSystem.IsSpawnPointGood(rule, ref position)) && zone.Contains(position) && (flag2 || !(position.y < ZoneSystem.instance.m_waterLevel + 0.5f)) && !((Object)(object)EffectArea.IsPointInsideArea(position, (Type)4, 30f) != (Object)null) && !Physics.CheckSphere(position + Vector3.up * 1.5f, 1f, LayerMask.GetMask(new string[3] { "piece", "static_solid", "Default" })))
					{
						flag4 = true;
						break;
					}
				}
				if (!flag4)
				{
					continue;
				}
				GameObject val5 = Object.Instantiate<GameObject>(val3, position + Vector3.up * 0.3f, Quaternion.Euler(0f, (float)Random.Range(0, 360), 0f));
				Character component = val5.GetComponent<Character>();
				ZNetView component2 = val5.GetComponent<ZNetView>();
				if ((Object)(object)component2 == (Object)null || !component2.IsValid() || (Object)(object)component == (Object)null)
				{
					Object.Destroy((Object)(object)val5);
					continue;
				}
				string text = Guid.NewGuid().ToString("N");
				ZDO zDO = component2.GetZDO();
				zDO.Set("gqs.zone", zone.ID);
				zDO.Set("gqs.token", text);
				zDO.Set("gqs.owner", player.GetPlayerID());
				zDO.Set("gqs.quest", Plugin.State.Quest);
				zDO.Set("gqs.attempt", Plugin.State.Attempt);
				zDO.Set("gqs.target", zone.Target);
				zDO.Set("gqs.center", zone.Center);
				zDO.Set("gqs.radius", zone.Radius);
				zDO.Set("gqs.expires", now + TimeSpan.FromSeconds(60.0).Ticks);
				zDO.Set("gqs.boss", role == Role.Boss);
				component.SetLevel(Rules.Stars(policy.Difficulty, Random.value) + 1);
				if (role == Role.Boss)
				{
					zone.BossSpawned = true;
					zDO.Set("gqs.name", "Vůdce loviště: " + zone.Target);
				}
				BaseAI component3 = val5.GetComponent<BaseAI>();
				if (component3 != null)
				{
					component3.Alert();
				}
				zone.Creatures.Add(new SpawnRecord
				{
					Token = text,
					Expires = now + TimeSpan.FromSeconds(90.0).Ticks,
					Boss = (role == Role.Boss),
					Large = flag3
				});
				if (flag3)
				{
					num4++;
				}
				num5++;
			}
			if (num5 > 0)
			{
				Plugin.Save();
			}
			else if (num2 > 0 && Time.realtimeSinceStartup > zone.LastBlockedNotice + 60f)
			{
				zone.LastBlockedNotice = Time.realtimeSinceStartup;
				Plugin.Notice(L.F("zone.blocked", LanguageRuntime.Creature(zone.Target)));
			}
		}

		private static GameObject SelectPrefab(string target, Role role, Settings settings)
		{
			settings.Families.TryGetValue(target, out var value);
			string text = role switch
			{
				Role.Boss => value?.Boss, 
				Role.Large => value?.Large, 
				Role.Strong => value?.Strong, 
				_ => target, 
			};
			if (string.IsNullOrWhiteSpace(text))
			{
				text = target;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab(text);
			if ((Object)(object)((prefab != null) ? prefab.GetComponent<Character>() : null) == (Object)null || prefab.GetComponent<Character>().m_boss)
			{
				text = target;
				prefab = ZNetScene.instance.GetPrefab(target);
			}
			if (prefab == null || prefab.GetComponent<Character>()?.m_boss != false)
			{
				return null;
			}
			return prefab;
		}
	}
	internal sealed class TaggedCreature : MonoBehaviour
	{
		[HarmonyPatch(typeof(Character), "Awake")]
		private static class Attach
		{
			private static void Postfix(Character __instance)
			{
				if (!(__instance is Player))
				{
					((Component)__instance).gameObject.AddComponent<TaggedCreature>();
				}
			}
		}

		[HarmonyPatch(typeof(Character), "OnDeath")]
		private static class Death
		{
			private static void Prefix(Character __instance)
			{
				ZNetView nview = __instance.m_nview;
				ZDO val = ((nview != null) ? nview.GetZDO() : null);
				if (val == null || Plugin.State == null)
				{
					return;
				}
				string zoneID = val.GetString("gqs.zone", "");
				string token = val.GetString("gqs.token", "");
				HuntZone huntZone = Plugin.State.Zones.FirstOrDefault((HuntZone z) => z.ID == zoneID);
				if (huntZone != null)
				{
					huntZone.Creatures.RemoveAll((SpawnRecord c) => c.Token == token);
					Plugin.Save();
				}
			}
		}

		internal const string Zone = "gqs.zone";

		internal const string Token = "gqs.token";

		internal const string Owner = "gqs.owner";

		internal const string Quest = "gqs.quest";

		internal const string Attempt = "gqs.attempt";

		internal const string Target = "gqs.target";

		internal const string Center = "gqs.center";

		internal const string Radius = "gqs.radius";

		internal const string Expires = "gqs.expires";

		internal const string Boss = "gqs.boss";

		internal const string Name = "gqs.name";

		internal const string ActiveAttempt = "gqs.activeAttempt";

		internal const string ActiveUntil = "gqs.activeUntil";

		internal const string ActiveZones = "gqs.activeZones";

		private Character character;

		private void Awake()
		{
			character = ((Component)this).GetComponent<Character>();
			((MonoBehaviour)this).InvokeRepeating("Check", 1f, 1f);
		}

		private void Check()
		{
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_022e: 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_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_024f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			ZNetView val = character?.m_nview;
			if ((Object)(object)val == (Object)null || !val.IsValid() || (Object)(object)ZNet.instance == (Object)null)
			{
				return;
			}
			ZDO zDO = val.GetZDO();
			if (string.IsNullOrEmpty(zDO.GetString("gqs.zone", "")))
			{
				return;
			}
			string text = zDO.GetString("gqs.name", "");
			if (!string.IsNullOrEmpty(text))
			{
				character.m_name = (zDO.GetBool("gqs.boss", false) ? L.F("zone.boss", LanguageRuntime.Creature(zDO.GetString("gqs.target", ""))) : text);
			}
			if (val.IsOwner() && !character.IsDead())
			{
				long ticks = ZNet.instance.GetTime().Ticks;
				Player player = Player.GetPlayer(zDO.GetLong("gqs.owner", 0L));
				object obj;
				if (player == null)
				{
					obj = null;
				}
				else
				{
					ZNetView nview = ((Character)player).m_nview;
					obj = ((nview != null) ? nview.GetZDO() : null);
				}
				ZDO val2 = (ZDO)obj;
				Vector3 vec = zDO.GetVec3("gqs.center", Vector3.zero);
				float num = zDO.GetFloat("gqs.radius", 0f);
				if ((Object)(object)player != (Object)null && !((Character)player).IsDead() && val2 != null && val2.GetString("gqs.activeAttempt", "") == zDO.GetString("gqs.attempt", "") && val2.GetLong("gqs.activeUntil", 0L) >= ticks && val2.GetString("gqs.activeZones", "").Split('|').Contains(zDO.GetString("gqs.zone", "")) && Rules.Inside(((Component)player).transform.position.x, ((Component)player).transform.position.z, vec.x, vec.z, num))
				{
					zDO.Set("gqs.expires", ticks + TimeSpan.FromSeconds(60.0).Ticks);
				}
				if (zDO.GetLong("gqs.expires", 0L) < ticks || !Rules.Inside(((Component)this).transform.position.x, ((Component)this).transform.position.z, vec.x, vec.z, num + 25f))
				{
					ZNetScene.instance.Destroy(((Component)this).gameObject);
				}
			}
		}

		internal static KillCredit ResolveKill(ZDO victim, ZDOID attacker)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			//IL_002b: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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_0083: 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_0091: 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_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: 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_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Expected O, but got Unknown
			if (string.IsNullOrEmpty(victim.GetString("gqs.zone", "")))
			{
				return null;
			}
			KillCredit result = new KillCredit
			{
				Suppress = true
			};
			ZDO zDO = ZDOMan.instance.GetZDO(attacker);
			if (zDO == null)
			{
				return result;
			}
			Vector3 vec = victim.GetVec3("gqs.center", Vector3.zero);
			float radius = victim.GetFloat("gqs.radius", 0f);
			Vector3 position = victim.GetPosition();
			Vector3 position2 = zDO.GetPosition();
			if (!Rules.CanCredit(victim.GetLong("gqs.owner", 0L), zDO.GetLong(ZDOVars.s_playerID, 0L), Rules.Inside(position.x, position.z, vec.x, vec.z, radius), Rules.Inside(position2.x, position2.z, vec.x, vec.z, radius)))
			{
				return result;
			}
			return new KillCredit
			{
				QuestID = victim.GetString("gqs.quest", ""),
				AttemptID = victim.GetString("gqs.attempt", ""),
				Prefab = victim.GetString("gqs.target", ""),
				RecipientPlayerID = victim.GetLong("gqs.owner", 0L),
				CreditKiller = true
			};
		}
	}
	internal static class ZoneMap
	{
		private static readonly Dictionary<string, PinData> pins = new Dictionary<string, PinData>();

		private static Minimap map;

		private static Sprite circle;

		internal static void Update(List<HuntZone> zones, string title)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			//IL_010e: 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_0078: 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_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_0207: Unknown result type (might be due to invalid IL or missing references)
			//IL_0216: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Minimap.instance == (Object)null)
			{
				return;
			}
			if ((Object)(object)map != (Object)(object)Minimap.instance)
			{
				Clear();
				map = Minimap.instance;
			}
			if ((Object)(object)circle == (Object)null)
			{
				Texture2D val = new Texture2D(128, 128, (TextureFormat)4, false);
				for (int i = 0; i < 128; i++)
				{
					for (int j = 0; j < 128; j++)
					{
						float num = Vector2.Distance(new Vector2((float)j + 0.5f, (float)i + 0.5f), new Vector2(64f, 64f));
						float num2 = ((num > 63f) ? 0f : ((num > 59f) ? 0.9f : 0.12f));
						val.SetPixel(j, i, new Color(1f, 0.08f, 0.04f, num2));
					}
				}
				val.Apply();
				circle = Sprite.Create(val, new Rect(0f, 0f, 128f, 128f), new Vector2(0.5f, 0.5f));
			}
			string[] array = pins.Keys.Where((string k) => !zones.Any((HuntZone z) => z.ID == k)).ToArray();
			foreach (string key in array)
			{
				map.RemovePin(pins[key]);
				pins.Remove(key);
			}
			foreach (HuntZone zone in zones)
			{
				string text = L.F("zone.pin", title, LanguageRuntime.Creature(zone.Target));
				if (pins.TryGetValue(zone.ID, out var value))
				{
					value.m_name = text;
					continue;
				}
				PinData val2 = map.AddPin(zone.Center, (PinType)13, text, false, false, 0L, default(PlatformUserID));
				val2.m_icon = circle;
				val2.m_worldSize = zone.Radius * 2f;
				pins.Add(zone.ID, val2);
			}
		}

		internal static void Clear()
		{
			if ((Object)(object)map != (Object)null)
			{
				foreach (PinData value in pins.Values)
				{
					map.RemovePin(value);
				}
			}
			pins.Clear();
			map = null;
		}
	}
}
namespace GaSQuestSpawner.Core
{
	public static class BiomeCatalog
	{
		public static string[] AquaticDefaults()
		{
			return new string[3] { "Leech", "Serpent", "BonemawSerpent" };
		}

		public static Dictionary<string, string> Defaults()
		{
			return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
			{
				["Greyling"] = "Meadows",
				["Boar"] = "Meadows",
				["Deer"] = "Meadows",
				["Neck"] = "Meadows",
				["Greydwarf"] = "BlackForest",
				["Greydwarf_Elite"] = "BlackForest",
				["Greydwarf_Shaman"] = "BlackForest",
				["Troll"] = "BlackForest",
				["Ghost"] = "BlackForest",
				["Bjorn"] = "BlackForest",
				["Draugr"] = "Swamp",
				["Draugr_Ranged"] = "Swamp",
				["Draugr_Elite"] = "Swamp",
				["Blob"] = "Swamp",
				["BlobElite"] = "Swamp",
				["Leech"] = "Swamp",
				["Wraith"] = "Swamp",
				["Abomination"] = "Swamp",
				["Surtling"] = "Swamp",
				["Skeleton"] = "BlackForest, Swamp",
				["Wolf"] = "Mountain",
				["Fenring"] = "Mountain",
				["Fenring_Cultist"] = "Mountain",
				["Hatchling"] = "Mountain",
				["StoneGolem"] = "Mountain",
				["Ulv"] = "Mountain",
				["Goblin"] = "Plains",
				["GoblinArcher"] = "Plains",
				["GoblinBrute"] = "Plains",
				["GoblinShaman"] = "Plains",
				["Lox"] = "Plains",
				["Deathquito"] = "Plains",
				["BlobTar"] = "Plains",
				["Unbjorn"] = "Plains",
				["Seeker"] = "Mistlands",
				["SeekerBrute"] = "Mistlands",
				["Tick"] = "Mistlands",
				["Gjall"] = "Mistlands",
				["Hare"] = "Mistlands",
				["Charred_Melee"] = "AshLands",
				["Charred_Archer"] = "AshLands",
				["Charred_Mage"] = "AshLands",
				["Morgen"] = "AshLands",
				["Morgen_NonSleeping"] = "AshLands",
				["FallenValkyrie"] = "AshLands",
				["Asksvin"] = "AshLands",
				["Volture"] = "AshLands",
				["Greydwarf_Frozen"] = "DeepNorth",
				["Skeleton_DeepNorth"] = "DeepNorth",
				["TrollFrost"] = "DeepNorth",
				["JotunWarrior"] = "DeepNorth",
				["JotunWitch"] = "DeepNorth",
				["Serpent"] = "Ocean",
				["BonemawSerpent"] = "Ocean"
			};
		}
	}
	public enum Difficulty
	{
		Easy,
		Normal,
		Hard,
		VeryHard
	}
	public enum Role
	{
		Basic,
		Strong,
		Large,
		Elite,
		Boss
	}
	public static class Rules
	{
		public static Difficulty EffectiveDifficulty(Difficulty server, Difficulty owner)
		{
			return server;
		}

		public static int Limit(Difficulty d, bool large = false)
		{
			return ((!large) ? new int[4] { 12, 14, 16, 18 } : new int[4] { 3, 4, 5, 6 })[(int)d];
		}

		public static int Batch(Difficulty d, bool large = false)
		{
			return ((!large) ? new int[4] { 3, 3, 4, 5 } : new int[4] { 1, 1, 1, 2 })[(int)d];
		}

		public static float Interval(Difficulty d, bool large = false)
		{
			return ((!large) ? new float[4] { 12f, 11f, 10f, 9f } : new float[4] { 18f, 16f, 14f, 12f })[(int)d];
		}

		public static int Stars(Difficulty d, double roll)
		{
			int num = (new int[4] { 0, 0, 1, 2 })[(int)d];
			int num2 = (new int[4] { 0, 1, 2, 4 })[(int)d];
			if (roll < 0.0)
			{
				roll = 0.0;
			}
			if (roll >= 1.0)
			{
				roll = 0.999999999;
			}
			return num + (int)(roll * (double)(num2 - num + 1));
		}

		public static int MaxStars(Difficulty d)
		{
			return (new int[4] { 0, 1, 2, 4 })[(int)d];
		}

		public static Role Pick(Difficulty d, double roll)
		{
			switch (d)
			{
			case Difficulty.Easy:
				return Role.Basic;
			case Difficulty.Normal:
				if (!(roll < 0.8))
				{
					return Role.Strong;
				}
				return Role.Basic;
			case Difficulty.Hard:
				if (!(roll < 0.4))
				{
					if (!(roll < 0.6))
					{
						if (!(roll < 0.75))
						{
							return Role.Elite;
						}
						return Role.Large;
					}
					return Role.Strong;
				}
				return Role.Basic;
			default:
				if (!(roll < 0.8))
				{
					return Role.Basic;
				}
				return Role.Elite;
			}
		}

		public static bool Inside(float x, float z, float cx, float cz, float radius)
		{
			return (x - cx) * (x - cx) + (z - cz) * (z - cz) <= radius * radius;
		}

		public static bool CanCredit(long owner, long killer, bool victimInside, bool killerInside)
		{
			return owner != 0L && killer != 0 && victimInside && killerInside;
		}

		public static IEnumerable<(float x, float z)> Candidates(float min, float max, double phase)
		{
			for (float r = min; r <= max; r += 25f)
			{
				int count = (int)Math.Ceiling(Math.PI * 2.0 * (double)r / 25.0);
				for (int i = 0; i < count; i++)
				{
					double num = phase + Math.PI * 2.0 * (double)i / (double)count;
					yield return (x: (float)Math.Cos(num) * r, z: (float)Math.Sin(num) * r);
				}
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}