Decompiled source of DeathRecap v1.0.6

plugins/DeathRecap.dll

Decompiled 3 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
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 Splatform;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("DeathRecap")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("DeathRecap")]
[assembly: AssemblyTitle("DeathRecap")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace DeathRecap;

[BepInPlugin("anwo.DeathRecap", "DeathRecap", "1.0.6")]
public class DeathRecapPlugin : BaseUnityPlugin
{
	internal sealed class HitEntry
	{
		public float Time;

		public string Source;

		public float Raw;

		public float Taken;

		public float HealthAfter;

		public float MaxHealth;

		public DamageTypes Types;

		public bool HasTypes;
	}

	internal sealed class Recap
	{
		public DateTime When;

		public List<HitEntry> Hits = new List<HitEntry>();

		public List<KeyValuePair<string, float>> ByAttacker = new List<KeyValuePair<string, float>>();

		public List<KeyValuePair<string, float>> ByType = new List<KeyValuePair<string, float>>();

		public float DeathTime;
	}

	public const string Guid = "anwo.DeathRecap";

	public const string Name = "DeathRecap";

	public const string Version = "1.0.6";

	internal static DeathRecapPlugin Instance;

	internal static ConfigEntry<KeyboardShortcut> RecapKey;

	internal static ConfigEntry<bool> ShowOnDeath;

	internal static ConfigEntry<bool> KeepOpenAfterRespawn;

	internal static ConfigEntry<float> HistorySeconds;

	internal static ConfigEntry<int> MaxLines;

	internal static ConfigEntry<float> Scale;

	internal static ConfigEntry<bool> ShowGraves;

	internal static ConfigEntry<bool> AnnounceDeaths;

	internal static ManualLogSource Log;

	private readonly List<HitEntry> _history = new List<HitEntry>();

	private Recap _recap;

	private bool _show;

	private bool _deathPending;

	private Player _deadPlayer;

	private Rect _window = new Rect(60f, 80f, 660f, 560f);

	private Vector2 _scroll;

	private Harmony _harmony;

	private GUIStyle _bold;

	private GUIStyle _rich;

	internal static HitData PendingHit;

	internal static float PendingRaw;

	internal static DamageTypes PendingTypes;

	private void Awake()
	{
		//IL_002b: 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_00b7: Expected O, but got Unknown
		//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ed: Expected O, but got Unknown
		//IL_0120: Unknown result type (might be due to invalid IL or missing references)
		//IL_012a: Expected O, but got Unknown
		//IL_0180: Unknown result type (might be due to invalid IL or missing references)
		//IL_018a: Expected O, but got Unknown
		//IL_01ce: Unknown result type (might be due to invalid IL or missing references)
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		RecapKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("1 - General", "Recap key", new KeyboardShortcut((KeyCode)279, Array.Empty<KeyCode>()), "Opens or closes the recap of your last death.");
		ShowOnDeath = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Show on death", true, "Open the recap automatically when you die.");
		KeepOpenAfterRespawn = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Keep open after respawn", false, "Keep the recap open after you respawn. When off, it closes as soon as you are back.");
		HistorySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("2 - Recap", "Seconds of history", 30f, new ConfigDescription("How many seconds before your death the recap covers.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 300f), Array.Empty<object>()));
		MaxLines = ((BaseUnityPlugin)this).Config.Bind<int>("2 - Recap", "Max hits listed", 25, new ConfigDescription("How many individual hits the recap lists.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(5, 200), Array.Empty<object>()));
		Scale = ((BaseUnityPlugin)this).Config.Bind<float>("3 - Look", "Scale", 1f, new ConfigDescription("Size of the recap window.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.6f, 2.5f), Array.Empty<object>()));
		ShowGraves = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Graves", "Show graves of other players", true, "Marks other players' graves on your map with a skull, until they have taken their things back. Needs DeathRecap on the host (or the dedicated server) too, which keeps track of the graves.");
		AnnounceDeaths = ((BaseUnityPlugin)this).Config.Bind<bool>("4 - Graves", "Announce deaths", true, "Says on screen and in the chat when another player dies, and in which biome. Only while Show graves of other players is on.");
		((BaseUnityPlugin)this).Config.Save();
		_harmony = new Harmony("anwo.DeathRecap");
		_harmony.PatchAll(typeof(DamagePatches));
		_harmony.PatchAll(typeof(GravePatches));
		((BaseUnityPlugin)this).Logger.LogInfo((object)string.Format("{0} {1} loaded. Recap key: {2}", "DeathRecap", "1.0.6", RecapKey.Value));
	}

	private void OnDestroy()
	{
		Harmony harmony = _harmony;
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
	}

	internal void Record(Character victim, HitData hit, float healthBefore)
	{
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			float health = victim.GetHealth();
			float num = Mathf.Max(0f, healthBefore - health);
			float totalDamage = hit.GetTotalDamage();
			if (!(num < 0.05f) || !(totalDamage < 0.1f))
			{
				HitEntry hitEntry = new HitEntry
				{
					Time = Time.time,
					Source = DescribeSource(hit),
					Taken = num,
					HealthAfter = health,
					MaxHealth = victim.GetMaxHealth()
				};
				if (PendingHit == hit)
				{
					hitEntry.Raw = PendingRaw;
					hitEntry.Types = PendingTypes;
					hitEntry.HasTypes = true;
				}
				else
				{
					hitEntry.Raw = totalDamage;
					hitEntry.Types = hit.m_damage;
					hitEntry.HasTypes = true;
				}
				PendingHit = null;
				_history.Add(hitEntry);
				float cutoff = Time.time - HistorySeconds.Value;
				_history.RemoveAll((HitEntry h) => h.Time < cutoff);
				if (_history.Count > 500)
				{
					_history.RemoveRange(0, _history.Count - 500);
				}
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogDebug((object)("Record failed: " + ex.Message));
		}
	}

	internal void OnLocalDeath(Player player)
	{
		_deathPending = true;
		_deadPlayer = player;
	}

	private static string DescribeSource(HitData hit)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Invalid comparison between Unknown and I4
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Invalid comparison between Unknown and I4
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Invalid comparison between Unknown and I4
		//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: Invalid comparison between Unknown and I4
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Invalid comparison between Unknown and I4
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_009d: Invalid comparison between Unknown and I4
		string text = HitTypeName(hit.m_hitType);
		Character val = null;
		try
		{
			val = hit.GetAttacker();
		}
		catch
		{
		}
		if ((Object)(object)val != (Object)null)
		{
			Player val2 = (Player)(object)((val is Player) ? val : null);
			string text2;
			if (val2 != null)
			{
				text2 = val2.GetPlayerName();
			}
			else
			{
				text2 = val.GetHoverName();
				int num = val.GetLevel() - 1;
				if (num > 0)
				{
					text2 = text2 + " " + new string('★', Mathf.Min(num, 10));
				}
			}
			if ((Object)(object)val == (Object)(object)Player.m_localPlayer)
			{
				if ((int)hit.m_hitType == 0 || (int)hit.m_hitType == 14 || (int)hit.m_hitType == 2 || (int)hit.m_hitType == 1)
				{
					return "Yourself";
				}
				return text + " (caused by you)";
			}
			if ((int)hit.m_hitType != 5 && (int)hit.m_hitType != 7 && (int)hit.m_hitType != 6)
			{
				return text2;
			}
			return text2 + " (" + text.ToLowerInvariant() + ")";
		}
		return text;
	}

	private static string HitTypeName(HitType t)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Expected I4, but got Unknown
		return (t - 1) switch
		{
			2 => "Fall damage", 
			3 => "Drowning", 
			4 => "Burning", 
			5 => "Freezing", 
			6 => "Poison", 
			7 => "Water", 
			8 => "Smoke", 
			9 => "Edge of the world", 
			10 => "Hit by an object", 
			13 => "Yourself", 
			14 => "Falling building piece", 
			15 => "Turret", 
			16 => "Boat", 
			18 => "Catapult", 
			19 => "Cinder fire", 
			20 => "Boiling sea", 
			21 => "Lava", 
			12 => "Falling tree", 
			17 => "Falling icicle", 
			11 => "Cart", 
			22 => "Obliterator", 
			23 => "Drawbridge", 
			0 => "An enemy", 
			1 => "Another player", 
			_ => "Unknown attacker", 
		};
	}

	private void BuildRecap()
	{
		Recap recap = new Recap
		{
			When = DateTime.Now,
			DeathTime = Time.time
		};
		float cutoff = recap.DeathTime - HistorySeconds.Value;
		List<HitEntry> list = _history.Where((HitEntry h) => h.Time >= cutoff).ToList();
		list.Reverse();
		recap.Hits = list;
		recap.ByAttacker = (from h in list
			group h by h.Source into g
			select new KeyValuePair<string, float>(g.Key, g.Sum((HitEntry h) => h.Taken)) into kv
			orderby kv.Value descending
			select kv).ToList();
		Dictionary<string, float> dictionary = new Dictionary<string, float>();
		foreach (HitEntry item in list)
		{
			if (item.HasTypes)
			{
				Add(dictionary, "Blunt", item.Types.m_blunt);
				Add(dictionary, "Slash", item.Types.m_slash);
				Add(dictionary, "Pierce", item.Types.m_pierce);
				Add(dictionary, "Fire", item.Types.m_fire);
				Add(dictionary, "Frost", item.Types.m_frost);
				Add(dictionary, "Lightning", item.Types.m_lightning);
				Add(dictionary, "Poison", item.Types.m_poison);
				Add(dictionary, "Spirit", item.Types.m_spirit);
				Add(dictionary, "Other", item.Types.m_damage);
			}
		}
		recap.ByType = (from kv in dictionary
			where kv.Value > 0.05f
			orderby kv.Value descending
			select kv).ToList();
		_recap = recap;
		_history.Clear();
	}

	private static void Add(Dictionary<string, float> d, string k, float v)
	{
		if (!(v <= 0f))
		{
			d.TryGetValue(k, out var value);
			d[k] = value + v;
		}
	}

	private void Update()
	{
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		Graves.Tick();
		try
		{
			if (_deathPending)
			{
				_deathPending = false;
				BuildRecap();
				if (ShowOnDeath.Value)
				{
					_show = true;
				}
			}
			if (!IsTyping())
			{
				KeyboardShortcut value = RecapKey.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					if (_recap == null)
					{
						Player localPlayer = Player.m_localPlayer;
						if (localPlayer != null)
						{
							((Character)localPlayer).Message((MessageType)1, "No death recorded yet this session", 0, (Sprite)null, false);
						}
					}
					else
					{
						_show = !_show;
					}
				}
			}
			if (_show && !KeepOpenAfterRespawn.Value && (Object)(object)_deadPlayer != (Object)null)
			{
				Player localPlayer2 = Player.m_localPlayer;
				if ((Object)(object)localPlayer2 != (Object)null && (Object)(object)localPlayer2 != (Object)(object)_deadPlayer && !((Character)localPlayer2).IsDead())
				{
					_show = false;
					_deadPlayer = null;
				}
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogDebug((object)("Update failed: " + ex.Message));
		}
	}

	private static bool IsTyping()
	{
		try
		{
			if (Console.IsVisible() || TextInput.IsVisible() || Menu.IsVisible() || Minimap.InTextInput())
			{
				return true;
			}
			Chat instance = Chat.instance;
			return (Object)(object)instance != (Object)null && instance.HasFocus();
		}
		catch
		{
			return false;
		}
	}

	private void OnGUI()
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: 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_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_0082: Expected O, but got Unknown
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		if (!_show || _recap == null)
		{
			return;
		}
		try
		{
			Matrix4x4 matrix = GUI.matrix;
			float value = Scale.Value;
			GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(value, value, 1f));
			GUISkin skin = GUI.skin;
			GUI.skin = PanelSkin.Get();
			_window = GUILayout.Window(384679, _window, new WindowFunction(DrawWindow), "What killed you", GUI.skin.window, Array.Empty<GUILayoutOption>());
			GUI.skin = skin;
			GUI.matrix = matrix;
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogDebug((object)("OnGUI failed: " + ex.Message));
		}
	}

	private void DrawWindow(int id)
	{
		GUISkin skin = GUI.skin;
		GUI.skin = PanelSkin.Get();
		try
		{
			DrawWindowContent();
		}
		finally
		{
			GUI.skin = skin;
		}
	}

	private void DrawWindowContent()
	{
		//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_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Expected O, but got Unknown
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Expected O, but got Unknown
		//IL_03e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_029c: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b9: Unknown result type (might be due to invalid IL or missing references)
		Recap recap = _recap;
		if (_bold == null)
		{
			_bold = new GUIStyle(GUI.skin.label)
			{
				fontStyle = (FontStyle)1,
				richText = true
			};
			_rich = new GUIStyle(GUI.skin.label)
			{
				richText = true,
				wordWrap = true
			};
		}
		GUIStyle bold = _bold;
		GUIStyle rich = _rich;
		if (recap.Hits.Count == 0)
		{
			GUILayout.Label("No damage was recorded before this death (for example, it came from a console command or a mod).", rich, Array.Empty<GUILayoutOption>());
		}
		else
		{
			HitEntry hitEntry = recap.Hits[0];
			GUILayout.Label($"Killing blow: <color=#ff6b5a>{Esc(hitEntry.Source)}</color> for {hitEntry.Taken:0} damage", bold, Array.Empty<GUILayoutOption>());
			float num = recap.DeathTime - recap.Hits[recap.Hits.Count - 1].Time;
			float num2 = recap.Hits.Sum((HitEntry h) => h.Taken);
			GUILayout.Label(string.Format("{0} {1}, {2:0} damage taken in the last {3:0.0} s before death", recap.Hits.Count, (recap.Hits.Count == 1) ? "hit" : "hits", num2, num), rich, Array.Empty<GUILayoutOption>());
			GUILayout.Space(6f);
			GUILayout.Label("Damage by attacker", bold, Array.Empty<GUILayoutOption>());
			foreach (KeyValuePair<string, float> item in recap.ByAttacker.Take(8))
			{
				GUILayout.Label($"  {Esc(item.Key)}: {item.Value:0} ({((num2 > 0f) ? (item.Value / num2 * 100f) : 0f):0}%)", rich, Array.Empty<GUILayoutOption>());
			}
			if (recap.ByType.Count > 0)
			{
				GUILayout.Space(6f);
				GUILayout.Label("Damage by type (before armour and resistances)", bold, Array.Empty<GUILayoutOption>());
				GUILayout.Label("  " + string.Join(", ", recap.ByType.Select((KeyValuePair<string, float> kv) => $"{kv.Key} {kv.Value:0}")), rich, Array.Empty<GUILayoutOption>());
			}
			GUILayout.Space(6f);
			GUILayout.Label("Hits, newest first", bold, Array.Empty<GUILayoutOption>());
			_scroll = GUILayout.BeginScrollView(_scroll, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(200f) });
			foreach (HitEntry item2 in recap.Hits.Take(MaxLines.Value))
			{
				float num3 = recap.DeathTime - item2.Time;
				string text = ((item2.Raw > 0.5f && item2.Taken < item2.Raw * 0.35f) ? " (mostly blocked or resisted)" : "");
				GUILayout.Label($"  -{num3:0.0} s  {Esc(item2.Source)}: lost {item2.Taken:0} of {item2.Raw:0}{text}, health left {Mathf.Max(0f, item2.HealthAfter):0}/{item2.MaxHealth:0}", rich, Array.Empty<GUILayoutOption>());
			}
			GUILayout.EndScrollView();
		}
		GUILayout.Space(6f);
		GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>());
		GUILayout.Label($"{RecapKey.Value} closes this and opens it again later.", rich, Array.Empty<GUILayoutOption>());
		if (GUILayout.Button("Close", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(90f) }))
		{
			_show = false;
		}
		GUILayout.EndHorizontal();
		GUI.DragWindow();
	}

	private static string Esc(string s)
	{
		return (s ?? "").Replace("<", "‹").Replace(">", "›");
	}
}
internal static class DamagePatches
{
	[HarmonyPrefix]
	[HarmonyPatch(typeof(Character), "RPC_Damage")]
	private static void RpcDamagePrefix(Character __instance, HitData hit)
	{
		//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)
		try
		{
			if (hit != null && !((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				DeathRecapPlugin.PendingHit = hit;
				DeathRecapPlugin.PendingRaw = hit.GetTotalDamage();
				DeathRecapPlugin.PendingTypes = hit.m_damage;
			}
		}
		catch
		{
		}
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(Character), "ApplyDamage")]
	private static void ApplyDamagePrefix(Character __instance, out float __state)
	{
		__state = -1f;
		try
		{
			if ((Object)(object)__instance != (Object)null && (Object)(object)__instance == (Object)(object)Player.m_localPlayer && !__instance.IsDead())
			{
				__state = __instance.GetHealth();
			}
		}
		catch
		{
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Character), "ApplyDamage")]
	private static void ApplyDamagePostfix(Character __instance, HitData hit, float __state)
	{
		if (!(__state < 0f) && hit != null)
		{
			DeathRecapPlugin.Instance?.Record(__instance, hit, __state);
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Player), "OnDeath")]
	private static void OnDeathPostfix(Player __instance)
	{
		try
		{
			if ((Object)(object)__instance != (Object)null && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				DeathRecapPlugin.Instance?.OnLocalDeath(__instance);
			}
		}
		catch
		{
		}
	}
}
internal static class Graves
{
	internal sealed class Grave
	{
		public ZDOID Id;

		public Vector3 Pos;

		public string Name;

		public long Owner;
	}

	private sealed class Shown
	{
		public Grave Grave;

		public PinData Pin;
	}

	private const string RpcList = "anwo_dr_graves";

	private const string RpcRequest = "anwo_dr_graves_req";

	private const string TombstonePrefab = "Player_tombstone";

	private const float RescanPause = 20f;

	private const int ScanEvery = 4;

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

	private static int s_scanIndex;

	private static float s_nextScan;

	private static string s_signature = "";

	private static List<Grave> s_current = new List<Grave>();

	private static bool s_scanned;

	private static readonly Dictionary<ZDOID, Shown> s_shown = new Dictionary<ZDOID, Shown>();

	private static List<Grave> s_pending;

	private static bool s_registered;

	private static bool s_requested;

	private static bool s_firstList = true;

	private static float s_nextPinCheck;

	private static bool s_failureLogged;

	private static readonly FieldRef<Minimap, List<PinData>> MapPins = MakeMapPins();

	private static FieldRef<Minimap, List<PinData>> MakeMapPins()
	{
		try
		{
			return AccessTools.FieldRefAccess<Minimap, List<PinData>>("m_pins");
		}
		catch
		{
			return null;
		}
	}

	internal static void Register()
	{
		s_found.Clear();
		s_scanIndex = 0;
		s_nextScan = 0f;
		s_signature = "";
		s_current = new List<Grave>();
		s_scanned = false;
		s_shown.Clear();
		s_pending = null;
		s_requested = false;
		s_firstList = true;
		s_registered = false;
		if (ZRoutedRpc.instance != null)
		{
			ZRoutedRpc.instance.Register<ZPackage>("anwo_dr_graves", (Action<long, ZPackage>)OnList);
			ZRoutedRpc.instance.Register("anwo_dr_graves_req", (Action<long>)OnRequest);
			s_registered = true;
		}
	}

	internal static void Tick()
	{
		if (!s_registered)
		{
			return;
		}
		try
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZRoutedRpc.instance != null)
			{
				if (ZNet.instance.IsServer())
				{
					HostTick();
				}
				ClientTick();
			}
		}
		catch (Exception ex)
		{
			if (!s_failureLogged)
			{
				s_failureLogged = true;
				ManualLogSource log = DeathRecapPlugin.Log;
				if (log != null)
				{
					log.LogWarning((object)("Shared graves failed (reported once): " + ex));
				}
			}
		}
	}

	private static void HostTick()
	{
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: 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_012d: Unknown result type (might be due to invalid IL or missing references)
		if (Time.time < s_nextScan || ZDOMan.instance == null || Time.frameCount % 4 != 0 || !ZDOMan.instance.GetAllZDOsWithPrefabIterative("Player_tombstone", s_found, ref s_scanIndex))
		{
			return;
		}
		List<Grave> list = new List<Grave>();
		foreach (ZDO item in s_found)
		{
			if (item != null && item.IsValid())
			{
				list.Add(new Grave
				{
					Id = item.m_uid,
					Pos = item.GetPosition(),
					Name = item.GetString(ZDOVars.s_ownerName, ""),
					Owner = item.GetLong(ZDOVars.s_owner, 0L)
				});
			}
		}
		s_found.Clear();
		s_scanIndex = 0;
		s_nextScan = Time.time + 20f;
		list.Sort((Grave a, Grave b) => ((ZDOID)(ref a.Id)).CompareTo(b.Id));
		StringBuilder stringBuilder = new StringBuilder();
		foreach (Grave item2 in list)
		{
			stringBuilder.Append(item2.Id).Append('@').Append(Mathf.RoundToInt(item2.Pos.x / 4f))
				.Append(',')
				.Append(Mathf.RoundToInt(item2.Pos.z / 4f))
				.Append(';');
		}
		string text = stringBuilder.ToString();
		bool num = !s_scanned || text != s_signature;
		s_signature = text;
		s_current = list;
		s_scanned = true;
		if (num)
		{
			Send(0L, list);
		}
	}

	private static void OnRequest(long sender)
	{
		if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && s_scanned)
		{
			Send(sender, s_current);
		}
	}

	private static void Send(long target, List<Grave> graves)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Expected O, but got Unknown
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		ZPackage val = new ZPackage();
		val.Write(1);
		val.Write(graves.Count);
		foreach (Grave grafe in graves)
		{
			val.Write(grafe.Id);
			val.Write(grafe.Pos);
			val.Write(grafe.Name ?? "");
			val.Write(grafe.Owner);
		}
		ZRoutedRpc.instance.InvokeRoutedRPC(target, "anwo_dr_graves", new object[1] { val });
	}

	private static void OnList(long sender, ZPackage pkg)
	{
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (pkg.ReadInt() == 1)
			{
				int num = pkg.ReadInt();
				List<Grave> list = new List<Grave>(Mathf.Clamp(num, 0, 1000));
				for (int i = 0; i < num; i++)
				{
					list.Add(new Grave
					{
						Id = pkg.ReadZDOID(),
						Pos = pkg.ReadVector3(),
						Name = pkg.ReadString(),
						Owner = pkg.ReadLong()
					});
				}
				s_pending = list;
			}
		}
		catch (Exception ex)
		{
			ManualLogSource log = DeathRecapPlugin.Log;
			if (log != null)
			{
				log.LogDebug((object)("Reading the grave list failed: " + ex.Message));
			}
		}
	}

	private static void ClientTick()
	{
		Player localPlayer = Player.m_localPlayer;
		if (!s_requested && (Object)(object)localPlayer != (Object)null)
		{
			s_requested = true;
			ZRoutedRpc.instance.InvokeRoutedRPC("anwo_dr_graves_req", Array.Empty<object>());
		}
		Minimap instance = Minimap.instance;
		if ((Object)(object)instance == (Object)null)
		{
			return;
		}
		if (!DeathRecapPlugin.ShowGraves.Value)
		{
			if (s_shown.Count > 0)
			{
				RemoveAll(instance);
			}
			return;
		}
		if (s_pending != null && (Object)(object)localPlayer != (Object)null)
		{
			List<Grave> list = s_pending;
			s_pending = null;
			Apply(instance, list);
		}
		if (Time.time >= s_nextPinCheck)
		{
			s_nextPinCheck = Time.time + 5f;
			KeepPins(instance);
		}
	}

	private static void Apply(Minimap map, List<Grave> list)
	{
		//IL_0068: 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_00b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: 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_014f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0156: 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)
		long num = (((Object)(object)Game.instance != (Object)null && Game.instance.GetPlayerProfile() != null) ? Game.instance.GetPlayerProfile().GetPlayerID() : 0);
		HashSet<ZDOID> keep = new HashSet<ZDOID>();
		foreach (Grave item in list)
		{
			if (num != 0L && item.Owner == num)
			{
				continue;
			}
			keep.Add(item.Id);
			if (s_shown.TryGetValue(item.Id, out var value))
			{
				value.Grave = item;
				if (value.Pin != null)
				{
					value.Pin.m_pos = item.Pos;
				}
				continue;
			}
			s_shown[item.Id] = new Shown
			{
				Grave = item,
				Pin = AddPin(map, item)
			};
			if (!s_firstList && DeathRecapPlugin.AnnounceDeaths.Value)
			{
				Announce(item);
			}
		}
		foreach (ZDOID item2 in s_shown.Keys.Where((ZDOID k) => !keep.Contains(k)).ToList())
		{
			Shown shown = s_shown[item2];
			if (shown.Pin != null)
			{
				map.RemovePin(shown.Pin);
			}
			s_shown.Remove(item2);
		}
		s_firstList = false;
	}

	private static PinData AddPin(Minimap map, Grave g)
	{
		//IL_0002: 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_001a: Unknown result type (might be due to invalid IL or missing references)
		return map.AddPin(g.Pos, (PinType)4, Label(g), false, false, 0L, default(PlatformUserID));
	}

	private static string Label(Grave g)
	{
		if (!string.IsNullOrEmpty(g.Name))
		{
			return g.Name + "'s grave";
		}
		return "Grave";
	}

	private static void KeepPins(Minimap map)
	{
		if (MapPins == null || s_shown.Count == 0)
		{
			return;
		}
		List<PinData> list = MapPins.Invoke(map);
		if (list == null)
		{
			return;
		}
		foreach (Shown value in s_shown.Values)
		{
			if (value.Pin == null || !list.Contains(value.Pin))
			{
				value.Pin = AddPin(map, value.Grave);
			}
		}
	}

	private static void RemoveAll(Minimap map)
	{
		foreach (Shown value in s_shown.Values)
		{
			if (value.Pin != null)
			{
				map.RemovePin(value.Pin);
			}
		}
		s_shown.Clear();
		s_firstList = true;
		s_requested = false;
	}

	private unsafe static void Announce(Grave g)
	{
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Invalid comparison between Unknown and I4
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: Invalid comparison between Unknown and I4
		string text = (string.IsNullOrEmpty(g.Name) ? "Someone" : g.Name);
		string text2 = "";
		try
		{
			if (WorldGenerator.instance != null && Localization.instance != null)
			{
				Biome biome = WorldGenerator.instance.GetBiome(g.Pos);
				string text3 = Localization.instance.Localize("$biome_" + ((object)(*(Biome*)(&biome))/*cast due to .constrained prefix*/).ToString().ToLowerInvariant());
				if ((int)biome == 256)
				{
					text2 = " at sea";
				}
				else if ((int)biome == 4)
				{
					text2 = " in the mountains";
				}
				else if (!string.IsNullOrEmpty(text3) && !text3.StartsWith("["))
				{
					text2 = " in the " + text3;
				}
			}
		}
		catch
		{
		}
		string text4 = text + " died" + text2 + ". Their grave is marked on your map.";
		MessageHud instance = MessageHud.instance;
		if (instance != null)
		{
			instance.ShowMessage((MessageType)1, text4, 0, (Sprite)null, false, false);
		}
		Chat instance2 = Chat.instance;
		if (instance2 != null)
		{
			((Terminal)instance2).AddString("<color=#e07070>" + text4 + "</color>");
		}
	}
}
internal static class GravePatches
{
	[HarmonyPostfix]
	[HarmonyPatch(typeof(Game), "Start")]
	private static void GameStart()
	{
		try
		{
			Graves.Register();
		}
		catch (Exception ex)
		{
			ManualLogSource log = DeathRecapPlugin.Log;
			if (log != null)
			{
				log.LogWarning((object)("Registering shared graves failed: " + ex.Message));
			}
		}
	}
}
internal static class PanelSkin
{
	private static GUISkin s_skin;

	internal static GUISkin Get()
	{
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Expected O, but got Unknown
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_0098: 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_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Expected O, but got Unknown
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d0: Expected O, but got Unknown
		//IL_00e0: 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)
		//IL_011b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0128: Expected O, but got Unknown
		//IL_012e: Unknown result type (might be due to invalid IL or missing references)
		//IL_013b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0140: Unknown result type (might be due to invalid IL or missing references)
		//IL_0148: Unknown result type (might be due to invalid IL or missing references)
		//IL_014d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0157: Expected O, but got Unknown
		//IL_0157: Unknown result type (might be due to invalid IL or missing references)
		//IL_015c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0166: Expected O, but got Unknown
		//IL_016b: Expected O, but got Unknown
		//IL_017f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0184: Unknown result type (might be due to invalid IL or missing references)
		//IL_0194: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01be: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_020c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0211: Unknown result type (might be due to invalid IL or missing references)
		//IL_0256: Unknown result type (might be due to invalid IL or missing references)
		//IL_025b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0263: Unknown result type (might be due to invalid IL or missing references)
		//IL_0268: Unknown result type (might be due to invalid IL or missing references)
		//IL_0272: Expected O, but got Unknown
		//IL_0272: Unknown result type (might be due to invalid IL or missing references)
		//IL_0277: Unknown result type (might be due to invalid IL or missing references)
		//IL_0281: Expected O, but got Unknown
		//IL_0286: Expected O, but got Unknown
		//IL_02a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_030b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0310: Unknown result type (might be due to invalid IL or missing references)
		//IL_031d: Expected O, but got Unknown
		//IL_0323: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)s_skin != (Object)null)
		{
			return s_skin;
		}
		GUISkin val = Object.Instantiate<GUISkin>(GUI.skin);
		((Object)val).hideFlags = (HideFlags)61;
		Color color = default(Color);
		((Color)(ref color))..ctor(0.95f, 0.93f, 0.88f);
		Color val2 = default(Color);
		((Color)(ref val2))..ctor(0.93f, 0.78f, 0.45f);
		Color frame = default(Color);
		((Color)(ref frame))..ctor(0.62f, 0.48f, 0.26f);
		val.window = new GUIStyle(val.window);
		SetAll(val.window, Tex(new Color(0.09f, 0.08f, 0.07f, 0.97f), frame), val2);
		val.window.border = new RectOffset(3, 3, 3, 3);
		val.window.padding = new RectOffset(14, 14, 38, 14);
		val.window.contentOffset = new Vector2(0f, -30f);
		val.window.fontSize = 20;
		val.window.fontStyle = (FontStyle)1;
		val.window.alignment = (TextAnchor)1;
		val.label = new GUIStyle(val.label)
		{
			fontSize = 16
		};
		SetText(val.label, color);
		val.button = new GUIStyle(val.button)
		{
			fontSize = 15,
			padding = new RectOffset(8, 8, 5, 5),
			border = new RectOffset(2, 2, 2, 2)
		};
		Texture2D background = Tex(new Color(0.22f, 0.19f, 0.15f, 1f), frame);
		SetAll(val.button, background, color);
		val.button.hover.background = Tex(new Color(0.32f, 0.27f, 0.2f, 1f), val2);
		val.button.active.background = Tex(new Color(0.15f, 0.13f, 0.1f, 1f), val2);
		Texture2D background2 = Tex(new Color(0.5f, 0.37f, 0.16f, 1f), val2);
		val.button.onNormal.background = background2;
		val.button.onHover.background = background2;
		val.button.onActive.background = background2;
		val.textField = new GUIStyle(val.textField)
		{
			fontSize = 16,
			padding = new RectOffset(6, 6, 5, 5),
			border = new RectOffset(2, 2, 2, 2)
		};
		SetAll(val.textField, Tex(new Color(0.03f, 0.03f, 0.03f, 1f), frame), Color.white);
		val.textField.focused.background = Tex(new Color(0.03f, 0.03f, 0.03f, 1f), val2);
		val.textField.onFocused.background = val.textField.focused.background;
		val.toggle = new GUIStyle(val.toggle)
		{
			fontSize = 15
		};
		SetText(val.toggle, color);
		s_skin = val;
		return val;
	}

	private static void SetText(GUIStyle style, Color color)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: 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_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: 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)
		style.normal.textColor = color;
		style.hover.textColor = color;
		style.active.textColor = color;
		style.focused.textColor = color;
		style.onNormal.textColor = color;
		style.onHover.textColor = color;
		style.onActive.textColor = color;
		style.onFocused.textColor = color;
	}

	private static void SetAll(GUIStyle style, Texture2D background, Color color)
	{
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		style.normal.background = background;
		style.hover.background = background;
		style.active.background = background;
		style.focused.background = background;
		style.onNormal.background = background;
		style.onHover.background = background;
		style.onActive.background = background;
		style.onFocused.background = background;
		SetText(style, color);
	}

	private static Texture2D Tex(Color fill, Color frame)
	{
		//IL_0004: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Expected O, but got Unknown
		//IL_0049: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		Texture2D val = new Texture2D(8, 8, (TextureFormat)4, false);
		((Object)val).hideFlags = (HideFlags)61;
		((Texture)val).filterMode = (FilterMode)0;
		((Texture)val).wrapMode = (TextureWrapMode)1;
		Color[] array = (Color[])(object)new Color[64];
		for (int i = 0; i < 8; i++)
		{
			for (int j = 0; j < 8; j++)
			{
				array[i * 8 + j] = ((j < 2 || i < 2 || j >= 6 || i >= 6) ? frame : fill);
			}
		}
		val.SetPixels(array);
		val.Apply();
		return val;
	}
}