Decompiled source of StatsLogger v1.2.2

gnp_StatsLogger/ValheimStatsLogger.dll

Decompiled 7 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using HarmonyLib;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ValheimStatsLogger")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ValheimStatsLogger")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("5c29271f-5dca-4909-aabc-9558286a0409")]
[assembly: AssemblyFileVersion("1.2.2.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.2.2.0")]
namespace ValheimStatsLogger;

[HarmonyPatch]
public static class BlockAndStagger
{
	[HarmonyPatch(typeof(Humanoid), "BlockAttack")]
	public static class Humanoid_BlockAttack_BlockAndStagger
	{
		private static MethodInfo miBlockDamage = AccessTools.Method(typeof(HitData), "BlockDamage", (Type[])null, (Type[])null);

		private static MethodInfo miStagger = AccessTools.Method(typeof(Character), "Stagger", (Type[])null, (Type[])null);

		private static MethodInfo miBlockTrigger = AccessTools.Method(typeof(Humanoid_BlockAttack_BlockAndStagger), "BlockTrigger", (Type[])null, (Type[])null);

		private static MethodInfo miStaggerTrigger = AccessTools.Method(typeof(Humanoid_BlockAttack_BlockAndStagger), "StaggerTrigger", (Type[])null, (Type[])null);

		private static readonly int Inserts = 2;

		[HarmonyTranspiler]
		public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Expected O, but got Unknown
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Expected O, but got Unknown
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Expected O, but got Unknown
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Expected O, but got Unknown
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Expected O, but got Unknown
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Expected O, but got Unknown
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Expected O, but got Unknown
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Expected O, but got Unknown
			int num = 0;
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			for (int i = 0; i < list.Count; i++)
			{
				if (list[i].opcode == OpCodes.Callvirt && (MethodInfo)list[i].operand == miBlockDamage)
				{
					list.Insert(i + 1, new CodeInstruction(OpCodes.Call, (object)miBlockTrigger));
					list.Insert(i + 1, list[i - 1]);
					list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_2, (object)null));
					list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_1, (object)null));
					list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
					DebugLogger.Log($"{typeof(Humanoid_BlockAttack_BlockAndStagger)} [Humanoid BlockAttack {miBlockTrigger.Name}] applied.");
					num++;
				}
				if (list[i].opcode == OpCodes.Callvirt && (MethodInfo)list[i].operand == miStagger && list[i - 4].opcode == OpCodes.Ldarg_2)
				{
					list.Insert(i + 1, new CodeInstruction(OpCodes.Call, (object)miStaggerTrigger));
					list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_2, (object)null));
					list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_1, (object)null));
					list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
					DebugLogger.Log($"{typeof(Humanoid_BlockAttack_BlockAndStagger)} [Humanoid BlockAttack {miStaggerTrigger.Name}] applied.");
					num++;
				}
			}
			if (num == 0)
			{
				DebugLogger.LogError($"{typeof(Humanoid_BlockAttack_BlockAndStagger)} [Humanoid BlockAttack] failed.");
			}
			else if (num != Inserts)
			{
				DebugLogger.LogWarning($"{typeof(Humanoid_BlockAttack_BlockAndStagger)} [Humanoid BlockAttack] applied {num}/{Inserts}.");
			}
			return list.AsEnumerable();
		}

		public static void BlockTrigger(Humanoid target, HitData hit, Character attacker, float dmg)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			if (StatsLogger.IsLocalPlayer((Character)(object)target))
			{
				ItemData weapon = null;
				if (((object)attacker).GetType() == typeof(Humanoid))
				{
					weapon = ((Humanoid)attacker).GetCurrentWeapon();
				}
				StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.Blocks, attacker, weapon, 1f);
				StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.BlockedDamage, attacker, weapon, dmg);
				StatsLogger.Stats.UpdateTotal(Settings.LogActions.Blocks, 1f);
				StatsLogger.Stats.UpdateTotal(Settings.LogActions.BlockedDamage, dmg);
			}
		}

		public static void StaggerTrigger(Humanoid target, HitData hit, Character attacker)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			if (StatsLogger.IsLocalPlayer((Character)(object)target))
			{
				ItemData weapon = null;
				if (((object)attacker).GetType() == typeof(Humanoid))
				{
					weapon = ((Humanoid)attacker).GetCurrentWeapon();
				}
				StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.Staggers, attacker, weapon, 1f);
				StatsLogger.Stats.UpdateTotal(Settings.LogActions.Staggers, 1f);
			}
		}
	}
}
internal class ConsoleAndChat
{
	[HarmonyPatch(typeof(Terminal), "InitTerminal")]
	private class AddCommands
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static ConsoleEvent <>9__0_0;

			internal void <Postfix>b__0_0(ConsoleEventArgs args)
			{
				//IL_0480: Unknown result type (might be due to invalid IL or missing references)
				if (args.Args.Length == 1)
				{
					args.Context.AddString("<color=purple>Stats Logger (by givenameplz)</color> [1.2.2]");
					args.Context.AddString("Stats Logging for your Characters!");
					args.Context.AddString("");
					args.Context.AddString("statslogger chat <color=#66ff66>[value:bool]</color> <color=#666666>(Announce Milestones in the Chatbox, this is LOCAL to the player only.)</color>");
					args.Context.AddString("statslogger console <color=#66ff66>[value:bool]</color> <color=#666666>(Announce Milestones in the Console.)</color>");
					args.Context.AddString("statslogger effect <color=#66ff66>[value:bool]</color> <color=#666666>(Announce Milestones with a level up effect.)</color>");
					args.Context.AddString("statslogger cooldown <color=#6666ff>[minutes:int]</color> <color=#666666>(Announce Milestones only every <n> minutes so you don't get spammed.)</color>");
					args.Context.AddString("statslogger retail <color=#666666>(Outputs the Stats tracked by Valheim: Kills, Deaths, Crafts, Builds and MANY MORE!)</color>");
					args.Context.AddString("statslogger reset <color=#666666>(Reset all stats on the current character.)</color>");
					args.Context.AddString("statslogger hidedeaths <color=#66ff66>[value:bool]</color> <color=#666666>(Hides any Player Death related stats.)</color>");
					args.Context.AddString("");
					args.Context.AddString("<color=orange>EXAMPLE:</color> statslogger chat false");
					args.Context.AddString("<color=orange>EXAMPLE:</color> statslogger cooldown 5");
					args.Context.AddString("<color=orange>EXAMPLE:</color> statslogger inckills false");
					args.Context.AddString("");
					args.Context.AddString("Announce Stats Milestones in Chatbox (Local): " + (StatsLogger.Stats.AnnounceChat ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
					args.Context.AddString("Announce Stats Milestones in Console: " + (StatsLogger.Stats.AnnounceConsole ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
					args.Context.AddString("Announce Stats Milestones with Effect: " + (StatsLogger.Stats.AnnounceEffect ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
					args.Context.AddString($"Announce Stats Milestones Cooldown: <color=green>{StatsLogger.Stats.AnnounceIntervalMinutes}</color>");
					args.Context.AddString("Hide Player Death Stats: " + (StatsLogger.Stats.HideDeathStats ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
				}
				else if (args.HasArgumentAnywhere("debug", 0, true))
				{
					StatsLogger.Stats.DebugMessagesInUnityConsole = !StatsLogger.Stats.DebugMessagesInUnityConsole;
					args.Context.AddString($"Debug output in UnityConsole. ({StatsLogger.Stats.DebugMessagesInUnityConsole})");
				}
				else if (args.HasArgumentAnywhere("announce", 0, true))
				{
					if (args.Args.Length == 3)
					{
						if (bool.TryParse(args.Args[2], out var result))
						{
							StatsLogger.Stats.AnnounceChat = result;
							StatsLogger.Stats.AnnounceConsole = result;
							StatsLogger.Stats.AnnounceEffect = result;
							args.Context.AddString("Announce Stats Milestones: " + (result ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
							StatsLogger.Stats.SaveSettings();
						}
						else
						{
							args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Announce Stats Milestones'");
						}
					}
					else
					{
						args.Context.AddString("Announce Stats Milestones in Chatbox (Local): " + (StatsLogger.Stats.AnnounceChat ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
						args.Context.AddString("Announce Stats Milestones in Console: " + (StatsLogger.Stats.AnnounceConsole ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
						args.Context.AddString("Announce Stats Milestones with Effect: " + (StatsLogger.Stats.AnnounceEffect ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
					}
				}
				else if (args.HasArgumentAnywhere("reset", 0, true))
				{
					if ((Object)(object)Player.m_localPlayer != (Object)null)
					{
						if (StatsLogger.Stats.ResetFlag)
						{
							if (args.HasArgumentAnywhere(Player.m_localPlayer.GetPlayerName().ToLower(), 0, true))
							{
								StatsLogger.Stats.ResetData(Player.m_localPlayer.GetPlayerName());
								args.Context.AddString("All StatsLogger Stats have been reset for '" + Player.m_localPlayer.GetPlayerName() + "'.");
							}
							StatsLogger.Stats.ResetFlag = false;
						}
						else
						{
							StatsLogger.Stats.ResetFlag = true;
							args.Context.AddString("<color=red>WARNING!</color> Do you want to reset ALL stats logged by the StatsLogger Mod for this Character? Type '/statslogger reset " + Player.m_localPlayer.GetPlayerName() + "' to confirm.");
						}
					}
					else
					{
						args.Context.AddString("<color=red>Not logged in with any character.</color>");
					}
				}
				else
				{
					if (args.HasArgumentAnywhere("retail", 0, true))
					{
						if (!((Object)(object)Game.instance != (Object)null))
						{
							return;
						}
						PlayerProfile playerProfile = Game.instance.GetPlayerProfile();
						if (playerProfile == null || playerProfile.m_playerStats == null)
						{
							return;
						}
						{
							foreach (KeyValuePair<PlayerStatType, float> stat in playerProfile.m_playerStats.m_stats)
							{
								args.Context.AddString($"{stat.Key}: {stat.Value}");
							}
							return;
						}
					}
					if (args.HasArgumentAnywhere("hidedeaths", 0, true))
					{
						if (args.Args.Length == 3)
						{
							if (bool.TryParse(args.Args[2], out var result2))
							{
								StatsLogger.Stats.HideDeathStats = result2;
								args.Context.AddString("Hide Player Death Stats: " + (StatsLogger.Stats.HideDeathStats ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
								StatsLogger.Stats.SaveSettings();
							}
							else
							{
								args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Hide Player Death Stats'");
							}
						}
						else
						{
							args.Context.AddString("Hide Player Death Stats: " + (StatsLogger.Stats.HideDeathStats ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
						}
					}
					else if (args.HasArgumentAnywhere("chat", 0, true))
					{
						if (args.Args.Length == 3)
						{
							if (bool.TryParse(args.Args[2], out var result3))
							{
								StatsLogger.Stats.AnnounceChat = result3;
								args.Context.AddString("Announce Stats Milestones in Chatbox (Local): " + (StatsLogger.Stats.AnnounceChat ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
								StatsLogger.Stats.SaveSettings();
							}
							else
							{
								args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Announce Stats Milestones in Chatbox (Local)'");
							}
						}
						else
						{
							args.Context.AddString("Announce Stats Milestones in Chatbox (Local): " + (StatsLogger.Stats.AnnounceChat ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
						}
					}
					else if (args.HasArgumentAnywhere("console", 0, true))
					{
						if (args.Args.Length == 3)
						{
							if (bool.TryParse(args.Args[2], out var result4))
							{
								StatsLogger.Stats.AnnounceConsole = result4;
								args.Context.AddString("Announce Stats Milestones in Console: " + (StatsLogger.Stats.AnnounceConsole ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
								StatsLogger.Stats.SaveSettings();
							}
							else
							{
								args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Announce Stats Milestones in Console'");
							}
						}
						else
						{
							args.Context.AddString("Announce Stats Milestones in Console: " + (StatsLogger.Stats.AnnounceConsole ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
						}
					}
					else if (args.HasArgumentAnywhere("effect", 0, true))
					{
						if (args.Args.Length == 3)
						{
							if (bool.TryParse(args.Args[2], out var result5))
							{
								StatsLogger.Stats.AnnounceEffect = result5;
								args.Context.AddString("Announce Stats Milestones with Effect: " + (StatsLogger.Stats.AnnounceEffect ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
								StatsLogger.Stats.SaveSettings();
							}
							else
							{
								args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Announce Stats Milestones with Effect'");
							}
						}
						else
						{
							args.Context.AddString("Announce Stats Milestones with Effect: " + (StatsLogger.Stats.AnnounceEffect ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
						}
					}
					else if (args.HasArgumentAnywhere("cooldown", 0, true))
					{
						if (args.Args.Length == 3)
						{
							if (int.TryParse(args.Args[2], out var result6) && result6 >= 0)
							{
								StatsLogger.Stats.AnnounceIntervalMinutes = result6;
								args.Context.AddString($"Announce Stats Milestones Cooldown: <color=green>{StatsLogger.Stats.AnnounceIntervalMinutes}</color>");
								StatsLogger.Stats.SaveSettings();
							}
							else
							{
								args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Announce Stats Milestones Cooldown'");
							}
						}
						else
						{
							args.Context.AddString($"Announce Stats Milestones Cooldown: <color=green>{StatsLogger.Stats.AnnounceIntervalMinutes}</color>");
						}
					}
					else
					{
						args.Context.AddString("<color=red>Error:</color> '" + args.FullLine + "' is not a recognized command for StatsLogger! Type 'statslogger' to see a list of valid commands.");
					}
				}
			}
		}

		private static void Postfix(Terminal __instance)
		{
			//IL_0032: 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_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			object obj = <>c.<>9__0_0;
			if (obj == null)
			{
				ConsoleEvent val = delegate(ConsoleEventArgs args)
				{
					//IL_0480: Unknown result type (might be due to invalid IL or missing references)
					if (args.Args.Length == 1)
					{
						args.Context.AddString("<color=purple>Stats Logger (by givenameplz)</color> [1.2.2]");
						args.Context.AddString("Stats Logging for your Characters!");
						args.Context.AddString("");
						args.Context.AddString("statslogger chat <color=#66ff66>[value:bool]</color> <color=#666666>(Announce Milestones in the Chatbox, this is LOCAL to the player only.)</color>");
						args.Context.AddString("statslogger console <color=#66ff66>[value:bool]</color> <color=#666666>(Announce Milestones in the Console.)</color>");
						args.Context.AddString("statslogger effect <color=#66ff66>[value:bool]</color> <color=#666666>(Announce Milestones with a level up effect.)</color>");
						args.Context.AddString("statslogger cooldown <color=#6666ff>[minutes:int]</color> <color=#666666>(Announce Milestones only every <n> minutes so you don't get spammed.)</color>");
						args.Context.AddString("statslogger retail <color=#666666>(Outputs the Stats tracked by Valheim: Kills, Deaths, Crafts, Builds and MANY MORE!)</color>");
						args.Context.AddString("statslogger reset <color=#666666>(Reset all stats on the current character.)</color>");
						args.Context.AddString("statslogger hidedeaths <color=#66ff66>[value:bool]</color> <color=#666666>(Hides any Player Death related stats.)</color>");
						args.Context.AddString("");
						args.Context.AddString("<color=orange>EXAMPLE:</color> statslogger chat false");
						args.Context.AddString("<color=orange>EXAMPLE:</color> statslogger cooldown 5");
						args.Context.AddString("<color=orange>EXAMPLE:</color> statslogger inckills false");
						args.Context.AddString("");
						args.Context.AddString("Announce Stats Milestones in Chatbox (Local): " + (StatsLogger.Stats.AnnounceChat ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
						args.Context.AddString("Announce Stats Milestones in Console: " + (StatsLogger.Stats.AnnounceConsole ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
						args.Context.AddString("Announce Stats Milestones with Effect: " + (StatsLogger.Stats.AnnounceEffect ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
						args.Context.AddString($"Announce Stats Milestones Cooldown: <color=green>{StatsLogger.Stats.AnnounceIntervalMinutes}</color>");
						args.Context.AddString("Hide Player Death Stats: " + (StatsLogger.Stats.HideDeathStats ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
					}
					else if (args.HasArgumentAnywhere("debug", 0, true))
					{
						StatsLogger.Stats.DebugMessagesInUnityConsole = !StatsLogger.Stats.DebugMessagesInUnityConsole;
						args.Context.AddString($"Debug output in UnityConsole. ({StatsLogger.Stats.DebugMessagesInUnityConsole})");
					}
					else if (args.HasArgumentAnywhere("announce", 0, true))
					{
						if (args.Args.Length == 3)
						{
							if (bool.TryParse(args.Args[2], out var result))
							{
								StatsLogger.Stats.AnnounceChat = result;
								StatsLogger.Stats.AnnounceConsole = result;
								StatsLogger.Stats.AnnounceEffect = result;
								args.Context.AddString("Announce Stats Milestones: " + (result ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
								StatsLogger.Stats.SaveSettings();
							}
							else
							{
								args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Announce Stats Milestones'");
							}
						}
						else
						{
							args.Context.AddString("Announce Stats Milestones in Chatbox (Local): " + (StatsLogger.Stats.AnnounceChat ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
							args.Context.AddString("Announce Stats Milestones in Console: " + (StatsLogger.Stats.AnnounceConsole ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
							args.Context.AddString("Announce Stats Milestones with Effect: " + (StatsLogger.Stats.AnnounceEffect ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
						}
					}
					else if (args.HasArgumentAnywhere("reset", 0, true))
					{
						if ((Object)(object)Player.m_localPlayer != (Object)null)
						{
							if (StatsLogger.Stats.ResetFlag)
							{
								if (args.HasArgumentAnywhere(Player.m_localPlayer.GetPlayerName().ToLower(), 0, true))
								{
									StatsLogger.Stats.ResetData(Player.m_localPlayer.GetPlayerName());
									args.Context.AddString("All StatsLogger Stats have been reset for '" + Player.m_localPlayer.GetPlayerName() + "'.");
								}
								StatsLogger.Stats.ResetFlag = false;
							}
							else
							{
								StatsLogger.Stats.ResetFlag = true;
								args.Context.AddString("<color=red>WARNING!</color> Do you want to reset ALL stats logged by the StatsLogger Mod for this Character? Type '/statslogger reset " + Player.m_localPlayer.GetPlayerName() + "' to confirm.");
							}
						}
						else
						{
							args.Context.AddString("<color=red>Not logged in with any character.</color>");
						}
					}
					else if (args.HasArgumentAnywhere("retail", 0, true))
					{
						if ((Object)(object)Game.instance != (Object)null)
						{
							PlayerProfile playerProfile = Game.instance.GetPlayerProfile();
							if (playerProfile != null && playerProfile.m_playerStats != null)
							{
								foreach (KeyValuePair<PlayerStatType, float> stat in playerProfile.m_playerStats.m_stats)
								{
									args.Context.AddString($"{stat.Key}: {stat.Value}");
								}
							}
						}
					}
					else if (args.HasArgumentAnywhere("hidedeaths", 0, true))
					{
						if (args.Args.Length == 3)
						{
							if (bool.TryParse(args.Args[2], out var result2))
							{
								StatsLogger.Stats.HideDeathStats = result2;
								args.Context.AddString("Hide Player Death Stats: " + (StatsLogger.Stats.HideDeathStats ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
								StatsLogger.Stats.SaveSettings();
							}
							else
							{
								args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Hide Player Death Stats'");
							}
						}
						else
						{
							args.Context.AddString("Hide Player Death Stats: " + (StatsLogger.Stats.HideDeathStats ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
						}
					}
					else if (args.HasArgumentAnywhere("chat", 0, true))
					{
						if (args.Args.Length == 3)
						{
							if (bool.TryParse(args.Args[2], out var result3))
							{
								StatsLogger.Stats.AnnounceChat = result3;
								args.Context.AddString("Announce Stats Milestones in Chatbox (Local): " + (StatsLogger.Stats.AnnounceChat ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
								StatsLogger.Stats.SaveSettings();
							}
							else
							{
								args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Announce Stats Milestones in Chatbox (Local)'");
							}
						}
						else
						{
							args.Context.AddString("Announce Stats Milestones in Chatbox (Local): " + (StatsLogger.Stats.AnnounceChat ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
						}
					}
					else if (args.HasArgumentAnywhere("console", 0, true))
					{
						if (args.Args.Length == 3)
						{
							if (bool.TryParse(args.Args[2], out var result4))
							{
								StatsLogger.Stats.AnnounceConsole = result4;
								args.Context.AddString("Announce Stats Milestones in Console: " + (StatsLogger.Stats.AnnounceConsole ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
								StatsLogger.Stats.SaveSettings();
							}
							else
							{
								args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Announce Stats Milestones in Console'");
							}
						}
						else
						{
							args.Context.AddString("Announce Stats Milestones in Console: " + (StatsLogger.Stats.AnnounceConsole ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
						}
					}
					else if (args.HasArgumentAnywhere("effect", 0, true))
					{
						if (args.Args.Length == 3)
						{
							if (bool.TryParse(args.Args[2], out var result5))
							{
								StatsLogger.Stats.AnnounceEffect = result5;
								args.Context.AddString("Announce Stats Milestones with Effect: " + (StatsLogger.Stats.AnnounceEffect ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
								StatsLogger.Stats.SaveSettings();
							}
							else
							{
								args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Announce Stats Milestones with Effect'");
							}
						}
						else
						{
							args.Context.AddString("Announce Stats Milestones with Effect: " + (StatsLogger.Stats.AnnounceEffect ? "<color=green>ON</color>" : "<color=red>OFF</color>"));
						}
					}
					else if (args.HasArgumentAnywhere("cooldown", 0, true))
					{
						if (args.Args.Length == 3)
						{
							if (int.TryParse(args.Args[2], out var result6) && result6 >= 0)
							{
								StatsLogger.Stats.AnnounceIntervalMinutes = result6;
								args.Context.AddString($"Announce Stats Milestones Cooldown: <color=green>{StatsLogger.Stats.AnnounceIntervalMinutes}</color>");
								StatsLogger.Stats.SaveSettings();
							}
							else
							{
								args.Context.AddString("<color=red>Error:</color> Invalid value '" + args.Args[2] + "' for 'Announce Stats Milestones Cooldown'");
							}
						}
						else
						{
							args.Context.AddString($"Announce Stats Milestones Cooldown: <color=green>{StatsLogger.Stats.AnnounceIntervalMinutes}</color>");
						}
					}
					else
					{
						args.Context.AddString("<color=red>Error:</color> '" + args.FullLine + "' is not a recognized command for StatsLogger! Type 'statslogger' to see a list of valid commands.");
					}
				};
				<>c.<>9__0_0 = val;
				obj = (object)val;
			}
			new ConsoleCommand("statslogger", "<color=red>[MOD]</color> Stats Logging for your Characters!", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}
	}
}
[HarmonyPatch]
public static class DamageAndHits
{
	public class Target
	{
		public Character Character;

		public HitData Hit;

		public ItemData Weapon;

		public DateTime Time = DateTime.Now;

		public DeathTypes DeathType;
	}

	public enum DeathTypes
	{
		Unknown,
		FallDamage,
		Drowning,
		EdgeOfTheWorld
	}

	public static DateTime LastCleanupAtkPlayer = DateTime.Now;

	public static readonly int IntervalSecondsAtkPlayer = 60;

	public static readonly int TTLSecondsAtkPlayer = 30;

	public static DateTime LastCleanupAtkByPlayer = DateTime.Now;

	public static readonly int IntervalSecondsAtkByPlayer = 60;

	public static readonly int TTLSecondsAtkByPlayer = 30;

	public static Dictionary<int, Target> RecentlyAttackedByPlayer = new Dictionary<int, Target>();

	public static Target LastAttackedPlayer = null;

	public static void SpawnOnDamaged<TypeOfObject>(Character __instance, HitData hit)
	{
		//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f9: 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_011e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0123: Unknown result type (might be due to invalid IL or missing references)
		//IL_018f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0195: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0131: 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_0208: Unknown result type (might be due to invalid IL or missing references)
		//IL_020d: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_021a: Unknown result type (might be due to invalid IL or missing references)
		//IL_021f: Unknown result type (might be due to invalid IL or missing references)
		if (StatsLogger.IsLocalPlayer(__instance))
		{
			Character attacker = hit.GetAttacker();
			if ((Object)(object)attacker != (Object)null)
			{
				ItemData weapon = null;
				if (typeof(TypeOfObject) == typeof(Humanoid))
				{
					weapon = ((Humanoid)attacker).GetCurrentWeapon();
				}
				LastAttackedPlayer = new Target
				{
					Character = attacker,
					Hit = hit,
					Weapon = weapon
				};
				StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.TakingHits, attacker, weapon, 1f);
				StatsLogger.Stats.UpdateTotal(Settings.LogActions.TakingHits, 1f);
				StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.DamageTaken, attacker, weapon, hit.GetTotalDamage());
				StatsLogger.Stats.UpdateTotal(Settings.LogActions.DamageTaken, hit.GetTotalDamage());
				StatsLogger.Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.LastDamageTaken.ToString(), 0f, useTimeBuffer: false, set: true);
				return;
			}
			Vector3 val = (Vector3)typeof(Character).GetField("m_lastGroundPoint", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(__instance);
			Vector3 val2 = (Vector3)typeof(Character).GetField("m_lastGroundNormal", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(__instance);
			if (hit.m_point == val && hit.m_dir == val2)
			{
				LastAttackedPlayer = new Target
				{
					DeathType = DeathTypes.FallDamage,
					Hit = hit
				};
				StatsLogger.Stats.UpdateSingle(Settings.LogActions.DamageTaken, Settings.StringDetailsMisc.Gravity.ToString(), hit.GetTotalDamage());
				StatsLogger.Stats.UpdateTotal(Settings.LogActions.DamageTaken, hit.GetTotalDamage());
			}
			else if (hit.m_point == __instance.GetCenterPoint() && hit.m_dir == Vector3.down)
			{
				LastAttackedPlayer = new Target
				{
					DeathType = DeathTypes.Drowning,
					Hit = hit
				};
				StatsLogger.Stats.UpdateSingle(Settings.LogActions.DamageTaken, Settings.StringDetailsMisc.Drowning.ToString(), hit.GetTotalDamage());
				StatsLogger.Stats.UpdateTotal(Settings.LogActions.DamageTaken, hit.GetTotalDamage());
			}
			else if (hit.m_point == Vector3.zero && hit.m_dir == Vector3.zero)
			{
				LastAttackedPlayer = new Target
				{
					DeathType = DeathTypes.EdgeOfTheWorld,
					Hit = hit
				};
			}
			else
			{
				LastAttackedPlayer = new Target
				{
					DeathType = DeathTypes.Unknown,
					Hit = hit
				};
				StatsLogger.Stats.UpdateSingle(Settings.LogActions.DamageTaken, Settings.StringDetailsMisc.Unknown.ToString(), hit.GetTotalDamage());
				StatsLogger.Stats.UpdateTotal(Settings.LogActions.DamageTaken, hit.GetTotalDamage());
			}
			StatsLogger.Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.LastDamageTaken.ToString(), 0f, useTimeBuffer: false, set: true);
		}
		else
		{
			if (!hit.HaveAttacker())
			{
				return;
			}
			Character attacker2 = hit.GetAttacker();
			if ((Object)(object)attacker2 != (Object)null && StatsLogger.IsLocalPlayer(attacker2))
			{
				ItemData currentWeapon = ((Humanoid)Player.m_localPlayer).GetCurrentWeapon();
				if (!RecentlyAttackedByPlayer.ContainsKey(((Object)__instance).GetInstanceID()))
				{
					RecentlyAttackedByPlayer.Add(((Object)__instance).GetInstanceID(), new Target
					{
						Character = __instance,
						Hit = hit,
						Weapon = currentWeapon
					});
				}
				else
				{
					RecentlyAttackedByPlayer[((Object)__instance).GetInstanceID()].Time = DateTime.Now;
					RecentlyAttackedByPlayer[((Object)__instance).GetInstanceID()].Hit = hit;
					RecentlyAttackedByPlayer[((Object)__instance).GetInstanceID()].Weapon = currentWeapon;
				}
				StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.Attacks, __instance, currentWeapon, 1f);
				StatsLogger.Stats.UpdateTotal(Settings.LogActions.Attacks, 1f);
				StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.DamageDone, __instance, currentWeapon, hit.GetTotalDamage());
				StatsLogger.Stats.UpdateTotal(Settings.LogActions.DamageDone, hit.GetTotalDamage());
			}
		}
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(Humanoid), "OnDamaged")]
	private static void HumanoidOnDamaged(ref Humanoid __instance, HitData hit)
	{
		SpawnOnDamaged<Humanoid>((Character)(object)__instance, hit);
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(Character), "OnDamaged")]
	private static void CharacterOnDamaged(ref Character __instance, HitData hit)
	{
		SpawnOnDamaged<Character>(__instance, hit);
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(Character), "OnDeath")]
	private static void CharacterOnDeath(Character __instance)
	{
		if (RecentlyAttackedByPlayer.ContainsKey(((Object)__instance).GetInstanceID()))
		{
			ItemData weapon = RecentlyAttackedByPlayer[((Object)__instance).GetInstanceID()].Weapon;
			StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.Kills, __instance, weapon, 1f);
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.Kills, 1f);
			RecentlyAttackedByPlayer.Remove(((Object)__instance).GetInstanceID());
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Player), "OnDeath")]
	private static void PlayerOnDeath(Player __instance)
	{
		if (!StatsLogger.IsLocalPlayer((Character)(object)__instance))
		{
			return;
		}
		if (LastAttackedPlayer != null)
		{
			if ((Object)(object)LastAttackedPlayer.Character != (Object)null)
			{
				ItemData weapon = LastAttackedPlayer.Weapon;
				StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.Deaths, LastAttackedPlayer.Character, weapon, 1f);
			}
			else if (LastAttackedPlayer.DeathType.HasFlag(DeathTypes.FallDamage))
			{
				StatsLogger.Stats.UpdateSingle(Settings.LogActions.Deaths, Settings.StringDetailsMisc.Gravity.ToString(), 1f);
			}
			else if (LastAttackedPlayer.DeathType.HasFlag(DeathTypes.Drowning))
			{
				StatsLogger.Stats.UpdateSingle(Settings.LogActions.Deaths, Settings.StringDetailsMisc.Drowning.ToString(), 1f);
			}
			else if (LastAttackedPlayer.DeathType.HasFlag(DeathTypes.EdgeOfTheWorld))
			{
				StatsLogger.Stats.UpdateSingle(Settings.LogActions.Deaths, Settings.StringDetailsMisc.EdgeOfTheWorld.ToString(), 1f);
			}
			else
			{
				StatsLogger.Stats.UpdateSingle(Settings.LogActions.Deaths, Settings.StringDetailsMisc.Unknown.ToString(), 1f);
			}
		}
		else
		{
			DebugLogger.LogWarning("No 'LastAttackedPlayer', this should not be possible.");
			StatsLogger.Stats.UpdateSingle(Settings.LogActions.Deaths, Settings.StringDetailsMisc.Unknown.ToString(), 1f);
		}
		StatsLogger.Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.LastDeath.ToString(), 0f, useTimeBuffer: false, set: true);
		StatsLogger.Stats.UpdateTotal(Settings.LogActions.Deaths, 1f);
		LastAttackedPlayer = null;
		StatsLogger.Stats.SaveFile(Player.m_localPlayer.GetPlayerName());
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Raven), "Damage")]
	private static void RavenDamage(ref Raven __instance, HitData hit)
	{
		if (hit.HaveAttacker())
		{
			Character attacker = hit.GetAttacker();
			if ((Object)(object)attacker != (Object)null && StatsLogger.IsLocalPlayer(attacker))
			{
				StatsLogger.Stats.UpdateSingle(Settings.LogActions.Dismissed, __instance.m_name, 1f);
			}
		}
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(Character), "Damage")]
	private static void Damage(ref Character __instance, HitData hit, ZNetView ___m_nview)
	{
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		if (hit.HaveAttacker())
		{
			Character attacker = hit.GetAttacker();
			if (StatsLogger.IsLocalPlayer(attacker) && !StatsLogger.IsLocalOwner(___m_nview))
			{
				LocalHitCollector.Hits.Add(new LocalHitCollector.LocalHit
				{
					Hit = hit,
					Target = __instance,
					Weapon = ((Humanoid)(Player)attacker).GetCurrentWeapon(),
					Type = ((object)__instance).GetType(),
					UID = ___m_nview.GetZDO().m_uid,
					TTL = TTLSecondsAtkByPlayer
				});
			}
		}
	}
}
public static class DebugLogger
{
	public enum LogTypes
	{
		Default,
		Warning,
		Error
	}

	public static bool Enabled = true;

	public static string Prefix = "";

	public static LogTypes MinLogLevel = LogTypes.Warning;

	public static void Log(string str)
	{
		Log(str, LogTypes.Default);
	}

	public static void LogWarning(string str)
	{
		Log(str, LogTypes.Warning);
	}

	public static void LogError(string str)
	{
		Log(str, LogTypes.Error);
	}

	private static void Log(string str, LogTypes type = LogTypes.Default)
	{
		if (Enabled && type >= MinLogLevel)
		{
			str = Prefix + " " + str;
			switch (type)
			{
			case LogTypes.Default:
				Debug.Log((object)str);
				break;
			case LogTypes.Warning:
				Debug.LogWarning((object)str);
				break;
			case LogTypes.Error:
				Debug.LogError((object)str);
				break;
			}
		}
	}
}
[HarmonyPatch]
public static class DestructibleObjects
{
	[HarmonyPatch(typeof(Destructible), "RPC_Damage")]
	public static class Destructible_RPC_Damage_DamageAndDeath
	{
		private static MethodInfo miCreate = AccessTools.Method(typeof(EffectList), "Create", (Type[])null, (Type[])null);

		private static MethodInfo miDestroy = AccessTools.Method(typeof(Destructible), "Destroy", (Type[])null, (Type[])null);

		private static MethodInfo miDamageTrigger = AccessTools.Method(typeof(Destructible_RPC_Damage_DamageAndDeath), "DamageTrigger", (Type[])null, (Type[])null);

		private static MethodInfo miDeathTrigger = AccessTools.Method(typeof(Destructible_RPC_Damage_DamageAndDeath), "DeathTrigger", (Type[])null, (Type[])null);

		private static readonly int Inserts = 2;

		[HarmonyTranspiler]
		public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Expected O, but got Unknown
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Expected O, but got Unknown
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Expected O, but got Unknown
			int num = 0;
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			for (int i = 0; i < list.Count; i++)
			{
				if (list[i].opcode == OpCodes.Callvirt && (MethodInfo)list[i].operand == miCreate)
				{
					list.Insert(i + 1, new CodeInstruction(OpCodes.Call, (object)miDamageTrigger));
					list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_2, (object)null));
					list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
					DebugLogger.Log($"{typeof(Destructible_RPC_Damage_DamageAndDeath)} [Destructible RPC_Damage {miDamageTrigger.Name}] applied.");
					num++;
				}
				if (list[i].opcode == OpCodes.Call && (MethodInfo)list[i].operand == miDestroy)
				{
					list.Insert(i + 1, new CodeInstruction(OpCodes.Call, (object)miDeathTrigger));
					list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_2, (object)null));
					list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
					DebugLogger.Log($"{typeof(Destructible_RPC_Damage_DamageAndDeath)} [Destructible RPC_Damage {miDeathTrigger.Name}] applied.");
					num++;
				}
			}
			if (num == 0)
			{
				DebugLogger.LogError($"{typeof(Destructible_RPC_Damage_DamageAndDeath)} [Destructible RPC_Damage] failed.");
			}
			else if (num != Inserts)
			{
				DebugLogger.LogWarning($"{typeof(Destructible_RPC_Damage_DamageAndDeath)} [Destructible RPC_Damage] applied {num}/{Inserts}.");
			}
			return list.AsEnumerable();
		}

		public static void DamageTrigger(Destructible target, HitData hit)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			float totalDamage = hit.GetTotalDamage();
			if (hit.HaveAttacker())
			{
				Character attacker = hit.GetAttacker();
				if (StatsLogger.IsLocalPlayer(attacker))
				{
					ItemData currentWeapon = ((Humanoid)(Player)attacker).GetCurrentWeapon();
					StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.ObjectAttacks, (object)target, currentWeapon, 1f, useTimeBuffer: false);
					StatsLogger.Stats.UpdateTotal(Settings.LogActions.ObjectAttacks, 1f);
					StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.ObjectDamageDone, (object)target, currentWeapon, totalDamage, useTimeBuffer: false);
					StatsLogger.Stats.UpdateTotal(Settings.LogActions.ObjectDamageDone, totalDamage);
				}
			}
		}

		public static void DeathTrigger(Destructible target, HitData hit)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			if (hit.HaveAttacker())
			{
				Character attacker = hit.GetAttacker();
				if (StatsLogger.IsLocalPlayer(attacker))
				{
					ItemData currentWeapon = ((Humanoid)(Player)attacker).GetCurrentWeapon();
					StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.ObjectsDestroyed, (object)target, currentWeapon, 1f, useTimeBuffer: false);
					StatsLogger.Stats.UpdateTotal(Settings.LogActions.ObjectsDestroyed, 1f);
				}
			}
		}
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(Destructible), "Damage")]
	private static void Damage(ref Destructible __instance, HitData hit, ZNetView ___m_nview)
	{
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		if (hit.HaveAttacker())
		{
			Character attacker = hit.GetAttacker();
			if (StatsLogger.IsLocalPlayer(attacker) && !StatsLogger.IsLocalOwner(___m_nview))
			{
				LocalHitCollector.Hits.Add(new LocalHitCollector.LocalHit
				{
					Hit = hit,
					Target = __instance,
					Weapon = ((Humanoid)(Player)attacker).GetCurrentWeapon(),
					Type = ((object)__instance).GetType(),
					UID = ___m_nview.GetZDO().m_uid
				});
			}
		}
	}
}
[HarmonyPatch]
public static class GuardianPower
{
	[HarmonyPatch(typeof(Player))]
	[HarmonyPatch("ActivateGuardianPower")]
	public static class Player_ActivateGuardianPower_ReturnFix
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			if (list.Count > 2)
			{
				if (list[list.Count - 1].opcode == OpCodes.Ret)
				{
					if (list[list.Count - 2].opcode == OpCodes.Ldc_I4_0)
					{
						list[list.Count - 2].opcode = OpCodes.Ldc_I4_1;
						DebugLogger.Log($"{typeof(Player_ActivateGuardianPower_ReturnFix)} applied.");
					}
					else
					{
						DebugLogger.LogWarning($"{typeof(Player_ActivateGuardianPower_ReturnFix)} failed: Return value is not as expected. ({list[list.Count - 2].opcode})");
					}
				}
				else
				{
					DebugLogger.LogError($"{typeof(Player_ActivateGuardianPower_ReturnFix)} failed: No last return found. ({list[list.Count - 1].opcode})");
				}
			}
			else
			{
				DebugLogger.LogError($"{typeof(Player_ActivateGuardianPower_ReturnFix)} failed: Not enough Code Instructions. ({list.Count})");
			}
			return list.AsEnumerable();
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Player), "ActivateGuardianPower")]
	private static void PlayerUsePower(ref Player __instance, StatusEffect ___m_guardianSE, bool __result)
	{
		if (StatsLogger.IsLocalPlayer((Character)(object)__instance) && __result)
		{
			StatsLogger.Stats.UpdateSingle(Settings.LogActions.UsedPower, ___m_guardianSE.m_name, 1f);
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.UsedPower, 1f);
		}
	}
}
[HarmonyPatch]
public static class Interactions
{
	[HarmonyPatch(typeof(Player), "Interact")]
	public static class Player_Interact_GameObjects
	{
		private static MethodInfo miInteract = AccessTools.Method(typeof(Interactable), "Interact", (Type[])null, (Type[])null);

		private static MethodInfo miInteractTrigger = AccessTools.Method(typeof(Interactions), "InteractTrigger", (Type[])null, (Type[])null);

		private static readonly int Inserts = 1;

		[HarmonyTranspiler]
		public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			int num = 0;
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			for (int i = 0; i < list.Count; i++)
			{
				if (list[i].opcode == OpCodes.Callvirt && (MethodInfo)list[i].operand == miInteract)
				{
					list.Insert(i - 3, new CodeInstruction(OpCodes.Ldarg_1, (object)null));
					i++;
					list.Insert(i - 3, new CodeInstruction(OpCodes.Call, (object)miInteractTrigger));
					i++;
					DebugLogger.Log($"{typeof(Player_Interact_GameObjects)} [Player Interact {miInteractTrigger.Name}] applied.");
					num++;
				}
			}
			if (num == 0)
			{
				DebugLogger.LogError($"{typeof(Player_Interact_GameObjects)} [Player Interact] failed.");
			}
			else if (num != Inserts)
			{
				DebugLogger.LogWarning($"{typeof(Player_Interact_GameObjects)} [Player Interact] applied {num}/{Inserts}.");
			}
			return list.AsEnumerable();
		}
	}

	public static void InteractTrigger(GameObject go)
	{
		Hoverable componentInParent = go.GetComponentInParent<Hoverable>();
		if (componentInParent != null)
		{
			StatsLogger.Stats.UpdateSingle(Settings.LogActions.Interacted, componentInParent.GetHoverName(), 1f);
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.Interacted, 1f);
		}
	}
}
[HarmonyPatch]
public static class Items
{
	[HarmonyPostfix]
	[HarmonyPatch(typeof(Player), "ConsumeItem")]
	private static void PlayerConsume(ref Player __instance, Inventory inventory, ItemData item, bool __result)
	{
		if (StatsLogger.IsLocalPlayer((Character)(object)__instance) && __result)
		{
			StatsLogger.Stats.UpdateSingle(Settings.LogActions.Consumed, item.m_shared.m_name, 1f);
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.Consumed, 1f);
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Inventory), "AddItem", new Type[]
	{
		typeof(string),
		typeof(int),
		typeof(int),
		typeof(int),
		typeof(long),
		typeof(string),
		typeof(bool)
	})]
	private static void PlayerCraft(ref Inventory __instance, string name, int stack, int quality, int variant, long crafterID, string crafterName, bool pickedUp, ItemData __result)
	{
		if (StatsLogger.IsLocalPlayerInventory(__instance) && __result != null && ((crafterName != null) & (crafterName.Length > 0)) && crafterName == Player.m_localPlayer.GetPlayerName())
		{
			StatsLogger.Stats.UpdateSingle(Settings.LogActions.Crafted, __result.m_shared.m_name, stack);
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.Crafted, stack);
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Inventory), "AddItem", new Type[] { typeof(ItemData) })]
	private static void PlayerPickup(ref Inventory __instance, ItemData item, ref bool __result)
	{
		if (StatsLogger.IsLocalPlayerInventory(__instance) && __result && item != null)
		{
			StatsLogger.Stats.UpdateSingle(Settings.LogActions.PickedUp, item.m_shared.m_name, item.m_stack);
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.PickedUp, item.m_stack);
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Fish), "Pickup", new Type[] { typeof(Humanoid) })]
	private static void FishPickup(ref Fish __instance, Humanoid character, ref bool __result)
	{
		if (StatsLogger.IsLocalPlayer((Character)(object)character) && __result && (Object)(object)__instance != (Object)null)
		{
			DebugLogger.Log("Picked up Fish: " + __instance.m_name);
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Humanoid), "DropItem")]
	private static void PlayerDropItem(ref Humanoid __instance, Inventory inventory, ItemData item, int amount, ref bool __result)
	{
		if (StatsLogger.IsLocalPlayer((Character)(object)__instance) && __result && item != null)
		{
			StatsLogger.Stats.UpdateSingle(Settings.LogActions.Dropped, item.m_shared.m_name, amount);
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.Dropped, amount);
		}
	}
}
[HarmonyPatch]
public static class LoadAndSave
{
	[HarmonyPostfix]
	[HarmonyPatch(typeof(PlayerProfile), "LoadPlayerData")]
	private static void PlayerLoaded(ref PlayerProfile __instance)
	{
		DebugLogger.Log("PlayerProfile: LoadPlayerData");
		if ((Object)(object)Player.m_localPlayer != (Object)null)
		{
			StatsLogger.Stats.LoadFile(Player.m_localPlayer.GetPlayerName());
			DebugLogger.Log("PLAYERSTATS ARE LOADED");
		}
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(Game), "SavePlayerProfile")]
	private static void PlayerSaved(ref Game __instance)
	{
		DebugLogger.Log("Game: SavePlayerProfile");
		if ((Object)(object)Player.m_localPlayer != (Object)null)
		{
			StatsLogger.Stats.SaveFile(Player.m_localPlayer.GetPlayerName());
			DebugLogger.Log("PLAYERSTATS ARE SAVED");
		}
	}
}
[HarmonyPatch]
public class LocalHitCollector
{
	public class LocalHit
	{
		public ZDOID UID;

		public object Target;

		public ItemData Weapon;

		public HitData Hit;

		public Type Type;

		public float TTL = 5f;

		public DateTime Time = DateTime.Now;

		public bool HitRegistered;
	}

	public static List<LocalHit> Hits = new List<LocalHit>();

	public static DateTime LastCleanup = DateTime.Now;

	public static readonly int IntervalSeconds = 10;

	[HarmonyPrefix]
	[HarmonyPatch(typeof(DamageText), "AddInworldText")]
	private static void Damaged(ref DamageText __instance, TextType type, Vector3 pos, float distance, float dmg, bool mySelf)
	{
		//IL_002b: 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_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0142: Expected O, but got Unknown
		//IL_0161: Unknown result type (might be due to invalid IL or missing references)
		//IL_0174: Expected O, but got Unknown
		if (Hits.Count <= 0)
		{
			return;
		}
		LocalHit localHit = null;
		foreach (LocalHit hit in Hits)
		{
			Vector3 point = hit.Hit.m_point;
			if (((Vector3)(ref point)).Equals(pos) && !hit.HitRegistered)
			{
				localHit = hit;
				DebugLogger.Log($"[LocalHitCollector] HIT FOUND: {localHit.Type}");
				break;
			}
		}
		if (localHit == null)
		{
			return;
		}
		if (dmg <= 0f)
		{
			Hits.Remove(localHit);
			return;
		}
		int index = Hits.IndexOf(localHit);
		Hits[index].HitRegistered = true;
		Hits[index].Time = DateTime.Now;
		Type type2 = localHit.Type;
		DebugLogger.Log($"[LocalHitCollector] HIT REGISTERED: {type2} {dmg}");
		if (type2 == typeof(Character) || type2 == typeof(Humanoid) || type2 == typeof(Player))
		{
			StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.Attacks, (Character)localHit.Target, localHit.Weapon, 1f);
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.Attacks, 1f);
			StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.DamageDone, (Character)localHit.Target, localHit.Weapon, dmg);
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.DamageDone, dmg);
		}
		else if (type2 == typeof(TreeBase))
		{
			StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.TreeAttacks, localHit.Target, localHit.Weapon, 1f);
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.TreeAttacks, 1f);
			StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.TreeDamageDone, localHit.Target, localHit.Weapon, dmg);
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.TreeDamageDone, dmg);
		}
		else if (type2 == typeof(TreeLog))
		{
			StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.ObjectAttacks, localHit.Target, localHit.Weapon, 1f);
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.ObjectAttacks, 1f);
			StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.ObjectDamageDone, localHit.Target, localHit.Weapon, dmg);
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.ObjectDamageDone, dmg);
		}
		else if (type2 == typeof(WearNTear))
		{
			StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.StructureAttacks, localHit.Target, localHit.Weapon, 1f);
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.StructureAttacks, 1f);
			StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.StructureDamageDone, localHit.Target, localHit.Weapon, dmg);
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.StructureDamageDone, dmg);
		}
		else if (type2 == typeof(Destructible))
		{
			StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.ObjectAttacks, localHit.Target, localHit.Weapon, 1f);
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.ObjectAttacks, 1f);
			StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.ObjectDamageDone, localHit.Target, localHit.Weapon, dmg);
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.ObjectDamageDone, dmg);
		}
		else if (type2 == typeof(MineRock5))
		{
			StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.RockAttacks, localHit.Target, localHit.Weapon, 1f);
			StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.RockDamageDone, localHit.Target, localHit.Weapon, dmg);
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.RockAttacks, 1f);
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.RockDamageDone, dmg);
		}
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(ZDOMan), "HandleDestroyedZDO")]
	private static void Destroyed(ZDOID uid)
	{
		//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_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Expected O, but got Unknown
		if (Hits.Count <= 0)
		{
			return;
		}
		LocalHit localHit = Hits.Find((LocalHit item) => ((ZDOID)(ref item.UID)).ID == ((ZDOID)(ref uid)).ID);
		if (localHit != null && localHit.HitRegistered)
		{
			DebugLogger.Log($"[LocalHitCollector] ZDO DESTROYED: {localHit.Type}");
			Hits.Remove(localHit);
			Type type = localHit.Type;
			if (type == typeof(Character) || type == typeof(Humanoid) || type == typeof(Player))
			{
				StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.Kills, (Character)localHit.Target, localHit.Weapon, 1f);
				StatsLogger.Stats.UpdateTotal(Settings.LogActions.Kills, 1f);
			}
			else if (type == typeof(TreeBase))
			{
				StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.TreesCut, localHit.Target, localHit.Weapon, 1f);
				StatsLogger.Stats.UpdateTotal(Settings.LogActions.TreesCut, 1f);
			}
			else if (type == typeof(TreeLog))
			{
				StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.ObjectsDestroyed, localHit.Target, localHit.Weapon, 1f);
				StatsLogger.Stats.UpdateTotal(Settings.LogActions.ObjectsDestroyed, 1f);
			}
			else if (type == typeof(WearNTear))
			{
				StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.StructuresDestroyed, localHit.Target, localHit.Weapon, 1f);
				StatsLogger.Stats.UpdateTotal(Settings.LogActions.StructuresDestroyed, 1f);
			}
			else if (type == typeof(Destructible))
			{
				StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.ObjectsDestroyed, localHit.Target, localHit.Weapon, 1f);
				StatsLogger.Stats.UpdateTotal(Settings.LogActions.ObjectsDestroyed, 1f);
			}
			else if (type == typeof(MineRock5))
			{
				StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.RocksMined, localHit.Target, localHit.Weapon, 1f);
				StatsLogger.Stats.UpdateTotal(Settings.LogActions.RocksMined, 1f);
			}
		}
	}
}
[HarmonyPatch]
public static class Mining
{
	[HarmonyPatch(typeof(MineRock5), "DamageArea")]
	public static class MineRock5_DamageArea_DamageAndDestroy
	{
		private static MethodInfo miSaveHealth = AccessTools.Method(typeof(MineRock5), "SaveHealth", (Type[])null, (Type[])null);

		private static MethodInfo miDamageTrigger = AccessTools.Method(typeof(MineRock5_DamageArea_DamageAndDestroy), "DamageTrigger", (Type[])null, (Type[])null);

		private static MethodInfo miDestroyTrigger = AccessTools.Method(typeof(MineRock5_DamageArea_DamageAndDestroy), "DestroyTrigger", (Type[])null, (Type[])null);

		private static int Inserts = 2;

		[HarmonyTranspiler]
		public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Expected O, but got Unknown
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Expected O, but got Unknown
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Expected O, but got Unknown
			int num = 0;
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			for (int i = 0; i < list.Count; i++)
			{
				if (list[i].opcode == OpCodes.Call && (MethodInfo)list[i].operand == miSaveHealth)
				{
					list.Insert(i + 1, new CodeInstruction(OpCodes.Call, (object)miDamageTrigger));
					list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_2, (object)null));
					list.Insert(i + 1, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
					DebugLogger.Log($"{typeof(MineRock5_DamageArea_DamageAndDestroy)} [MineRock5 DamageArea {miDamageTrigger.Name}] applied.");
					num++;
				}
				if (list[i].opcode == OpCodes.Ret && list[i - 1].opcode == OpCodes.Ldc_I4_1)
				{
					list.Insert(i - 2, new CodeInstruction(OpCodes.Call, (object)miDestroyTrigger));
					list.Insert(i - 2, new CodeInstruction(OpCodes.Ldarg_2, (object)null));
					list.Insert(i - 2, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
					i += 3;
					DebugLogger.Log($"{typeof(MineRock5_DamageArea_DamageAndDestroy)} [MineRock5 DamageArea {miDestroyTrigger.Name}] applied.");
					num++;
				}
			}
			if (num == 0)
			{
				DebugLogger.LogError($"{typeof(MineRock5_DamageArea_DamageAndDestroy)} [MineRock5 DamageArea] failed.");
			}
			else if (num != Inserts)
			{
				DebugLogger.LogWarning($"{typeof(MineRock5_DamageArea_DamageAndDestroy)} [MineRock5 DamageArea] applied {num}/{Inserts}.");
			}
			return list.AsEnumerable();
		}

		public static void DamageTrigger(MineRock5 target, HitData hit)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (hit.HaveAttacker())
			{
				Character attacker = hit.GetAttacker();
				if (StatsLogger.IsLocalPlayer(attacker))
				{
					ItemData currentWeapon = ((Humanoid)(Player)attacker).GetCurrentWeapon();
					float totalDamage = hit.GetTotalDamage();
					StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.RockAttacks, (object)target, currentWeapon, 1f, useTimeBuffer: false);
					StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.RockDamageDone, (object)target, currentWeapon, totalDamage, useTimeBuffer: false);
					StatsLogger.Stats.UpdateTotal(Settings.LogActions.RockAttacks, 1f);
					StatsLogger.Stats.UpdateTotal(Settings.LogActions.RockDamageDone, totalDamage);
				}
			}
		}

		public static void DestroyTrigger(MineRock5 target, HitData hit)
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (hit.HaveAttacker())
			{
				Character attacker = hit.GetAttacker();
				if (StatsLogger.IsLocalPlayer(attacker))
				{
					ItemData currentWeapon = ((Humanoid)(Player)attacker).GetCurrentWeapon();
					StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.RocksMined, (object)target, currentWeapon, 1f, useTimeBuffer: false);
					StatsLogger.Stats.UpdateTotal(Settings.LogActions.RocksMined, 1f);
				}
			}
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(MineRock), "RPC_Hit")]
	private static void TestMine(ref MineRock __instance, HitData hit)
	{
		DebugLogger.Log($"MineRock {__instance.m_name} {hit.GetAttacker().m_name} {hit.GetTotalDamage()}");
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(MineRock5), "Damage")]
	private static void Damage(ref MineRock5 __instance, HitData hit, ZNetView ___m_nview)
	{
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		if (hit.HaveAttacker())
		{
			Character attacker = hit.GetAttacker();
			if (StatsLogger.IsLocalPlayer(attacker) && !StatsLogger.IsLocalOwner(___m_nview))
			{
				LocalHitCollector.Hits.Add(new LocalHitCollector.LocalHit
				{
					Hit = hit,
					Target = __instance,
					Weapon = ((Humanoid)(Player)attacker).GetCurrentWeapon(),
					Type = ((object)__instance).GetType(),
					UID = ___m_nview.GetZDO().m_uid
				});
			}
		}
	}
}
[HarmonyPatch]
public static class Misc
{
	[HarmonyPostfix]
	[HarmonyPatch(typeof(Player), "PlacePiece")]
	private static void PlayerPlacePiece(ref Player __instance, Piece piece, bool __result)
	{
		if (StatsLogger.IsLocalPlayer((Character)(object)__instance) && __result)
		{
			StatsLogger.Stats.UpdateSingle(Settings.LogActions.Built, piece.m_name, 1f);
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.Built, 1f);
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Player), "OnJump")]
	private static void PlayerJump(ref Player __instance)
	{
		if (StatsLogger.IsLocalPlayer((Character)(object)__instance))
		{
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.Jumps, 1f);
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Player), "TeleportTo")]
	private static void PlayerTeleport(ref Player __instance, bool __result)
	{
		if (StatsLogger.IsLocalPlayer((Character)(object)__instance) && __result)
		{
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.Teleported, 1f);
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Player), "SetSleeping")]
	private static void PlayerSleep(ref Player __instance, bool sleep)
	{
		if (StatsLogger.IsLocalPlayer((Character)(object)__instance) && sleep)
		{
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.Sleeps, 1f);
		}
	}
}
[HarmonyPatch]
public static class Projectiles
{
	public class Target
	{
		public GameObject Object;

		public Collider Collider;

		public ItemData Item;

		public DateTime Time = DateTime.Now;
	}

	public static Dictionary<int, Target> FiredProjectiles = new Dictionary<int, Target>();

	[HarmonyPrefix]
	[HarmonyPatch(typeof(Attack), "FireProjectileBurst")]
	private static void AttackFireProjectileBurst(ref Attack __instance, ItemData ___m_ammoItem, ItemData ___m_weapon, Humanoid ___m_character)
	{
		if (StatsLogger.IsLocalPlayer((Character)(object)___m_character))
		{
			DebugLogger.Log($"[DEBUG]: {___m_weapon.m_shared.m_name} using {___m_ammoItem?.m_shared?.m_name} x{__instance?.m_projectiles}");
			StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.ProjectilesFired, (object)___m_ammoItem, ___m_weapon, 1f, useTimeBuffer: false);
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.ProjectilesFired, 1f);
		}
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(Projectile), "OnHit")]
	private static void ProjectileOnHit(ref Projectile __instance, Collider collider, Character ___m_owner, ItemData ___m_spawnItem)
	{
		if (StatsLogger.IsLocalPlayer(___m_owner) && ((Object)(object)__instance.m_spawnOnHit != (Object)null || ___m_spawnItem != null))
		{
			GameObject val = (Object.op_Implicit((Object)(object)collider) ? Projectile.FindHitObject(collider) : null);
			if ((Object)(object)val != (Object)null)
			{
				DebugLogger.Log($"[DEBUG] {___m_spawnItem.m_shared.m_name} {((object)val).GetType()}");
			}
		}
	}
}
[HarmonyPatch]
public static class ShipTriggers
{
	public static bool IsShipping;

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Ship), "OnTriggerEnter")]
	private static void Enter(ref Ship __instance)
	{
		if ((Object)(object)Player.m_localPlayer != (Object)null)
		{
			IsShipping = __instance.IsPlayerInBoat(Player.m_localPlayer);
		}
		else
		{
			IsShipping = false;
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(Ship), "OnTriggerExit")]
	private static void Exit(ref Ship __instance)
	{
		if ((Object)(object)Player.m_localPlayer != (Object)null)
		{
			IsShipping = __instance.IsPlayerInBoat(Player.m_localPlayer);
		}
		else
		{
			IsShipping = false;
		}
	}
}
[BepInPlugin("de.givenameplz.statslogger", "Stats Logger (by givenameplz)", "1.2.2")]
public class StatsLogger : BaseUnityPlugin
{
	public const string GUID = "de.givenameplz.statslogger";

	public const string DisplayName = "Stats Logger (by givenameplz)";

	public const string Version = "1.2.2";

	public const string Description = "Stats Logging for your Characters!";

	public const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

	public static Settings Stats = new Settings();

	public static bool Debugging = false;

	private static Random RNG = new Random();

	public static string RandomString(int length, string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
	{
		return new string((from s in Enumerable.Repeat(chars, length)
			select s[RNG.Next(s.Length)]).ToArray());
	}

	public static DateTime UnixTimeStampToDateTime(ulong unixTimeStamp, bool outputAsLocalTime = false)
	{
		DateTime dateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
		if (outputAsLocalTime)
		{
			return dateTime.AddSeconds(unixTimeStamp).ToLocalTime();
		}
		return dateTime.AddSeconds(unixTimeStamp).ToUniversalTime();
	}

	public static ulong DateTimeToUnixTimeStamp(DateTime dateTime)
	{
		DateTime dateTime2 = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
		return (ulong)(dateTime.ToUniversalTime() - dateTime2).TotalSeconds;
	}

	private void Awake()
	{
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		DebugLogger.Prefix = "de.givenameplz.statslogger";
		new Harmony("de.givenameplz.statslogger").PatchAll();
		Stats.LoadSettings();
		UserInterface.Init();
	}

	private void Update()
	{
		//IL_0563: Unknown result type (might be due to invalid IL or missing references)
		//IL_0568: Unknown result type (might be due to invalid IL or missing references)
		//IL_05e2: Unknown result type (might be due to invalid IL or missing references)
		if (DamageAndHits.LastCleanupAtkByPlayer.CompareTo(DateTime.Now.AddSeconds(-DamageAndHits.IntervalSecondsAtkByPlayer)) < 0)
		{
			DamageAndHits.LastCleanupAtkByPlayer = DateTime.Now;
			if (DamageAndHits.RecentlyAttackedByPlayer.Count > 0)
			{
				foreach (int item in DamageAndHits.RecentlyAttackedByPlayer.Keys.Where((int key) => DamageAndHits.RecentlyAttackedByPlayer[key].Time < DateTime.Now.AddSeconds(-DamageAndHits.TTLSecondsAtkByPlayer)).ToList())
				{
					DebugLogger.Log($"[DamageAndHits.RecentlyAttackedByPlayer] REM {item}");
					DamageAndHits.RecentlyAttackedByPlayer.Remove(item);
				}
			}
		}
		if (DamageAndHits.LastCleanupAtkPlayer.CompareTo(DateTime.Now.AddSeconds(-DamageAndHits.IntervalSecondsAtkPlayer)) < 0)
		{
			DamageAndHits.LastCleanupAtkPlayer = DateTime.Now;
			if (DamageAndHits.LastAttackedPlayer != null && DamageAndHits.LastAttackedPlayer.Time < DateTime.Now.AddSeconds(-DamageAndHits.TTLSecondsAtkPlayer))
			{
				DebugLogger.Log("[DamageAndHits.LastAttackedPlayer] REM " + (Object.op_Implicit((Object)(object)DamageAndHits.LastAttackedPlayer.Character) ? DamageAndHits.LastAttackedPlayer.Character.m_name : "unknown"));
				DamageAndHits.LastAttackedPlayer = null;
			}
		}
		if (Structures.LastCleanup.CompareTo(DateTime.Now.AddSeconds(-Structures.IntervalSeconds)) < 0)
		{
			Structures.LastCleanup = DateTime.Now;
			if (Structures.LastAttackedByPlayer.Count > 0)
			{
				foreach (int item2 in Structures.LastAttackedByPlayer.Keys.Where((int key) => Structures.LastAttackedByPlayer[key].Time < DateTime.Now.AddSeconds(-Structures.TTLSeconds)).ToList())
				{
					DebugLogger.Log($"[Structures.LastAttackedByPlayer] REM {item2}");
					Structures.LastAttackedByPlayer.Remove(item2);
				}
			}
		}
		if (LocalHitCollector.LastCleanup.CompareTo(DateTime.Now.AddSeconds(-LocalHitCollector.IntervalSeconds)) < 0 && LocalHitCollector.Hits.Count > 0)
		{
			foreach (LocalHitCollector.LocalHit item3 in LocalHitCollector.Hits.Where((LocalHitCollector.LocalHit item) => item.Time < DateTime.Now.AddSeconds(0f - item.TTL)).ToList())
			{
				DebugLogger.Log($"[LocalHitCollector.Hits] REM {((ZDOID)(ref item3.UID)).ID}");
				LocalHitCollector.Hits.Remove(item3);
			}
		}
		if (!((Object)(object)Player.m_localPlayer != (Object)null))
		{
			return;
		}
		Stats.AutoSave(Player.m_localPlayer.GetPlayerName());
		float deltaTime = Time.deltaTime;
		Stats.BurstUpdate(deltaTime);
		if (ShipTriggers.IsShipping)
		{
			Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Shipping.ToString(), deltaTime, useTimeBuffer: true);
		}
		if (((Character)Player.m_localPlayer).IsSwimming())
		{
			Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Swimming.ToString(), deltaTime, useTimeBuffer: true);
		}
		if (((Character)Player.m_localPlayer).IsSneaking())
		{
			Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Sneaking.ToString(), deltaTime, useTimeBuffer: true);
		}
		if (((Character)Player.m_localPlayer).IsCrouching())
		{
			Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Crouching.ToString(), deltaTime, useTimeBuffer: true);
		}
		if (((Character)Player.m_localPlayer).IsSitting())
		{
			Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Sitting.ToString(), deltaTime, useTimeBuffer: true);
		}
		if (Player.m_localPlayer.IsSafeInHome())
		{
			Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.SafeAtHome.ToString(), deltaTime, useTimeBuffer: true);
		}
		if (((Character)Player.m_localPlayer).IsPVPEnabled())
		{
			Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.PvPModeEnabled.ToString(), deltaTime, useTimeBuffer: true);
		}
		if (((Character)Player.m_localPlayer).IsRunning())
		{
			Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Running.ToString(), deltaTime, useTimeBuffer: true);
		}
		if (((Character)Player.m_localPlayer).InBed())
		{
			Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Sleeping.ToString(), deltaTime, useTimeBuffer: true);
		}
		if (VagonCheck.IsVagoning)
		{
			Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Carting.ToString(), deltaTime, useTimeBuffer: true);
		}
		if (((Character)Player.m_localPlayer).IsEncumbered())
		{
			Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Encumbered.ToString(), deltaTime, useTimeBuffer: true);
		}
		if (Player.m_localPlayer.InShelter())
		{
			Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Sheltered.ToString(), deltaTime, useTimeBuffer: true);
		}
		if (((Character)Player.m_localPlayer).InInterior())
		{
			Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Inside.ToString(), deltaTime, useTimeBuffer: true);
		}
		if (((Character)Player.m_localPlayer).InPlaceMode())
		{
			Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.Building.ToString(), deltaTime, useTimeBuffer: true);
		}
		Settings stats = Stats;
		Biome currentBiome = Player.m_localPlayer.GetCurrentBiome();
		stats.UpdateSingle(Settings.LogActions.Biome, ((object)(Biome)(ref currentBiome)).ToString(), deltaTime, useTimeBuffer: true);
		Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.LastDeath.ToString(), deltaTime, useTimeBuffer: true);
		Stats.UpdateSingle(Settings.LogActions.Time, Settings.StringDetailsTime.LastDamageTaken.ToString(), deltaTime, useTimeBuffer: true);
		foreach (StatusEffect statusEffect in ((SEMan)typeof(Player).GetField("m_seman", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(Player.m_localPlayer)).GetStatusEffects())
		{
			Stats.UpdateSingle(Settings.LogActions.StatusEffect, statusEffect.m_name, deltaTime, useTimeBuffer: true);
		}
		Stats.UpdateTotal(Settings.LogActions.Time, deltaTime, useTimeBuffer: true);
	}

	public static bool IsLocalOwner(ZNetView nw)
	{
		//IL_0014: 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)
		if ((Object)(object)Player.m_localPlayer == (Object)null)
		{
			return false;
		}
		ZDOID zDOID = ((Character)Player.m_localPlayer).GetZDOID();
		if ((Object)(object)nw == (Object)null)
		{
			return false;
		}
		ZDO zDO = nw.GetZDO();
		if (zDO == null)
		{
			return false;
		}
		if (!zDO.HasOwner())
		{
			return false;
		}
		if (zDO.IsOwner())
		{
			return true;
		}
		return ((ZDOID)(ref zDOID)).UserID == zDO.GetOwner();
	}

	public static bool IsLocalPlayer(Character character)
	{
		if ((Object)(object)Player.m_localPlayer == (Object)null)
		{
			return false;
		}
		if ((Object)(object)character == (Object)null)
		{
			return false;
		}
		return ((Object)Player.m_localPlayer).GetInstanceID() == ((Object)character).GetInstanceID();
	}

	public static bool IsLocalPlayerInventory(Inventory inventory)
	{
		if ((Object)(object)Player.m_localPlayer == (Object)null)
		{
			return false;
		}
		if (inventory == null)
		{
			return false;
		}
		return ((object)((Humanoid)Player.m_localPlayer).GetInventory())?.Equals((object?)inventory) ?? false;
	}
}
public class Settings
{
	public enum LogTypes
	{
		Single,
		Total
	}

	public enum LogDetails
	{
		Target,
		Level,
		Weapon,
		Player
	}

	public enum StringDetailsTime
	{
		Shipping,
		Swimming,
		Sneaking,
		Sitting,
		SafeAtHome,
		PvPModeEnabled,
		Running,
		Sleeping,
		Crouching,
		Carting,
		Encumbered,
		Sheltered,
		Inside,
		Building,
		LastDeath,
		LastDamageTaken
	}

	public enum StringDetailsMisc
	{
		Unknown,
		Tree,
		TreeLog,
		EdgeOfTheWorld,
		Drowning,
		Gravity
	}

	public enum LogActions
	{
		Dismissed,
		Kills,
		UsedPower,
		Consumed,
		Deaths,
		Crafted,
		Built,
		Jumps,
		Sleeps,
		Attacks,
		TakingHits,
		PickedUp,
		Dropped,
		Teleported,
		DamageDone,
		DamageTaken,
		Destroyed,
		ObjectDamageDone,
		ObjectAttacks,
		ObjectsDestroyed,
		StructureDamageDone,
		StructureAttacks,
		StructuresDestroyed,
		TreeDamageDone,
		TreeAttacks,
		TreesCut,
		RockDamageDone,
		RockAttacks,
		RocksMined,
		Time,
		StatusEffect,
		Biome,
		Blocks,
		BlockedDamage,
		Staggers,
		Interacted,
		ProjectilesFired
	}

	public class CharacterStats
	{
		public int Version = 2;

		public string CharacterName;

		public List<StatsEntry> Entries = new List<StatsEntry>();

		public CharacterStats(string character)
		{
			CharacterName = character;
		}
	}

	public class StatsEntry
	{
		private string _name;

		private LogActions _action;

		private LogTypes _type;

		private Dictionary<LogDetails, string> _details;

		public string Name
		{
			get
			{
				return _name;
			}
			set
			{
				string[] array = value.Split(new char[1] { '|' });
				_type = (LogTypes)Enum.Parse(typeof(LogTypes), array[0], ignoreCase: true);
				_action = (LogActions)Enum.Parse(typeof(LogActions), array[2], ignoreCase: true);
				_details = new Dictionary<LogDetails, string>();
				string[] array2 = array[1].Split(new char[1] { ',' });
				for (int i = 0; i < array2.Length; i++)
				{
					string[] array3 = array2[i].Split(new char[1] { ':' });
					if (array3.Length == 2 && Enum.TryParse<LogDetails>(array3[0], ignoreCase: true, out var result))
					{
						_details.Add(result, array3[1]);
					}
				}
				_name = value;
			}
		}

		public DateTime FirstLogged { get; set; }

		public DateTime LastLogged { get; set; }

		public float Amount { get; set; }

		public LogActions GetLogAction()
		{
			return _action;
		}

		public LogTypes GetLogType()
		{
			return _type;
		}

		public Dictionary<LogDetails, string> GetDetails()
		{
			return _details;
		}

		public bool IsDetail(LogDetails detail, string value)
		{
			if (_details.ContainsKey(detail))
			{
				return _details[detail] == value;
			}
			return false;
		}

		public string GetDetail(LogDetails detail)
		{
			if (_details.ContainsKey(detail))
			{
				return _details[detail];
			}
			return null;
		}

		public bool HasOnlyDetail(LogDetails detail)
		{
			if (_details.Count == 1 && _details.ContainsKey(detail))
			{
				return true;
			}
			return false;
		}

		public bool HasDetail(LogDetails detail)
		{
			if (_details.ContainsKey(detail))
			{
				return true;
			}
			return false;
		}

		public string GetPrettyName()
		{
			string text = "";
			if (_type == LogTypes.Total)
			{
				text += "Total ";
			}
			if (_details.ContainsKey(LogDetails.Weapon) && !_details.ContainsKey(LogDetails.Target))
			{
				text = text + _details[LogDetails.Weapon] + " ";
			}
			if (_details.ContainsKey(LogDetails.Target))
			{
				text = text + _details[LogDetails.Target] + " ";
				if (_details.ContainsKey(LogDetails.Level))
				{
					text = text + "(Level " + _details[LogDetails.Level] + ") ";
				}
				if (_details.ContainsKey(LogDetails.Weapon))
				{
					text = text + "with " + _details[LogDetails.Weapon] + " ";
				}
			}
			return text + _action;
		}

		public string GetFormattedAmount()
		{
			string text = "";
			if (_action.HasFlag(LogActions.Time) || _action.HasFlag(LogActions.Biome) || _action.HasFlag(LogActions.StatusEffect))
			{
				float num = Amount % 60f;
				float num2 = Amount / 60f % 60f;
				float num3 = Amount / 3600f;
				return $"{num3:00}:{num2:00}:{num:00}";
			}
			return Math.Round(Amount, 1).ToString();
		}
	}

	private static readonly string AppDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "givenameplz", "ValheimStatsLogger");

	private static readonly string FilenameSettings = Path.Combine(AppDataPath, "settings.cfg");

	private static readonly string FileExt = "dat";

	private const BindingFlags Flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

	private const int CurrentDataVersion = 2;

	private CharacterStats StatsInformation;

	private bool HasChanges;

	private int BackupsMax = 3;

	private int BackupsPointer = 1;

	private DateTime LastSave = DateTime.Now;

	private readonly int AutoSaveIntervalMinutes = 15;

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

	private float BurstBuffer;

	private readonly float BurstLimitSeconds = 5f;

	public int AnnounceIntervalMinutes = 5;

	public DateTime LastAnnounce = DateTime.Now;

	public bool AnnounceChat;

	public bool AnnounceConsole = true;

	public bool AnnounceEffect = true;

	public bool HideDeathStats;

	public bool DebugMessagesInUnityConsole;

	public bool ResetFlag;

	public bool HiddenLogLevelFound;

	public Settings()
	{
		Directory.CreateDirectory(AppDataPath);
		DebugLogger.Log("[STATS] Initialized.");
	}

	public void ResetData(string character)
	{
		StatsInformation = new CharacterStats(character);
	}

	public DateTime ParseALotOfDateTimeFormats(string rawdate, bool assumeLocal = true)
	{
		if (DateTime.TryParse(rawdate, out var result))
		{
			return result;
		}
		string[] formats = new string[4] { "dd.MM.yyyy hh:mm:ss", "dd/MM/yyyy hh:mm:ss", "yyyy-MM-dd hh:mm:ss", "MM/dd/yyyy hh:mm:ss tt" };
		if (DateTime.TryParseExact(rawdate, formats, CultureInfo.InvariantCulture, DateTimeStyles.AllowWhiteSpaces | (assumeLocal ? DateTimeStyles.AssumeLocal : DateTimeStyles.AssumeUniversal), out result))
		{
			return result;
		}
		return DateTime.Now;
	}

	public void LoadFile(string character)
	{
		string text = StatsLogger.RandomString(5);
		string text2 = Path.Combine(AppDataPath, character + "." + FileExt);
		string destFileName = Path.Combine(AppDataPath, character + "." + FileExt + ".fkt-" + text);
		StatsInformation = new CharacterStats(character);
		if (!File.Exists(text2))
		{
			return;
		}
		FileStream fileStream = null;
		try
		{
			fileStream = new FileStream(text2, FileMode.Open);
			BinaryReader binaryReader = new BinaryReader(fileStream);
			int num = binaryReader.ReadInt32();
			string destFileName2 = Path.Combine(AppDataPath, $"{character}.{FileExt}.v{num}-{text}");
			switch (num)
			{
			case 1:
			{
				DebugLogger.LogWarning($"[STATS] Converting version '{num}' to '{2}'");
				StatsInformation.Version = 2;
				StatsInformation.CharacterName = binaryReader.ReadString();
				int num5 = binaryReader.ReadInt32();
				for (int j = 0; j < num5; j++)
				{
					StatsInformation.Entries.Add(new StatsEntry
					{
						Name = binaryReader.ReadString(),
						FirstLogged = ParseALotOfDateTimeFormats(binaryReader.ReadString()),
						LastLogged = ParseALotOfDateTimeFormats(binaryReader.ReadString()),
						Amount = binaryReader.ReadSingle()
					});
					ulong num6 = StatsLogger.DateTimeToUnixTimeStamp(StatsInformation.Entries[j].FirstLogged);
					ulong num7 = StatsLogger.DateTimeToUnixTimeStamp(StatsInformation.Entries[j].LastLogged);
					DebugLogger.Log($"{StatsInformation.Entries[j].Name} ({StatsInformation.Entries[j].Amount})");
					DebugLogger.Log($"FIRST: {StatsInformation.Entries[j].FirstLogged} | {num6} | UTC: {StatsLogger.UnixTimeStampToDateTime(num6)} | Local: {StatsLogger.UnixTimeStampToDateTime(num6, outputAsLocalTime: true)}");
					DebugLogger.Log($"LAST: {StatsInformation.Entries[j].LastLogged} | {num7} | UTC: {StatsLogger.UnixTimeStampToDateTime(num7)} | Local: {StatsLogger.UnixTimeStampToDateTime(num7, outputAsLocalTime: true)}");
				}
				fileStream.Close();
				File.Move(text2, destFileName2);
				HasChanges = true;
				SaveFile(character);
				break;
			}
			default:
				DebugLogger.LogError($"[STATS] Wrong version '{num}'");
				fileStream.Close();
				File.Move(text2, destFileName2);
				break;
			case 2:
			{
				StatsInformation.Version = num;
				StatsInformation.CharacterName = binaryReader.ReadString();
				int num2 = binaryReader.ReadInt32();
				for (int i = 0; i < num2; i++)
				{
					StatsInformation.Entries.Add(new StatsEntry
					{
						Name = binaryReader.ReadString(),
						FirstLogged = StatsLogger.UnixTimeStampToDateTime(binaryReader.ReadUInt64(), outputAsLocalTime: true),
						LastLogged = StatsLogger.UnixTimeStampToDateTime(binaryReader.ReadUInt64(), outputAsLocalTime: true),
						Amount = binaryReader.ReadSingle()
					});
					ulong num3 = StatsLogger.DateTimeToUnixTimeStamp(StatsInformation.Entries[i].FirstLogged);
					ulong num4 = StatsLogger.DateTimeToUnixTimeStamp(StatsInformation.Entries[i].LastLogged);
					DebugLogger.Log($"{StatsInformation.Entries[i].Name} ({StatsInformation.Entries[i].Amount})");
					DebugLogger.Log($"FIRST: {StatsInformation.Entries[i].FirstLogged} | {num3} | UTC: {StatsLogger.UnixTimeStampToDateTime(num3)} | Local: {StatsLogger.UnixTimeStampToDateTime(num3, outputAsLocalTime: true)}");
					DebugLogger.Log($"LAST: {StatsInformation.Entries[i].LastLogged} | {num4} | UTC: {StatsLogger.UnixTimeStampToDateTime(num4)} | Local: {StatsLogger.UnixTimeStampToDateTime(num4, outputAsLocalTime: true)}");
				}
				fileStream.Close();
				break;
			}
			}
		}
		catch (Exception ex)
		{
			DebugLogger.LogError("[STATS] Error loading: " + ex.Message);
			StatsInformation = new CharacterStats(character);
			if (File.Exists(text2) && fileStream != null)
			{
				fileStream.Close();
				File.Move(text2, destFileName);
			}
		}
	}

	public void SaveFile(string character)
	{
		LastSave = DateTime.Now;
		ApplyTimeBuffers();
		if (!HasChanges)
		{
			return;
		}
		HasChanges = false;
		string text = Path.Combine(AppDataPath, character + "." + FileExt);
		string destFileName = Path.Combine(AppDataPath, $"{character}.{FileExt}.bkp{BackupsPointer}");
		if (File.Exists(text))
		{
			if (File.Exists(text))
			{
				File.Copy(text, destFileName, overwrite: true);
			}
			if (++BackupsPointer > BackupsMax)
			{
				BackupsPointer = 1;
			}
		}
		try
		{
			FileStream fileStream = new FileStream(text, FileMode.Create);
			BinaryWriter binaryWriter = new BinaryWriter(fileStream);
			binaryWriter.Write(StatsInformation.Version);
			binaryWriter.Write(StatsInformation.CharacterName);
			binaryWriter.Write(StatsInformation.Entries.Count);
			foreach (StatsEntry entry in StatsInformation.Entries)
			{
				binaryWriter.Write(entry.Name);
				binaryWriter.Write(StatsLogger.DateTimeToUnixTimeStamp(entry.FirstLogged));
				binaryWriter.Write(StatsLogger.DateTimeToUnixTimeStamp(entry.LastLogged));
				binaryWriter.Write(entry.Amount);
			}
			binaryWriter.Flush();
			binaryWriter.Close();
			fileStream.Close();
		}
		catch (Exception ex)
		{
			DebugLogger.LogError("[STATS] Error saving: " + ex.Message);
		}
	}

	public void AutoSave(string character)
	{
		if (HasChanges && LastSave.CompareTo(DateTime.Now.AddMinutes(-AutoSaveIntervalMinutes)) < 0)
		{
			SaveFile(character);
		}
	}

	public void LoadSettings()
	{
		DebugLogger.Log("[STATS] Loading Settings...");
		AnnounceChat = false;
		AnnounceConsole = true;
		AnnounceEffect = true;
		if (!File.Exists(FilenameSettings))
		{
			return;
		}
		string[] array = File.ReadAllLines(FilenameSettings);
		for (int i = 0; i < array.Length; i++)
		{
			string[] array2 = array[i].Split(new char[1] { '=' }, 2);
			if (array2.Length != 2)
			{
				continue;
			}
			DebugLogger.LogTypes result6;
			if (array2[0] == "announcechat")
			{
				if (bool.TryParse(array2[1], out var result))
				{
					AnnounceChat = result;
					DebugLogger.Log("[STATS] Setting: " + array2[0] + "=" + array2[1]);
				}
			}
			else if (array2[0] == "announceconsole")
			{
				if (bool.TryParse(array2[1], out var result2))
				{
					AnnounceConsole = result2;
					DebugLogger.Log("[STATS] Setting: " + array2[0] + "=" + array2[1]);
				}
			}
			else if (array2[0] == "announceeffect")
			{
				if (bool.TryParse(array2[1], out var result3))
				{
					AnnounceEffect = result3;
					DebugLogger.Log("[STATS] Setting: " + array2[0] + "=" + array2[1]);
				}
			}
			else if (array2[0] == "announceinterval")
			{
				if (int.TryParse(array2[1], out var result4))
				{
					AnnounceIntervalMinutes = result4;
					DebugLogger.Log("[STATS] Setting: " + array2[0] + "=" + array2[1]);
				}
			}
			else if (array2[0] == "hidedeathstats")
			{
				if (bool.TryParse(array2[1], out var result5))
				{
					HideDeathStats = result5;
					DebugLogger.Log("[STATS] Setting: " + array2[0] + "=" + array2[1]);
				}
			}
			else if (array2[0] == "loglevel" && Enum.TryParse<DebugLogger.LogTypes>(array2[1], out result6))
			{
				HiddenLogLevelFound = true;
				DebugLogger.MinLogLevel = result6;
				DebugLogger.Log("[STATS] Setting: " + array2[0] + "=" + array2[1]);
			}
		}
	}

	public void SaveSettings()
	{
		DebugLogger.Log("[STATS] Saving Settings...");
		List<string> list = new List<string>
		{
			"announcechat=" + (AnnounceChat ? "true" : "false"),
			"announceconsole=" + (AnnounceConsole ? "true" : "false"),
			"announceeffect=" + (AnnounceEffect ? "true" : "false"),
			"announceinterval=" + AnnounceIntervalMinutes,
			"hidedeathstats=" + (HideDeathStats ? "true" : "false")
		};
		if (HiddenLogLevelFound)
		{
			list.Add($"loglevel={DebugLogger.MinLogLevel}");
		}
		File.WriteAllLines(FilenameSettings, list);
	}

	public void BurstUpdate(float dt)
	{
		BurstBuffer += dt;
		if (BurstBuffer > BurstLimitSeconds)
		{
			ApplyTimeBuffers();
		}
	}

	private void ApplyTimeBuffers()
	{
		BurstBuffer = 0f;
		string[] array = TimeBuffer.Keys.ToArray();
		foreach (string text in array)
		{
			Update(text, TimeBuffer[text]);
			TimeBuffer.Remove(text);
		}
	}

	public void UpdateTotal(LogActions action, float value, bool useTimeBuffer = false)
	{
		InternalUpdate(LogTypes.Total, "", action, value, useTimeBuffer);
	}

	public void UpdateCombinedSingle(LogActions action, Character target, ItemData weapon, float value, bool useTimeBuffer = false)
	{
		string text = (((Object)(object)target != (Object)null) ? target.m_name : StringDetailsMisc.Unknown.ToString());
		string text2 = ((weapon != null) ? weapon.m_shared.m_name : StringDetailsMisc.Unknown.ToString());
		string target2 = $"target:{text},level:{target.GetLevel()},weapon:{text2}";
		InternalUpdateSingle(action, target2, value, useTimeBuffer);
		target2 = $"target:{text},level:{target.GetLevel()}";
		InternalUpdateSingle(action, target2, value, useTimeBuffer);
		target2 = "target:" + text + ",weapon:" + text2;
		InternalUpdateSingle(action, target2, value, useTimeBuffer);
		target2 = "target:" + text;
		InternalUpdateSingle(action, target2, value, useTimeBuffer);
		target2 = "weapon:" + text2;
		InternalUpdateSingle(action, target2, value, useTimeBuffer);
	}

	public void UpdateCombinedSingle(LogActions action, object target, ItemData weapon, float value, bool useTimeBuffer = false)
	{
		//IL_0030: 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_00c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ca: Expected O, but got Unknown
		//IL_011e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d5: 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)
		//IL_017f: Unknown result type (might be due to invalid IL or missing references)
		string text;
		if (target == null)
		{
			text = StringDetailsMisc.Unknown.ToString();
		}
		else if (target.GetType() == typeof(TreeBase))
		{
			text = ((Object)(TreeBase)target).name.Replace("(Clone)", "").Trim();
		}
		else if (target.GetType() == typeof(TreeLog))
		{
			text = ((Object)(TreeLog)target).name.Replace("(Clone)", "").Trim();
		}
		else if (!(target.GetType() == typeof(WearNTear)))
		{
			text = ((target.GetType() == typeof(Destructible)) ? ((Object)(Destructible)target).name.Replace("(Clone)", "").Trim() : ((target.GetType() == typeof(MineRock5)) ? ((MineRock5)target).m_name : ((target == null || !(target.GetType() == typeof(ItemData))) ? StringDetailsMisc.Unknown.ToString() : ((ItemData)target).m_shared.m_name)));
		}
		else
		{
			Piece val = (Piece)typeof(WearNTear).GetField("m_piece", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(target);
			text = ((!((Object)(object)val == (Object)null)) ? val.m_name : ((Object)(WearNTear)target).name.Replace("(Clone)", "").Trim());
		}
		string text2 = ((weapon != null && weapon.m_shared != null) ? weapon.m_shared.m_name : StringDetailsMisc.Unknown.ToString());
		string target2;
		if (target != null)
		{
			target2 = "target:" + text + ",weapon:" + text2;
			InternalUpdateSingle(action, target2, value, useTimeBuffer);
			target2 = "target:" + text;
			InternalUpdateSingle(action, target2, value, useTimeBuffer);
		}
		target2 = "weapon:" + text2;
		InternalUpdateSingle(action, target2, value, useTimeBuffer);
	}

	public void UpdateSingle(LogActions action, string target, float value, bool useTimeBuffer = false, bool set = false)
	{
		string target2 = "target:" + target;
		InternalUpdateSingle(action, target2, value, useTimeBuffer, set);
	}

	private void InternalUpdateSingle(LogActions action, string target, float value, bool useTimeBuffer = false, bool set = false)
	{
		InternalUpdate(LogTypes.Single, target, action, value, useTimeBuffer, set);
	}

	private void InternalUpdate(LogTypes type, string target, LogActions action, float value, bool useTimeBuffer = false, bool set = false)
	{
		string text = type.ToString().ToLower() + "|" + target + "|" + action.ToString().ToLower();
		if (useTimeBuffer)
		{
			if (!TimeBuffer.ContainsKey(text))
			{
				TimeBuffer.Add(text, 0f);
			}
			TimeBuffer[text] += value;
		}
		else
		{
			if (set && TimeBuffer.ContainsKey(text))
			{
				TimeBuffer.Remove(text);
			}
			Update(text, value, set);
		}
	}

	private void Update(string name, float value, bool set = false)
	{
		if (StatsInformation == null)
		{
			DebugLogger.LogWarning("[STATS] NO CHARACTER LOADED");
			return;
		}
		HasChanges = true;
		float oldAmount = 0f;
		StatsEntry statsEntry = StatsInformation.Entries.Find((StatsEntry x) => x.Name == name);
		if (statsEntry == null)
		{
			statsEntry = new StatsEntry
			{
				Name = name,
				Amount = value,
				FirstLogged = DateTime.Now,
				LastLogged = DateTime.Now
			};
			StatsInformation.Entries.Add(statsEntry);
		}
		else
		{
			oldAmount = statsEntry.Amount;
			if (set)
			{
				statsEntry.Amount = value;
			}
			else
			{
				statsEntry.Amount += value;
			}
			statsEntry.LastLogged = DateTime.Now;
		}
		CheckAnnounce(statsEntry, oldAmount);
		if (DebugMessagesInUnityConsole)
		{
			DebugLogger.Log($"[STATS] {statsEntry.Name}: {statsEntry.Amount}");
		}
	}

	public List<StatsEntry> GetStatsList()
	{
		if (StatsInformation == null)
		{
			DebugLogger.LogWarning("[STATS] StatsInformation not loaded!");
			return new List<StatsEntry>();
		}
		return StatsInformation.Entries;
	}

	private void CheckAnnounce(StatsEntry stat, float oldAmount)
	{
		//IL_0164: Unknown result type (might be due to invalid IL or missing references)
		//IL_016b: Expected O, but got Unknown
		//IL_0177: Unknown result type (might be due to invalid IL or missing references)
		//IL_017e: Unknown result type (might be due to invalid IL or missing references)
		if (LastAnnounce.CompareTo(DateTime.Now.AddMinutes(-AnnounceIntervalMinutes)) > 0)
		{
			return;
		}
		int[] array = new int[7] { 1, 5, 10, 25, 50, 75, 100 };
		bool flag = false;
		int num = 100;
		if (oldAmount > (float)num)
		{
			while (oldAmount > (float)(num / 10))
			{
				num *= 10;
			}
			num /= 10;
			if (stat.Amount - oldAmount >= (float)num)
			{
				flag = true;
			}
			else if (oldAmount % (float)num > stat.Amount % (float)num)
			{
				flag = true;
			}
		}
		else
		{
			int[] array2 = array;
			foreach (int num2 in array2)
			{
				if (oldAmount < (float)num2 && stat.Amount >= (float)num2)
				{
					flag = true;
					break;
				}
			}
		}
		if (flag && (Object)(object)Chat.instance != (Object)null && (!HideDeathStats || (stat.GetLogAction() != LogActions.Deaths && (stat.GetDetail(LogDetails.Target) == null || !(stat.GetDetail(LogDetails.Target).ToLower() == StringDetailsTime.LastDeath.ToString().ToLower())))))
		{
			LastAnnounce = DateTime.Now;
			string text = "<color=purple>[STATS]</color> " + stat.GetPrettyName() + ": " + stat.GetFormattedAmount();
			if (AnnounceEffect && (Object)(object)Player.m_localPlayer != (Object)null)
			{
				Transform val = (Transform)typeof(Player).GetField("m_head", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(Player.m_localPlayer);
				Player.m_localPlayer.m_skillLevelupEffects.Create(val.position, val.rotation, val, 0.2f, -1);
				MessageHud.instance.QueueUnlockMsg(UserInterface.StatsLoggerIcon, "Character Stats", stat.GetPrettyName() + ": " + stat.GetFormattedAmount());
			}
			if (AnnounceChat && (Object)(object)Chat.instance != (Object)null)
			{
				typeof(Chat).GetMethod("AddString", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null).Invoke(Chat.instance, new object[1] { text });
			}
			if (AnnounceConsole && (Object)(object)Console.instance != (Object)null)
			{
				typeof(Console).GetMethod("AddString", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(string) }, null).Invoke(Console.instance, new object[1] { text });
			}
		}
	}
}
[HarmonyPatch]
public static class Structures
{
	public class Target
	{
		public HitData Hit;

		public ItemData Weapon;

		public DateTime Time = DateTime.Now;
	}

	[HarmonyPatch(typeof(WearNTear), "RPC_Damage")]
	public static class WearNTear_RPC_Damage_Damage
	{
		private static MethodInfo miApplyDamage = AccessTools.Method(typeof(WearNTear), "ApplyDamage", (Type[])null, (Type[])null);

		private static MethodInfo miDamageTrigger = AccessTools.Method(typeof(WearNTear_RPC_Damage_Damage), "DamageTrigger", (Type[])null, (Type[])null);

		private static readonly int Inserts = 1;

		[HarmonyTranspiler]
		public static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected O, but got Unknown
			int num = 0;
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			for (int i = 0; i < list.Count; i++)
			{
				if (list[i].opcode == OpCodes.Call && (MethodInfo)list[i].operand == miApplyDamage)
				{
					list.Insert(i + 2, new CodeInstruction(OpCodes.Call, (object)miDamageTrigger));
					list.Insert(i + 2, list[i - 1]);
					list.Insert(i + 2, new CodeInstruction(OpCodes.Ldarg_2, (object)null));
					list.Insert(i + 2, new CodeInstruction(OpCodes.Ldarg_0, (object)null));
					DebugLogger.Log($"{typeof(WearNTear_RPC_Damage_Damage)} [WearNTear RPC_Damage {miDamageTrigger.Name}] applied.");
					num++;
				}
			}
			if (num == 0)
			{
				DebugLogger.LogError($"{typeof(WearNTear_RPC_Damage_Damage)} [WearNTear RPC_Damage] failed.");
			}
			else if (num != Inserts)
			{
				DebugLogger.LogWarning($"{typeof(WearNTear_RPC_Damage_Damage)} [WearNTear RPC_Damage] applied {num}/{Inserts}.");
			}
			return list.AsEnumerable();
		}

		public static void DamageTrigger(WearNTear target, HitData hit, float dmg)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			if (!hit.HaveAttacker())
			{
				return;
			}
			Character attacker = hit.GetAttacker();
			if (StatsLogger.IsLocalPlayer(attacker))
			{
				ItemData currentWeapon = ((Humanoid)(Player)attacker).GetCurrentWeapon();
				if (LastAttackedByPlayer.ContainsKey(((Object)target).GetInstanceID()))
				{
					LastAttackedByPlayer.Remove(((Object)target).GetInstanceID());
				}
				LastAttackedByPlayer.Add(((Object)target).GetInstanceID(), new Target
				{
					Hit = hit,
					Time = DateTime.Now,
					Weapon = currentWeapon
				});
				StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.StructureAttacks, (object)target, currentWeapon, 1f, useTimeBuffer: false);
				StatsLogger.Stats.UpdateTotal(Settings.LogActions.StructureAttacks, 1f);
				StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.StructureDamageDone, (object)target, currentWeapon, dmg, useTimeBuffer: false);
				StatsLogger.Stats.UpdateTotal(Settings.LogActions.StructureDamageDone, dmg);
			}
			else if (LastAttackedByPlayer.ContainsKey(((Object)target).GetInstanceID()))
			{
				LastAttackedByPlayer.Remove(((Object)target).GetInstanceID());
			}
		}
	}

	public static Dictionary<int, Target> LastAttackedByPlayer = new Dictionary<int, Target>();

	public static DateTime LastCleanup = DateTime.Now;

	public static readonly int IntervalSeconds = 10;

	public static readonly int TTLSeconds = 5;

	[HarmonyPrefix]
	[HarmonyPatch(typeof(WearNTear), "Destroy")]
	private static void DestroyPieces(ref WearNTear __instance, Piece ___m_piece)
	{
		if (LastAttackedByPlayer.ContainsKey(((Object)__instance).GetInstanceID()))
		{
			ItemData weapon = LastAttackedByPlayer[((Object)__instance).GetInstanceID()].Weapon;
			StatsLogger.Stats.UpdateCombinedSingle(Settings.LogActions.StructuresDestroyed, (object)__instance, weapon, 1f, useTimeBuffer: false);
			StatsLogger.Stats.UpdateTotal(Settings.LogActions.StructuresDestroyed, 1f);
			LastAttackedByPlayer.Remove(((Object)__instance).GetInstanceID());
		}
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(WearNTear), "Damage")]
	private static void Damage(ref WearNTear __instance, ref HitData hit, ZNetView ___m_nview)
	{
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//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)
		if (hit.HaveAttacker())
		{
			Character attacker = hit.GetAttacker();
			if (StatsLogger.IsLocalPlayer(attacker) && !StatsLogger.IsLocalOwner(___m_nview))
			{
				LocalHitCollector.Hits.Add(new LocalHitCollector.LocalHit
				{
					Hit = hit,
					Target = __instance,
					Weapon = ((Humanoid)(Player)attacker).GetCurrentWeapon(),
					Type = ((object)__instance).GetType(),
					UID = ___m_nview.GetZDO()