Decompiled source of CrewStats v1.1.0

CrewStats.dll

Decompiled 4 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

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

[BepInPlugin("dukr.crewstats", "CrewStats", "1.1.0")]
public class Plugin : BaseUnityPlugin
{
	private class Def
	{
		public int id;

		public string label;

		public string kind;
	}

	private class Tab
	{
		public string name;

		public string page = "rows";

		public List<Def> defs = new List<Def>();
	}

	private class BP
	{
		public string name;

		public string nemesis;

		public bool online;

		public float bestLife;

		public int nemesisCount;

		public Dictionary<int, float[]> v = new Dictionary<int, float[]>();
	}

	private class DeathLine
	{
		public long ticks;

		public string player;

		public string killer;

		public int biome;
	}

	private class BossLine
	{
		public long ticks;

		public string boss;

		public string players;
	}

	private class AwardRow
	{
		public string title;

		public string winner;

		public string value;

		public Color col;
	}

	internal static Plugin I;

	private const string RpcUp = "crew_up";

	private const string RpcReq = "crew_req";

	private const string RpcBoard = "crew_board";

	private const string RpcDeath = "crew_death";

	private static readonly CultureInfo Inv = CultureInfo.InvariantCulture;

	private const int PotionId = 1000;

	private const int TimeOnlineId = 1100;

	private static readonly List<Tab> tabs = new List<Tab>();

	private static readonly Dictionary<int, Def> byId = new Dictionary<int, Def>();

	private static readonly List<int> allIds = new List<int>();

	private static readonly List<int> customIds = new List<int>();

	private static readonly Dictionary<string, int> resByToken = new Dictionary<string, int>();

	private static readonly HashSet<string> bossTokens = new HashSet<string> { "$enemy_eikthyr", "$enemy_gdking", "$enemy_bonemass", "$enemy_dragon", "$enemy_goblinking", "$enemy_seekerqueen", "$enemy_fader", "$enemy_frozenking_p3" };

	private static int idKills = -1;

	private static int idDeaths = -1;

	private static int idBoss = -1;

	private static readonly Dictionary<string, int> sid = new Dictionary<string, int>();

	private static readonly string[,] Res = new string[24, 4]
	{
		{ "2001", "$item_copperore", "Copper ore", "ore" },
		{ "2002", "$item_tinore", "Tin ore", "ore" },
		{ "2003", "$item_ironore", "Iron ore", "ore" },
		{ "2004", "$item_ironscrap", "Iron scrap", "ore" },
		{ "2005", "$item_silverore", "Silver ore", "ore" },
		{ "2006", "$item_blackmetalscrap", "Black metal scrap", "ore" },
		{ "2007", "$item_flametalore", "Flametal ore", "ore" },
		{ "2008", "$item_goldore", "Gold ore", "ore" },
		{ "2010", "$item_stone", "Stone", "stone" },
		{ "2011", "$item_flint", "Flint", "stone" },
		{ "2012", "$item_coal", "Coal", "stone" },
		{ "2013", "$item_obsidian", "Obsidian", "stone" },
		{ "2014", "$item_crystal", "Crystal", "stone" },
		{ "2015", "$item_blackmarble", "Black marble", "stone" },
		{ "2016", "$item_grausten", "Grausten", "stone" },
		{ "2017", "$item_sulfurstone", "Sulfur stone", "stone" },
		{ "2018", "$item_ice", "Ice", "ice" },
		{ "2020", "$item_wood", "Wood", "wood" },
		{ "2021", "$item_finewood", "Fine wood", "wood" },
		{ "2022", "$item_roundlog", "Core wood", "wood" },
		{ "2023", "$item_elderbark", "Elder bark", "wood" },
		{ "2024", "$item_yggdrasilwood", "Yggdrasil wood", "wood" },
		{ "2025", "$item_blackwood", "Ashwood", "wood" },
		{ "2026", "$item_frostwood", "Timberwood", "wood" }
	};

	private ConfigEntry<KeyboardShortcut> toggleKey;

	private ConfigEntry<float> reportSeconds;

	private ConfigEntry<float> windowScale;

	private ConfigEntry<string> logFile;

	private string dataDir;

	private object registeredFor;

	private readonly Store store = new Store();

	private bool dirty;

	private float saveAt;

	private float onlineAt;

	private readonly Dictionary<string, float> deathAt = new Dictionary<string, float>();

	private static readonly Dictionary<int, float> pend = new Dictionary<int, float>();

	private Dictionary<int, float> lastSent;

	private Dictionary<string, float> lastSentCre;

	private bool loginSent;

	private float spawnT;

	private float nextReport;

	private float warnAt;

	private readonly List<BP> board = new List<BP>();

	private readonly Dictionary<int, string> cNames = new Dictionary<int, string>();

	private readonly List<DeathLine> deathLines = new List<DeathLine>();

	private readonly List<BossLine> bossLines = new List<BossLine>();

	private bool boardDirty;

	private float boardAt;

	private bool open;

	private int tab;

	private int period = 1;

	private float reqAt;

	private bool needRender;

	private GameObject root;

	private RectTransform panel;

	private Font font;

	private readonly Dictionary<string, Sprite> sprites = new Dictionary<string, Sprite>();

	private readonly List<GameObject> dyn = new List<GameObject>();

	private static readonly string[] Periods = new string[3] { "Today", "Last 7 days", "All time" };

	private static readonly uint[] Palette = new uint[12]
	{
		5809919u, 16756060u, 6935196u, 13994726u, 15126370u, 15759496u, 6084053u, 11048959u, 11851371u, 15569340u,
		7912913u, 14067320u
	};

	private static readonly Color Parchment = new Color(0.93f, 0.89f, 0.78f, 1f);

	private static readonly Color Gold = new Color(1f, 0.8f, 0.35f, 1f);

	private static readonly Color GoldDim = new Color(0.86f, 0.7f, 0.4f, 1f);

	private static readonly Color Dim = new Color(0.62f, 0.58f, 0.48f, 1f);

	private GameObject invBtn;

	private static object locInstance;

	private static MethodInfo locMethod;

	private static bool locTried;

	private static int Stat(string name)
	{
		if (!sid.TryGetValue(name, out var value))
		{
			return -1;
		}
		return value;
	}

	private static void BuildCatalog()
	{
		if (tabs.Count > 0)
		{
			return;
		}
		Tab t = Add("Combat");
		D(t, "EnemyKills", "Enemies killed", "n");
		D(t, "EnemyKillsLastHits", "Killing blows", "n");
		D(t, "BossKills", "Bosses killed", "n");
		D(t, "EnemyHits", "Hits dealt", "n");
		D(t, "HitsTakenEnemies", "Hits taken", "n");
		D(t, "ArrowsShot", "Arrows shot", "n");
		D(t, "Deaths", "Deaths", "n");
		t = Add("Gathering");
		D(t, "Mines", "Rocks mined", "n");
		D(t, "MineHits", "Mining hits", "n");
		D(t, "TreeChops", "Tree hits", "n");
		D(t, "Tree", "Trees felled", "n");
		D(t, "Logs", "Logs cut", "n");
		D(t, "HarvestCrop", "Crops harvested", "n");
		D(t, "HarvestBerry", "Berries picked", "n");
		D(t, "HarvestMushroom", "Mushrooms picked", "n");
		D(t, "FishCaught", "Fish caught", "n");
		t = Add("Ore & more");
		for (int i = 0; i < Res.GetLength(0); i++)
		{
			int num = int.Parse(Res[i, 0], Inv);
			Custom(t, num, Res[i, 2], "n");
			resByToken[Res[i, 1]] = num;
		}
		t = Add("Craft & food");
		D(t, "Crafts", "Items crafted", "n");
		D(t, "Upgrades", "Items upgraded", "n");
		D(t, "CraftFood", "Food cooked", "n");
		D(t, "CraftGrill", "Grilled", "n");
		D(t, "CraftWeapon", "Weapons crafted", "n");
		D(t, "CraftArmor", "Armor crafted", "n");
		D(t, "FoodEaten", "Food eaten", "n");
		Custom(t, 1000, "Potions drunk", "n");
		D(t, "BuiltPieces", "Pieces built", "n");
		D(t, "CreatureTamed", "Animals tamed", "n");
		t = Add("Travel");
		Custom(t, 1100, "Time online", "h");
		D(t, "DistanceWalk", "Walked", "km");
		D(t, "DistanceRun", "Ran", "km");
		D(t, "DistanceSail", "Sailed", "km");
		D(t, "DistanceAir", "In the air", "km");
		D(t, "PortalsUsed", "Portals used", "n");
		D(t, "TimeInBase", "Time in base", "h");
		D(t, "TimeOutOfBase", "Time out of base", "h");
		D(t, "Sleep", "Nights slept", "n");
		D(t, "Jumps", "Jumps", "n");
		t = Add("Creatures");
		t.page = "creatures";
		t = Add("Deaths");
		D(t, "Deaths", "All deaths", "n");
		D(t, "DeathByEnemyHit", "By enemies", "n");
		D(t, "DeathByFall", "Fall", "n");
		D(t, "DeathByDrowning", "Drowning", "n");
		D(t, "DeathByBurning", "Burning", "n");
		D(t, "DeathByFreezing", "Freezing", "n");
		D(t, "DeathByPoisoned", "Poison", "n");
		D(t, "DeathByAshlandsLava", "Lava", "n");
		D(t, "DeathByCinderFire", "Cinder fire", "n");
		D(t, "DeathByBoat", "Boat", "n");
		D(t, "TombstonesOpenedOwn", "Own tombstones opened", "n");
		t = Add("Awards");
		t.page = "awards";
		foreach (Tab tab in tabs)
		{
			foreach (Def def in tab.defs)
			{
				if (!allIds.Contains(def.id) && !customIds.Contains(def.id) && def.id < 1000)
				{
					allIds.Add(def.id);
				}
			}
		}
		idKills = Stat("EnemyKills");
		idDeaths = Stat("Deaths");
		idBoss = Stat("BossKills");
	}

	private static Tab Add(string name)
	{
		Tab tab = new Tab();
		tab.name = name;
		tabs.Add(tab);
		return tab;
	}

	private static void D(Tab t, string enumName, string label, string kind)
	{
		object obj;
		try
		{
			obj = Enum.Parse(typeof(PlayerStatType), enumName);
		}
		catch (Exception)
		{
			return;
		}
		int num = (int)obj;
		sid[enumName] = num;
		if (!byId.TryGetValue(num, out var value))
		{
			value = new Def();
			value.id = num;
			value.label = label;
			value.kind = kind;
			byId[num] = value;
		}
		t.defs.Add(value);
	}

	private static void Custom(Tab t, int id, string label, string kind)
	{
		if (!byId.TryGetValue(id, out var value))
		{
			value = new Def();
			value.id = id;
			value.label = label;
			value.kind = kind;
			byId[id] = value;
		}
		if (!customIds.Contains(id))
		{
			customIds.Add(id);
		}
		t.defs.Add(value);
	}

	private static bool ClientCustom(int id)
	{
		if (id != 1000)
		{
			if (id >= 2000 && id < 3000)
			{
				return byId.ContainsKey(id);
			}
			return false;
		}
		return true;
	}

	private void Awake()
	{
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Expected O, but got Unknown
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Expected O, but got Unknown
		//IL_014b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0151: Expected O, but got Unknown
		I = this;
		BuildCatalog();
		toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Window", "Toggle key", new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>()), "Opens and closes the crew statistics window. Left/Right arrows change the tab, Up/Down the period.");
		windowScale = ((BaseUnityPlugin)this).Config.Bind<float>("Window", "Scale", 1f, new ConfigDescription("Size of the window.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.6f, 2f), Array.Empty<object>()));
		reportSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Client", "ReportSeconds", 30f, new ConfigDescription("How often your game sends its statistics to the server (only when something changed).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(15f, 600f), Array.Empty<object>()));
		logFile = ((BaseUnityPlugin)this).Config.Bind<string>("Server", "LogFile", Path.Combine(Paths.BepInExRootPath, "crewstats.log"), "Where the server writes what players gained outside of this server.");
		dataDir = Path.Combine(Paths.BepInExRootPath, "crewstats-data");
		store.KillsId = idKills;
		store.DeathsId = idDeaths;
		try
		{
			Directory.CreateDirectory(dataDir);
			store.Load(Path.Combine(dataDir, "stats.tsv"));
		}
		catch (Exception)
		{
		}
		Harmony val = new Harmony("dukr.crewstats");
		Type[] array = new Type[6]
		{
			typeof(RegisterRpc),
			typeof(ConsumePatch),
			typeof(CommandPatch),
			typeof(PickupPatch),
			typeof(DeathPatch),
			typeof(LogoutPatch)
		};
		foreach (Type type in array)
		{
			try
			{
				val.CreateClassProcessor(type).Patch();
			}
			catch (Exception ex2)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("CrewStats patch " + type.Name + " failed: " + ex2.Message));
			}
		}
		PrepareAll();
		((BaseUnityPlugin)this).Logger.LogInfo((object)"CrewStats 1.1.0 loaded");
	}

	private static void PrepareAll()
	{
		try
		{
			Type[] types = typeof(Plugin).Assembly.GetTypes();
			foreach (Type type in types)
			{
				if (type.ContainsGenericParameters)
				{
					continue;
				}
				MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo methodInfo in methods)
				{
					if (!methodInfo.IsAbstract && !methodInfo.ContainsGenericParameters)
					{
						try
						{
							RuntimeHelpers.PrepareMethod(methodInfo.MethodHandle);
						}
						catch (Exception)
						{
						}
					}
				}
			}
		}
		catch (Exception)
		{
		}
	}

	private static string Clean(string s, int max)
	{
		if (string.IsNullOrEmpty(s))
		{
			return "";
		}
		StringBuilder stringBuilder = new StringBuilder();
		foreach (char c in s)
		{
			if (c >= ' ' && c != '\u007f')
			{
				stringBuilder.Append(c);
				if (stringBuilder.Length >= max)
				{
					break;
				}
			}
		}
		return stringBuilder.ToString().Trim();
	}

	private void Log(string text)
	{
		try
		{
			File.AppendAllText(logFile.Value, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " " + text.Replace('\n', ' ') + Environment.NewLine);
		}
		catch (Exception)
		{
		}
	}

	internal void Register()
	{
		if (ZRoutedRpc.instance != null && registeredFor != ZRoutedRpc.instance)
		{
			registeredFor = ZRoutedRpc.instance;
			ZRoutedRpc.instance.Register<ZPackage>("crew_up", (Action<long, ZPackage>)OnUp);
			ZRoutedRpc.instance.Register<ZPackage>("crew_req", (Action<long, ZPackage>)OnReq);
			ZRoutedRpc.instance.Register<ZPackage>("crew_board", (Action<long, ZPackage>)OnBoard);
			ZRoutedRpc.instance.Register<ZPackage>("crew_death", (Action<long, ZPackage>)OnDeathRpc);
		}
	}

	private static string PeerName(long sender)
	{
		foreach (ZNetPeer peer in ZNet.instance.GetPeers())
		{
			if (peer.m_uid == sender)
			{
				return Clean(peer.m_playerName, 48);
			}
		}
		return null;
	}

	private void ServerTick(float now)
	{
		if (onlineAt <= 0f)
		{
			onlineAt = now;
		}
		if (now - onlineAt >= 10f)
		{
			float num = Mathf.Min(now - onlineAt, 30f);
			onlineAt = now;
			DateTime now2 = DateTime.Now;
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				string text = Clean(peer.m_playerName, 48);
				if (text.Length > 0)
				{
					store.AddCustom(text, 1100, num, now2);
				}
			}
			dirty = true;
		}
		if (dirty && now >= saveAt)
		{
			saveAt = now + 60f;
			dirty = false;
			try
			{
				store.Save(Path.Combine(dataDir, "stats.tsv"), DateTime.Now);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("CrewStats save failed: " + ex.Message));
			}
		}
	}

	private static float Fin(float v)
	{
		if (!float.IsNaN(v) && !float.IsInfinity(v))
		{
			return v;
		}
		return 0f;
	}

	private void OnUp(long sender, ZPackage pkg)
	{
		if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
		{
			return;
		}
		try
		{
			string text = PeerName(sender);
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			int num = pkg.ReadByte();
			if (num != 1 && num != 2)
			{
				return;
			}
			bool flag = pkg.ReadBool();
			int num2 = pkg.ReadInt();
			if (num2 < 0 || num2 > 400)
			{
				return;
			}
			Dictionary<int, float> dictionary = new Dictionary<int, float>();
			for (int i = 0; i < num2; i++)
			{
				int num3 = pkg.ReadInt();
				float value = Fin(pkg.ReadSingle());
				if (num3 < 1000 && byId.ContainsKey(num3))
				{
					dictionary[num3] = value;
				}
			}
			Dictionary<int, float> dictionary2 = new Dictionary<int, float>();
			int num4 = pkg.ReadInt();
			if (num4 < 0 || num4 > 60)
			{
				return;
			}
			for (int j = 0; j < num4; j++)
			{
				int num5 = pkg.ReadInt();
				float value2 = Fin(pkg.ReadSingle());
				if (ClientCustom(num5))
				{
					dictionary2[num5] = value2;
				}
			}
			if (num >= 2)
			{
				int num6 = pkg.ReadInt();
				if (num6 < 0 || num6 > 250)
				{
					return;
				}
				for (int k = 0; k < num6; k++)
				{
					string text2 = Clean(pkg.ReadString(), 48);
					float value3 = Fin(pkg.ReadSingle());
					if (text2.Length >= 8 && text2.StartsWith("$enemy_", StringComparison.Ordinal))
					{
						int num7 = Store.CreatureId(text2);
						store.NoteName(num7, text2);
						if (store.names.ContainsKey(num7))
						{
							dictionary[num7] = value3;
						}
					}
				}
			}
			if (!store.players.ContainsKey(text) && store.players.Count >= 64)
			{
				return;
			}
			DateTime now = DateTime.Now;
			Dictionary<int, double> deltas;
			Dictionary<int, double> dictionary3 = store.Report(text, dictionary, dictionary2, flag, now, out deltas);
			dirty = true;
			foreach (KeyValuePair<int, double> item in deltas)
			{
				if (Store.IsCreature(item.Key) && store.names.TryGetValue(item.Key, out var value4) && bossTokens.Contains(value4))
				{
					store.AddBoss(value4, text, now);
				}
			}
			if (flag && dictionary3.Count > 0)
			{
				List<KeyValuePair<int, double>> list = new List<KeyValuePair<int, double>>(dictionary3);
				list.RemoveAll((KeyValuePair<int, double> kv) => Store.IsCreature(kv.Key));
				list.Sort((KeyValuePair<int, double> a, KeyValuePair<int, double> b) => b.Value.CompareTo(a.Value));
				StringBuilder stringBuilder = new StringBuilder("OFFLINE " + text + " changed outside this server:");
				for (int num8 = 0; num8 < list.Count && num8 < 14; num8++)
				{
					stringBuilder.Append((num8 == 0) ? " " : ", ").Append(byId.TryGetValue(list[num8].Key, out var value5) ? value5.label : list[num8].Key.ToString()).Append(" +")
						.Append(list[num8].Value.ToString("0.#", Inv));
				}
				if (list.Count > 0)
				{
					Log(stringBuilder.ToString());
				}
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("CrewStats report from peer " + sender + ": " + ex.Message));
		}
	}

	private void OnDeathRpc(long sender, ZPackage pkg)
	{
		if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
		{
			return;
		}
		try
		{
			string text = PeerName(sender);
			if (!string.IsNullOrEmpty(text) && pkg.ReadByte() == 1)
			{
				string killer = Clean(pkg.ReadString(), 48);
				int biome = pkg.ReadInt();
				float realtimeSinceStartup = Time.realtimeSinceStartup;
				if (!deathAt.TryGetValue(text, out var value) || !(realtimeSinceStartup - value < 8f))
				{
					deathAt[text] = realtimeSinceStartup;
					store.AddDeath(text, killer, biome, DateTime.Now);
					dirty = true;
				}
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("CrewStats death from peer " + sender + ": " + ex.Message));
		}
	}

	private void OnReq(long sender, ZPackage pkg)
	{
		//IL_011b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: Expected O, but got Unknown
		if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
		{
			return;
		}
		try
		{
			DateTime now = DateTime.Now;
			HashSet<string> hashSet = new HashSet<string>();
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				hashSet.Add(Clean(peer.m_playerName, 48));
			}
			List<KeyValuePair<string, PlayerData>> list = new List<KeyValuePair<string, PlayerData>>(store.players);
			list.RemoveAll((KeyValuePair<string, PlayerData> kv) => !kv.Value.hasBase);
			list.Sort((KeyValuePair<string, PlayerData> a, KeyValuePair<string, PlayerData> b) => b.Value.lastSeen.CompareTo(a.Value.lastSeen));
			if (list.Count > 10)
			{
				list.RemoveRange(10, list.Count - 10);
			}
			List<int> list2 = new List<int>(allIds);
			list2.AddRange(customIds);
			list2.AddRange(store.names.Keys);
			HashSet<int> hashSet2 = new HashSet<int>();
			ZPackage val = new ZPackage();
			val.Write((byte)2);
			val.Write(list.Count);
			foreach (KeyValuePair<string, PlayerData> item in list)
			{
				int count;
				string text = store.Nemesis(item.Value, out count);
				val.Write(item.Key);
				val.Write(hashSet.Contains(item.Key));
				val.Write((float)item.Value.bestLife);
				val.Write(text);
				val.Write(count);
				List<int> list3 = new List<int>();
				List<float[]> list4 = new List<float[]>();
				foreach (int item2 in list2)
				{
					float num = (float)store.Value(item.Value, item2, 0, now);
					float num2 = (float)store.Value(item.Value, item2, 1, now);
					float num3 = (float)store.Value(item.Value, item2, 2, now);
					if (num != 0f || num2 != 0f || num3 != 0f)
					{
						list3.Add(item2);
						list4.Add(new float[3] { num, num2, num3 });
						if (Store.IsCreature(item2))
						{
							hashSet2.Add(item2);
						}
					}
				}
				val.Write(list3.Count);
				for (int num4 = 0; num4 < list3.Count; num4++)
				{
					val.Write(list3[num4]);
					val.Write(list4[num4][0]);
					val.Write(list4[num4][1]);
					val.Write(list4[num4][2]);
				}
			}
			val.Write(hashSet2.Count);
			foreach (int item3 in hashSet2)
			{
				val.Write(item3);
				val.Write(store.names[item3]);
			}
			int num5 = Math.Min(12, store.deaths.Count);
			val.Write(num5);
			for (int num6 = 0; num6 < num5; num6++)
			{
				DeathRec deathRec = store.deaths[num6];
				val.Write(deathRec.ticks);
				val.Write(deathRec.player);
				val.Write(deathRec.killer);
				val.Write(deathRec.biome);
			}
			int num7 = Math.Min(12, store.bosses.Count);
			val.Write(num7);
			for (int num8 = 0; num8 < num7; num8++)
			{
				BossRec bossRec = store.bosses[num8];
				val.Write(bossRec.ticks);
				val.Write(bossRec.boss);
				val.Write(string.Join(", ", bossRec.players.ToArray()));
			}
			ZRoutedRpc.instance.InvokeRoutedRPC(sender, "crew_board", new object[1] { val });
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("CrewStats board: " + ex.Message));
		}
	}

	internal static void AddPend(int id, float n)
	{
		pend.TryGetValue(id, out var value);
		pend[id] = value + n;
	}

	internal static bool ResId(string token, out int id)
	{
		return resByToken.TryGetValue(token ?? "", out id);
	}

	private void Update()
	{
		if ((Object)(object)ZNet.instance == (Object)null || ZRoutedRpc.instance == null)
		{
			return;
		}
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		if (ZNet.instance.IsServer())
		{
			try
			{
				ServerTick(realtimeSinceStartup);
				return;
			}
			catch (Exception)
			{
				return;
			}
		}
		if ((Object)(object)Player.m_localPlayer == (Object)null)
		{
			loginSent = false;
			spawnT = 0f;
			lastSent = null;
			lastSentCre = null;
			HideWindow();
			return;
		}
		if (spawnT <= 0f)
		{
			spawnT = realtimeSinceStartup;
		}
		try
		{
			WindowTick(realtimeSinceStartup);
			InventoryButtonTick();
		}
		catch (Exception ex2)
		{
			Warn("window: " + ex2.Message);
		}
		if (realtimeSinceStartup < nextReport || realtimeSinceStartup - spawnT < 15f)
		{
			return;
		}
		nextReport = realtimeSinceStartup + Mathf.Max(15f, reportSeconds.Value);
		try
		{
			Report(!loginSent);
		}
		catch (Exception ex3)
		{
			Warn("report: " + ex3.Message);
		}
	}

	private void Warn(string m)
	{
		float realtimeSinceStartup = Time.realtimeSinceStartup;
		if (!(realtimeSinceStartup < warnAt))
		{
			warnAt = realtimeSinceStartup + 30f;
			((BaseUnityPlugin)this).Logger.LogWarning((object)("CrewStats " + m));
		}
	}

	internal void FlushNow()
	{
		try
		{
			if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer() && (Object)(object)Player.m_localPlayer != (Object)null && loginSent)
			{
				Report(login: false);
			}
		}
		catch (Exception)
		{
		}
	}

	private void Report(bool login)
	{
		//IL_0218: Unknown result type (might be due to invalid IL or missing references)
		//IL_021f: Expected O, but got Unknown
		PlayerProfile val = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null);
		if (val == null)
		{
			return;
		}
		PlayerStats val2 = ((val.m_playerStats != null && val.m_playerStats.Length != 0) ? val.m_playerStats[0] : null);
		if (val2 == null || val2.m_stats == null)
		{
			return;
		}
		ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
		if (serverPeer == null)
		{
			return;
		}
		Dictionary<int, float> dictionary = new Dictionary<int, float>();
		foreach (int allId in allIds)
		{
			if (val2.m_stats.TryGetValue((PlayerStatType)allId, out var value))
			{
				dictionary[allId] = value;
			}
		}
		Dictionary<string, float> dictionary2 = new Dictionary<string, float>();
		if (val2.m_enemyStats != null && val2.m_enemyStats.Length != 0 && val2.m_enemyStats[0] != null)
		{
			foreach (KeyValuePair<string, float> item in val2.m_enemyStats[0])
			{
				if (item.Key != null && item.Key.StartsWith("$enemy_", StringComparison.Ordinal) && dictionary2.Count < 240)
				{
					dictionary2[item.Key] = item.Value;
				}
			}
		}
		bool flag = login || pend.Count > 0 || lastSent == null || lastSentCre == null;
		if (!flag)
		{
			foreach (KeyValuePair<int, float> item2 in dictionary)
			{
				if (!lastSent.TryGetValue(item2.Key, out var value2) || value2 != item2.Value)
				{
					flag = true;
					break;
				}
			}
		}
		if (!flag)
		{
			foreach (KeyValuePair<string, float> item3 in dictionary2)
			{
				if (!lastSentCre.TryGetValue(item3.Key, out var value3) || value3 != item3.Value)
				{
					flag = true;
					break;
				}
			}
		}
		if (!flag)
		{
			return;
		}
		ZPackage val3 = new ZPackage();
		val3.Write((byte)2);
		val3.Write(login);
		val3.Write(dictionary.Count);
		foreach (KeyValuePair<int, float> item4 in dictionary)
		{
			val3.Write(item4.Key);
			val3.Write(item4.Value);
		}
		List<KeyValuePair<int, float>> list = new List<KeyValuePair<int, float>>(pend);
		if (list.Count > 50)
		{
			list.RemoveRange(50, list.Count - 50);
		}
		val3.Write(list.Count);
		foreach (KeyValuePair<int, float> item5 in list)
		{
			val3.Write(item5.Key);
			val3.Write(item5.Value);
		}
		val3.Write(dictionary2.Count);
		foreach (KeyValuePair<string, float> item6 in dictionary2)
		{
			val3.Write(item6.Key);
			val3.Write(item6.Value);
		}
		ZRoutedRpc.instance.InvokeRoutedRPC(serverPeer.m_uid, "crew_up", new object[1] { val3 });
		lastSent = dictionary;
		lastSentCre = dictionary2;
		foreach (KeyValuePair<int, float> item7 in list)
		{
			float num = pend[item7.Key] - item7.Value;
			if (num > 0.001f)
			{
				pend[item7.Key] = num;
			}
			else
			{
				pend.Remove(item7.Key);
			}
		}
		loginSent = true;
	}

	internal void SendDeath(string killer, int biome)
	{
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Expected O, but got Unknown
		try
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer() && ZRoutedRpc.instance != null)
			{
				ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
				if (serverPeer != null)
				{
					ZPackage val = new ZPackage();
					val.Write((byte)1);
					val.Write(Clean(killer, 48));
					val.Write(biome);
					ZRoutedRpc.instance.InvokeRoutedRPC(serverPeer.m_uid, "crew_death", new object[1] { val });
				}
			}
		}
		catch (Exception)
		{
		}
	}

	private bool FromServer(long sender)
	{
		try
		{
			ZNetPeer val = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetServerPeer() : null);
			if (val != null && val.m_uid == sender)
			{
				return true;
			}
		}
		catch (Exception)
		{
		}
		return false;
	}

	private void OnBoard(long sender, ZPackage pkg)
	{
		if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer() || !FromServer(sender))
		{
			return;
		}
		try
		{
			if (pkg.ReadByte() != 2)
			{
				return;
			}
			int num = pkg.ReadInt();
			if (num < 0 || num > 12)
			{
				return;
			}
			List<BP> list = new List<BP>();
			for (int i = 0; i < num; i++)
			{
				BP bP = new BP();
				bP.name = Clean(pkg.ReadString(), 48);
				bP.online = pkg.ReadBool();
				bP.bestLife = pkg.ReadSingle();
				bP.nemesis = Clean(pkg.ReadString(), 48);
				bP.nemesisCount = pkg.ReadInt();
				int num2 = pkg.ReadInt();
				if (num2 < 0 || num2 > 600)
				{
					return;
				}
				for (int j = 0; j < num2; j++)
				{
					int key = pkg.ReadInt();
					float num3 = pkg.ReadSingle();
					float num4 = pkg.ReadSingle();
					float num5 = pkg.ReadSingle();
					bP.v[key] = new float[3] { num3, num4, num5 };
				}
				list.Add(bP);
			}
			Dictionary<int, string> dictionary = new Dictionary<int, string>();
			int num6 = pkg.ReadInt();
			if (num6 < 0 || num6 > 700)
			{
				return;
			}
			for (int k = 0; k < num6; k++)
			{
				int key2 = pkg.ReadInt();
				dictionary[key2] = Clean(pkg.ReadString(), 48);
			}
			List<DeathLine> list2 = new List<DeathLine>();
			int num7 = pkg.ReadInt();
			if (num7 < 0 || num7 > 20)
			{
				return;
			}
			for (int l = 0; l < num7; l++)
			{
				DeathLine deathLine = new DeathLine();
				deathLine.ticks = pkg.ReadLong();
				deathLine.player = Clean(pkg.ReadString(), 48);
				deathLine.killer = Clean(pkg.ReadString(), 48);
				deathLine.biome = pkg.ReadInt();
				list2.Add(deathLine);
			}
			List<BossLine> list3 = new List<BossLine>();
			int num8 = pkg.ReadInt();
			if (num8 < 0 || num8 > 20)
			{
				return;
			}
			for (int m = 0; m < num8; m++)
			{
				BossLine bossLine = new BossLine();
				bossLine.ticks = pkg.ReadLong();
				bossLine.boss = Clean(pkg.ReadString(), 48);
				bossLine.players = Clean(pkg.ReadString(), 120);
				list3.Add(bossLine);
			}
			board.Clear();
			board.AddRange(list);
			cNames.Clear();
			foreach (KeyValuePair<int, string> item in dictionary)
			{
				cNames[item.Key] = item.Value;
			}
			deathLines.Clear();
			deathLines.AddRange(list2);
			bossLines.Clear();
			bossLines.AddRange(list3);
			boardDirty = true;
			boardAt = Time.realtimeSinceStartup;
		}
		catch (Exception ex)
		{
			Warn("board parse: " + ex.Message);
		}
	}

	private void RequestBoard()
	{
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Expected O, but got Unknown
		ZNetPeer val = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetServerPeer() : null);
		if (val != null)
		{
			ZPackage val2 = new ZPackage();
			val2.Write((byte)1);
			ZRoutedRpc.instance.InvokeRoutedRPC(val.m_uid, "crew_req", new object[1] { val2 });
		}
	}

	private static bool UiBlocked()
	{
		try
		{
			if (Menu.IsVisible() || Minimap.IsOpen() || TextInput.IsVisible())
			{
				return true;
			}
		}
		catch (Exception)
		{
		}
		try
		{
			if (Console.IsVisible())
			{
				return true;
			}
		}
		catch (Exception)
		{
		}
		return false;
	}

	internal void Toggle()
	{
		open = !open;
		if (open)
		{
			RequestBoard();
			reqAt = Time.realtimeSinceStartup;
			needRender = true;
		}
	}

	private void HideWindow()
	{
		if ((Object)(object)root != (Object)null && root.activeSelf)
		{
			root.SetActive(false);
		}
	}

	private void WindowTick(float now)
	{
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: 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_002f: 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)
		bool flag = (Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus();
		KeyboardShortcut value = toggleKey.Value;
		KeyCode mainKey = ((KeyboardShortcut)(ref value)).MainKey;
		if ((int)mainKey != 0 && !flag && Input.GetKeyDown(mainKey))
		{
			Toggle();
		}
		if (!open || UiBlocked())
		{
			HideWindow();
			return;
		}
		if (!flag)
		{
			if (Input.GetKeyDown((KeyCode)275))
			{
				tab = (tab + 1) % tabs.Count;
				needRender = true;
			}
			if (Input.GetKeyDown((KeyCode)276))
			{
				tab = (tab + tabs.Count - 1) % tabs.Count;
				needRender = true;
			}
			if (Input.GetKeyDown((KeyCode)273))
			{
				period = (period + 2) % 3;
				needRender = true;
			}
			if (Input.GetKeyDown((KeyCode)274))
			{
				period = (period + 1) % 3;
				needRender = true;
			}
		}
		if (now - reqAt > 10f)
		{
			reqAt = now;
			RequestBoard();
		}
		if (!((Object)(object)root == (Object)null) || CreateWindow())
		{
			if (!root.activeSelf)
			{
				root.SetActive(true);
				needRender = true;
			}
			if (boardDirty || needRender)
			{
				boardDirty = false;
				needRender = false;
				Render();
			}
		}
	}

	private static Font ResolveFont()
	{
		Font[] array = Resources.FindObjectsOfTypeAll<Font>();
		foreach (Font val in array)
		{
			if ((Object)(object)val != (Object)null && ((Object)val).name == "AveriaSerifLibre-Bold")
			{
				return val;
			}
		}
		array = Resources.FindObjectsOfTypeAll<Font>();
		foreach (Font val2 in array)
		{
			if ((Object)(object)val2 != (Object)null && val2.HasCharacter('Č'))
			{
				return val2;
			}
		}
		try
		{
			Font builtinResource = Resources.GetBuiltinResource<Font>("Arial.ttf");
			if ((Object)(object)builtinResource != (Object)null)
			{
				return builtinResource;
			}
		}
		catch (Exception)
		{
		}
		try
		{
			Font builtinResource2 = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
			if ((Object)(object)builtinResource2 != (Object)null)
			{
				return builtinResource2;
			}
		}
		catch (Exception)
		{
		}
		return null;
	}

	private Sprite FindSprite(string name)
	{
		if (sprites.TryGetValue(name, out var value) && (Object)(object)value != (Object)null)
		{
			return value;
		}
		value = null;
		try
		{
			Sprite[] array = Resources.FindObjectsOfTypeAll<Sprite>();
			foreach (Sprite val in array)
			{
				if ((Object)(object)val != (Object)null && ((Object)val).name == name)
				{
					value = val;
					break;
				}
			}
		}
		catch (Exception)
		{
		}
		sprites[name] = value;
		return value;
	}

	private static void SetRect(RectTransform r, float x, float y, float w, float h)
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: 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_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		r.anchorMin = new Vector2(0f, 1f);
		r.anchorMax = new Vector2(0f, 1f);
		r.pivot = new Vector2(0f, 1f);
		r.anchoredPosition = new Vector2(x, y);
		r.sizeDelta = new Vector2(w, h);
	}

	private Image AddImage(Transform parent, string name, string sprite, Color col, bool sliced, bool fill)
	{
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: 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_0077: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject(name, new Type[2]
		{
			typeof(RectTransform),
			typeof(Image)
		});
		val.transform.SetParent(parent, false);
		RectTransform val2 = (RectTransform)val.transform;
		val2.anchorMin = Vector2.zero;
		val2.anchorMax = Vector2.one;
		val2.offsetMin = Vector2.zero;
		val2.offsetMax = Vector2.zero;
		Image component = val.GetComponent<Image>();
		((Graphic)component).raycastTarget = false;
		((Graphic)component).color = col;
		Sprite val3 = ((sprite != null) ? FindSprite(sprite) : null);
		if ((Object)(object)val3 != (Object)null)
		{
			component.sprite = val3;
			if (sliced)
			{
				component.type = (Type)1;
				component.pixelsPerUnitMultiplier = 2.5f;
			}
		}
		return component;
	}

	private Text AddText(Transform parent, string text, float x, float y, float w, float h, int size, TextAnchor al, FontStyle st, Color col)
	{
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Expected O, but got Unknown
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Expected O, but got Unknown
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: 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)
		GameObject val = new GameObject("T", new Type[2]
		{
			typeof(RectTransform),
			typeof(Text)
		});
		val.transform.SetParent(parent, false);
		SetRect((RectTransform)val.transform, x, y, w, h);
		Text component = val.GetComponent<Text>();
		component.font = font;
		component.fontSize = size;
		component.fontStyle = st;
		component.alignment = al;
		((Graphic)component).color = col;
		((Graphic)component).raycastTarget = false;
		component.text = text;
		component.horizontalOverflow = (HorizontalWrapMode)1;
		component.verticalOverflow = (VerticalWrapMode)0;
		Outline obj = val.AddComponent<Outline>();
		((Shadow)obj).effectColor = new Color(0f, 0f, 0f, 0.8f);
		((Shadow)obj).effectDistance = new Vector2(1f, -1f);
		dyn.Add(val);
		return component;
	}

	private void AddClick(float x, float y, float w, float h, UnityAction act)
	{
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Expected O, but got Unknown
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Expected O, but got Unknown
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject("Click", new Type[3]
		{
			typeof(RectTransform),
			typeof(Image),
			typeof(Button)
		});
		val.transform.SetParent((Transform)(object)panel, false);
		SetRect((RectTransform)val.transform, x, y, w, h);
		Image component = val.GetComponent<Image>();
		((Graphic)component).color = new Color(1f, 1f, 1f, 0.004f);
		((Graphic)component).raycastTarget = true;
		((UnityEvent)val.GetComponent<Button>().onClick).AddListener(act);
		dyn.Add(val);
	}

	private void InventoryButtonTick()
	{
		//IL_0083: Unknown result type (might be due to invalid IL or missing references)
		//IL_008d: Expected O, but got Unknown
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00af: 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_00c2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_0102: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0166: Unknown result type (might be due to invalid IL or missing references)
		//IL_019d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_0211: Unknown result type (might be due to invalid IL or missing references)
		//IL_0217: 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)
		//IL_021d: Unknown result type (might be due to invalid IL or missing references)
		//IL_022f: 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_023b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0246: Unknown result type (might be due to invalid IL or missing references)
		//IL_0250: Unknown result type (might be due to invalid IL or missing references)
		//IL_0265: Unknown result type (might be due to invalid IL or missing references)
		//IL_0278: Unknown result type (might be due to invalid IL or missing references)
		//IL_0288: Unknown result type (might be due to invalid IL or missing references)
		//IL_02bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c9: Expected O, but got Unknown
		if ((Object)(object)invBtn != (Object)null)
		{
			return;
		}
		InventoryGui instance = InventoryGui.instance;
		if ((Object)(object)instance == (Object)null || !InventoryGui.IsVisible())
		{
			return;
		}
		RectTransform player = instance.m_player;
		if (!((Object)(object)player == (Object)null))
		{
			if ((Object)(object)font == (Object)null)
			{
				font = ResolveFont();
			}
			invBtn = new GameObject("CrewStatsButton", new Type[3]
			{
				typeof(RectTransform),
				typeof(Image),
				typeof(Button)
			});
			invBtn.transform.SetParent((Transform)(object)player, false);
			RectTransform val = (RectTransform)invBtn.transform;
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(1f, 0f);
			val.anchorMax = val2;
			val.anchorMin = val2;
			val.pivot = new Vector2(0f, 1f);
			val.anchoredPosition = new Vector2(8f, -95f);
			val.sizeDelta = new Vector2(40f, 40f);
			Image component = invBtn.GetComponent<Image>();
			((Graphic)component).color = new Color(1f, 1f, 1f, 0.004f);
			((Graphic)component).raycastTarget = true;
			AddImage(invBtn.transform, "Fill", "panel_interior_bkg_128", new Color(0.55f, 0.36f, 0.18f, 0.9f), sliced: true, fill: false);
			AddImage(invBtn.transform, "Border", "panel_border_128", new Color(1f, 1f, 1f, 0.95f), sliced: true, fill: false);
			float[] array = new float[4] { 10f, 20f, 15f, 26f };
			for (int i = 0; i < array.Length; i++)
			{
				GameObject val3 = new GameObject("Bar" + i, new Type[2]
				{
					typeof(RectTransform),
					typeof(Image)
				});
				val3.transform.SetParent(invBtn.transform, false);
				RectTransform val4 = (RectTransform)val3.transform;
				((Vector2)(ref val2))..ctor(0f, 0f);
				val4.anchorMax = val2;
				val4.anchorMin = val2;
				val4.pivot = new Vector2(0f, 0f);
				val4.anchoredPosition = new Vector2(8f + (float)i * 6.5f, 7f);
				val4.sizeDelta = new Vector2(5f, array[i]);
				Image component2 = val3.GetComponent<Image>();
				((Graphic)component2).color = Gold;
				((Graphic)component2).raycastTarget = false;
			}
			((UnityEvent)invBtn.GetComponent<Button>().onClick).AddListener((UnityAction)delegate
			{
				Toggle();
			});
		}
	}

	private bool CreateWindow()
	{
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Expected O, but got Unknown
		//IL_00ab: 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_00dd: Expected O, but got Unknown
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0105: Expected O, but got Unknown
		//IL_011b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0120: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_0127: Unknown result type (might be due to invalid IL or missing references)
		//IL_013d: Unknown result type (might be due to invalid IL or missing references)
		//IL_014d: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			font = ResolveFont();
			if ((Object)(object)font == (Object)null)
			{
				return false;
			}
			root = new GameObject("CrewStatsWindow", new Type[4]
			{
				typeof(RectTransform),
				typeof(Canvas),
				typeof(CanvasScaler),
				typeof(GraphicRaycaster)
			});
			Object.DontDestroyOnLoad((Object)(object)root);
			Canvas component = root.GetComponent<Canvas>();
			component.renderMode = (RenderMode)0;
			component.sortingOrder = 41;
			CanvasScaler component2 = root.GetComponent<CanvasScaler>();
			component2.uiScaleMode = (ScaleMode)1;
			component2.referenceResolution = new Vector2(1920f, 1080f);
			component2.matchWidthOrHeight = 0.5f;
			GameObject val = new GameObject("Panel", new Type[1] { typeof(RectTransform) });
			val.transform.SetParent(root.transform, false);
			panel = (RectTransform)val.transform;
			RectTransform obj = panel;
			Vector2 anchorMin = (panel.anchorMax = new Vector2(0.5f, 0.5f));
			obj.anchorMin = anchorMin;
			panel.pivot = new Vector2(0.5f, 0.5f);
			panel.anchoredPosition = Vector2.zero;
			return true;
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("CrewStats window creation failed: " + ex.Message));
			if ((Object)(object)root != (Object)null)
			{
				Object.Destroy((Object)(object)root);
			}
			root = null;
			return false;
		}
	}

	private static string Fmt(Def d, float v)
	{
		if (v <= 0f)
		{
			return "-";
		}
		if (d.kind == "km")
		{
			return (v / 1000f).ToString("0.0", Inv) + " km";
		}
		if (d.kind == "h")
		{
			int num = (int)(v / 60f);
			return num / 60 + "h " + (num % 60).ToString("00") + "m";
		}
		return ((long)Math.Round(v)).ToString("#,0", Inv).Replace(',', ' ');
	}

	private static string FmtN(double v)
	{
		return ((long)Math.Round(v)).ToString("#,0", Inv).Replace(',', ' ');
	}

	private static string Localize(string token)
	{
		try
		{
			if (!locTried || locInstance == null)
			{
				locTried = true;
				Type type = AccessTools.TypeByName("Localization");
				if (type != null)
				{
					FieldInfo fieldInfo = AccessTools.Field(type, "m_instance");
					PropertyInfo propertyInfo = AccessTools.Property(type, "instance");
					locInstance = ((propertyInfo != null) ? propertyInfo.GetValue(null, null) : ((fieldInfo != null) ? fieldInfo.GetValue(null) : null));
					locMethod = AccessTools.Method(type, "Localize", new Type[1] { typeof(string) }, (Type[])null);
				}
			}
			if (locInstance != null && locMethod != null)
			{
				string text = locMethod.Invoke(locInstance, new object[1] { token }) as string;
				if (!string.IsNullOrEmpty(text) && text != token && text.IndexOf('$') < 0)
				{
					return text;
				}
			}
		}
		catch (Exception)
		{
		}
		string text2 = (token.StartsWith("$enemy_", StringComparison.Ordinal) ? token.Substring(7) : token.TrimStart(new char[1] { '$' }));
		text2 = text2.Replace('_', ' ');
		if (text2.Length <= 0)
		{
			return token;
		}
		return char.ToUpperInvariant(text2[0]) + text2.Substring(1);
	}

	private static string KillerName(string k)
	{
		if (string.IsNullOrEmpty(k))
		{
			return "unknown";
		}
		if (k.StartsWith("player:", StringComparison.Ordinal))
		{
			return k.Substring(7) + " (PvP)";
		}
		if (k.StartsWith("$hit_", StringComparison.Ordinal))
		{
			return Localize("$" + k.Substring(5)).Replace("$", "");
		}
		return Localize(k);
	}

	private static string BiomeName(int b)
	{
		return b switch
		{
			1 => "Meadows", 
			2 => "Swamp", 
			4 => "Mountain", 
			8 => "Black Forest", 
			16 => "Plains", 
			32 => "Ashlands", 
			64 => "Deep North", 
			256 => "Ocean", 
			512 => "Mistlands", 
			_ => "?", 
		};
	}

	private float V(BP p, int id)
	{
		if (id < 0 || !p.v.TryGetValue(id, out var value))
		{
			return 0f;
		}
		return value[period];
	}

	private float VS(BP p, params int[] ids)
	{
		float num = 0f;
		foreach (int id in ids)
		{
			num += V(p, id);
		}
		return num;
	}

	private static int[] ResIds(string group)
	{
		List<int> list = new List<int>();
		for (int i = 0; i < Res.GetLength(0); i++)
		{
			if (Res[i, 3] == group)
			{
				list.Add(int.Parse(Res[i, 0], Inv));
			}
		}
		return list.ToArray();
	}

	private Color PlayerColor(string name)
	{
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		uint num = Palette[(uint)Hash(name) % (uint)Palette.Length];
		return new Color((float)((num >> 16) & 0xFF) / 255f, (float)((num >> 8) & 0xFF) / 255f, (float)(num & 0xFF) / 255f, 1f);
	}

	private List<AwardRow> BuildAwards()
	{
		//IL_028e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0293: Unknown result type (might be due to invalid IL or missing references)
		List<AwardRow> list = new List<AwardRow>();
		int[] ore = ResIds("ore");
		int[] wood = ResIds("wood");
		int[] stone = ResIds("stone");
		int[] ice = ResIds("ice");
		Aw(list, "Slayer - most enemies killed", (BP p) => V(p, idKills), "n");
		Aw(list, "Boss hunter - most bosses killed", (BP p) => V(p, idBoss), "n");
		Aw(list, "Marksman - most arrows shot", (BP p) => V(p, Stat("ArrowsShot")), "n");
		Aw(list, "Punching bag - most hits taken", (BP p) => V(p, Stat("HitsTakenEnemies")), "n");
		Aw(list, "Daredevil - most deaths", (BP p) => V(p, idDeaths), "n");
		Aw(list, "Miner - most ore picked up", (BP p) => VS(p, ore), "n");
		Aw(list, "Lumberjack - most wood picked up", (BP p) => VS(p, wood), "n");
		Aw(list, "Mason - most stone picked up", (BP p) => VS(p, stone), "n");
		Aw(list, "Iceman - most ice picked up", (BP p) => VS(p, ice), "n");
		Aw(list, "Explorer - farthest on foot", (BP p) => VS(p, Stat("DistanceWalk"), Stat("DistanceRun")), "km");
		Aw(list, "Sailor - farthest at sea", (BP p) => V(p, Stat("DistanceSail")), "km");
		Aw(list, "Chef - most food cooked", (BP p) => VS(p, Stat("CraftFood"), Stat("CraftGrill")), "n");
		Aw(list, "Blacksmith - most gear forged", (BP p) => VS(p, Stat("CraftWeapon"), Stat("CraftArmor"), Stat("Upgrades")), "n");
		Aw(list, "Farmer - most harvested", (BP p) => VS(p, Stat("HarvestCrop"), Stat("HarvestBerry"), Stat("HarvestMushroom")), "n");
		Aw(list, "Angler - most fish caught", (BP p) => V(p, Stat("FishCaught")), "n");
		Aw(list, "Homebody - most time in base", (BP p) => V(p, Stat("TimeInBase")), "h");
		Aw(list, "Veteran - most time online", (BP p) => V(p, 1100), "h");
		AwardRow awardRow = null;
		float num = 0f;
		foreach (BP item in board)
		{
			if (item.bestLife > num)
			{
				num = item.bestLife;
				awardRow = new AwardRow();
				awardRow.winner = item.name;
				awardRow.col = PlayerColor(item.name);
			}
		}
		if (awardRow != null)
		{
			awardRow.title = "Legendary life - most kills without dying (all time)";
			awardRow.value = FmtN(num);
			list.Add(awardRow);
		}
		return list;
	}

	private void Aw(List<AwardRow> rows, string title, Func<BP, float> f, string kind)
	{
		//IL_00a5: 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)
		BP bP = null;
		float num = 0f;
		bool flag = false;
		foreach (BP item in board)
		{
			float num2 = f(item);
			if (num2 > num)
			{
				num = num2;
				bP = item;
				flag = false;
			}
			else if (num2 == num && num2 > 0f)
			{
				flag = true;
			}
		}
		if (bP != null && !(num <= 0f))
		{
			AwardRow awardRow = new AwardRow();
			awardRow.title = title;
			awardRow.winner = bP.name + (flag ? " (tie)" : "");
			awardRow.col = PlayerColor(bP.name);
			Def def = new Def();
			def.kind = kind;
			awardRow.value = Fmt(def, num);
			rows.Add(awardRow);
		}
	}

	private List<Def> CreatureDefs()
	{
		Dictionary<int, double> dictionary = new Dictionary<int, double>();
		foreach (BP item in board)
		{
			foreach (KeyValuePair<int, float[]> item2 in item.v)
			{
				if (Store.IsCreature(item2.Key) && item2.Value[period] > 0f)
				{
					dictionary.TryGetValue(item2.Key, out var value);
					dictionary[item2.Key] = value + (double)item2.Value[period];
				}
			}
		}
		List<KeyValuePair<int, double>> list = new List<KeyValuePair<int, double>>(dictionary);
		list.Sort((KeyValuePair<int, double> a, KeyValuePair<int, double> b) => b.Value.CompareTo(a.Value));
		List<Def> list2 = new List<Def>();
		for (int num = 0; num < list.Count && num < 16; num++)
		{
			Def def = new Def();
			def.id = list[num].Key;
			def.kind = "n";
			def.label = (cNames.TryGetValue(list[num].Key, out var value2) ? Localize(value2) : "?");
			list2.Add(def);
		}
		return list2;
	}

	private void Render()
	{
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Expected O, but got Unknown
		//IL_025a: Unknown result type (might be due to invalid IL or missing references)
		//IL_026a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0289: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_02fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_031c: Expected O, but got Unknown
		//IL_0341: Unknown result type (might be due to invalid IL or missing references)
		//IL_0372: Unknown result type (might be due to invalid IL or missing references)
		//IL_03ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_03e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_04ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_04a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0645: Unknown result type (might be due to invalid IL or missing references)
		//IL_0415: Unknown result type (might be due to invalid IL or missing references)
		//IL_041f: Expected O, but got Unknown
		//IL_04d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_04e0: Expected O, but got Unknown
		//IL_06b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_06c5: Unknown result type (might be due to invalid IL or missing references)
		//IL_06e6: Expected O, but got Unknown
		//IL_0684: Unknown result type (might be due to invalid IL or missing references)
		//IL_071f: Unknown result type (might be due to invalid IL or missing references)
		//IL_056f: Unknown result type (might be due to invalid IL or missing references)
		//IL_05a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_05b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_05e9: Expected O, but got Unknown
		//IL_0b4c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0882: Unknown result type (might be due to invalid IL or missing references)
		//IL_0bf5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0ba3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0bb4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0bd2: Expected O, but got Unknown
		//IL_0f66: Unknown result type (might be due to invalid IL or missing references)
		//IL_0f6b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0f6f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0f74: Unknown result type (might be due to invalid IL or missing references)
		//IL_0fac: Unknown result type (might be due to invalid IL or missing references)
		//IL_0df8: Unknown result type (might be due to invalid IL or missing references)
		//IL_07b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_07fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_082c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0764: Unknown result type (might be due to invalid IL or missing references)
		//IL_0775: Unknown result type (might be due to invalid IL or missing references)
		//IL_0793: Expected O, but got Unknown
		//IL_08c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0e3e: Unknown result type (might be due to invalid IL or missing references)
		//IL_09d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_092f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0976: Unknown result type (might be due to invalid IL or missing references)
		//IL_0e9b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0ed1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0f22: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a4f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a9b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0af7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0ccd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0cd2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0ced: Unknown result type (might be due to invalid IL or missing references)
		//IL_0cfb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0d2a: Expected O, but got Unknown
		//IL_0d75: Unknown result type (might be due to invalid IL or missing references)
		//IL_0d6e: Unknown result type (might be due to invalid IL or missing references)
		foreach (GameObject item in dyn)
		{
			if ((Object)(object)item != (Object)null)
			{
				Object.Destroy((Object)(object)item);
			}
		}
		dyn.Clear();
		foreach (Transform item2 in (Transform)panel)
		{
			Transform val = item2;
			if ((Object)(object)val != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)val).gameObject);
			}
		}
		Tab tab = tabs[this.tab];
		int num = Math.Max(1, board.Count);
		List<Def> list = ((tab.page == "creatures") ? CreatureDefs() : tab.defs);
		List<AwardRow> list2 = ((tab.page == "awards") ? BuildAwards() : null);
		bool flag = tab.name == "Deaths";
		int num2 = 0;
		if (list2 != null)
		{
			num2 = 2 + Math.Min(bossLines.Count, 6) + ((bossLines.Count == 0) ? 1 : 0) + 2 + Math.Min(NemesisCount(), 5) + ((NemesisCount() == 0) ? 1 : 0);
		}
		else if (flag)
		{
			num2 = 2 + Math.Min(deathLines.Count, 6) + ((deathLines.Count == 0) ? 1 : 0);
		}
		int num3 = ((list2 != null) ? (list2.Count + ((list2.Count == 0) ? 1 : 0)) : Math.Max(1, list.Count));
		float num4 = 14f;
		foreach (Tab tab2 in tabs)
		{
			num4 += 22f + (float)tab2.name.Length * 9f + 6f;
		}
		float num5 = Mathf.Max(190f + 96f * (float)num + 36f, Mathf.Max(num4 + 14f, (list2 != null) ? 640f : 0f));
		float num6 = 92f + 24f * (float)(num3 + 1 + num2) + 34f;
		panel.sizeDelta = new Vector2(num5, num6);
		((Transform)panel).localScale = Vector3.one * Mathf.Clamp(windowScale.Value, 0.6f, 2f);
		AddImage((Transform)(object)panel, "Interior", "panel_interior_bkg_128", new Color(1f, 1f, 1f, 0.92f), sliced: true, fill: false);
		SetRect((RectTransform)((Component)AddImage((Transform)(object)panel, "Band", "panel_interior_bkg_128", new Color(0.55f, 0.36f, 0.18f, 0.55f), sliced: true, fill: false)).transform, 4f, -4f, num5 - 8f, 34f);
		AddImage((Transform)(object)panel, "Border", "panel_border_128", new Color(1f, 1f, 1f, 0.95f), sliced: true, fill: false);
		AddText((Transform)(object)panel, "Crew stats", 14f, -4f, 220f, 34f, 20, (TextAnchor)3, (FontStyle)1, Gold);
		float num7 = num5 - 14f;
		for (int num8 = 2; num8 >= 0; num8--)
		{
			float num9 = ((num8 == 1) ? 108f : 78f);
			num7 -= num9;
			AddText((Transform)(object)panel, Periods[num8], num7, -4f, num9, 34f, 14, (TextAnchor)4, (FontStyle)1, (num8 == period) ? Gold : Dim);
			int pi = num8;
			AddClick(num7, -4f, num9, 34f, (UnityAction)delegate
			{
				period = pi;
				needRender = true;
			});
		}
		float num10 = 14f;
		for (int num11 = 0; num11 < tabs.Count; num11++)
		{
			float num12 = 22f + (float)tabs[num11].name.Length * 9f;
			AddText((Transform)(object)panel, tabs[num11].name, num10, -44f, num12, 24f, 15, (TextAnchor)4, (FontStyle)1, (num11 == this.tab) ? Gold : Dim);
			int ti = num11;
			AddClick(num10, -44f, num12, 24f, (UnityAction)delegate
			{
				this.tab = ti;
				needRender = true;
			});
			num10 += num12 + 6f;
		}
		if (list2 == null)
		{
			for (int num13 = 0; num13 < board.Count; num13++)
			{
				BP bP = board[num13];
				AddText((Transform)(object)panel, bP.name, 208f + (float)num13 * 96f, -66f, 96f - (bP.online ? 12f : 0f), 24f, 13, (TextAnchor)5, (FontStyle)1, PlayerColor(bP.name));
				if (bP.online)
				{
					SetRect((RectTransform)((Component)AddImage((Transform)(object)panel, "On", null, new Color(0.45f, 0.85f, 0.45f, 1f), sliced: false, fill: true)).transform, 208f + (float)num13 * 96f + 96f - 9f, -73f, 7f, 7f);
				}
			}
		}
		else
		{
			AddText((Transform)(object)panel, "Who leads in " + Periods[period].ToLowerInvariant() + " (counted on this server only)", 18f, -66f, num5 - 36f, 24f, 13, (TextAnchor)3, (FontStyle)0, Dim);
		}
		if (board.Count == 0)
		{
			AddText((Transform)(object)panel, "No data yet. Statistics arrive when players play with CrewStats.", 18f, -66f, num5 - 36f, 24f, 13, (TextAnchor)3, (FontStyle)0, Dim);
		}
		SetRect((RectTransform)((Component)AddImage((Transform)(object)panel, "Sep", "panel_separator", new Color(1f, 1f, 1f, 0.7f), sliced: false, fill: false)).transform, 12f, -90f, num5 - 24f, 4f);
		float num14 = -96f;
		if (list2 != null)
		{
			if (list2.Count == 0)
			{
				AddText((Transform)(object)panel, "Nothing to award yet.", 18f, num14, num5 - 36f, 24f, 14, (TextAnchor)3, (FontStyle)0, Dim);
			}
			int num15 = 0;
			while (num15 < list2.Count)
			{
				AwardRow awardRow = list2[num15];
				if (num15 % 2 == 1)
				{
					SetRect((RectTransform)((Component)AddImage((Transform)(object)panel, "Row", null, new Color(1f, 0.95f, 0.8f, 0.05f), sliced: false, fill: true)).transform, 10f, num14, num5 - 20f, 24f);
				}
				AddText((Transform)(object)panel, awardRow.title, 18f, num14, num5 * 0.55f, 24f, 14, (TextAnchor)3, (FontStyle)0, Parchment);
				AddText((Transform)(object)panel, awardRow.winner, num5 * 0.55f + 10f, num14, num5 * 0.45f - 90f, 24f, 14, (TextAnchor)3, (FontStyle)1, awardRow.col);
				AddText((Transform)(object)panel, awardRow.value, num5 - 94f, num14, 80f, 24f, 14, (TextAnchor)5, (FontStyle)1, Gold);
				num15++;
				num14 -= 24f;
			}
			num14 -= 12f;
			AddText((Transform)(object)panel, "Boss chronicle", 18f, num14, num5 - 36f, 24f, 15, (TextAnchor)3, (FontStyle)1, Gold);
			num14 -= 24f;
			if (bossLines.Count == 0)
			{
				AddText((Transform)(object)panel, "No boss killed with CrewStats yet.", 18f, num14, num5 - 36f, 24f, 13, (TextAnchor)3, (FontStyle)0, Dim);
				num14 -= 24f;
			}
			int num16 = 0;
			while (num16 < bossLines.Count && num16 < 6)
			{
				BossLine bossLine = bossLines[num16];
				DateTime dateTime = new DateTime(bossLine.ticks);
				AddText((Transform)(object)panel, dateTime.ToString("dd.MM. HH:mm", Inv), 18f, num14, 110f, 24f, 13, (TextAnchor)3, (FontStyle)0, Dim);
				AddText((Transform)(object)panel, Localize(bossLine.boss) + "  -  " + bossLine.players, 130f, num14, num5 - 150f, 24f, 13, (TextAnchor)3, (FontStyle)0, Parchment);
				num16++;
				num14 -= 24f;
			}
			num14 -= 12f;
			AddText((Transform)(object)panel, "Nemesis (what killed each player most)", 18f, num14, num5 - 36f, 24f, 15, (TextAnchor)3, (FontStyle)1, Gold);
			num14 -= 24f;
			int num17 = 0;
			foreach (BP item3 in board)
			{
				if (item3.nemesisCount > 0 && num17 < 5)
				{
					num17++;
					AddText((Transform)(object)panel, item3.name, 18f, num14, 160f, 24f, 13, (TextAnchor)3, (FontStyle)1, PlayerColor(item3.name));
					AddText((Transform)(object)panel, KillerName(item3.nemesis) + "  x" + item3.nemesisCount, 180f, num14, num5 - 200f, 24f, 13, (TextAnchor)3, (FontStyle)0, Parchment);
					num14 -= 24f;
				}
			}
			if (num17 == 0)
			{
				AddText((Transform)(object)panel, "No deaths recorded yet.", 18f, num14, num5 - 36f, 24f, 13, (TextAnchor)3, (FontStyle)0, Dim);
			}
		}
		else
		{
			if (list.Count == 0)
			{
				AddText((Transform)(object)panel, (tab.page == "creatures") ? "No kills counted yet." : "-", 18f, num14, num5 - 36f, 24f, 14, (TextAnchor)3, (FontStyle)0, Dim);
			}
			for (int num18 = 0; num18 < list.Count; num18++)
			{
				Def def = list[num18];
				float num19 = 0f - (96f + (float)num18 * 24f);
				if (num18 % 2 == 1)
				{
					SetRect((RectTransform)((Component)AddImage((Transform)(object)panel, "Row", null, new Color(1f, 0.95f, 0.8f, 0.05f), sliced: false, fill: true)).transform, 10f, num19, num5 - 20f, 24f);
				}
				AddText((Transform)(object)panel, def.label, 18f, num19, 190f, 24f, 14, (TextAnchor)3, (FontStyle)0, Parchment);
				float num20 = 0f;
				for (int num21 = 0; num21 < board.Count; num21++)
				{
					if (board[num21].v.TryGetValue(def.id, out var value) && value[period] > num20)
					{
						num20 = value[period];
					}
				}
				for (int num22 = 0; num22 < board.Count; num22++)
				{
					float[] value2;
					float num23 = (board[num22].v.TryGetValue(def.id, out value2) ? value2[period] : 0f);
					float num24 = 208f + (float)num22 * 96f;
					if (num23 > 0f && num20 > 0f)
					{
						Color col = PlayerColor(board[num22].name);
						col.a = 0.28f;
						SetRect((RectTransform)((Component)AddImage((Transform)(object)panel, "Bar", null, col, sliced: false, fill: true)).transform, num24 + 2f, num19 - 2f, 88f * Mathf.Clamp01(num23 / num20), 20f);
					}
					bool flag2 = num23 > 0f && num23 >= num20;
					AddText((Transform)(object)panel, Fmt(def, num23), num24, num19, 92f, 24f, 14, (TextAnchor)5, (FontStyle)(flag2 ? 1 : 0), flag2 ? Gold : Parchment);
				}
			}
			num14 = 0f - (96f + (float)Math.Max(1, list.Count) * 24f) - 12f;
			if (flag)
			{
				AddText((Transform)(object)panel, "Latest deaths", 18f, num14, num5 - 36f, 24f, 15, (TextAnchor)3, (FontStyle)1, Gold);
				num14 -= 24f;
				if (deathLines.Count == 0)
				{
					AddText((Transform)(object)panel, "No deaths recorded yet.", 18f, num14, num5 - 36f, 24f, 13, (TextAnchor)3, (FontStyle)0, Dim);
				}
				int num25 = 0;
				while (num25 < deathLines.Count && num25 < 6)
				{
					DeathLine deathLine = deathLines[num25];
					DateTime dateTime2 = new DateTime(deathLine.ticks);
					AddText((Transform)(object)panel, dateTime2.ToString("dd.MM. HH:mm", Inv), 18f, num14, 110f, 24f, 13, (TextAnchor)3, (FontStyle)0, Dim);
					AddText((Transform)(object)panel, deathLine.player, 130f, num14, 130f, 24f, 13, (TextAnchor)3, (FontStyle)1, PlayerColor(deathLine.player));
					AddText((Transform)(object)panel, KillerName(deathLine.killer) + "  (" + BiomeName(deathLine.biome) + ")", 262f, num14, num5 - 280f, 24f, 13, (TextAnchor)3, (FontStyle)0, Parchment);
					num25++;
					num14 -= 24f;
				}
			}
		}
		RectTransform parent = panel;
		KeyboardShortcut value3 = toggleKey.Value;
		AddText((Transform)(object)parent, "Left/Right arrows or click: tab     Up/Down arrows or click: period     " + ((object)((KeyboardShortcut)(ref value3)).MainKey/*cast due to .constrained prefix*/).ToString() + ": close     (counted on this server only)", 14f, 0f - (num6 - 28f), num5 - 28f, 24f, 12, (TextAnchor)3, (FontStyle)0, Dim);
	}

	private int NemesisCount()
	{
		int num = 0;
		foreach (BP item in board)
		{
			if (item.nemesisCount > 0)
			{
				num++;
			}
		}
		return num;
	}

	private static int Hash(string s)
	{
		uint num = 2166136261u;
		foreach (char c in s)
		{
			num = (num ^ c) * 16777619;
		}
		return (int)(num & 0x7FFFFFFF);
	}
}
[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
internal static class RegisterRpc
{
	private static void Postfix()
	{
		try
		{
			if ((Object)(object)Plugin.I != (Object)null)
			{
				Plugin.I.Register();
			}
		}
		catch (Exception)
		{
		}
	}
}
[HarmonyPatch(typeof(Player), "ConsumeItem")]
internal static class ConsumePatch
{
	private static void Postfix(Player __instance, ItemData item, bool __result)
	{
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Invalid comparison between Unknown and I4
		try
		{
			if (__result && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && item != null && item.m_shared != null)
			{
				SharedData shared = item.m_shared;
				if ((int)shared.m_itemType == 2 && shared.m_food <= 0f && shared.m_foodStamina <= 0f && shared.m_foodEitr <= 0f && (Object)(object)shared.m_consumeStatusEffect != (Object)null)
				{
					Plugin.AddPend(1000, 1f);
				}
			}
		}
		catch (Exception)
		{
		}
	}
}
internal class PickState
{
	public int id;

	public int stack;

	public ItemDrop drop;
}
[HarmonyPatch(typeof(Humanoid), "Pickup", new Type[]
{
	typeof(GameObject),
	typeof(bool),
	typeof(bool)
})]
internal static class PickupPatch
{
	private static void Prefix(Humanoid __instance, GameObject __0, ref PickState __state)
	{
		__state = null;
		try
		{
			if (!((Object)(object)__0 == (Object)null) && !((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				ItemDrop component = __0.GetComponent<ItemDrop>();
				if (!((Object)(object)component == (Object)null) && component.m_itemData != null && component.m_itemData.m_shared != null && !component.m_itemData.m_pickedUp && Plugin.ResId(component.m_itemData.m_shared.m_name, out var id))
				{
					PickState pickState = new PickState();
					pickState.id = id;
					pickState.stack = component.m_itemData.m_stack;
					pickState.drop = component;
					__state = pickState;
				}
			}
		}
		catch (Exception)
		{
		}
	}

	private static void Postfix(bool __result, PickState __state)
	{
		try
		{
			if (__state != null)
			{
				int num = (__result ? __state.stack : (((Object)(object)__state.drop != (Object)null && __state.drop.m_itemData != null) ? (__state.stack - __state.drop.m_itemData.m_stack) : 0));
				if (num > 0 && num <= 1000)
				{
					Plugin.AddPend(__state.id, num);
				}
			}
		}
		catch (Exception)
		{
		}
	}
}
[HarmonyPatch(typeof(Player), "OnDeath")]
internal static class DeathPatch
{
	private static readonly FieldInfo lastHit = AccessTools.Field(typeof(Character), "m_lastHit");

	private static void Postfix(Player __instance)
	{
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: Expected I4, but got Unknown
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !((Object)(object)Plugin.I == (Object)null))
			{
				string killer = "";
				HitData val = (HitData)((lastHit != null) ? /*isinst with value type is only supported in some contexts*/: null);
				if (val != null)
				{
					Character attacker = val.GetAttacker();
					killer = ((attacker is Player) ? ("player:" + ((Player)attacker).GetPlayerName()) : ((!((Object)(object)attacker != (Object)null)) ? ("$hit_" + ((object)Unsafe.As<HitType, HitType>(ref val.m_hitType)/*cast due to .constrained prefix*/).ToString().ToLowerInvariant()) : attacker.m_name));
				}
				int biome = 0;
				try
				{
					biome = (int)Heightmap.FindBiome(((Component)__instance).transform.position);
				}
				catch (Exception)
				{
				}
				Plugin.I.SendDeath(killer, biome);
			}
		}
		catch (Exception)
		{
		}
	}
}
[HarmonyPatch(typeof(Game), "Logout")]
internal static class LogoutPatch
{
	private static void Prefix()
	{
		try
		{
			if ((Object)(object)Plugin.I != (Object)null)
			{
				Plugin.I.FlushNow();
			}
		}
		catch (Exception)
		{
		}
	}
}
[HarmonyPatch(typeof(Terminal), "InitTerminal")]
internal static class CommandPatch
{
	[Serializable]
	[CompilerGenerated]
	private sealed class <>c
	{
		public static readonly <>c <>9 = new <>c();

		public static ConsoleEvent <>9__1_0;

		internal void <Postfix>b__1_0(ConsoleEventArgs args)
		{
			if ((Object)(object)Plugin.I != (Object)null)
			{
				Plugin.I.Toggle();
			}
		}
	}

	private static bool done;

	private static void Postfix()
	{
		//IL_0042: 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_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Expected O, but got Unknown
		try
		{
			if (done)
			{
				return;
			}
			done = true;
			object obj = <>c.<>9__1_0;
			if (obj == null)
			{
				ConsoleEvent val = delegate
				{
					if ((Object)(object)Plugin.I != (Object)null)
					{
						Plugin.I.Toggle();
					}
				};
				<>c.<>9__1_0 = val;
				obj = (object)val;
			}
			new ConsoleCommand("crew", "CrewStats: opens or closes the crew statistics window", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}
		catch (Exception)
		{
		}
	}
}
internal class PlayerData
{
	public Dictionary<int, float> last = new Dictionary<int, float>();

	public Dictionary<int, double> total = new Dictionary<int, double>();

	public Dictionary<string, Dictionary<int, double>> daily = new Dictionary<string, Dictionary<int, double>>();

	public Dictionary<int, double> offline = new Dictionary<int, double>();

	public Dictionary<string, int> killers = new Dictionary<string, int>();

	public bool hasBase;

	public DateTime lastSeen;

	public double lifeKills;

	public double bestLife;
}
internal class DeathRec
{
	public long ticks;

	public string player;

	public string killer;

	public int biome;
}
internal class BossRec
{
	public long ticks;

	public string boss;

	public List<string> players = new List<string>();
}
internal class Store
{
	public readonly Dictionary<string, PlayerData> players = new Dictionary<string, PlayerData>();

	public readonly Dictionary<int, string> names = new Dictionary<int, string>();

	public readonly List<DeathRec> deaths = new List<DeathRec>();

	public readonly List<BossRec> bosses = new List<BossRec>();

	public const double MaxDelta = 1000000.0;

	public const int KeepDays = 60;

	public const int MaxNames = 600;

	public const int MaxDeaths = 40;

	public const int MaxBosses = 30;

	public const int CreatureBase = 536870912;

	public static readonly TimeSpan ContinueWindow = TimeSpan.FromMinutes(3.0);

	public int KillsId = -1;

	public int DeathsId = -1;

	private static readonly CultureInfo Inv = CultureInfo.InvariantCulture;

	public static string Day(DateTime t)
	{
		return t.ToString("yyyy-MM-dd", Inv);
	}

	public static bool IsCreature(int id)
	{
		return (id & 0x70000000) == 536870912;
	}

	public static int CreatureId(string token)
	{
		uint num = 2166136261u;
		string text = token ?? "";
		foreach (char c in text)
		{
			num = (num ^ c) * 16777619;
		}
		return (int)(0x20000000 | (num & 0xFFFFFFF));
	}

	public PlayerData Get(string name)
	{
		if (!players.TryGetValue(name, out var value))
		{
			value = new PlayerData();
			players[name] = value;
		}
		return value;
	}

	public void NoteName(int id, string token)
	{
		if (names.Count < 600 && !names.ContainsKey(id) && !string.IsNullOrEmpty(token))
		{
			names[id] = Clean(token);
		}
	}

	private void Add(PlayerData p, int id, double d, DateTime now)
	{
		p.total.TryGetValue(id, out var value);
		p.total[id] = value + d;
		string key = Day(now);
		if (!p.daily.TryGetValue(key, out var value2))
		{
			value2 = new Dictionary<int, double>();
			p.daily[key] = value2;
		}
		value2.TryGetValue(id, out var value3);
		value2[id] = value3 + d;
	}

	public void AddCustom(string name, int id, double d, DateTime now)
	{
		if (players.TryGetValue(name, out var value) && value.hasBase && !(d <= 0.0) && !(d > 1000000.0))
		{
			Add(value, id, d, now);
		}
	}

	public Dictionary<int, double> Report(string name, Dictionary<int, float> abs, Dictionary<int, float> adds, bool login, DateTime now)
	{
		Dictionary<int, double> deltas;
		return Report(name, abs, adds, login, now, out deltas);
	}

	public Dictionary<int, double> Report(string name, Dictionary<int, float> abs, Dictionary<int, float> adds, bool login, DateTime now, out Dictionary<int, double> deltas)
	{
		PlayerData playerData = Get(name);
		DateTime lastSeen = playerData.lastSeen;
		playerData.lastSeen = now;
		Dictionary<int, double> dictionary = new Dictionary<int, double>();
		deltas = new Dictionary<int, double>();
		bool flag = login && playerData.hasBase && now - lastSeen >= ContinueWindow;
		if (!playerData.hasBase)
		{
			foreach (KeyValuePair<int, float> ab in abs)
			{
				playerData.last[ab.Key] = ab.Value;
			}
			playerData.hasBase = true;
		}
		else if (flag)
		{
			foreach (KeyValuePair<int, float> ab2 in abs)
			{
				float value;
				bool flag2 = playerData.last.TryGetValue(ab2.Key, out value);
				if (!flag2 && IsCreature(ab2.Key))
				{
					flag2 = true;
					value = 0f;
				}
				double num = ab2.Value - value;
				if (flag2 && num > 0.0)
				{
					dictionary[ab2.Key] = num;
					playerData.offline.TryGetValue(ab2.Key, out var value2);
					playerData.offline[ab2.Key] = value2 + num;
				}
				playerData.last[ab2.Key] = ab2.Value;
			}
		}
		else
		{
			foreach (KeyValuePair<int, float> ab3 in abs)
			{
				float value3;
				bool flag3 = playerData.last.TryGetValue(ab3.Key, out value3);
				if (!flag3 && IsCreature(ab3.Key))
				{
					flag3 = true;
					value3 = 0f;
				}
				double num2 = ab3.Value - value3;
				if (flag3 && num2 > 0.0 && num2 <= 1000000.0)
				{
					Add(playerData, ab3.Key, num2, now);
					deltas[ab3.Key] = num2;
				}
				playerData.last[ab3.Key] = ab3.Value;
			}
			if (KillsId >= 0 && deltas.TryGetValue(KillsId, out var value4))
			{
				playerData.lifeKills += value4;
			}
			if (playerData.lifeKills > playerData.bestLife)
			{
				playerData.bestLife = playerData.lifeKills;
			}
			if (DeathsId >= 0 && deltas.TryGetValue(DeathsId, out var value5) && value5 > 0.0)
			{
				playerData.lifeKills = 0.0;
			}
		}
		if (adds != null)
		{
			foreach (KeyValuePair<int, float> add in adds)
			{
				if (add.Value > 0f && (double)add.Value <= 1000000.0)
				{
					Add(playerData, add.Key, add.Value, now);
				}
			}
		}
		return dictionary;
	}

	public void AddDeath(string player, string killer, int biome, DateTime now)
	{
		DeathRec deathRec = new DeathRec();
		deathRec.ticks = now.Ticks;
		deathRec.player = Clean(player);
		deathRec.killer = Clean(killer);
		deathRec.biome = biome;
		deaths.Insert(0, deathRec);
		if (deaths.Count > 40)
		{
			deaths.RemoveRange(40, deaths.Count - 40);
		}
		if (deathRec.killer.Length > 0 && players.TryGetValue(deathRec.player, out var value) && (value.killers.Count < 60 || value.killers.ContainsKey(deathRec.killer)))
		{
			value.killers.TryGetValue(deathRec.killer, out var value2);
			value.killers[deathRec.killer] = value2 + 1;
		}
	}

	public void AddBoss(string boss, string player, DateTime now)
	{
		player = Clean(player);
		boss = Clean(boss);
		foreach (BossRec boss2 in bosses)
		{
			if (boss2.boss == boss && Math.Abs(now.Ticks - boss2.ticks) < TimeSpan.FromMinutes(10.0).Ticks)
			{
				if (!boss2.players.Contains(player))
				{
					boss2.players.Add(player);
				}
				return;
			}
		}
		BossRec bossRec = new BossRec();
		bossRec.ticks = now.Ticks;
		bossRec.boss = boss;
		bossRec.players.Add(player);
		bosses.Insert(0, bossRec);
		if (bosses.Count > 30)
		{
			bosses.RemoveRange(30, bosses.Count - 30);
		}
	}

	public string Nemesis(PlayerData p, out int count)
	{
		string result = "";
		count = 0;
		foreach (KeyValuePair<string, int> killer in p.killers)
		{
			if (killer.Value > count)
			{
				count = killer.Value;
				result = killer.Key;
			}
		}
		return result;
	}

	public double Value(PlayerData p, int id, int period, DateTime now)
	{
		if (period == 2)
		{
			p.total.TryGetValue(id, out var value);
			return value;
		}
		double num = 0.0;
		int num2 = ((period == 0) ? 1 : 7);
		for (int i = 0; i < num2; i++)
		{
			if (p.daily.TryGetValue(Day(now.AddDays(-i)), out var value2) && value2.TryGetValue(id, out var value3))
			{
				num += value3;
			}
		}
		return num;
	}

	public void Save(string path, DateTime now)
	{
		StringBuilder stringBuilder = new StringBuilder();
		string strB = Day(now.AddDays(-60.0));
		foreach (KeyValuePair<string, PlayerData> player in players)
		{
			string value = Clean(player.Key);
			PlayerData value2 = player.Value;
			stringBuilder.Append("P\t").Append(value).Append('\t')
				.Append(value2.lastSeen.Ticks)
				.Append('\t')
				.Append(value2.hasBase ? 1 : 0)
				.Append('\t')
				.Append(value2.lifeKills.ToString("R", Inv))
				.Append('\t')
				.Append(value2.bestLife.ToString("R", Inv))
				.Append('\n');
			foreach (KeyValuePair<int, float> item in value2.last)
			{
				stringBuilder.Append("L\t").Append(value).Append('\t')
					.Append(item.Key)
					.Append('\t')
					.Append(item.Value.ToString("R", Inv))
					.Append('\n');
			}
			foreach (KeyValuePair<int, double> item2 in value2.total)
			{
				stringBuilder.Append("T\t").Append(value).Append('\t')
					.Append(item2.Key)
					.Append('\t')
					.Append(item2.Value.ToString("R", Inv))
					.Append('\n');
			}
			foreach (KeyValuePair<int, double> item3 in value2.offline)
			{
				stringBuilder.Append("O\t").Append(value).Append('\t')
					.Append(item3.Key)
					.Append('\t')
					.Append(item3.Value.ToString("R", Inv))
					.Append('\n');
			}
			foreach (KeyValuePair<string, int> killer in value2.killers)
			{
				stringBuilder.Append("K\t").Append(value).Append('\t')
					.Append(Clean(killer.Key))
					.Append('\t')
					.Append(killer.Value)
					.Append('\n');
			}
			foreach (KeyValuePair<string, Dictionary<int, double>> item4 in value2.daily)
			{
				if (string.CompareOrdinal(item4.Key, strB) < 0)
				{
					continue;
				}
				foreach (KeyValuePair<int, double> item5 in item4.Value)
				{
					stringBuilder.Append("D\t").Append(value).Append('\t')
						.Append(item4.Key)
						.Append('\t')
						.Append(item5.Key)
						.Append('\t')
						.Append(item5.Value.ToString("R", Inv))
						.Append('\n');
				}
			}
		}
		foreach (KeyValuePair<int, string> name in names)
		{
			stringBuilder.Append("N\t").Append(name.Key).Append('\t')
				.Append(Clean(name.Value))
				.Append('\n');
		}
		foreach (DeathRec death in deaths)
		{
			stringBuilder.Append("X\t").Append(death.ticks).Append('\t')
				.Append(Clean(death.player))
				.Append('\t')
				.Append(Clean(death.killer))
				.Append('\t')
				.Append(death.biome)
				.Append('\n');
		}
		foreach (BossRec boss in bosses)
		{
			stringBuilder.Append("B\t").Append(boss.ticks).Append('\t')
				.Append(Clean(boss.boss))
				.Append('\t')
				.Append(Clean(string.Join(",", boss.players.ToArray())))
				.Append('\n');
		}
		string text = path + ".tmp";
		File.WriteAllText(text, stringBuilder.ToString());
		File.Copy(text, path, overwrite: true);
	}

	public void Load(string path)
	{
		if (!File.Exists(path))
		{
			return;
		}
		List<DeathRec> list = new List<DeathRec>();
		List<BossRec> list2 = new List<BossRec>();
		string[] array = File.ReadAllLines(path);
		for (int i = 0; i < array.Length; i++)
		{
			string[] array2 = array[i].Split(new char[1] { '\t' });
			if (array2.Length < 3)
			{
				continue;
			}
			try
			{
				switch (array2[0])
				{
				case "N":
					names[int.Parse(array2[1], Inv)] = array2[2];
					continue;
				case "X":
					if (array2.Length >= 5)
					{
						DeathRec deathRec = new DeathRec();
						deathRec.ticks = long.Parse(array2[1], Inv);
						deathRec.player = array2[2];
						deathRec.killer = array2[3];
						deathRec.biome = int.Parse(array2[4], Inv);
						list.Add(deathRec);
					}
					continue;
				case "B":
				{
					BossRec bossRec = new BossRec();
					bossRec.ticks = long.Parse(array2[1], Inv);
					bossRec.boss = array2[2];
					if (array2.Length >= 4)
					{
						string[] array3 = array2[3].Split(new char[1] { ',' });
						foreach (string text in array3)
						{
							if (text.Length > 0)
							{
								bossRec.players.Add(text);
							}
						}
					}
					list2.Add(bossRec);
					continue;
				}
				}
				PlayerData playerData = Get(array2[1]);
				if (array2[0] == "P" && array2.Length >= 4)
				{
					playerData.lastSeen = new DateTime(long.Parse(array2[2], Inv));
					playerData.hasBase = array2[3] == "1";
					if (array2.Length >= 6)
					{
						playerData.lifeKills = double.Parse(array2[4], Inv);
						playerData.bestLife = double.Parse(array2[5], Inv);
					}
				}
				else if (array2[0] == "L")
				{
					playerData.last[int.Parse(array2[2], Inv)] = float.Parse(array2[3], Inv);
				}
				else if (array2[0] == "T")
				{
					playerData.total[int.Parse(array2[2], Inv)] = double.Parse(array2[3], Inv);
				}
				else if (array2[0] == "O")
				{
					playerData.offline[int.Parse(array2[2], Inv)] = double.Parse(array2[3], Inv);
				}
				else if (array2[0] == "K" && array2.Length >= 4)
				{
					playerData.killers[array2[2]] = int.Parse(array2[3], Inv);
				}
				else if (array2[0] == "D" && array2.Length >= 5)
				{
					if (!playerData.daily.TryGetValue(array2[2], out var value))
					{
						value = new Dictionary<int, double>();
						playerData.daily[array2[2]] = value;
					}
					value[int.Parse(array2[3], Inv)] = double.Parse(array2[4], Inv);
				}
			}
			catch (Exception)
			{
			}
		}
		deaths.Clear();
		deaths.AddRange(list);
		bosses.Clear();
		bosses.AddRange(list2);
	}

	private static string Clean(string s)
	{
		StringBuilder stringBuilder = new StringBuilder();
		string text = s ?? "";
		foreach (char c in text)
		{
			if (c >= ' ' && c != '\u007f')
			{
				stringBuilder.Append(c);
				if (stringBuilder.Length >= 48)
				{
					break;
				}
			}
		}
		return stringBuilder.ToString().Trim();
	}
}