Decompiled source of PortalHall v0.2.2

plugins/PortalHall.dll

Decompiled 6 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using PortalHall.Core;
using Sansara.Chronicle;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("PortalHall")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.2.0")]
[assembly: AssemblyInformationalVersion("0.2.2+6832af49c98836335819d0608bd429b70015f4f0")]
[assembly: AssemblyProduct("PortalHall")]
[assembly: AssemblyTitle("PortalHall")]
[assembly: AssemblyVersion("0.2.2.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Sansara.Chronicle
{
	public sealed class ChroniclePlayer
	{
		public long Id { get; }

		public string Name { get; }

		public ChroniclePlayer(long id, string name)
		{
			Id = id;
			Name = name ?? "";
		}
	}
	public sealed class ChronicleEntry
	{
		public double Time;

		public string Date = "";

		public string Kind = "";

		public string Id = "";

		public string Result = "";

		public List<ChroniclePlayer> Players = new List<ChroniclePlayer>();

		public const string Victory = "victory";

		public bool IsVictory
		{
			get
			{
				if (!(Result == "victory"))
				{
					return Result == "found";
				}
				return true;
			}
		}

		public static string Clean(string s)
		{
			return new string((s ?? "").Where((char c) => c != '\t' && c != '\n' && c != '\r' && c != '|' && c != '=').ToArray()).Trim();
		}

		public string ToLine()
		{
			string text = string.Join("|", Players.Select((ChroniclePlayer p) => p.Id.ToString(CultureInfo.InvariantCulture) + "=" + Clean(p.Name)));
			return string.Join("\t", Time.ToString("R", CultureInfo.InvariantCulture), Clean(Date), Clean(Kind), Clean(Id), Clean(Result), text);
		}

		public static ChronicleEntry Parse(string line)
		{
			if (string.IsNullOrWhiteSpace(line) || line.TrimStart(Array.Empty<char>()).StartsWith("#", StringComparison.Ordinal))
			{
				return null;
			}
			string[] array = line.TrimEnd(new char[1] { '\r' }).Split(new char[1] { '\t' });
			if (array.Length < 5 || !double.TryParse(array[0], NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || array[2].Trim() == "" || array[3].Trim() == "")
			{
				return null;
			}
			ChronicleEntry chronicleEntry = new ChronicleEntry
			{
				Time = result,
				Date = array[1].Trim(),
				Kind = array[2].Trim(),
				Id = array[3].Trim(),
				Result = array[4].Trim()
			};
			if (array.Length > 5)
			{
				string[] array2 = array[5].Split(new char[1] { '|' }, StringSplitOptions.RemoveEmptyEntries);
				foreach (string text in array2)
				{
					int num = text.IndexOf('=');
					long.TryParse((num > 0) ? text.Substring(0, num) : "0", NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2);
					chronicleEntry.Players.Add(new ChroniclePlayer(result2, (num >= 0) ? text.Substring(num + 1) : text));
				}
			}
			return chronicleEntry;
		}

		public static List<ChronicleEntry> ParseAll(string text, out int bad)
		{
			List<ChronicleEntry> list = new List<ChronicleEntry>();
			bad = 0;
			string[] array = (text ?? "").Split(new char[1] { '\n' });
			foreach (string text2 in array)
			{
				ChronicleEntry chronicleEntry = Parse(text2);
				if (chronicleEntry != null)
				{
					list.Add(chronicleEntry);
				}
				else if (!string.IsNullOrWhiteSpace(text2) && !text2.TrimStart(Array.Empty<char>()).StartsWith("#", StringComparison.Ordinal))
				{
					bad++;
				}
			}
			return list;
		}
	}
	public static class ChronicleFile
	{
		public static string PathFor(string configDir, string world)
		{
			string text = string.Concat((world ?? "world").Split(Path.GetInvalidFileNameChars()));
			return Path.Combine(configDir, "sansara.Chronicle", text + ".tsv");
		}

		public static void Append(string path, ChronicleEntry entry)
		{
			Directory.CreateDirectory(Path.GetDirectoryName(path));
			File.AppendAllText(path, entry.ToLine() + "\n", new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
		}

		public static List<ChronicleEntry> Read(string path, out int bad)
		{
			bad = 0;
			if (!File.Exists(path))
			{
				return new List<ChronicleEntry>();
			}
			return ChronicleEntry.ParseAll(File.ReadAllText(path), out bad);
		}
	}
	public sealed class FirstVictory
	{
		public string Id;

		public string Date;

		public List<string> Names = new List<string>();

		public int Count;
	}
	public sealed class Leader
	{
		public string Name;

		public int Count;
	}
	public static class ChronicleStats
	{
		public static Dictionary<string, FirstVictory> Firsts(IEnumerable<ChronicleEntry> all, string kind)
		{
			Dictionary<string, FirstVictory> dictionary = new Dictionary<string, FirstVictory>();
			foreach (ChronicleEntry item in from e in all
				where e.Kind == kind && e.IsVictory
				orderby e.Time
				select e)
			{
				if (!dictionary.TryGetValue(item.Id, out var value))
				{
					value = new FirstVictory
					{
						Id = item.Id,
						Date = item.Date,
						Names = (from p in item.Players
							select p.Name into n
							where n != ""
							select n).Distinct().ToList()
					};
					dictionary[item.Id] = value;
				}
				value.Count++;
			}
			return dictionary;
		}

		public static List<Leader> Leaders(IEnumerable<ChronicleEntry> all, string kind)
		{
			Dictionary<long, int> dictionary = new Dictionary<long, int>();
			Dictionary<long, string> names = new Dictionary<long, string>();
			foreach (ChronicleEntry item in from e in all
				where e.Kind == kind && e.IsVictory
				orderby e.Time
				select e)
			{
				foreach (ChroniclePlayer item2 in from p in item.Players
					group p by (p.Id == 0L) ? p.Name : p.Id.ToString() into g
					select g.First())
				{
					long key = ((item2.Id != 0L) ? item2.Id : item2.Name.GetHashCode());
					dictionary[key] = ((!dictionary.TryGetValue(key, out var value)) ? 1 : (value + 1));
					if (item2.Name != "")
					{
						names[key] = item2.Name;
					}
				}
			}
			string value2;
			return (from kv in dictionary
				select new Leader
				{
					Name = (names.TryGetValue(kv.Key, out value2) ? value2 : "?"),
					Count = kv.Value
				} into l
				orderby l.Count descending
				select l).ThenBy<Leader, string>((Leader l) => l.Name, StringComparer.Ordinal).ToList();
		}

		public static string Names(IList<string> names, int max)
		{
			if (names.Count == 0)
			{
				return "";
			}
			for (int num = names.Count; num >= 1; num--)
			{
				string text = string.Join(", ", names.Take(num)) + ((num < names.Count) ? $" +{names.Count - num}" : "");
				if (text.Length <= max)
				{
					return text;
				}
			}
			string text2 = names[0];
			if (text2.Length <= max)
			{
				return text2;
			}
			return text2.Substring(0, Math.Max(1, max - 1)) + "…";
		}
	}
}
namespace PortalHall
{
	[HarmonyPatch(typeof(ZoneSystem), "RPC_SetGlobalKey")]
	internal static class BossWatch
	{
		private const float VictorRadius = 60f;

		private static void Prefix(long sender, string name)
		{
			//IL_0051: 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_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || name == null || !name.StartsWith("defeated_", StringComparison.Ordinal))
				{
					return;
				}
				ZNetPeer peer = ZNet.instance.GetPeer(sender);
				ZDO val = ((peer == null || ((ZDOID)(ref peer.m_characterID)).IsNone()) ? null : ZDOMan.instance.GetZDO(peer.m_characterID));
				if (val == null)
				{
					return;
				}
				Vector3 position = val.GetPosition();
				List<ChroniclePlayer> list = new List<ChroniclePlayer>();
				foreach (ZNetPeer peer2 in ZNet.instance.GetPeers())
				{
					ZDO val2 = (((ZDOID)(ref peer2.m_characterID)).IsNone() ? null : ZDOMan.instance.GetZDO(peer2.m_characterID));
					if (val2 != null && Vector3.Distance(val2.GetPosition(), position) <= 60f)
					{
						list.Add(new ChroniclePlayer(val2.GetLong(ZDOVars.s_playerID, 0L), peer2.m_playerName));
					}
				}
				ChronicleEntry entry = new ChronicleEntry
				{
					Time = ZNet.instance.GetTimeSeconds(),
					Date = DateTime.UtcNow.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture),
					Kind = "boss",
					Id = name,
					Result = "victory",
					Players = list
				};
				ChronicleFile.Append(ChronicleFile.PathFor(Paths.ConfigPath, ZNet.instance.GetWorldName()), entry);
				Plugin.Log.LogInfo((object)("Чертог славы: победа над боссом " + name + " — " + string.Join(", ", list.ConvertAll((ChroniclePlayer p) => p.Name)) + "."));
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Чертог славы: ошибка записи победы над боссом: " + ex));
			}
		}
	}
	internal static class Fame
	{
		private const string Version = "fame|2";

		private static DateTime _rewardsStamp;

		private static List<Leader> _rewardLeaders = new List<Leader>();

		public static void Tick(HallBounds hub, Style style, List<ZDO> hubOurs)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: 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_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = default(Vector3);
			((Vector3)(ref val))..ctor(Hall.HubAnchor.x, hub.FloorTop, FameLayout.OffsetZ);
			List<ZDO> list = (from z in Hall.Near(val)
				where z.GetString("sansara.PortalHall.sig", "") != ""
				select z).ToList();
			if (!style.FamePortal)
			{
				if (list.Count > 0)
				{
					list.ForEach(Hall.Destroy);
					Plugin.Log.LogInfo((object)$"Чертог славы: выключен — убрано деталей {list.Count}.");
				}
				return;
			}
			List<DecorItem> list2 = (from i in FameLayout.Build(val.x, val.y, val.z, style)
				where (Object)(object)Hall.Prefab(i.Prefab) != (Object)null
				select i).ToList();
			string sig = "fame|2|" + Hall.StyleKey(style);
			if (list.Count != list2.Count || list.Any((ZDO z) => z.GetString("sansara.PortalHall.sig", "") != sig))
			{
				Rebuild(list2, list, sig);
				list = (from z in Hall.Near(val)
					where z.GetString("sansara.PortalHall.sig", "") != ""
					select z).ToList();
			}
			Dictionary<string, ZDO> dictionary = (from z in list
				where z.GetString("sansara.PortalHall.role", "") != ""
				group z by z.GetString("sansara.PortalHall.role", "")).ToDictionary((IGrouping<string, ZDO> g) => g.Key, (IGrouping<string, ZDO> g) => g.First());
			LinkPortals(((IEnumerable<ZDO>)hubOurs).FirstOrDefault((Func<ZDO, bool>)((ZDO z) => z.GetString("sansara.PortalHall.role", "") == "fame.portal.hub")), dictionary.TryGetValue("fame.portal.fame", out var value) ? value : null);
			UpdateBoard(dictionary);
		}

		private static void Rebuild(List<DecorItem> items, List<ZDO> ours, string sig)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			List<ZDO> list = ours.ToList();
			int num = 0;
			int num2 = 0;
			foreach (DecorItem item in items)
			{
				int hash = StringExtensionMethods.GetStableHashCode(((Object)Hall.Prefab(item.Prefab)).name);
				Vector3 pos = new Vector3(item.X, item.Y, item.Z);
				ZDO val = ((IEnumerable<ZDO>)list).FirstOrDefault((Func<ZDO, bool>)((ZDO z) => z.GetPrefab() == hash && Vector3.Distance(z.GetPosition(), pos) < 0.05f && Quaternion.Angle(z.GetRotation(), Quaternion.Euler(item.RotX, item.RotY, 0f)) < 1f));
				if (val != null)
				{
					list.Remove(val);
					val.SetOwner(ZDOMan.GetSessionID());
					val.Set("sansara.PortalHall.sig", sig);
					val.Set("sansara.PortalHall.role", item.Role ?? "");
					num++;
				}
				else
				{
					Hall.Place(item, sig);
					num2++;
				}
			}
			list.ForEach(Hall.Destroy);
			Plugin.Log.LogInfo((object)$"Чертог славы: оставлено деталей {num}, поставлено {num2}, убрано {list.Count}.");
		}

		private static void LinkPortals(ZDO hub, ZDO fame)
		{
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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)
			if (hub == null || fame == null)
			{
				return;
			}
			ZDO[] array = (ZDO[])(object)new ZDO[2] { hub, fame };
			foreach (ZDO val in array)
			{
				ZDO val2 = ((val == hub) ? fame : hub);
				if (val.GetString(ZDOVars.s_tag, "") != "Чертог славы" || val.GetConnectionZDOID((ConnectionType)1) != val2.m_uid)
				{
					val.SetOwner(ZDOMan.GetSessionID());
					val.Set(ZDOVars.s_tag, "Чертог славы");
					val.SetConnection((ConnectionType)1, val2.m_uid);
					ZDOMan.instance.ForceSendZDO(val.m_uid);
				}
			}
		}

		private static void UpdateBoard(Dictionary<string, ZDO> byRole)
		{
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			int bad;
			List<ChronicleEntry> chronicle = ChronicleFile.Read(ChronicleFile.PathFor(Paths.ConfigPath, ZNet.instance.GetWorldName()), out bad);
			HashSet<string> worldKeys = new HashSet<string>(ZoneSystem.instance.GetGlobalKeys());
			foreach (KeyValuePair<string, string> item in FameBoard.Texts(chronicle, worldKeys, RewardLeaders()))
			{
				string text = "<color=white>" + item.Value + "</color>";
				if (byRole.TryGetValue(item.Key, out var value) && value.GetString(ZDOVars.s_text, "") != text)
				{
					value.SetOwner(ZDOMan.GetSessionID());
					value.Set(ZDOVars.s_text, text);
					ZDOMan.instance.ForceSendZDO(value.m_uid);
				}
			}
			foreach (KeyValuePair<string, string> item2 in FameBoard.Trophies(worldKeys))
			{
				if (byRole.TryGetValue(item2.Key, out var value2))
				{
					int num = ((!(item2.Value == "")) ? StringExtensionMethods.GetStableHashCode(item2.Value) : 0);
					if (value2.GetInt(ZDOVars.s_item, 0) != num)
					{
						value2.SetOwner(ZDOMan.GetSessionID());
						value2.Set(ZDOVars.s_item, num, false);
						value2.Set(ZDOVars.s_variant, 0, false);
						value2.Set(ZDOVars.s_quality, 1, false);
						ZDOMan.instance.ForceSendZDO(value2.m_uid);
						ZRoutedRpc.instance.InvokeRoutedRPC(0L, value2.m_uid, "SetVisualItem", new object[4] { num, 0, 1, 0 });
					}
				}
			}
		}

		private static List<Leader> RewardLeaders()
		{
			string path = Path.Combine(Paths.ConfigPath, "sansara.EventRewards.queue.json");
			string path2 = Path.Combine(Paths.ConfigPath, "sansara.EventRewards.players.json");
			if (!File.Exists(path))
			{
				return _rewardLeaders;
			}
			DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(path);
			if (lastWriteTimeUtc == _rewardsStamp)
			{
				return _rewardLeaders;
			}
			_rewardsStamp = lastWriteTimeUtc;
			try
			{
				Dictionary<long, int> dictionary = new Dictionary<long, int>();
				foreach (Match item in Regex.Matches(File.ReadAllText(path), "\\{[^{}]*\\}"))
				{
					if (Regex.IsMatch(item.Value, "\"state\"\\s*:\\s*\"delivered\"") && long.TryParse(Regex.Match(item.Value, "\"playerId\"\\s*:\\s*(-?\\d+)").Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
					{
						dictionary[result] = ((!dictionary.TryGetValue(result, out var value)) ? 1 : (value + 1));
					}
				}
				Dictionary<long, string> names = new Dictionary<long, string>();
				if (File.Exists(path2))
				{
					foreach (Match item2 in Regex.Matches(File.ReadAllText(path2), "\\{[^{}]*\\}"))
					{
						if (long.TryParse(Regex.Match(item2.Value, "\"playerId\"\\s*:\\s*(-?\\d+)").Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2))
						{
							names[result2] = Regex.Match(item2.Value, "\"name\"\\s*:\\s*\"([^\"]*)\"").Groups[1].Value;
						}
					}
				}
				_rewardLeaders = (from kv in dictionary
					select new Leader
					{
						Name = ((names.TryGetValue(kv.Key, out var value2) && value2 != "") ? value2 : "?"),
						Count = kv.Value
					} into l
					orderby l.Count descending
					select l).ThenBy<Leader, string>((Leader l) => l.Name, StringComparer.Ordinal).ToList();
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Чертог славы: не удалось прочитать данные Event Rewards: " + ex.Message));
			}
			return _rewardLeaders;
		}
	}
	internal static class Hall
	{
		internal const string SigKey = "sansara.PortalHall.sig";

		internal const string RoleKey = "sansara.PortalHall.role";

		private const float MinHallY = 3000f;

		private const float SearchRadius = 200f;

		private static readonly int HubFloor = StringExtensionMethods.GetStableHashCode("Piece_grausten_floor_4x4");

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

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

		private static string _lastReport;

		internal static Vector3 HubAnchor => new Vector3(10820f, 0f, 0f);

		public static void Tick()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bc: Unknown result type (might be due to invalid IL or missing references)
			List<ZDO> source = Near(HubAnchor);
			List<ZDO> list = source.Where((ZDO z) => z.GetString("sansara.PortalHall.sig", "") != "").ToList();
			List<ZDO> list2 = source.Where((ZDO z) => z.GetPrefab() == HubFloor && z.GetPosition().y >= 3000f).ToList();
			HallBounds hallBounds = HallBounds.FromFloorTiles(list2.Select((ZDO z) => new TilePos(z.GetPosition().x, z.GetPosition().y, z.GetPosition().z)));
			Style style = Plugin.CurrentStyle();
			bool flag = Plugin.Enabled.Value && hallBounds != null && HallLayout.Raised(hallBounds, style);
			if (hallBounds != null)
			{
				KeepHubCeiling(hallBounds, list2, !flag);
			}
			if (hallBounds == null || !Plugin.Enabled.Value)
			{
				if (list.Count > 0)
				{
					list.ForEach(Destroy);
					Plugin.Log.LogInfo((object)string.Format("Чертог: {0} — убрано деталей {1}.", Plugin.Enabled.Value ? "зала порталов нет" : "мод выключен", list.Count));
				}
				Report(Plugin.Enabled.Value ? "Чертог: зал порталов ServersideQoL не найден, жду." : null);
				return;
			}
			List<DecorItem> list3 = (from i in HallLayout.Build(hallBounds, style)
				where (Object)(object)Prefab(i.Prefab) != (Object)null
				select i).ToList();
			string sig = hallBounds.Signature() + "|" + StyleKey(style);
			if (list.Count == list3.Count && list.All((ZDO z) => z.GetString("sansara.PortalHall.sig", "") == sig))
			{
				Fame.Tick(hallBounds, style, list);
				return;
			}
			list.ForEach(Destroy);
			foreach (DecorItem item in list3)
			{
				Place(item, sig);
			}
			Plugin.Log.LogInfo((object)($"Чертог: зал {hallBounds.TilesX}×{hallBounds.TilesZ} плит, центр ({hallBounds.CenterX:0.#}; {hallBounds.FloorTop:0.##}; {hallBounds.CenterZ:0.#}), " + $"высота {HallLayout.InnerHeight(hallBounds, style):0.#} м — убрано деталей {list.Count}, поставлено {list3.Count}."));
			_lastReport = null;
			Fame.Tick(hallBounds, style, (from z in Near(HubAnchor)
				where z.GetString("sansara.PortalHall.sig", "") != ""
				select z).ToList());
		}

		private static void KeepHubCeiling(HallBounds b, List<ZDO> hubTiles, bool keep)
		{
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			float floorY = b.FloorTop - 0.25f;
			float ceilingY = floorY + 4.5f;
			List<ZDO> list = hubTiles.Where((ZDO z) => Mathf.Abs(z.GetPosition().y - ceilingY) < 0.3f).ToList();
			if (!keep)
			{
				if (list.Count > 0)
				{
					list.ForEach(Destroy);
					Plugin.Log.LogInfo((object)$"Чертог: потолок зала ServersideQoL убран ({list.Count} плит) — зал выше.");
				}
				return;
			}
			List<ZDO> list2 = hubTiles.Where((ZDO z) => Mathf.Abs(z.GetPosition().y - floorY) < 0.3f).ToList();
			HashSet<Vector2Int> present = new HashSet<Vector2Int>(list.Select((ZDO z) => Cell(z.GetPosition())));
			List<ZDO> list3 = list2.Where((ZDO z) => !present.Contains(Cell(z.GetPosition()))).ToList();
			if (list3.Count == 0)
			{
				return;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab(HubFloor);
			long num = list2[0].GetLong(ZDOVars.s_creator, 0L);
			foreach (ZDO item in list3)
			{
				Vector3 position = item.GetPosition();
				Create(prefab, new Vector3(position.x, ceilingY, position.z), item.GetRotation()).Set(ZDOVars.s_creator, num);
			}
			Plugin.Log.LogInfo((object)$"Чертог: потолок зала ServersideQoL восстановлен ({list3.Count} плит).");
		}

		private static Vector2Int Cell(Vector3 p)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			return new Vector2Int(Mathf.RoundToInt(p.x), Mathf.RoundToInt(p.z));
		}

		private static void Report(string text)
		{
			if (text != null && text != _lastReport)
			{
				Plugin.Log.LogInfo((object)text);
			}
			_lastReport = text;
		}

		internal static List<ZDO> Near(Vector3 anchor)
		{
			//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_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)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			Found.Clear();
			int num = Mathf.CeilToInt(3.125f);
			ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(anchor), new SimulationDistance(num, 0, true), Found, (List<ZDO>)null);
			return Found.Where((ZDO z) => Mathf.Abs(z.GetPosition().x - anchor.x) <= 200f && Mathf.Abs(z.GetPosition().z - anchor.z) <= 200f).ToList();
		}

		internal static string StyleKey(Style s)
		{
			return string.Join(";", s.FloorTiles, s.WallTiles, s.CeilingTiles, s.WallTileFlip, s.Throne, s.Retinue, s.Lights, s.FloorTile, s.WallTile, s.ThronePrefab, s.ThroneRotY, s.Wolf, s.Raven, s.Gold, s.Carpet, s.Light, s.Height, s.Lanterns, s.Lantern, s.LanternHeight, s.LanternEvery, s.FamePortal, s.Portal);
		}

		internal static GameObject Prefab(string name)
		{
			GameObject prefab = ZNetScene.instance.GetPrefab(name);
			if (((Object)(object)prefab == (Object)null || (Object)(object)prefab.GetComponent<ZNetView>() == (Object)null) && Missing.Add(name))
			{
				Plugin.Log.LogWarning((object)("Чертог: префаба " + name + " нет в игре — пропущен."));
				return null;
			}
			if (!((Object)(object)prefab != (Object)null) || !((Object)(object)prefab.GetComponent<ZNetView>() != (Object)null))
			{
				return null;
			}
			return prefab;
		}

		internal static void Place(DecorItem item, string sig)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			ZDO val = Create(Prefab(item.Prefab), new Vector3(item.X, item.Y, item.Z), Quaternion.Euler(item.RotX, item.RotY, 0f));
			val.Set("sansara.PortalHall.sig", sig);
			if (item.Role != null)
			{
				val.Set("sansara.PortalHall.role", item.Role);
			}
		}

		private static ZDO Create(GameObject prefab, Vector3 pos, Quaternion rot)
		{
			//IL_0018: 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_004c: Unknown result type (might be due to invalid IL or missing references)
			ZNetView component = prefab.GetComponent<ZNetView>();
			int stableHashCode = StringExtensionMethods.GetStableHashCode(((Object)prefab).name);
			ZDO val = ZDOMan.instance.CreateNewZDO(pos, stableHashCode);
			val.Persistent = component.m_persistent;
			val.Type = component.m_type;
			val.Distant = component.m_distant;
			val.SetPrefab(stableHashCode);
			val.SetRotation(rot);
			val.Set("HasFields", true);
			if ((Object)(object)prefab.GetComponent<Piece>() != (Object)null)
			{
				val.Set("HasFieldsPiece", true);
				val.Set("Piece.m_canBeRemoved", false);
			}
			if ((Object)(object)prefab.GetComponent<WearNTear>() != (Object)null)
			{
				val.Set("HasFieldsWearNTear", true);
				val.Set("WearNTear.m_noRoofWear", false);
				val.Set("WearNTear.m_noSupportWear", false);
				val.Set("WearNTear.m_health", -1f);
			}
			if ((Object)(object)prefab.GetComponent<ItemStand>() != (Object)null)
			{
				val.Set("HasFieldsItemStand", true);
				val.Set("ItemStand.m_canBeRemoved", false);
			}
			if ((Object)(object)prefab.GetComponent<Fireplace>() != (Object)null)
			{
				val.Set("HasFieldsFireplace", true);
				val.Set("Fireplace.m_infiniteFuel", true);
				val.Set("Fireplace.m_disableCoverCheck", true);
			}
			return val;
		}

		internal static void Destroy(ZDO zdo)
		{
			zdo.SetOwner(ZDOMan.GetSessionID());
			ZDOMan.instance.DestroyZDO(zdo);
		}
	}
	[BepInPlugin("sansara.PortalHall", "Portal Hall", "0.2.2")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "sansara.PortalHall";

		public const string Name = "Portal Hall";

		public const string Version = "0.2.2";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		private static ConfigEntry<bool> _floor;

		private static ConfigEntry<bool> _walls;

		private static ConfigEntry<bool> _ceiling;

		private static ConfigEntry<bool> _throne;

		private static ConfigEntry<bool> _retinue;

		private static ConfigEntry<bool> _lights;

		private static ConfigEntry<bool> _wallFlip;

		private static ConfigEntry<string> _floorTile;

		private static ConfigEntry<string> _wallTile;

		private static ConfigEntry<string> _thronePrefab;

		private static ConfigEntry<string> _wolf;

		private static ConfigEntry<string> _raven;

		private static ConfigEntry<string> _gold;

		private static ConfigEntry<string> _carpet;

		private static ConfigEntry<string> _light;

		private static ConfigEntry<float> _throneRot;

		private static ConfigEntry<float> _height;

		private static ConfigEntry<float> _lanternHeight;

		private static ConfigEntry<bool> _lanterns;

		private static ConfigEntry<string> _lantern;

		private static ConfigEntry<int> _lanternEvery;

		private static ConfigEntry<bool> _famePortal;

		private float _nextTick;

		private FileSystemWatcher _watcher;

		private volatile bool _reload;

		private void Awake()
		{
			//IL_036e: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Убранство зала порталов. false — мод убирает всё, что поставил.");
			_floor = ((BaseUnityPlugin)this).Config.Bind<bool>("Lining", "FloorTiles", true, "Пол — плитка поверх граустена.");
			_walls = ((BaseUnityPlugin)this).Config.Bind<bool>("Lining", "WallTiles", true, "Стены изнутри — настенная плитка.");
			_ceiling = ((BaseUnityPlugin)this).Config.Bind<bool>("Lining", "CeilingTiles", true, "Потолок — перевёрнутая напольная плитка.");
			_wallFlip = ((BaseUnityPlugin)this).Config.Bind<bool>("Lining", "WallTileFlip", false, "Развернуть настенную плитку лицом к залу, если она видна изнанкой.");
			_floorTile = ((BaseUnityPlugin)this).Config.Bind<string>("Lining", "FloorTilePrefab", "blackmarble_tile_floor_2x2", "Плитка пола и потолка (2×2 м).");
			_wallTile = ((BaseUnityPlugin)this).Config.Bind<string>("Lining", "WallTilePrefab", "blackmarble_tile_wall_2x4", "Плитка стен (2 м шириной, 4 м высотой).");
			_throne = ((BaseUnityPlugin)this).Config.Bind<bool>("Throne", "Throne", true, "Трон в центре зала и дорожка перед ним.");
			_thronePrefab = ((BaseUnityPlugin)this).Config.Bind<string>("Throne", "ThronePrefab", "piece_blackmarble_throne", "Трон.");
			_throneRot = ((BaseUnityPlugin)this).Config.Bind<float>("Throne", "ThroneRotationY", 180f, "Поворот трона, градусы (подобрать, если трон смотрит не в зал).");
			_carpet = ((BaseUnityPlugin)this).Config.Bind<string>("Throne", "CarpetPrefab", "jute_carpet", "Дорожка перед троном.");
			_retinue = ((BaseUnityPlugin)this).Config.Bind<bool>("Throne", "Retinue", true, "Волки и вороны у трона, золото за ним.");
			_wolf = ((BaseUnityPlugin)this).Config.Bind<string>("Throne", "WolfPrefab", "darkwood_wolf", "Волки (Гери и Фреки).");
			_raven = ((BaseUnityPlugin)this).Config.Bind<string>("Throne", "RavenPrefab", "darkwood_raven", "Вороны (Хугин и Мунин).");
			_gold = ((BaseUnityPlugin)this).Config.Bind<string>("Throne", "GoldPrefab", "bar_gold_stack", "Золото у трона.");
			_lights = ((BaseUnityPlugin)this).Config.Bind<bool>("Lights", "Lights", true, "Светильники вокруг трона.");
			_height = ((BaseUnityPlugin)this).Config.Bind<float>("Lining", "Height", 8f, "Высота зала изнутри, м. Больше 4 — потолок зала ServersideQoL убирается, мраморные стены и потолок поднимаются (зал ServersideQoL — 4 м); 4 — как построил ServersideQoL (его потолок возвращается).");
			_lanterns = ((BaseUnityPlugin)this).Config.Bind<bool>("Lights", "Lanterns", true, "Настенные фонари двергов над порталами (если зал выше LanternHeight + 1 м).");
			_lantern = ((BaseUnityPlugin)this).Config.Bind<string>("Lights", "LanternPrefab", "piece_dvergr_lantern", "Настенный фонарь.");
			_lanternHeight = ((BaseUnityPlugin)this).Config.Bind<float>("Lights", "LanternHeight", 6f, "Высота фонарей над полом, м.");
			_famePortal = ((BaseUnityPlugin)this).Config.Bind<bool>("Fame", "HallOfFame", true, "Чертог славы: отдельный зал рядом с залом порталов, вход — портал за троном.");
			_lanternEvery = ((BaseUnityPlugin)this).Config.Bind<int>("Lights", "LanternEvery", 2, "Через сколько плит зала (по 4 м) вешать фонари вдоль стены.");
			_light = ((BaseUnityPlugin)this).Config.Bind<string>("Lights", "LightPrefab", "piece_groundtorch_mist", "Светильник (по умолчанию «Факел с магическим огоньком» Туманных земель: без огня и дыма, разгоняет туман).");
			_watcher = new FileSystemWatcher(Path.GetDirectoryName(((BaseUnityPlugin)this).Config.ConfigFilePath), Path.GetFileName(((BaseUnityPlugin)this).Config.ConfigFilePath));
			_watcher.Changed += delegate
			{
				_reload = true;
			};
			_watcher.EnableRaisingEvents = true;
			new Harmony("sansara.PortalHall").PatchAll(typeof(BossWatch));
			Log.LogInfo((object)"Portal Hall 0.2.2 загружен.");
		}

		internal static Style CurrentStyle()
		{
			return new Style
			{
				FloorTiles = _floor.Value,
				WallTiles = _walls.Value,
				CeilingTiles = _ceiling.Value,
				WallTileFlip = _wallFlip.Value,
				Throne = _throne.Value,
				Retinue = _retinue.Value,
				Lights = _lights.Value,
				FloorTile = _floorTile.Value,
				WallTile = _wallTile.Value,
				ThronePrefab = _thronePrefab.Value,
				ThroneRotY = _throneRot.Value,
				Wolf = _wolf.Value,
				Raven = _raven.Value,
				Gold = _gold.Value,
				Carpet = _carpet.Value,
				Light = _light.Value,
				Height = _height.Value,
				Lanterns = _lanterns.Value,
				Lantern = _lantern.Value,
				LanternHeight = _lanternHeight.Value,
				LanternEvery = _lanternEvery.Value,
				FamePortal = _famePortal.Value
			};
		}

		private void Update()
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null || (Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null || !ZoneSystem.instance.LocationsGenerated || Time.time < _nextTick)
			{
				return;
			}
			_nextTick = Time.time + 10f;
			try
			{
				if (_reload)
				{
					_reload = false;
					((BaseUnityPlugin)this).Config.Reload();
					Log.LogInfo((object)"Чертог: настройки перечитаны.");
				}
				Hall.Tick();
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Чертог: ошибка: " + ex));
			}
		}
	}
}
namespace PortalHall.Core
{
	public static class FameRoles
	{
		public const string HubPortal = "fame.portal.hub";

		public const string FamePortal = "fame.portal.fame";

		public const string TreasureSign = "treasure.sign";

		public const string PortalTag = "Чертог славы";

		public static string BossStand(int i)
		{
			return "fame.boss.stand." + i;
		}

		public static string BossSign(int i)
		{
			return "fame.boss.sign." + i;
		}

		public static string TrialSign(int i)
		{
			return "fame.trial.sign." + i;
		}

		public static string RaidSign(int i)
		{
			return "fame.raid.sign." + i;
		}

		public static string RecordSign(int i)
		{
			return "fame.record.sign." + i;
		}

		public static string Header(string what)
		{
			return "fame.header." + what;
		}

		public static string OrderChest(int i)
		{
			return "order.chest." + i;
		}

		public static string OrderSign(int i)
		{
			return "order.sign." + i;
		}
	}
	public sealed class Titled
	{
		public string Id { get; }

		public string Title { get; }

		public string Trophy { get; }

		public Titled(string id, string title, string trophy = null)
		{
			Id = id;
			Title = title;
			Trophy = trophy;
		}
	}
	public static class FameCatalog
	{
		public static readonly Titled[] Bosses = new Titled[9]
		{
			new Titled("defeated_eikthyr", "Эйктюр", "TrophyEikthyr"),
			new Titled("defeated_gdking", "Древний", "TrophyTheElder"),
			new Titled("defeated_bonemass", "Масса костей", "TrophyBonemass"),
			new Titled("defeated_dragon", "Модер", "TrophyDragonQueen"),
			new Titled("defeated_goblinking", "Яглут", "TrophyGoblinKing"),
			new Titled("defeated_queen", "Королева", "TrophySeekerQueen"),
			new Titled("defeated_fader", "Фадер", "TrophyFader"),
			new Titled("defeated_writhan", "Скручень", "TrophyWrithan"),
			new Titled("defeated_frozenking_p3", "Ледяной король")
		};

		public static readonly Titled[] Trials = new Titled[14]
		{
			new Titled("eikthyr", "Поляна Эйктюра"),
			new Titled("eikthyr_plus", "Поляна Эйктюра+"),
			new Titled("elder", "Святилище Древнего"),
			new Titled("elder_plus", "Святилище Древнего+"),
			new Titled("bonemass", "Логово Массы"),
			new Titled("bonemass_plus", "Логово Массы+"),
			new Titled("moder", "Логово Модер"),
			new Titled("moder_plus", "Логово Модер+"),
			new Titled("yagluth", "Алтарь Яглута"),
			new Titled("yagluth_plus", "Алтарь Яглута+"),
			new Titled("queen", "Логово Королевы"),
			new Titled("queen_plus", "Логово Королевы+"),
			new Titled("fader", "Алтарь Фадера"),
			new Titled("fader_plus", "Алтарь Фадера+")
		};

		public static readonly Titled[] Raids = new Titled[21]
		{
			new Titled("army_eikthyr", "Свита Эйктюра"),
			new Titled("army_theelder", "Грейдворфы"),
			new Titled("foresttrolls", "Тролли"),
			new Titled("skeletons", "Скелеты"),
			new Titled("ghosts", "Призраки"),
			new Titled("hildirboss1", "Бренна"),
			new Titled("army_bonemass", "Драугры"),
			new Titled("blobs", "Слизни"),
			new Titled("surtlings", "Суртлинги"),
			new Titled("army_moder", "Дракончики"),
			new Titled("wolves", "Волки"),
			new Titled("bats", "Летучие мыши"),
			new Titled("hildirboss2", "Гейрафа"),
			new Titled("army_goblin", "Фулинги"),
			new Titled("hildirboss3", "Зил и Тангр"),
			new Titled("army_gjall", "Гьяллы"),
			new Titled("army_seekers", "Искатели"),
			new Titled("army_charred", "Обугленные"),
			new Titled("army_charredspawners", "Камни обугленных"),
			new Titled("army_jotuns", "Ётуны"),
			new Titled("army_elakingar", "Элакинги")
		};

		public static readonly Titled[] Records = new Titled[6]
		{
			new Titled("trial", "Испытания"),
			new Titled("raid", "Набеги"),
			new Titled("boss", "Боссы"),
			new Titled("rewards", "Награды"),
			new Titled("hunt", "Охота"),
			new Titled("treasure", "Клады")
		};

		public const int RecordPlaces = 3;
	}
	public static class FameLayout
	{
		private enum Wall
		{
			South,
			North,
			West,
			East
		}

		public const float Half = 12f;

		public const float Height = 8f;

		public const float SignWidth = 1.2f;

		public const float SignRow = 0.7f;

		public const float WallFace = 11.8f;

		public static readonly float OffsetZ = 300f;

		public static List<DecorItem> Build(float ax, float ay, float az, Style s)
		{
			List<DecorItem> items = new List<DecorItem>();
			for (float num = -10f; num < 12f; num += 4f)
			{
				for (float num2 = -10f; num2 < 12f; num2 += 4f)
				{
					Add("Piece_grausten_floor_4x4", num, -0.25f, num2, 0f);
				}
			}
			float num3 = 0.2f;
			for (float num4 = -11f; num4 < 12f; num4 += 2f)
			{
				for (float num5 = -11f; num5 < 12f; num5 += 2f)
				{
					Add(s.FloorTile, num4, 0f, num5, 0f);
					Add(s.FloorTile, num4, 8f, num5, 0f, 180f);
				}
			}
			for (int i = 0; i < 2; i++)
			{
				float y = (float)i * 4f;
				for (float num6 = -11f; num6 < 12f; num6 += 2f)
				{
					Add(s.WallTile, num6, y, -12f, 0f);
					Add(s.WallTile, num6, y, 12f, 180f);
					Add(s.WallTile, -12f, y, num6, 90f);
					Add(s.WallTile, 12f, y, num6, 270f);
				}
			}
			Add(s.Portal, 0f, num3, -10.5f, 0f, 0f, "fame.portal.fame");
			Sign(Add, 0f, num3 + 3.9f, Wall.South, FameRoles.Header("gate"));
			Sign(Add, -7.5f, num3 + 2.9f, Wall.South, FameRoles.Header("orders"));
			for (int j = 0; j < 3; j++)
			{
				float num7 = -10f + 2.5f * (float)j;
				Add("piece_chest_blackmetal", num7, num3, -11.1f, 0f, 0f, FameRoles.OrderChest(j));
				Sign(Add, num7, num3 + 1.9f, Wall.South, FameRoles.OrderSign(j));
			}
			Sign(Add, 7.5f, num3 + 2.9f, Wall.South, FameRoles.Header("treasure"));
			Sign(Add, 7.5f, num3 + 2.2f, Wall.South, "treasure.sign");
			int num8 = FameCatalog.Bosses.Length;
			for (int k = 0; k < num8; k++)
			{
				float x = ((float)k - (float)(num8 - 1) / 2f) * 2.2f;
				Add("blackmarble_base_1", x, num3 + 1f, 11f, 0f);
				Add("itemstandh", x, num3 + 2f, 11f, 0f, 0f, FameRoles.BossStand(k));
				Add("sign", x, num3 + 1f, 9.93f, 180f, 0f, FameRoles.BossSign(k));
			}
			Sign(Add, 0f, num3 + 6.8f, Wall.North, FameRoles.Header("records"));
			int num9 = FameCatalog.Records.Length * 3;
			for (int l = 0; l < num9; l++)
			{
				int num10 = l / 3;
				int num11 = l % 3;
				float along = ((float)num10 - (float)(FameCatalog.Records.Length - 1) / 2f) * 3.4f;
				Sign(Add, along, num3 + 4.6f + (float)(2 - num11) * 0.7f, Wall.North, FameRoles.RecordSign(l));
			}
			Grid(Add, Wall.West, FameRoles.Header("trials"), FameCatalog.Trials.Length, FameRoles.TrialSign, num3);
			Grid(Add, Wall.East, FameRoles.Header("raids"), FameCatalog.Raids.Length, FameRoles.RaidSign, num3);
			for (float num12 = -9f; num12 <= 9f; num12 += 6f)
			{
				Add(s.Lantern, num12, s.LanternHeight, -11.8f, 90f);
				Add(s.Lantern, num12, s.LanternHeight, 11.8f, 270f);
				Add(s.Lantern, -11.8f, s.LanternHeight, num12, 180f);
				Add(s.Lantern, 11.8f, s.LanternHeight, num12, 0f);
			}
			float[] array = new float[2] { -10.5f, 10.5f };
			foreach (float x2 in array)
			{
				float[] array2 = new float[2] { -8.5f, 8.5f };
				foreach (float z in array2)
				{
					Add(s.Light, x2, num3, z, 0f);
				}
			}
			return items;
			void Add(string prefab, float num13, float num14, float num15, float rotY, float rotX = 0f, string role = null)
			{
				items.Add(new DecorItem(prefab, ax + num13, ay + num14, az + num15, rotY, rotX, role));
			}
		}

		private static void Sign(Action<string, float, float, float, float, float, string> add, float along, float y, Wall wall, string role)
		{
			switch (wall)
			{
			case Wall.South:
				add("sign", along, y, -11.7300005f, 0f, 0f, role);
				break;
			case Wall.North:
				add("sign", along, y, 11.7300005f, 180f, 0f, role);
				break;
			case Wall.West:
				add("sign", -11.7300005f, y, along, 90f, 0f, role);
				break;
			default:
				add("sign", 11.7300005f, y, along, 270f, 0f, role);
				break;
			}
		}

		private static void Grid(Action<string, float, float, float, float, float, string> add, Wall wall, string header, int count, Func<int, string> role, float g)
		{
			Sign(add, 0f, g + 5.2f, wall, header);
			int num = (count + 6 - 1) / 6;
			for (int i = 0; i < count; i++)
			{
				int num2 = i / 6;
				int num3 = i % 6;
				float along = ((float)num2 - (float)(num - 1) / 2f) * 1.2f * 2f;
				Sign(add, along, g + 4.4f - (float)num3 * 0.7f, wall, role(i));
			}
		}
	}
	public static class FameBoard
	{
		public const int SignLimit = 50;

		public static Dictionary<string, string> Texts(IList<ChronicleEntry> chronicle, ICollection<string> worldKeys, IList<Leader> rewardLeaders)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>
			{
				[FameRoles.Header("gate")] = "Чертог славы",
				[FameRoles.Header("orders")] = "Заказы",
				[FameRoles.Header("treasure")] = "Клад",
				[FameRoles.Header("records")] = "Рекорды",
				[FameRoles.Header("trials")] = "Испытания",
				[FameRoles.Header("raids")] = "Набеги"
			};
			Dictionary<string, FirstVictory> dictionary2 = ChronicleStats.Firsts(chronicle, "boss");
			for (int i = 0; i < FameCatalog.Bosses.Length; i++)
			{
				Titled titled = FameCatalog.Bosses[i];
				FirstVictory value;
				string s = ((!dictionary2.TryGetValue(titled.Id, out value)) ? (worldKeys.Contains(titled.Id) ? (titled.Title + " · побеждён до летописи") : (titled.Title + " · не побеждён")) : Line(titled.Title, value));
				dictionary[FameRoles.BossSign(i)] = Fit(s);
			}
			Fill(dictionary, chronicle, "trial", FameCatalog.Trials, FameRoles.TrialSign);
			Fill(dictionary, chronicle, "raid", FameCatalog.Raids, FameRoles.RaidSign);
			for (int j = 0; j < FameCatalog.Records.Length; j++)
			{
				Titled titled2 = FameCatalog.Records[j];
				IList<Leader> list2;
				if (!(titled2.Id == "rewards"))
				{
					IList<Leader> list = ChronicleStats.Leaders(chronicle, titled2.Id);
					list2 = list;
				}
				else
				{
					list2 = rewardLeaders;
				}
				IList<Leader> list3 = list2;
				for (int k = 0; k < 3; k++)
				{
					string text = ((k == 0) ? (titled2.Title + ": ") : (k + 1 + ". "));
					text = ((k >= list3.Count) ? (text + "—") : (text + ((k == 0) ? "1. " : "") + list3[k].Name + " — " + list3[k].Count.ToString(CultureInfo.InvariantCulture)));
					dictionary[FameRoles.RecordSign(j * 3 + k)] = Fit(text);
				}
			}
			return dictionary;
		}

		public static Dictionary<string, string> Trophies(ICollection<string> worldKeys)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>();
			for (int i = 0; i < FameCatalog.Bosses.Length; i++)
			{
				Titled titled = FameCatalog.Bosses[i];
				dictionary[FameRoles.BossStand(i)] = ((titled.Trophy != null && worldKeys.Contains(titled.Id)) ? titled.Trophy : "");
			}
			return dictionary;
		}

		private static void Fill(Dictionary<string, string> t, IList<ChronicleEntry> chronicle, string kind, Titled[] catalog, Func<int, string> role)
		{
			Dictionary<string, FirstVictory> dictionary = ChronicleStats.Firsts(chronicle, kind);
			for (int i = 0; i < catalog.Length; i++)
			{
				t[role(i)] = Fit(dictionary.TryGetValue(catalog[i].Id, out var value) ? Line(catalog[i].Title, value) : (catalog[i].Title + ": —"));
			}
		}

		internal static string Line(string title, FirstVictory f)
		{
			string text = ShortDate(f.Date);
			string text2 = ((text != "") ? (" · " + text) : "") + ((f.Count > 1) ? (" ×" + f.Count.ToString(CultureInfo.InvariantCulture)) : "");
			string text3 = title + " · ";
			int num = 50 - text3.Length - text2.Length;
			string text4 = ((f.Names.Count > 0 && num > 3) ? ChronicleStats.Names(f.Names, num) : ((f.Names.Count == 0) ? "до летописи" : ""));
			return text3 + text4 + text2;
		}

		internal static string ShortDate(string iso)
		{
			if (!DateTime.TryParseExact(iso, "yyyy-MM-dd", CultureInfo.InvariantCulture, DateTimeStyles.None, out var result))
			{
				return "";
			}
			return result.ToString("dd.MM.yy", CultureInfo.InvariantCulture);
		}

		internal static string Fit(string s)
		{
			if (s.Length > 50)
			{
				return s.Substring(0, 49) + "…";
			}
			return s;
		}
	}
	public struct TilePos
	{
		public float X;

		public float Y;

		public float Z;

		public TilePos(float x, float y, float z)
		{
			X = x;
			Y = y;
			Z = z;
		}
	}
	public sealed class HallBounds
	{
		public const float Tile = 4f;

		public const float FloorHalfThickness = 0.25f;

		public const float CeilingAbove = 4.5f;

		public const float WallHalfThickness = 0.2f;

		public float MinX;

		public float MaxX;

		public float MinZ;

		public float MaxZ;

		public float FloorTop;

		public float CeilingBottom;

		public int TilesX;

		public int TilesZ;

		public float CenterX => (MinX + MaxX) / 2f;

		public float CenterZ => (MinZ + MaxZ) / 2f;

		public float Height => CeilingBottom - FloorTop;

		public static HallBounds FromFloorTiles(IEnumerable<TilePos> tiles)
		{
			List<TilePos> list = tiles.ToList();
			if (list.Count == 0)
			{
				return null;
			}
			float floorY = list.Min((TilePos t) => t.Y);
			List<TilePos> list2 = list.Where((TilePos t) => Math.Abs(t.Y - floorY) < 0.5f).ToList();
			float num = list2.Min((TilePos t) => t.X);
			float num2 = list2.Max((TilePos t) => t.X);
			float num3 = list2.Min((TilePos t) => t.Z);
			float num4 = list2.Max((TilePos t) => t.Z);
			int num5 = (int)Math.Round((num2 - num) / 4f) + 1;
			int num6 = (int)Math.Round((num4 - num3) / 4f) + 1;
			if (num5 < 1 || num6 < 1 || list2.Count != num5 * num6)
			{
				return null;
			}
			return new HallBounds
			{
				MinX = num - 2f,
				MaxX = num2 + 2f,
				MinZ = num3 - 2f,
				MaxZ = num4 + 2f,
				FloorTop = floorY + 0.25f,
				CeilingBottom = floorY + 4.5f - 0.25f,
				TilesX = num5,
				TilesZ = num6
			};
		}

		public string Signature()
		{
			return string.Format(CultureInfo.InvariantCulture, "{0:F1},{1:F1},{2:F1},{3:F1},{4:F1}", MinX, MaxX, MinZ, MaxZ, FloorTop);
		}
	}
	public sealed class DecorItem
	{
		public string Role { get; }

		public string Prefab { get; }

		public float X { get; }

		public float Y { get; }

		public float Z { get; }

		public float RotY { get; }

		public float RotX { get; }

		public DecorItem(string prefab, float x, float y, float z, float rotY, float rotX = 0f, string role = null)
		{
			Prefab = prefab;
			X = x;
			Y = y;
			Z = z;
			RotY = rotY;
			RotX = rotX;
			Role = role;
		}
	}
	public sealed class Style
	{
		public bool FloorTiles = true;

		public bool WallTiles = true;

		public bool CeilingTiles = true;

		public bool Throne = true;

		public bool Retinue = true;

		public bool Lights = true;

		public string FloorTile = "blackmarble_tile_floor_2x2";

		public string WallTile = "blackmarble_tile_wall_2x4";

		public bool WallTileFlip;

		public string ThronePrefab = "piece_blackmarble_throne";

		public float ThroneRotY = 180f;

		public string Wolf = "darkwood_wolf";

		public string Raven = "darkwood_raven";

		public string Gold = "bar_gold_stack";

		public string Carpet = "jute_carpet";

		public string Light = "piece_groundtorch_mist";

		public float Height = 8f;

		public bool Lanterns = true;

		public string Lantern = "piece_dvergr_lantern";

		public float LanternHeight = 6f;

		public int LanternEvery = 2;

		public bool FamePortal = true;

		public string Portal = "portal_wood";
	}
	public static class HallLayout
	{
		public const float TileStep = 2f;

		public const float TileThickness = 0.2f;

		public const float WallTileHeight = 4f;

		public const float PortalMargin = 3.5f;

		public static List<DecorItem> Build(HallBounds b, Style s)
		{
			List<DecorItem> list = new List<DecorItem>();
			float y = b.FloorTop + (s.FloorTiles ? 0.2f : 0f);
			float num = InnerHeight(b, s);
			bool flag = Raised(b, s);
			bool flag2 = s.CeilingTiles || flag;
			if (s.FloorTiles || flag2)
			{
				foreach (float item in Steps(b.MinX, b.MaxX))
				{
					foreach (float item2 in Steps(b.MinZ, b.MaxZ))
					{
						if (s.FloorTiles)
						{
							list.Add(new DecorItem(s.FloorTile, item, b.FloorTop, item2, 0f));
						}
						if (flag2)
						{
							list.Add(new DecorItem(s.FloorTile, item, b.FloorTop + num, item2, 0f, 180f));
						}
					}
				}
			}
			int num2 = (int)Math.Ceiling(num / 4f - 0.01f);
			for (int i = ((!s.WallTiles) ? 1 : 0); i < num2; i++)
			{
				AddWalls(list, b, s, b.FloorTop + (float)i * 4f);
			}
			if (s.Lanterns && num >= s.LanternHeight + 1f)
			{
				AddLanterns(list, b, s);
			}
			if (s.Throne || s.Retinue || s.Lights)
			{
				AddThroneGroup(list, b, s, y);
			}
			float num3 = 6.5f;
			if (s.FamePortal && (b.MaxZ - b.MinZ) / 2f - 3.5f >= num3)
			{
				list.Add(new DecorItem(s.Portal, b.CenterX, y, b.CenterZ + num3, 180f, 0f, "fame.portal.hub"));
			}
			return list;
		}

		public static float InnerHeight(HallBounds b, Style s)
		{
			return Math.Max(s.Height, b.Height);
		}

		public static bool Raised(HallBounds b, Style s)
		{
			return s.Height > b.Height + 0.01f;
		}

		internal static IEnumerable<float> Steps(float min, float max)
		{
			int n = (int)Math.Round((max - min) / 2f);
			for (int i = 0; i < n; i++)
			{
				yield return min + 1f + (float)i * 2f;
			}
		}

		private static void AddWalls(List<DecorItem> items, HallBounds b, Style s, float y)
		{
			float num = 0.019999996f;
			float num2 = (s.WallTileFlip ? 180f : 0f);
			foreach (float item in Steps(b.MinX, b.MaxX))
			{
				items.Add(new DecorItem(s.WallTile, item, y, b.MinZ + num + (s.WallTileFlip ? 0.2f : 0f), 0f + num2));
				items.Add(new DecorItem(s.WallTile, item, y, b.MaxZ - num - (s.WallTileFlip ? 0.2f : 0f), 180f + num2));
			}
			foreach (float item2 in Steps(b.MinZ, b.MaxZ))
			{
				items.Add(new DecorItem(s.WallTile, b.MinX + num + (s.WallTileFlip ? 0.2f : 0f), y, item2, 90f + num2));
				items.Add(new DecorItem(s.WallTile, b.MaxX - num - (s.WallTileFlip ? 0.2f : 0f), y, item2, 270f + num2));
			}
		}

		private static void AddLanterns(List<DecorItem> items, HallBounds b, Style s)
		{
			float num = 0.22f;
			float y = b.FloorTop + s.LanternHeight;
			int num2 = Math.Max(1, s.LanternEvery);
			for (int i = 0; i < b.TilesX; i += num2)
			{
				float x = b.MinX + 2f + (float)i * 4f;
				items.Add(new DecorItem(s.Lantern, x, y, b.MinZ + num, 90f));
				items.Add(new DecorItem(s.Lantern, x, y, b.MaxZ - num, 270f));
			}
			for (int j = 0; j < b.TilesZ; j += num2)
			{
				float z = b.MinZ + 2f + (float)j * 4f;
				items.Add(new DecorItem(s.Lantern, b.MinX + num, y, z, 180f));
				items.Add(new DecorItem(s.Lantern, b.MaxX - num, y, z, 0f));
			}
		}

		private static void AddThroneGroup(List<DecorItem> items, HallBounds b, Style s, float y)
		{
			float cx = b.CenterX;
			float cz = b.CenterZ;
			float halfX = (b.MaxX - b.MinX) / 2f - 3.5f;
			float halfZ = (b.MaxZ - b.MinZ) / 2f - 3.5f;
			if (s.Throne)
			{
				Add(s.ThronePrefab, 0f, 1.5f, s.ThroneRotY);
				for (int i = 0; i < 3; i++)
				{
					Add(s.Carpet, 0f, -0.5f - 3f * (float)i, 0f);
				}
			}
			if (s.Retinue)
			{
				Add(s.Wolf, -2.2f, 2.5f, 180f);
				Add(s.Wolf, 2.2f, 2.5f, 180f);
				Add(s.Raven, -1.4f, 3.6f, 180f);
				Add(s.Raven, 1.4f, 3.6f, 180f);
				Add(s.Gold, -3.4f, 2.8f, 0f);
				Add(s.Gold, 3.4f, 2.8f, 0f);
			}
			if (s.Lights)
			{
				Add(s.Light, -4.5f, 3f, 0f);
				Add(s.Light, 4.5f, 3f, 0f);
				Add(s.Light, -4.5f, -4f, 0f);
				Add(s.Light, 4.5f, -4f, 0f);
			}
			void Add(string prefab, float dx, float dz, float rot)
			{
				if (Fits(dx, dz))
				{
					items.Add(new DecorItem(prefab, cx + dx, y, cz + dz, rot));
				}
			}
			bool Fits(float dx, float dz)
			{
				if (Math.Abs(dx) <= halfX)
				{
					return Math.Abs(dz) <= halfZ;
				}
				return false;
			}
		}
	}
}