Decompiled source of TrophyAssister v1.2.1

TrophyAssister.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Koendertjh.Shared;
using Microsoft.CodeAnalysis;
using TMPro;
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: AssemblyCompany("TrophyAssister")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.2.1.0")]
[assembly: AssemblyInformationalVersion("1.2.1+af389ae5100ed540a2d527f8099d9b1aad31e9d0")]
[assembly: AssemblyProduct("TrophyAssister")]
[assembly: AssemblyTitle("TrophyAssister")]
[assembly: AssemblyVersion("1.2.1.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 Koendertjh.Shared
{
	internal static class ShapeIcon
	{
		private const int Size = 128;

		private const int MaxDistance = 16;

		internal static Sprite Create(string name, Func<float, float, bool> inside)
		{
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Expected O, but got Unknown
			//IL_016c: 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)
			float[] array = new float[16384];
			for (int i = 0; i < 128; i++)
			{
				for (int j = 0; j < 128; j++)
				{
					int num = 0;
					for (int k = 0; k < 4; k++)
					{
						for (int l = 0; l < 4; l++)
						{
							if (inside((float)j + ((float)l + 0.5f) / 4f, (float)i + ((float)k + 0.5f) / 4f))
							{
								num++;
							}
						}
					}
					array[i * 128 + j] = (float)num / 16f;
				}
			}
			float[] array2 = new float[16384];
			for (int m = 0; m < 128; m++)
			{
				for (int n = 0; n < 128; n++)
				{
					array2[m * 128 + n] = ((array[m * 128 + n] > 0f) ? DistanceToEdge(array, n, m) : 0f);
				}
			}
			Color32[] array3 = (Color32[])(object)new Color32[16384];
			for (int num2 = 0; num2 < 128; num2++)
			{
				for (int num3 = 0; num3 < 128; num3++)
				{
					float num4 = array[num2 * 128 + num3];
					byte b = (byte)(Mathf.Clamp01((num4 > 0f) ? Shade(array2, num3, num2) : 1f) * 255f);
					array3[num2 * 128 + num3] = new Color32(b, b, b, (byte)(num4 * 255f));
				}
			}
			Texture2D val = new Texture2D(128, 128, (TextureFormat)4, false)
			{
				filterMode = (FilterMode)1,
				wrapMode = (TextureWrapMode)1
			};
			val.SetPixels32(array3);
			val.Apply();
			Sprite obj = Sprite.Create(val, new Rect(0f, 0f, 128f, 128f), new Vector2(0.5f, 0.5f), 100f);
			((Object)obj).name = name;
			return obj;
		}

		private static float Shade(float[] distance, int x, int y)
		{
			float num = Mathf.PerlinNoise((float)x * 0.07f + 3.1f, (float)y * 0.07f + 7.7f) * 0.6f + Mathf.PerlinNoise((float)x * 0.19f + 11.3f, (float)y * 0.19f + 1.9f) * 0.4f;
			float num2 = Mathf.Lerp(0.78f, 1f, num) * Mathf.Lerp(1f, 0.86f, (float)x / 128f);
			float num3 = distance[y * 128 + x];
			if (num3 < 2.5f)
			{
				num2 *= Mathf.Lerp(0.55f, 1f, num3 / 2.5f);
			}
			else if (num3 >= 6f && num3 < 8f && DeepNearby(distance, x, y))
			{
				num2 *= 0.62f;
			}
			return num2;
		}

		private static bool DeepNearby(float[] distance, int x, int y)
		{
			for (int i = -5; i <= 5; i++)
			{
				for (int j = -5; j <= 5; j++)
				{
					int num = x + j;
					int num2 = y + i;
					if (num >= 0 && num2 >= 0 && num < 128 && num2 < 128 && distance[num2 * 128 + num] >= 13f)
					{
						return true;
					}
				}
			}
			return false;
		}

		private static float DistanceToEdge(float[] coverage, int x, int y)
		{
			int num = 256;
			for (int i = -16; i <= 16; i++)
			{
				int num2 = y + i;
				for (int j = -16; j <= 16; j++)
				{
					int num3 = j * j + i * i;
					if (num3 < num)
					{
						int num4 = x + j;
						if (num4 < 0 || num2 < 0 || num4 >= 128 || num2 >= 128 || coverage[num2 * 128 + num4] < 0.5f)
						{
							num = num3;
						}
					}
				}
			}
			return Mathf.Sqrt((float)num);
		}

		internal static bool Trapezoid(float x, float y, float bottom, float top, float bottomLeft, float bottomRight, float topLeft, float topRight)
		{
			if (y < bottom || y > top)
			{
				return false;
			}
			float num = (y - bottom) / (top - bottom);
			if (x >= Mathf.Lerp(bottomLeft, topLeft, num))
			{
				return x <= Mathf.Lerp(bottomRight, topRight, num);
			}
			return false;
		}

		internal static bool Ellipse(float x, float y, float cx, float cy, float rx, float ry)
		{
			float num = (x - cx) / rx;
			float num2 = (y - cy) / ry;
			return num * num + num2 * num2 <= 1f;
		}
	}
}
namespace TrophyAssister
{
	internal static class Commands
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static ConsoleEvent <0>__List;

			public static ConsoleEvent <1>__Reset;

			public static ConsoleOptionsFetcher <2>__TabOptions;
		}

		public static void Register()
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_004f: 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_005a: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Expected O, but got Unknown
			try
			{
				object obj = <>O.<0>__List;
				if (obj == null)
				{
					ConsoleEvent val = List;
					<>O.<0>__List = val;
					obj = (object)val;
				}
				new ConsoleCommand("trophypity", "TrophyAssister: list this character's dry-kill counters", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				object obj2 = <>O.<1>__Reset;
				if (obj2 == null)
				{
					ConsoleEvent val2 = Reset;
					<>O.<1>__Reset = val2;
					obj2 = (object)val2;
				}
				object obj3 = <>O.<2>__TabOptions;
				if (obj3 == null)
				{
					ConsoleOptionsFetcher val3 = TabOptions;
					<>O.<2>__TabOptions = val3;
					obj3 = (object)val3;
				}
				new ConsoleCommand("trophyreset", "TrophyAssister: reset dry kills - trophyreset <creature> | trophyreset all", (ConsoleEvent)obj2, false, false, false, false, false, false, (ConsoleOptionsFetcher)obj3, true, false, false);
			}
			catch (Exception ex)
			{
				TrophyAssisterPlugin.Log.LogWarning((object)("Couldn't add console commands: " + ex.Message));
			}
		}

		private static void List(ConsoleEventArgs args)
		{
			if (!CurrentCharacter(args, out var charKey))
			{
				return;
			}
			List<KeyValuePair<string, int>> list = PityStore.ForCharacter(charKey);
			if (list.Count == 0)
			{
				Say(args, "TrophyAssister: no dry kills counted for this character.");
				return;
			}
			Say(args, $"TrophyAssister: {list.Count} creature(s) with dry kills:");
			foreach (KeyValuePair<string, int> item in list)
			{
				Say(args, $"  {DisplayName(item.Key)} ({item.Key}): {item.Value}");
			}
		}

		private static void Reset(ConsoleEventArgs args)
		{
			if (!CurrentCharacter(args, out var charKey))
			{
				return;
			}
			string input = string.Join(" ", args.Args.Skip(1)).Trim();
			if (input.Length == 0)
			{
				Say(args, "Usage: trophyreset <creature>  or  trophyreset all   (see trophypity for names)");
				return;
			}
			if (input.Equals("all", StringComparison.OrdinalIgnoreCase))
			{
				string text = PityStore.Backup();
				int num = PityStore.ResetCharacter(charKey);
				Say(args, $"TrophyAssister: reset {num} counter(s) for this character." + ((text != null) ? (" Backup: " + text) : ""));
				return;
			}
			KeyValuePair<string, int> keyValuePair = PityStore.ForCharacter(charKey).FirstOrDefault((KeyValuePair<string, int> e) => e.Key.Equals(input, StringComparison.OrdinalIgnoreCase) || DisplayName(e.Key).Equals(input, StringComparison.OrdinalIgnoreCase));
			if (keyValuePair.Key == null)
			{
				Say(args, "TrophyAssister: no dry kills counted for '" + input + "'. Type trophypity to see the list.");
				return;
			}
			PityStore.Reset(charKey, keyValuePair.Key);
			Say(args, $"TrophyAssister: reset {DisplayName(keyValuePair.Key)} ({keyValuePair.Value} dry kills).");
		}

		private static List<string> TabOptions()
		{
			List<string> list = new List<string> { "all" };
			try
			{
				if ((Object)(object)Player.m_localPlayer != (Object)null)
				{
					list.AddRange(from e in PityStore.ForCharacter(GameUtil.CharKey(Player.m_localPlayer))
						select e.Key);
				}
			}
			catch
			{
			}
			return list;
		}

		private static bool CurrentCharacter(ConsoleEventArgs args, out string charKey)
		{
			charKey = (((Object)(object)Player.m_localPlayer != (Object)null) ? GameUtil.CharKey(Player.m_localPlayer) : null);
			if (charKey == null)
			{
				Say(args, "TrophyAssister: load into a world with a character first.");
			}
			return charKey != null;
		}

		private static string DisplayName(string creaturePrefab)
		{
			try
			{
				GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(creaturePrefab) : null);
				Character val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Character>() : null);
				if ((Object)(object)val2 != (Object)null)
				{
					return GameUtil.Localize(val2.m_name);
				}
			}
			catch
			{
			}
			return creaturePrefab;
		}

		private static void Say(ConsoleEventArgs args, string text)
		{
			if ((Object)(object)args?.Context != (Object)null)
			{
				args.Context.AddString(text);
			}
			else
			{
				TrophyAssisterPlugin.Log.LogInfo((object)text);
			}
		}
	}
	internal static class PityLabel
	{
		private class Line
		{
			public string Text;

			public float Until;

			public int Alpha = -1;
		}

		private const float ShowSeconds = 6f;

		private const float FadeSeconds = 1f;

		private const int MaxLines = 3;

		private static readonly List<Line> Lines = new List<Line>();

		private static readonly StringBuilder Text = new StringBuilder();

		private static TMP_Text _label;

		private static bool _changed;

		public static void Show(string text)
		{
			Lines.Add(new Line
			{
				Text = text,
				Until = Time.time + 6f
			});
			if (Lines.Count > 3)
			{
				Lines.RemoveAt(0);
			}
			_changed = true;
			Refresh();
		}

		public static void Tick()
		{
			if (Lines.Count == 0)
			{
				return;
			}
			for (int num = Lines.Count - 1; num >= 0; num--)
			{
				if (!(Lines[num].Until > Time.time))
				{
					Lines.RemoveAt(num);
					_changed = true;
				}
			}
			Refresh();
		}

		private static void Refresh()
		{
			TMP_Text val = Label();
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			bool flag = Lines.Count > 0 && !Hud.IsUserHidden();
			if (((Component)val).gameObject.activeSelf != flag)
			{
				((Component)val).gameObject.SetActive(flag);
			}
			if (!flag)
			{
				return;
			}
			bool flag2 = _changed;
			foreach (Line line in Lines)
			{
				int num = Mathf.RoundToInt(Mathf.Clamp01((line.Until - Time.time) / 1f) * 255f);
				if (num != line.Alpha)
				{
					line.Alpha = num;
					flag2 = true;
				}
			}
			if (!flag2)
			{
				return;
			}
			_changed = false;
			Text.Length = 0;
			foreach (Line line2 in Lines)
			{
				if (Text.Length > 0)
				{
					Text.Append('\n');
				}
				Text.Append("<alpha=#").Append(line2.Alpha.ToString("X2")).Append('>')
					.Append(line2.Text);
			}
			val.text = Text.ToString();
		}

		private static TMP_Text Label()
		{
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Expected O, but got Unknown
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_label != (Object)null)
			{
				return _label;
			}
			TMP_Text val = (((Object)(object)MessageHud.instance != (Object)null) ? MessageHud.instance.m_messageText : null);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, val.transform.parent);
			((Object)val2).name = "TrophyAssister_pity";
			foreach (Transform item in val2.transform)
			{
				Object.Destroy((Object)(object)((Component)item).gameObject);
			}
			_label = val2.GetComponent<TMP_Text>();
			((Graphic)_label).canvasRenderer.SetAlpha(1f);
			_label.fontSize = val.fontSize * 0.8f;
			_label.text = "";
			RectTransform val3 = (RectTransform)val2.transform;
			RectTransform val4 = (RectTransform)val.transform;
			Vector2 anchoredPosition = val4.anchoredPosition;
			Rect rect = val4.rect;
			val3.anchoredPosition = anchoredPosition - new Vector2(0f, ((Rect)(ref rect)).height);
			return _label;
		}
	}
	[BepInPlugin("com.koendertjh.trophyassister", "TrophyAssister", "1.2.1")]
	public class TrophyAssisterPlugin : BaseUnityPlugin
	{
		public const string ModGuid = "com.koendertjh.trophyassister";

		public const string ModName = "TrophyAssister";

		public const string ModVersion = "1.2.1";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> NotifyOnDrop;

		internal static ConfigEntry<bool> ShowPityProgress;

		internal static ConfigEntry<bool> LogKills;

		internal static ConfigEntry<float> ChanceMultiplier;

		internal static ConfigEntry<bool> OnlyUncollected;

		internal static ConfigEntry<float> StarChanceMultiplier;

		internal static ConfigEntry<bool> PityEnabled;

		internal static ConfigEntry<float> PityBonusPerKill;

		internal static ConfigEntry<PityMode> PityModeSetting;

		internal static ConfigEntry<float> PityGrowthPerKill;

		internal static ConfigEntry<float> RareThreshold;

		internal static ConfigEntry<float> RareGrowthPerKill;

		internal static ConfigEntry<int> GuaranteeAfterKills;

		internal static ConfigEntry<bool> OnlyCountMyKills;

		internal static ConfigEntry<bool> ShowChancesInList;

		internal static ConfigEntry<bool> ShowMissingTrophies;

		internal static ConfigEntry<bool> SpoilerFree;

		internal static ConfigEntry<string> HiddenTrophies;

		internal static ConfigEntry<bool> TrackerEnabled;

		internal static ConfigEntry<float> TrackerRange;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Expected O, but got Unknown
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0120: Expected O, but got Unknown
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Expected O, but got Unknown
			//IL_01d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01da: Expected O, but got Unknown
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0217: Expected O, but got Unknown
			//IL_024a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Expected O, but got Unknown
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0289: Expected O, but got Unknown
			//IL_0380: Unknown result type (might be due to invalid IL or missing references)
			//IL_038a: Expected O, but got Unknown
			//IL_039f: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a9: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Enabled", true, "Master switch.");
			NotifyOnDrop = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "NotifyOnDrop", true, "Show a message when a trophy drops.");
			ShowPityProgress = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "ShowPityProgress", true, "After a kill without a trophy, show the dry kills and new chance below the top-left messages.");
			LogKills = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "LogKills", false, "Write a line to BepInEx/LogOutput.log for every kill of a creature with a trophy: who landed the deciding hit, whether it counted for pity, and the result. For troubleshooting.");
			ChanceMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Base boost", "ChanceMultiplier", 1f, new ConfigDescription("Multiplies the normal trophy chance before pity is added. 1 = vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>()));
			OnlyUncollected = ((BaseUnityPlugin)this).Config.Bind<bool>("2 - Base boost", "OnlyUncollected", false, "Only boost (and count pity for) trophies this character has never picked up.");
			StarChanceMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Base boost", "StarChanceMultiplier", 2f, new ConfigDescription("Each star multiplies the trophy chance by this. 2 = vanilla (1 star = 2x, 2 stars = 4x). Higher makes starred creatures drop trophies more often; the chance can't go below vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 10f), Array.Empty<object>()));
			PityEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Pity", "Enabled", true, "Increase the trophy chance with every kill that didn't drop one.");
			PityModeSetting = ((BaseUnityPlugin)this).Config.Bind<PityMode>("3 - Pity", "Mode", PityMode.Compound, "Compound: every dry kill multiplies the current chance by (1 + GrowthPerKill). 10% trophy -> 11% -> 12.1% -> 13.31% ...  Rare trophies grow slower in absolute terms.\nFlat: every dry kill adds FlatBonusPerKill percentage points (0.05 = +5% each kill).");
			PityGrowthPerKill = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Pity", "GrowthPerKill", 0.1f, new ConfigDescription("Compound mode: growth per dry kill. 0.10 = current chance grows by 10% each kill.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
			RareThreshold = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Pity", "RareThreshold", 0.05f, new ConfigDescription("Compound mode: trophies with a vanilla chance at or below this (0.05 = 5%) count as rare.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			RareGrowthPerKill = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Pity", "RareGrowthPerKill", 0.2f, new ConfigDescription("Compound mode: growth per dry kill for rare trophies. 0.20 = +20% of current chance each kill.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
			PityBonusPerKill = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Pity", "FlatBonusPerKill", 0.05f, new ConfigDescription("Flat mode: chance added per dry kill. 0.05 = +5 percentage points.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			GuaranteeAfterKills = ((BaseUnityPlugin)this).Config.Bind<int>("3 - Pity", "GuaranteeAfterKills", 0, new ConfigDescription("Hard pity: guaranteed trophy after this many dry kills. 0 = off.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000), Array.Empty<object>()));
			OnlyCountMyKills = ((BaseUnityPlugin)this).Config.Bind<bool>("3 - Pity", "OnlyCountMyKills", true, "Only your kills add pity: your last hit, a burn/poison tick after your hit, or a kill by your summons and tamed creatures following you. Kills by other players always count for them. Turn off to also count kills by creatures and traps on creatures your game owns.");
			ShowChancesInList = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Trophy list", "ShowChances", true, "Show the current drop chance and dry-kill count on each trophy in the Trophies tab.");
			ShowMissingTrophies = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Trophy list", "ShowMissing", true, "Add trophies you haven't collected yet to the list, greyed out.");
			SpoilerFree = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Trophy list", "SpoilerFree", true, "Missing trophies of creatures you've never killed show a '?' icon and '??????' as name. After your first kill they show greyed out with their name. Off = all missing trophies show greyed out.");
			HiddenTrophies = ((BaseUnityPlugin)this).Config.Bind<string>("4 - Trophy list", "HiddenTrophies", "TrophyDeerWhite", "Trophy prefab names (comma separated) never added to the list as missing. TrophyDeerWhite is unfinished game content that has no name in the game's text files.");
			TrackerEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("5 - Wishbone tracker", "Enabled", true, "Show a small wishbone on each trophy in the Trophies tab. Click it to track that trophy: while the wishbone is equipped, it also pings (in red) for living creatures that drop a tracked trophy.");
			TrackerRange = ((BaseUnityPlugin)this).Config.Bind<float>("5 - Wishbone tracker", "Range", 0f, new ConfigDescription("How close (in meters) a creature with a tracked trophy must be for the wishbone to find it. 0 = any creature the game has loaded around you (follows the game's simulation distance setting).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 500f), Array.Empty<object>()));
			PityStore.Load();
			TrackedStore.Load();
			_harmony = new Harmony("com.koendertjh.trophyassister");
			Patcher.SafePatch(_harmony, typeof(KillCredit));
			Patcher.SafePatch(_harmony, typeof(GenerateDropListPatch));
			Patcher.SafePatch(_harmony, typeof(TrophyListPatch));
			Patcher.SafePatch(_harmony, typeof(TrophyNet));
			Patcher.SafePatch(_harmony, typeof(SaveOnLogout));
			Patcher.SafePatch(_harmony, typeof(WishboneIconPatch));
			Commands.Register();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"TrophyAssister 1.2.1 loaded (client-side).");
		}

		private void Update()
		{
			PityLabel.Tick();
			TrophyFinder.Tick();
			PityStore.SaveIfDue();
		}

		private void OnDestroy()
		{
			PityStore.Save();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	[HarmonyPatch]
	internal static class GenerateDropListPatch
	{
		internal class Entry
		{
			public Drop Drop;

			public float BaseChance;

			public float Vanilla;

			public float Target;
		}

		internal class State
		{
			public string CharKey;

			public string CreatureKey;

			public string CreatureName;

			public int LevelMultiplier;

			public readonly List<Entry> Trophies = new List<Entry>();

			public long HandOffTo;

			public string HandOffCreature;

			public int HandOffLevel;
		}

		private static bool Prepare()
		{
			return TargetMethod() != null;
		}

		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(typeof(CharacterDrop), "GenerateDropList", (Type[])null, (Type[])null);
		}

		private static void Prefix(CharacterDrop __instance, out State __state)
		{
			__state = null;
			try
			{
				if (!TrophyAssisterPlugin.Enabled.Value || (Object)(object)__instance == (Object)null || __instance.m_drops == null || (Object)(object)Player.m_localPlayer == (Object)null)
				{
					return;
				}
				Character component = ((Component)__instance).GetComponent<Character>();
				string text = GameUtil.PrefabName(((Component)__instance).gameObject);
				int num = ((!((Object)(object)component != (Object)null)) ? 1 : component.GetLevel());
				long num2 = KillCredit.Killer(component);
				if (num2 != 0L && num2 != ZDOMan.GetSessionID())
				{
					if (TrophyAssisterPlugin.LogKills.Value)
					{
						TrophyAssisterPlugin.Log.LogInfo((object)($"Kill: {text} ({num - 1} star), last hit by {KillCredit.LastAttackerName(component)}, " + "another player's kill: sent to them"));
					}
					__state = new State
					{
						HandOffTo = num2,
						HandOffCreature = text,
						HandOffLevel = num
					};
				}
				else
				{
					bool flag = TrophyAssisterPlugin.PityEnabled.Value && (!TrophyAssisterPlugin.OnlyCountMyKills.Value || num2 != 0);
					if (TrophyAssisterPlugin.LogKills.Value)
					{
						TrophyAssisterPlugin.Log.LogInfo((object)($"Kill: {text} ({num - 1} star), last hit by {KillCredit.LastAttackerName(component)}, " + $"counts for pity: {flag}"));
					}
					string creatureName = (((Object)(object)component != (Object)null) ? GameUtil.Localize(component.m_name) : text);
					__state = Build(__instance.m_drops, text, creatureName, num, flag);
				}
			}
			catch (Exception ex)
			{
				TrophyAssisterPlugin.Log.LogWarning((object)("Prefix error (drop left vanilla): " + ex.Message));
			}
		}

		private static void Postfix(CharacterDrop __instance, State __state, List<KeyValuePair<GameObject, int>> __result)
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			if (__state == null || __result == null)
			{
				return;
			}
			try
			{
				HashSet<string> dropped = new HashSet<string>(from kv in __result
					where (Object)(object)kv.Key != (Object)null
					select ((Object)kv.Key).name);
				if (__state.HandOffTo != 0L)
				{
					TrophyNet.SendKill(__state.HandOffTo, __state.HandOffCreature, __state.HandOffLevel, TrophyNet.DropCenter(__instance), dropped);
					return;
				}
				KeyValuePair<GameObject, int> item = Resolve(__state, dropped);
				if ((Object)(object)item.Key != (Object)null)
				{
					__result.Add(item);
				}
			}
			catch (Exception ex)
			{
				TrophyAssisterPlugin.Log.LogWarning((object)("Postfix error: " + ex.Message));
			}
		}

		internal static State Build(List<Drop> drops, string creatureKey, string creatureName, int level, bool countPity)
		{
			Player localPlayer = Player.m_localPlayer;
			string text = GameUtil.CharKey(localPlayer);
			if ((Object)(object)localPlayer == (Object)null || text == null || drops == null)
			{
				return null;
			}
			int num = Mathf.Max(1, (int)Mathf.Pow(2f, (float)(level - 1)));
			float num2 = Mathf.Max((float)num, Mathf.Pow(TrophyAssisterPlugin.StarChanceMultiplier.Value, (float)(level - 1)));
			HashSet<string> hashSet = null;
			State state = null;
			foreach (Drop drop in drops)
			{
				if (drop == null || !GameUtil.IsTrophy(drop.m_prefab, out var _))
				{
					continue;
				}
				if (TrophyAssisterPlugin.OnlyUncollected.Value)
				{
					hashSet = hashSet ?? new HashSet<string>(localPlayer.GetTrophies());
					if (hashSet.Contains(((Object)drop.m_prefab).name))
					{
						continue;
					}
				}
				int dry = (countPity ? PityStore.Get(text, creatureKey) : 0);
				float num3 = Mathf.Clamp01(drop.m_chance * (float)((!drop.m_levelMultiplier) ? 1 : num));
				float num4 = Mathf.Clamp01(Chance.Next(drop.m_chance, dry) * (drop.m_levelMultiplier ? num2 : 1f));
				if (!(num4 <= num3) || countPity)
				{
					if (state == null)
					{
						state = new State
						{
							CharKey = text,
							CreatureKey = (countPity ? creatureKey : null),
							CreatureName = creatureName,
							LevelMultiplier = num
						};
					}
					state.Trophies.Add(new Entry
					{
						Drop = drop,
						BaseChance = drop.m_chance,
						Vanilla = num3,
						Target = num4
					});
				}
			}
			return state;
		}

		internal static KeyValuePair<GameObject, int> Resolve(State state, ICollection<string> dropped)
		{
			KeyValuePair<GameObject, int> result = default(KeyValuePair<GameObject, int>);
			bool flag = state.Trophies.Any((Entry t) => dropped.Contains(((Object)t.Drop.m_prefab).name));
			bool flag2 = false;
			if (!flag)
			{
				foreach (Entry trophy in state.Trophies)
				{
					if (!(trophy.Target <= trophy.Vanilla) && !(trophy.Vanilla >= 1f))
					{
						float num = (trophy.Target - trophy.Vanilla) / (1f - trophy.Vanilla);
						if (!(Random.value >= num))
						{
							result = new KeyValuePair<GameObject, int>(trophy.Drop.m_prefab, Amount(trophy.Drop, state.LevelMultiplier));
							flag = (flag2 = true);
							break;
						}
					}
				}
			}
			if (flag)
			{
				string text = (flag2 ? "pity roll" : null);
				if (state.CreatureKey != null)
				{
					int num2 = PityStore.Get(state.CharKey, state.CreatureKey);
					PityStore.Reset(state.CharKey, state.CreatureKey);
					if (num2 > 0)
					{
						text = ((text != null) ? (text + ", ") : "") + string.Format("after {0} dry kill{1}", num2, (num2 == 1) ? "" : "s");
					}
				}
				if (TrophyAssisterPlugin.NotifyOnDrop.Value)
				{
					Hud.Center("Trophy dropped: " + state.CreatureName + "!" + ((text != null) ? ("  (" + text + ")") : ""));
				}
				if (TrophyAssisterPlugin.LogKills.Value)
				{
					TrophyAssisterPlugin.Log.LogInfo((object)("  -> trophy dropped (" + (flag2 ? "pity roll" : "game roll") + ")"));
				}
			}
			else if (state.CreatureKey != null)
			{
				int num3 = PityStore.Increment(state.CharKey, state.CreatureKey);
				float num4 = Chance.Next(state.Trophies[0].BaseChance, num3);
				if (TrophyAssisterPlugin.LogKills.Value)
				{
					TrophyAssisterPlugin.Log.LogInfo((object)$"  -> no trophy, dry kills = {num3}, next chance {num4:P0} (0 star)");
				}
				if (TrophyAssisterPlugin.ShowPityProgress.Value)
				{
					PityLabel.Show(string.Format("{0} trophy: {1} dry kill{2}, next chance {3:P0}", state.CreatureName, num3, (num3 == 1) ? "" : "s", num4));
				}
			}
			return result;
		}

		private static int Amount(Drop drop, int levelMult)
		{
			int num = (drop.m_dontScale ? Random.Range(drop.m_amountMin, drop.m_amountMax) : Game.instance.ScaleDrops(drop.m_prefab, drop.m_amountMin, drop.m_amountMax));
			if (drop.m_levelMultiplier && !drop.m_levelMultiplierNotAmount)
			{
				num *= levelMult;
			}
			if (drop.m_onePerPlayer && (Object)(object)ZNet.instance != (Object)null)
			{
				num = ZNet.instance.GetNrOfPlayers();
			}
			return Mathf.Clamp(num, 1, 100);
		}
	}
	[HarmonyPatch]
	internal static class KillCredit
	{
		private class Credit
		{
			public long Peer;

			public string Who;
		}

		private static readonly ConditionalWeakTable<Character, Credit> LastAttacker = new ConditionalWeakTable<Character, Credit>();

		private static bool Prepare()
		{
			return TargetMethod() != null;
		}

		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(typeof(Character), "ApplyDamage", (Type[])null, (Type[])null);
		}

		private static void Prefix(Character __instance, HitData hit)
		{
			try
			{
				if ((Object)(object)__instance == (Object)null || hit == null || !hit.HaveAttacker() || hit.GetTotalDamage() <= 0.1f)
				{
					return;
				}
				Character attacker = hit.GetAttacker();
				if (!((Object)(object)attacker == (Object)null))
				{
					Credit orCreateValue = LastAttacker.GetOrCreateValue(__instance);
					orCreateValue.Peer = GameUtil.PlayerPeer(attacker);
					if (TrophyAssisterPlugin.LogKills.Value)
					{
						orCreateValue.Who = attacker.m_name;
					}
				}
			}
			catch
			{
			}
		}

		public static long Killer(Character victim)
		{
			if ((Object)(object)victim != (Object)null && LastAttacker.TryGetValue(victim, out var value))
			{
				return value.Peer;
			}
			return GameUtil.LastHitPeer(victim);
		}

		public static string LastAttackerName(Character victim)
		{
			if (!((Object)(object)victim != (Object)null) || !LastAttacker.TryGetValue(victim, out var value) || value.Who == null)
			{
				return "unknown (no tracked hit)";
			}
			return GameUtil.Localize(value.Who);
		}
	}
	[HarmonyPatch(typeof(Game), "Start")]
	internal static class TrophyNet
	{
		private const string RpcKill = "TrophyAssister_Kill";

		private static void Postfix()
		{
			try
			{
				ZRoutedRpc instance = ZRoutedRpc.instance;
				if (instance != null)
				{
					instance.Register<ZPackage>("TrophyAssister_Kill", (Action<long, ZPackage>)OnKill);
				}
			}
			catch (Exception ex)
			{
				TrophyAssisterPlugin.Log.LogWarning((object)("Couldn't register kill message: " + ex.Message));
			}
		}

		public static Vector3 DropCenter(CharacterDrop drop)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: 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_0034: Unknown result type (might be due to invalid IL or missing references)
			Character component = ((Component)drop).GetComponent<Character>();
			return (((Object)(object)component != (Object)null) ? component.GetCenterPoint() : ((Component)drop).transform.position) + ((Component)drop).transform.TransformVector(drop.m_spawnOffset);
		}

		public static void SendKill(long peer, string creatureKey, int level, Vector3 center, IEnumerable<string> dropped)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Expected O, but got Unknown
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			if (ZRoutedRpc.instance != null)
			{
				ZPackage val = new ZPackage();
				val.Write(creatureKey);
				val.Write(level);
				val.Write(center);
				val.Write(string.Join(",", dropped));
				ZRoutedRpc.instance.InvokeRoutedRPC(peer, "TrophyAssister_Kill", new object[1] { val });
			}
		}

		private static void OnKill(long sender, ZPackage pkg)
		{
			//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_0122: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (!TrophyAssisterPlugin.Enabled.Value || (Object)(object)ZNetScene.instance == (Object)null)
				{
					return;
				}
				string text = pkg.ReadString();
				int num = pkg.ReadInt();
				Vector3 val = pkg.ReadVector3();
				HashSet<string> dropped = new HashSet<string>(pkg.ReadString().Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries));
				GameObject prefab = ZNetScene.instance.GetPrefab(text);
				CharacterDrop val2 = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<CharacterDrop>() : null);
				if ((Object)(object)val2 == (Object)null)
				{
					return;
				}
				Character component = prefab.GetComponent<Character>();
				bool value = TrophyAssisterPlugin.PityEnabled.Value;
				if (TrophyAssisterPlugin.LogKills.Value)
				{
					TrophyAssisterPlugin.Log.LogInfo((object)$"Kill: {text} ({num - 1} star), your kill on another player's game, counts for pity: {value}");
				}
				string creatureName = (((Object)(object)component != (Object)null) ? GameUtil.Localize(component.m_name) : text);
				GenerateDropListPatch.State state = GenerateDropListPatch.Build(val2.m_drops, text, creatureName, num, value);
				if (state != null)
				{
					KeyValuePair<GameObject, int> item = GenerateDropListPatch.Resolve(state, dropped);
					if ((Object)(object)item.Key != (Object)null)
					{
						CharacterDrop.DropItems(new List<KeyValuePair<GameObject, int>> { item }, val, 0.5f, false);
					}
				}
			}
			catch (Exception ex)
			{
				TrophyAssisterPlugin.Log.LogWarning((object)("Kill message error: " + ex.Message));
			}
		}
	}
	public enum PityMode
	{
		Compound,
		Flat
	}
	internal static class Chance
	{
		public static float Next(float baseChance, int dry)
		{
			float num = baseChance * TrophyAssisterPlugin.ChanceMultiplier.Value;
			if (TrophyAssisterPlugin.PityEnabled.Value && dry > 0)
			{
				if (TrophyAssisterPlugin.PityModeSetting.Value == PityMode.Compound)
				{
					float num2 = ((baseChance <= TrophyAssisterPlugin.RareThreshold.Value + 0.0001f) ? TrophyAssisterPlugin.RareGrowthPerKill.Value : TrophyAssisterPlugin.PityGrowthPerKill.Value);
					num *= Mathf.Pow(1f + num2, (float)dry);
				}
				else
				{
					num += TrophyAssisterPlugin.PityBonusPerKill.Value * (float)dry;
				}
				int value = TrophyAssisterPlugin.GuaranteeAfterKills.Value;
				if (value > 0 && dry >= value)
				{
					num = 1f;
				}
			}
			return Mathf.Clamp01(num);
		}
	}
	internal static class GameUtil
	{
		private static readonly FieldInfo LastHitField = AccessTools.Field(typeof(Character), "m_lastHit");

		public static bool IsTrophy(GameObject prefab, out ItemDrop item)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Invalid comparison between Unknown and I4
			item = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<ItemDrop>() : null);
			if ((Object)(object)item != (Object)null && item.m_itemData?.m_shared != null)
			{
				return (int)item.m_itemData.m_shared.m_itemType == 13;
			}
			return false;
		}

		public static long LastHitPeer(Character character)
		{
			long sessionID = ZDOMan.GetSessionID();
			if ((Object)(object)character == (Object)null || LastHitField == null)
			{
				return sessionID;
			}
			try
			{
				object? value = LastHitField.GetValue(character);
				HitData val = (HitData)((value is HitData) ? value : null);
				if (val != null)
				{
					return PlayerPeer(val.GetAttacker());
				}
				return 0L;
			}
			catch
			{
				return sessionID;
			}
		}

		public static long PlayerPeer(Character attacker)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Invalid comparison between Unknown and I4
			if ((Object)(object)attacker == (Object)null)
			{
				return 0L;
			}
			if (attacker is Player)
			{
				return Owner(attacker);
			}
			if (!attacker.IsTamed() && (int)attacker.GetFaction() != 11)
			{
				return 0L;
			}
			MonsterAI component = ((Component)attacker).GetComponent<MonsterAI>();
			GameObject val = (((Object)(object)component != (Object)null) ? component.GetFollowTarget() : null);
			Player val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Player>() : null);
			if ((Object)(object)val2 != (Object)null)
			{
				return Owner((Character)(object)val2);
			}
			ZNetView component2 = ((Component)attacker).GetComponent<ZNetView>();
			if ((Object)(object)component2 == (Object)null || !component2.IsValid())
			{
				return 0L;
			}
			string follow = component2.GetZDO().GetString(ZDOVars.s_follow, "");
			if (follow.Length > 0)
			{
				Player val3 = ((IEnumerable<Player>)Player.GetAllPlayers()).FirstOrDefault((Func<Player, bool>)((Player p) => (Object)(object)p != (Object)null && p.GetPlayerName() == follow));
				if (!((Object)(object)val3 != (Object)null))
				{
					return 0L;
				}
				return Owner((Character)(object)val3);
			}
			return component2.GetZDO().GetOwner();
		}

		private static long Owner(Character c)
		{
			ZNetView component = ((Component)c).GetComponent<ZNetView>();
			if (!((Object)(object)component != (Object)null) || !component.IsValid())
			{
				return 0L;
			}
			return component.GetZDO().GetOwner();
		}

		public static string PrefabName(GameObject go)
		{
			string name = ((Object)go).name;
			int num = name.IndexOf("(Clone)", StringComparison.Ordinal);
			return ((num >= 0) ? name.Substring(0, num) : name).Trim();
		}

		public static string CharKey(Player player)
		{
			long num;
			if (!((Object)(object)Game.instance != (Object)null))
			{
				num = 0L;
			}
			else
			{
				PlayerProfile playerProfile = Game.instance.GetPlayerProfile();
				num = ((playerProfile != null) ? playerProfile.GetPlayerID() : 0);
			}
			long num2 = num;
			if (num2 == 0L && (Object)(object)player != (Object)null)
			{
				num2 = player.GetPlayerID();
			}
			if (num2 == 0L)
			{
				return null;
			}
			return num2.ToString();
		}

		public static string Localize(string token)
		{
			if (string.IsNullOrEmpty(token) || Localization.instance == null)
			{
				return token;
			}
			return Localization.instance.Localize(token);
		}
	}
	internal static class Hud
	{
		public static void Center(string text)
		{
			Show((MessageType)2, text);
		}

		public static void TopLeft(string text)
		{
			Show((MessageType)1, text);
		}

		private static void Show(MessageType type, string text)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				MessageHud instance = MessageHud.instance;
				if (instance != null)
				{
					instance.ShowMessage(type, text, 0, (Sprite)null, false, true);
				}
			}
			catch
			{
			}
		}
	}
	internal static class PityStore
	{
		private const float SaveDelay = 30f;

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

		private static bool _dirty;

		private static float _saveAt;

		private static string FilePath => Path.Combine(Paths.ConfigPath, "TrophyAssister_pity.txt");

		private static void MarkDirty()
		{
			if (!_dirty)
			{
				_dirty = true;
				_saveAt = Time.realtimeSinceStartup + 30f;
			}
		}

		public static void SaveIfDue()
		{
			if (_dirty && Time.realtimeSinceStartup >= _saveAt)
			{
				Save();
			}
		}

		public static void SaveIfDirty()
		{
			if (_dirty)
			{
				Save();
			}
		}

		private static string Key(string ch, string creature)
		{
			return ch + "|" + creature;
		}

		public static int Get(string ch, string creature)
		{
			if (!Counts.TryGetValue(Key(ch, creature), out var value))
			{
				return 0;
			}
			return value;
		}

		public static int Increment(string ch, string creature, int amount = 1)
		{
			int num = Get(ch, creature) + Math.Max(1, amount);
			Counts[Key(ch, creature)] = num;
			MarkDirty();
			return num;
		}

		public static void Reset(string ch, string creature)
		{
			if (Counts.Remove(Key(ch, creature)))
			{
				MarkDirty();
			}
		}

		public static List<KeyValuePair<string, int>> ForCharacter(string ch)
		{
			string prefix = ch + "|";
			return (from k in Counts
				where k.Key.StartsWith(prefix, StringComparison.Ordinal)
				select new KeyValuePair<string, int>(k.Key.Substring(prefix.Length), k.Value) into k
				orderby k.Key
				select k).ToList();
		}

		public static int ResetCharacter(string ch)
		{
			List<string> list = Counts.Keys.Where((string k) => k.StartsWith(ch + "|", StringComparison.Ordinal)).ToList();
			foreach (string item in list)
			{
				Counts.Remove(item);
			}
			if (list.Count > 0)
			{
				Save();
			}
			return list.Count;
		}

		public static string Backup()
		{
			try
			{
				if (!File.Exists(FilePath))
				{
					return null;
				}
				string text = Path.ChangeExtension(FilePath, ".bak");
				File.Copy(FilePath, text, overwrite: true);
				return text;
			}
			catch (Exception ex)
			{
				ManualLogSource log = TrophyAssisterPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Couldn't back up pity file: " + ex.Message));
				}
				return null;
			}
		}

		public static void Load()
		{
			try
			{
				if (!File.Exists(FilePath))
				{
					return;
				}
				string[] array = File.ReadAllLines(FilePath);
				foreach (string text in array)
				{
					int num = text.LastIndexOf('=');
					if (num > 0 && int.TryParse(text.Substring(num + 1), out var result) && result > 0)
					{
						Counts[text.Substring(0, num)] = result;
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource log = TrophyAssisterPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Couldn't load pity file: " + ex.Message));
				}
			}
		}

		public static void Save()
		{
			_dirty = false;
			try
			{
				File.WriteAllLines(FilePath, from k in Counts
					orderby k.Key
					select $"{k.Key}={k.Value}");
			}
			catch (Exception ex)
			{
				ManualLogSource log = TrophyAssisterPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Couldn't save pity file: " + ex.Message));
				}
			}
		}
	}
	[HarmonyPatch(typeof(Game), "Logout")]
	internal static class SaveOnLogout
	{
		private static void Prefix()
		{
			PityStore.SaveIfDirty();
		}
	}
	internal static class Patcher
	{
		public static void SafePatch(Harmony harmony, Type patchClass)
		{
			try
			{
				harmony.CreateClassProcessor(patchClass).Patch();
			}
			catch (Exception ex)
			{
				TrophyAssisterPlugin.Log.LogError((object)("Couldn't patch " + patchClass.Name + " - the game code probably changed in an update. The mod is inactive but the game will run normally. Details: " + ex.Message));
			}
		}
	}
	[HarmonyPatch]
	internal static class TrophyListPatch
	{
		private static readonly FieldInfo ListField = AccessTools.Field(typeof(InventoryGui), "m_trophyList");

		private static readonly Color GreyIcon = new Color(0.25f, 0.25f, 0.25f, 0.85f);

		private static readonly Color GreyText = new Color(0.55f, 0.55f, 0.55f, 1f);

		private static bool Prepare()
		{
			int num;
			if (TargetMethod() != null)
			{
				num = ((ListField != null) ? 1 : 0);
				if (num != 0)
				{
					goto IL_002d;
				}
			}
			else
			{
				num = 0;
			}
			TrophyAssisterPlugin.Log.LogWarning((object)"Trophy list layout changed in this game version - list additions disabled, drops still boosted.");
			goto IL_002d;
			IL_002d:
			return (byte)num != 0;
		}

		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(typeof(InventoryGui), "UpdateTrophyList", (Type[])null, (Type[])null);
		}

		private static void Postfix(InventoryGui __instance)
		{
			//IL_026f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0276: Expected O, but got Unknown
			//IL_027c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0281: Unknown result type (might be due to invalid IL or missing references)
			//IL_028f: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e5: 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_037f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0395: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0307: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_0357: Unknown result type (might be due to invalid IL or missing references)
			bool value = TrophyAssisterPlugin.TrackerEnabled.Value;
			if (!TrophyAssisterPlugin.ShowChancesInList.Value && !TrophyAssisterPlugin.ShowMissingTrophies.Value && !value)
			{
				return;
			}
			try
			{
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null || (Object)(object)ObjectDB.instance == (Object)null)
				{
					return;
				}
				List<GameObject> list = ListField.GetValue(__instance) as List<GameObject>;
				GameObject trophieElementPrefab = __instance.m_trophieElementPrefab;
				RectTransform trophieListRoot = __instance.m_trophieListRoot;
				string text = GameUtil.CharKey(localPlayer);
				if (list == null || (Object)(object)trophieElementPrefab == (Object)null || (Object)(object)trophieListRoot == (Object)null || text == null)
				{
					return;
				}
				TrophyIndex.Build();
				HashSet<string> collected = new HashSet<string>(localPlayer.GetTrophies());
				if (TrophyAssisterPlugin.ShowChancesInList.Value || value)
				{
					foreach (GameObject item in list)
					{
						if ((Object)(object)item == (Object)null)
						{
							continue;
						}
						string text2 = TrophyIndex.TrophyForIcon(UiUtil.IconSprite(item.transform));
						if (text2 == null)
						{
							continue;
						}
						if (value)
						{
							TrackButton.Add(item.transform, text2, text);
						}
						if (TrophyAssisterPlugin.ShowChancesInList.Value)
						{
							string text3 = ChanceLine(text2, text, collected: true);
							if (text3 != null)
							{
								UiUtil.PrependText(item.transform, "description", text3);
							}
						}
					}
				}
				if (!TrophyAssisterPlugin.ShowMissingTrophies.Value)
				{
					return;
				}
				HashSet<string> hidden = new HashSet<string>(from s in TrophyAssisterPlugin.HiddenTrophies.Value.Split(new char[1] { ',' })
					select s.Trim() into s
					where s.Length > 0
					select s, StringComparer.OrdinalIgnoreCase);
				List<TrophyInfo> list2 = (from t in TrophyIndex.AllTrophies
					where !collected.Contains(t.PrefabName) && !hidden.Contains(t.PrefabName)
					orderby t.SortKey
					select t).ToList();
				if (list2.Count == 0)
				{
					return;
				}
				SlotGrid slotGrid = new SlotGrid(list, __instance.m_trophieListSpace);
				Dictionary<string, float> killed = Discovery.KilledCreatures();
				float num = slotGrid.LowestY;
				foreach (TrophyInfo item2 in list2)
				{
					GameObject val = Object.Instantiate<GameObject>(trophieElementPrefab, (Transform)(object)trophieListRoot);
					val.SetActive(true);
					RectTransform val2 = (RectTransform)val.transform;
					val2.anchoredPosition = slotGrid.Take(item2.Slot);
					num = Mathf.Min(num, val2.anchoredPosition.y - slotGrid.Space);
					bool num2 = !TrophyAssisterPlugin.SpoilerFree.Value || Discovery.HasKilled(item2.PrefabName, text, killed);
					Image val3 = UiUtil.IconImage((Transform)(object)val2);
					if (num2)
					{
						if ((Object)(object)val3 != (Object)null)
						{
							val3.sprite = item2.Icon;
							((Graphic)val3).color = GreyIcon;
						}
						UiUtil.SetText((Transform)(object)val2, "name", item2.DisplayName, GreyText);
						string text4 = "<i>Not collected yet</i>";
						if (TrophyAssisterPlugin.ShowChancesInList.Value)
						{
							string text5 = ChanceLine(item2.PrefabName, text, collected: false);
							if (text5 != null)
							{
								text4 = text5 + "\n" + text4;
							}
						}
						UiUtil.SetText((Transform)(object)val2, "description", text4, GreyText);
						if (value)
						{
							TrackButton.Add((Transform)(object)val2, item2.PrefabName, text);
						}
					}
					else
					{
						UiUtil.ShowQuestionMark((Transform)(object)val2, val3, GreyText);
						UiUtil.SetText((Transform)(object)val2, "name", "??????", GreyText);
						UiUtil.SetText((Transform)(object)val2, "description", "<i>Not discovered yet</i>", GreyText);
					}
					list.Add(val);
				}
				Rect rect = trophieListRoot.rect;
				if (((Rect)(ref rect)).height < 0f - num)
				{
					trophieListRoot.SetSizeWithCurrentAnchors((Axis)1, 0f - num);
				}
			}
			catch (Exception ex)
			{
				TrophyAssisterPlugin.Log.LogWarning((object)("Trophy list addition failed: " + ex.Message));
			}
		}

		private static string ChanceLine(string trophy, string charKey, bool collected)
		{
			if (!TrophyIndex.Sources.TryGetValue(trophy, out var value) || value.Count == 0)
			{
				return "<color=#9a9a9a>Drop chance: unknown</color>";
			}
			bool flag = TrophyAssisterPlugin.Enabled.Value && !(TrophyAssisterPlugin.OnlyUncollected.Value && collected);
			TrophySource trophySource = null;
			float num = -1f;
			int num2 = 0;
			foreach (TrophySource item in value)
			{
				int num3 = (flag ? PityStore.Get(charKey, item.CreaturePrefab) : 0);
				float num4 = (flag ? Chance.Next(item.BaseChance, num3) : item.BaseChance);
				if (num4 > num)
				{
					num = num4;
					trophySource = item;
					num2 = num3;
				}
			}
			string arg = ((num >= 0.5f) ? "#7CFC7C" : ((num >= 0.2f) ? "#FFD24A" : "#FF9A5A"));
			string text = $"<color={arg}>Next drop: {num:P0}</color>";
			if (value.Count > 1)
			{
				text = text + " - " + trophySource.CreatureName;
			}
			if (num2 > 0)
			{
				text += string.Format(" - {0} dry kill{1}", num2, (num2 == 1) ? "" : "s");
			}
			if (!flag && TrophyAssisterPlugin.Enabled.Value)
			{
				text += " - (no boost)";
			}
			return text;
		}
	}
	internal class TrophySource
	{
		public string CreaturePrefab;

		public string CreatureToken;

		public string CreatureName;

		public float BaseChance;
	}
	internal class TrophyInfo
	{
		public string PrefabName;

		public string DisplayName;

		public Sprite Icon;

		public string SortKey;

		public Vector2Int Slot;
	}
	internal static class TrophyIndex
	{
		public static readonly Dictionary<string, List<TrophySource>> Sources = new Dictionary<string, List<TrophySource>>();

		public static readonly List<TrophyInfo> AllTrophies = new List<TrophyInfo>();

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

		private static bool _built;

		public static string TrophyForIcon(Sprite s)
		{
			if (!((Object)(object)s != (Object)null) || !ByIcon.TryGetValue(s, out var value))
			{
				return null;
			}
			return value;
		}

		public static void Build()
		{
			//IL_029c: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			if (_built || (Object)(object)ObjectDB.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return;
			}
			Sources.Clear();
			AllTrophies.Clear();
			ByIcon.Clear();
			foreach (GameObject prefab in ZNetScene.instance.m_prefabs)
			{
				CharacterDrop val = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<CharacterDrop>() : null);
				if (val?.m_drops == null)
				{
					continue;
				}
				Character component = prefab.GetComponent<Character>();
				foreach (Drop drop in val.m_drops)
				{
					if (drop != null && GameUtil.IsTrophy(drop.m_prefab, out var _))
					{
						if (!Sources.TryGetValue(((Object)drop.m_prefab).name, out var value))
						{
							value = (Sources[((Object)drop.m_prefab).name] = new List<TrophySource>());
						}
						value.Add(new TrophySource
						{
							CreaturePrefab = ((Object)prefab).name,
							CreatureToken = (((Object)(object)component != (Object)null) ? component.m_name : null),
							CreatureName = (((Object)(object)component != (Object)null) ? GameUtil.Localize(component.m_name) : ((Object)prefab).name),
							BaseChance = drop.m_chance
						});
					}
				}
			}
			foreach (GameObject item3 in ObjectDB.instance.m_items)
			{
				if (!GameUtil.IsTrophy(item3, out var item2))
				{
					continue;
				}
				Sprite icon = item2.m_itemData.GetIcon();
				string text = GameUtil.Localize(item2.m_itemData.m_shared.m_name);
				if (text.EndsWith(" trophy", StringComparison.OrdinalIgnoreCase))
				{
					text = text.Substring(0, text.Length - 7);
				}
				if (Sources.ContainsKey(((Object)item3).name))
				{
					AllTrophies.Add(new TrophyInfo
					{
						PrefabName = ((Object)item3).name,
						DisplayName = text,
						Icon = icon,
						SortKey = $"{1f - Sources[((Object)item3).name].Max((TrophySource s) => s.BaseChance):0.000}_{text}",
						Slot = item2.m_itemData.m_shared.m_trophyPos
					});
					if ((Object)(object)icon != (Object)null && !ByIcon.ContainsKey(icon))
					{
						ByIcon[icon] = ((Object)item3).name;
					}
				}
			}
			_built = AllTrophies.Count > 0;
		}
	}
	internal static class Discovery
	{
		public static Dictionary<string, float> KilledCreatures()
		{
			try
			{
				Game instance = Game.instance;
				object result;
				if (instance == null)
				{
					result = null;
				}
				else
				{
					PlayerProfile playerProfile = instance.GetPlayerProfile();
					result = ((playerProfile != null) ? playerProfile.m_playerStats[0].m_enemyStats[0] : null);
				}
				return (Dictionary<string, float>)result;
			}
			catch (Exception ex)
			{
				TrophyAssisterPlugin.Log.LogWarning((object)("Couldn't read kill stats: " + ex.Message));
				return null;
			}
		}

		public static bool HasKilled(string trophy, string charKey, Dictionary<string, float> killed)
		{
			if (!TrophyIndex.Sources.TryGetValue(trophy, out var value))
			{
				return false;
			}
			foreach (TrophySource item in value)
			{
				if (killed != null && item.CreatureToken != null && killed.TryGetValue(item.CreatureToken, out var value2) && value2 > 0f)
				{
					return true;
				}
				if (PityStore.Get(charKey, item.CreaturePrefab) > 0)
				{
					return true;
				}
			}
			return false;
		}
	}
	internal class SlotGrid
	{
		public readonly float Space;

		private readonly HashSet<Vector2Int> _used = new HashSet<Vector2Int>();

		private int _columns = 1;

		private int _nextOverflow = -1;

		public float LowestY { get; private set; }

		public SlotGrid(List<GameObject> elements, float space)
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			Space = ((space > 0f) ? space : 160f);
			LowestY = 0f;
			foreach (GameObject element in elements)
			{
				if (!((Object)(object)element == (Object)null))
				{
					Vector2 anchoredPosition = ((RectTransform)element.transform).anchoredPosition;
					MarkUsed(new Vector2Int(Mathf.RoundToInt(anchoredPosition.x / Space), Mathf.RoundToInt((0f - anchoredPosition.y) / Space)));
				}
			}
			foreach (TrophyInfo allTrophy in TrophyIndex.AllTrophies)
			{
				_columns = Mathf.Max(_columns, ((Vector2Int)(ref allTrophy.Slot)).x + 1);
			}
		}

		public Vector2 Take(Vector2Int slot)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			if (_used.Contains(slot))
			{
				int num = ((TrophyIndex.AllTrophies.Count > 0) ? TrophyIndex.AllTrophies.Max((TrophyInfo t) => ((Vector2Int)(ref t.Slot)).y) : 0);
				num = Mathf.Max(num, (_used.Count > 0) ? _used.Max((Vector2Int u) => ((Vector2Int)(ref u)).y) : 0);
				if (_nextOverflow < 0)
				{
					_nextOverflow = (num + 1) * _columns;
				}
				do
				{
					((Vector2Int)(ref slot))..ctor(_nextOverflow % _columns, _nextOverflow / _columns);
					_nextOverflow++;
				}
				while (_used.Contains(slot));
			}
			MarkUsed(slot);
			return new Vector2((float)((Vector2Int)(ref slot)).x * Space, (float)(-((Vector2Int)(ref slot)).y) * Space);
		}

		private void MarkUsed(Vector2Int slot)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			_used.Add(slot);
			LowestY = Mathf.Min(LowestY, (float)(-((Vector2Int)(ref slot)).y) * Space - Space);
		}
	}
	internal static class UiUtil
	{
		public static Image IconImage(Transform el)
		{
			Transform val = el.Find("icon_bkg/icon");
			if ((Object)(object)val != (Object)null)
			{
				Image component = ((Component)val).GetComponent<Image>();
				if (component != null)
				{
					return component;
				}
			}
			return ((IEnumerable<Image>)((Component)el).GetComponentsInChildren<Image>(true)).FirstOrDefault((Func<Image, bool>)((Image i) => ((Object)i).name == "icon"));
		}

		public static Sprite IconSprite(Transform el)
		{
			Image obj = IconImage(el);
			if (obj == null)
			{
				return null;
			}
			return obj.sprite;
		}

		public static void ShowQuestionMark(Transform el, Image icon, Color color)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: 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_0084: 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_008f: 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_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_00f1: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)icon == (Object)null)
			{
				return;
			}
			((Behaviour)icon).enabled = false;
			Transform val = el.Find("name");
			if (!((Object)(object)val == (Object)null))
			{
				GameObject obj = Object.Instantiate<GameObject>(((Component)val).gameObject, ((Component)icon).transform);
				((Object)obj).name = "trophyassister_unknown";
				RectTransform val2 = (RectTransform)obj.transform;
				val2.anchorMin = Vector2.zero;
				val2.anchorMax = Vector2.one;
				val2.pivot = new Vector2(0.5f, 0.5f);
				val2.offsetMin = Vector2.zero;
				val2.offsetMax = Vector2.zero;
				((Transform)val2).localScale = Vector3.one;
				TMP_Text component = obj.GetComponent<TMP_Text>();
				if (!((Object)(object)component == (Object)null))
				{
					Rect rect = ((RectTransform)((Component)icon).transform).rect;
					float height = ((Rect)(ref rect)).height;
					component.enableAutoSizing = false;
					component.fontSize = ((height > 0f) ? (height * 0.8f) : 48f);
					component.alignment = (TextAlignmentOptions)514;
					((Graphic)component).color = color;
					component.text = "?";
				}
			}
		}

		private static TMP_Text Text(Transform el, string child)
		{
			Transform val = el.Find(child);
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return ((Component)val).GetComponent<TMP_Text>();
		}

		public static void SetText(Transform el, string child, string value, Color? color = null)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			TMP_Text val = Text(el, child);
			if (!((Object)(object)val == (Object)null))
			{
				val.text = value;
				if (color.HasValue)
				{
					((Graphic)val).color = color.Value;
				}
			}
		}

		public static void PrependText(Transform el, string child, string line)
		{
			TMP_Text val = Text(el, child);
			if ((Object)(object)val != (Object)null)
			{
				val.text = line + "\n" + val.text;
			}
		}
	}
	internal static class TrackedStore
	{
		private static readonly HashSet<string> Tracked = new HashSet<string>();

		private static string FilePath => Path.Combine(Paths.ConfigPath, "TrophyAssister_tracked.txt");

		public static int Version { get; private set; }

		private static string Key(string ch, string trophy)
		{
			return ch + "|" + trophy;
		}

		public static bool IsTracked(string ch, string trophy)
		{
			return Tracked.Contains(Key(ch, trophy));
		}

		public static bool Toggle(string ch, string trophy)
		{
			string item = Key(ch, trophy);
			bool num = !Tracked.Remove(item);
			if (num)
			{
				Tracked.Add(item);
			}
			Version++;
			Save();
			return num;
		}

		public static List<string> ForCharacter(string ch)
		{
			string prefix = ch + "|";
			return (from k in Tracked
				where k.StartsWith(prefix, StringComparison.Ordinal)
				select k.Substring(prefix.Length)).ToList();
		}

		public static void Load()
		{
			try
			{
				if (!File.Exists(FilePath))
				{
					return;
				}
				string[] array = File.ReadAllLines(FilePath);
				foreach (string text in array)
				{
					if (text.IndexOf('|') > 0)
					{
						Tracked.Add(text.Trim());
					}
				}
				Version++;
			}
			catch (Exception ex)
			{
				ManualLogSource log = TrophyAssisterPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Couldn't load tracked trophies: " + ex.Message));
				}
			}
		}

		private static void Save()
		{
			try
			{
				File.WriteAllLines(FilePath, Tracked.OrderBy((string k) => k));
			}
			catch (Exception ex)
			{
				ManualLogSource log = TrophyAssisterPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Couldn't save tracked trophies: " + ex.Message));
				}
			}
		}
	}
	internal static class TrophyFinder
	{
		private const float SearchInterval = 1f;

		private const float PingOffset = 3f;

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

		private static string _creaturesFor;

		private static int _creaturesVersion = -1;

		private static Character _target;

		private static float _searchTimer;

		private static float _pingTimer;

		public static bool HasTarget => (Object)(object)_target != (Object)null;

		public static void Tick()
		{
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: 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)
			Player localPlayer = Player.m_localPlayer;
			SE_Finder val = ((TrophyAssisterPlugin.Enabled.Value && TrophyAssisterPlugin.TrackerEnabled.Value) ? Wishbone(localPlayer) : null);
			if ((Object)(object)val == (Object)null)
			{
				_target = null;
				_searchTimer = 0f;
				return;
			}
			float deltaTime = Time.deltaTime;
			_searchTimer += deltaTime;
			if (_searchTimer > 1f)
			{
				_searchTimer = 0f;
				Character val2 = FindClosest(localPlayer);
				if ((Object)(object)val2 != (Object)(object)_target)
				{
					_target = val2;
					_pingTimer = 0f;
				}
			}
			if ((Object)(object)_target == (Object)null)
			{
				return;
			}
			if (_target.IsDead())
			{
				_target = null;
				return;
			}
			Transform transform = ((Component)localPlayer).transform;
			float num = Mathf.Clamp01(Utils.DistanceXZ(transform.position, ((Component)_target).transform.position) / PingRange());
			_pingTimer += deltaTime;
			if (!(_pingTimer <= Mathf.Lerp(val.m_closeFrequency, val.m_distantFrequency, num)))
			{
				_pingTimer = 0f;
				RedTint.Apply(((num < 0.2f) ? val.m_pingEffectNear : ((num < 0.6f) ? val.m_pingEffectMed : val.m_pingEffectFar)).Create(PingPosition(transform, _target), PingRotation(transform, _target), (Transform)null, 1f, -1, ((Character)localPlayer).GetZDOID()));
			}
		}

		private static Vector3 PingPosition(Transform me, Character target)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = Direction(me, target);
			float num = Mathf.Min(3f, Utils.DistanceXZ(me.position, ((Component)target).transform.position));
			return me.position + val * num + Vector3.up;
		}

		private static Quaternion PingRotation(Transform me, Character target)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			return Quaternion.LookRotation(Direction(me, target), Vector3.up);
		}

		private static Vector3 Direction(Transform me, Character target)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = ((Component)target).transform.position - me.position;
			val.y = 0f;
			if (!(((Vector3)(ref val)).sqrMagnitude > 0.01f))
			{
				return me.forward;
			}
			return ((Vector3)(ref val)).normalized;
		}

		private static float PingRange()
		{
			//IL_002a: 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)
			float value = TrophyAssisterPlugin.TrackerRange.Value;
			if (value > 0f)
			{
				return value;
			}
			int num;
			if (!((Object)(object)ZNet.instance != (Object)null))
			{
				num = 1;
			}
			else
			{
				SimulationDistance syncedSimulationDistance = ZNet.instance.GetSyncedSimulationDistance();
				num = ((SimulationDistance)(ref syncedSimulationDistance)).NearSimulationDistance;
			}
			int num2 = num;
			return ((float)Mathf.Max(1, num2) + 0.5f) * 64f;
		}

		private static SE_Finder Wishbone(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return null;
			}
			foreach (StatusEffect statusEffect in ((Character)player).GetSEMan().GetStatusEffects())
			{
				SE_Finder val = (SE_Finder)(object)((statusEffect is SE_Finder) ? statusEffect : null);
				if (val != null)
				{
					return val;
				}
			}
			return null;
		}

		private static Character FindClosest(Player player)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			if (!RefreshCreatures())
			{
				return null;
			}
			Vector3 position = ((Component)player).transform.position;
			float value = TrophyAssisterPlugin.TrackerRange.Value;
			Character result = null;
			float num = ((value > 0f) ? value : float.MaxValue);
			foreach (Character allCharacter in Character.GetAllCharacters())
			{
				if ((Object)(object)allCharacter == (Object)null || allCharacter is Player || allCharacter.IsDead() || allCharacter.IsTamed())
				{
					continue;
				}
				float num2 = Vector3.Distance(position, ((Component)allCharacter).transform.position);
				if (!(num2 >= num))
				{
					ZNetView component = ((Component)allCharacter).GetComponent<ZNetView>();
					ZDO val = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null);
					if (val != null && Creatures.Contains(val.GetPrefab()))
					{
						result = allCharacter;
						num = num2;
					}
				}
			}
			return result;
		}

		private static bool RefreshCreatures()
		{
			string text = GameUtil.CharKey(Player.m_localPlayer);
			if (text != _creaturesFor || TrackedStore.Version != _creaturesVersion)
			{
				TrophyIndex.Build();
				Creatures.Clear();
				if (text != null)
				{
					foreach (string item in TrackedStore.ForCharacter(text))
					{
						if (!TrophyIndex.Sources.TryGetValue(item, out var value))
						{
							continue;
						}
						foreach (TrophySource item2 in value)
						{
							Creatures.Add(StringExtensionMethods.GetStableHashCode(item2.CreaturePrefab));
						}
					}
				}
				_creaturesFor = text;
				_creaturesVersion = TrackedStore.Version;
			}
			return Creatures.Count > 0;
		}
	}
	internal static class RedTint
	{
		private static readonly int[] ColorProps = new int[3]
		{
			Shader.PropertyToID("_Color"),
			Shader.PropertyToID("_TintColor"),
			Shader.PropertyToID("_EmissionColor")
		};

		private static MaterialPropertyBlock _block;

		public static void Apply(GameObject[] effects)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: 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_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			if (effects == null)
			{
				return;
			}
			try
			{
				foreach (GameObject val in effects)
				{
					if ((Object)(object)val == (Object)null)
					{
						continue;
					}
					ParticleSystem[] componentsInChildren = val.GetComponentsInChildren<ParticleSystem>(true);
					foreach (ParticleSystem obj in componentsInChildren)
					{
						MainModule main = obj.main;
						((MainModule)(ref main)).startColor = Red(((MainModule)(ref main)).startColor);
						ColorOverLifetimeModule colorOverLifetime = obj.colorOverLifetime;
						if (((ColorOverLifetimeModule)(ref colorOverLifetime)).enabled)
						{
							((ColorOverLifetimeModule)(ref colorOverLifetime)).color = Red(((ColorOverLifetimeModule)(ref colorOverLifetime)).color);
						}
					}
					Light[] componentsInChildren2 = val.GetComponentsInChildren<Light>(true);
					foreach (Light obj2 in componentsInChildren2)
					{
						obj2.color = Red(obj2.color);
					}
					Renderer[] componentsInChildren3 = val.GetComponentsInChildren<Renderer>(true);
					for (int j = 0; j < componentsInChildren3.Length; j++)
					{
						TintRenderer(componentsInChildren3[j]);
					}
				}
			}
			catch (Exception ex)
			{
				TrophyAssisterPlugin.Log.LogWarning((object)("Couldn't colour the wishbone ping: " + ex.Message));
			}
		}

		private static void TintRenderer(Renderer r)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			Material sharedMaterial = r.sharedMaterial;
			if ((Object)(object)sharedMaterial == (Object)null)
			{
				return;
			}
			_block = (MaterialPropertyBlock)(((object)_block) ?? ((object)new MaterialPropertyBlock()));
			r.GetPropertyBlock(_block);
			bool flag = false;
			int[] colorProps = ColorProps;
			foreach (int num in colorProps)
			{
				if (sharedMaterial.HasProperty(num))
				{
					_block.SetColor(num, Red(sharedMaterial.GetColor(num)));
					flag = true;
				}
			}
			if (flag)
			{
				r.SetPropertyBlock(_block);
			}
		}

		public static Color Red(Color c)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			float num = Mathf.Max(c.r, Mathf.Max(c.g, c.b));
			return new Color(num, num * 0.12f, num * 0.08f, c.a);
		}

		private static Gradient Red(Gradient g)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: 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_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			if (g == null)
			{
				return null;
			}
			GradientColorKey[] colorKeys = g.colorKeys;
			for (int i = 0; i < colorKeys.Length; i++)
			{
				colorKeys[i].color = Red(colorKeys[i].color);
			}
			Gradient val = new Gradient
			{
				mode = g.mode
			};
			val.SetKeys(colorKeys, g.alphaKeys);
			return val;
		}

		private static MinMaxGradient Red(MinMaxGradient g)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected I4, but got Unknown
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: 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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			ParticleSystemGradientMode mode = ((MinMaxGradient)(ref g)).mode;
			switch (mode - 1)
			{
			case 1:
				return new MinMaxGradient(Red(((MinMaxGradient)(ref g)).colorMin), Red(((MinMaxGradient)(ref g)).colorMax));
			case 0:
				return new MinMaxGradient(Red(((MinMaxGradient)(ref g)).gradient));
			case 2:
				return new MinMaxGradient(Red(((MinMaxGradient)(ref g)).gradientMin), Red(((MinMaxGradient)(ref g)).gradientMax));
			case 3:
			{
				MinMaxGradient result = default(MinMaxGradient);
				((MinMaxGradient)(ref result))..ctor(Red(((MinMaxGradient)(ref g)).gradient));
				((MinMaxGradient)(ref result)).mode = (ParticleSystemGradientMode)4;
				return result;
			}
			default:
				return new MinMaxGradient(Red(((MinMaxGradient)(ref g)).color));
			}
		}
	}
	[HarmonyPatch]
	internal static class WishboneIconPatch
	{
		private static readonly FieldInfo IconsField = AccessTools.Field(typeof(Hud), "m_statusEffects");

		private static readonly Color IconRed = new Color(1f, 0.3f, 0.25f, 1f);

		private static bool Prepare()
		{
			if (TargetMethod() != null)
			{
				return IconsField != null;
			}
			return false;
		}

		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(typeof(Hud), "UpdateStatusEffects", (Type[])null, (Type[])null);
		}

		private static void Postfix(Hud __instance, List<StatusEffect> statusEffects)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			if (!TrophyFinder.HasTarget || statusEffects == null)
			{
				return;
			}
			try
			{
				if (!(IconsField.GetValue(__instance) is List<RectTransform> list))
				{
					return;
				}
				for (int i = 0; i < statusEffects.Count && i < list.Count; i++)
				{
					if (statusEffects[i] is SE_Finder && !((Object)(object)list[i] == (Object)null))
					{
						Transform obj = ((Transform)list[i]).Find("Icon");
						Image val = ((obj != null) ? ((Component)obj).GetComponent<Image>() : null);
						if ((Object)(object)val != (Object)null)
						{
							((Graphic)val).color = IconRed;
						}
					}
				}
			}
			catch
			{
			}
		}
	}
	internal static class TrackButton
	{
		private const float Size = 30f;

		private const string TrackTip = "Track with the wishbone";

		private const string UntrackTip = "Stop tracking";

		private static readonly Color Tracked = new Color(1f, 0.25f, 0.2f, 1f);

		private static readonly Color Untracked = new Color(0.6f, 0.6f, 0.6f, 0.6f);

		private static Sprite _sprite;

		public static void Add(Transform el, string trophy, string charKey)
		{
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Expected O, but got Unknown
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: 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_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010b: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_025c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0266: Expected O, but got Unknown
			Image val = UiUtil.IconImage(el);
			Transform val2 = (((Object)(object)val != (Object)null && (Object)(object)((Component)val).transform.parent != (Object)(object)el) ? ((Component)val).transform.parent : ((val != null) ? ((Component)val).transform : null));
			if ((Object)(object)val2 == (Object)null)
			{
				return;
			}
			GameObject val3 = new GameObject("TrophyAssister_track", new Type[3]
			{
				typeof(RectTransform),
				typeof(Image),
				typeof(Button)
			});
			RectTransform val4 = (RectTransform)val3.transform;
			((Transform)val4).SetParent(el, false);
			((Transform)val4).SetAsLastSibling();
			Vector2 val5 = default(Vector2);
			((Vector2)(ref val5))..ctor(0.5f, 0.5f);
			val4.pivot = val5;
			Vector2 anchorMin = (val4.anchorMax = val5);
			val4.anchorMin = anchorMin;
			val4.sizeDelta = new Vector2(30f, 30f);
			Vector3[] array = (Vector3[])(object)new Vector3[4];
			((RectTransform)val2).GetWorldCorners(array);
			((Transform)val4).localPosition = el.InverseTransformPoint(array[2]) - new Vector3(9f, 9f, 0f);
			Image image = val3.GetComponent<Image>();
			image.sprite = Icon();
			image.preserveAspect = true;
			Button component = val3.GetComponent<Button>();
			ColorBlock colors = ((Selectable)component).colors;
			((ColorBlock)(ref colors)).normalColor = new Color(0.8f, 0.8f, 0.8f, 1f);
			((ColorBlock)(ref colors)).highlightedColor = Color.white;
			((ColorBlock)(ref colors)).selectedColor = ((ColorBlock)(ref colors)).normalColor;
			((ColorBlock)(ref colors)).pressedColor = new Color(0.6f, 0.6f, 0.6f, 1f);
			((Selectable)component).colors = colors;
			Navigation navigation = default(Navigation);
			((Navigation)(ref navigation)).mode = (Mode)0;
			((Selectable)component).navigation = navigation;
			UITooltip tooltip = val3.AddComponent<UITooltip>();
			UITooltip obj = tooltip;
			InventoryGui instance = InventoryGui.instance;
			object tooltipPrefab;
			if (instance == null)
			{
				tooltipPrefab = null;
			}
			else
			{
				Button repairButton = instance.m_repairButton;
				tooltipPrefab = ((repairButton == null) ? null : ((Component)repairButton).GetComponent<UITooltip>()?.m_tooltipPrefab);
			}
			obj.m_tooltipPrefab = (GameObject)tooltipPrefab;
			tooltip.m_topic = "";
			Show(image, tooltip, TrackedStore.IsTracked(charKey, trophy));
			((UnityEvent)component.onClick).AddListener((UnityAction)delegate
			{
				bool flag = TrackedStore.Toggle(charKey, trophy);
				Show(image, tooltip, flag);
				string text = TrophyIndex.AllTrophies.FirstOrDefault((TrophyInfo t) => t.PrefabName == trophy)?.DisplayName ?? trophy;
				Hud.TopLeft(flag ? ("Tracking " + text + " with the wishbone") : ("Stopped tracking " + text));
			});
		}

		private static void Show(Image image, UITooltip tooltip, bool tracked)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			((Graphic)image).color = (tracked ? Tracked : Untracked);
			tooltip.m_text = (tracked ? "Stop tracking" : "Track with the wishbone");
		}

		private static Sprite Icon()
		{
			//IL_0024: 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_0030: 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_004e: 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_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_sprite != (Object)null)
			{
				return _sprite;
			}
			Vector2 joint = new Vector2(100f, 86f);
			Vector2[] left = Curve(joint, new Vector2(54f, 77f), new Vector2(14f, 63f));
			Vector2[] down = Curve(joint, new Vector2(99f, 30f), new Vector2(58f, 15f));
			Vector2[] stem = Curve(joint, new Vector2(108f, 101f), new Vector2(115f, 115f));
			return _sprite = ShapeIcon.Create("TrophyAssister_Wishbone", (float x, float y) => OnBone(left, 7f, 5.5f, x, y) || OnBone(down, 7f, 5.5f, x, y) || OnBone(stem, 8f, 8f, x, y) || ShapeIcon.Ellipse(x, y, joint.x, joint.y, 9f, 9f) || ShapeIcon.Ellipse(x, y, 13f, 63f, 7f, 7f) || ShapeIcon.Ellipse(x, y, 57f, 19f, 7f, 7f) || ShapeIcon.Ellipse(x, y, 115f, 116f, 8f, 9f));
		}

		private static Vector2[] Curve(Vector2 a, Vector2 b, Vector2 c)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			Vector2[] array = (Vector2[])(object)new Vector2[17];
			for (int i = 0; i < array.Length; i++)
			{
				float num = (float)i / ((float)array.Length - 1f);
				array[i] = (1f - num) * (1f - num) * a + 2f * (1f - num) * num * b + num * num * c;
			}
			return array;
		}

		private static bool OnBone(Vector2[] bone, float start, float end, float x, float y)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor(x, y);
			for (int i = 0; i < bone.Length - 1; i++)
			{
				Vector2 val2 = bone[i];
				Vector2 val3 = bone[i + 1] - val2;
				float num = Mathf.Clamp01(Vector2.Dot(val - val2, val3) / ((Vector2)(ref val3)).sqrMagnitude);
				float num2 = Mathf.Lerp(start, end, ((float)i + num) / (float)(bone.Length - 1));
				Vector2 val4 = val - (val2 + val3 * num);
				if (((Vector2)(ref val4)).sqrMagnitude <= num2 * num2)
				{
					return true;
				}
			}
			return false;
		}
	}
	internal static class PluginInfo
	{
		public const string Version = "1.2.1";
	}
}