Decompiled source of The Vengeance System v1.0.0

RevengeDeath.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace RevengeDeath
{
	public static class GhostState
	{
		public static bool Active;

		public static Player Ghost;

		public static ZNetView Tombstone;

		public static float TimeLeft;

		private static bool s_forceVanillaDeath;

		private const string CooldownKey = "RevengeDeath_SpiritSpentUntil";

		public static bool IsGhost(Character c)
		{
			if (Active && (Object)(object)c != (Object)null)
			{
				return (Object)(object)c == (Object)(object)Ghost;
			}
			return false;
		}

		public static bool GhostInArena()
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Ghost != (Object)null)
			{
				return Utils.DistanceXZ(((Component)Ghost).transform.position, RevengeTarget.DeathPosition) <= Plugin.LeashRadius.Value;
			}
			return false;
		}

		public static bool CanFight()
		{
			if (Active && GhostInArena())
			{
				return RevengeTarget.InArena();
			}
			return false;
		}

		public static void Enter(Player player)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			Active = true;
			Ghost = player;
			TimeLeft = Plugin.GhostTimeout.Value;
			Tombstone = CreateTombStoneKeepEquipped(player);
			((Character)player).m_isDead = false;
			((Character)player).m_zanim.SetBool(Character.s_dead, false);
			((Character)player).SetHealth(Mathf.Min(Plugin.GhostHealth.Value, ((Character)player).GetMaxHealth()));
			GhostSync.Publish(player, RevengeTarget.EnemyId, RevengeTarget.DeathPosition);
			GhostVisuals.Begin(player, RevengeTarget.Enemy, RevengeTarget.Name);
			Plugin.Log.LogInfo((object)string.Format("[RevengeDeath] Ghost state entered. HP {0}, timeout {1}s, tombstone: {2}", ((Character)player).GetHealth(), TimeLeft, ((Object)(object)Tombstone != (Object)null) ? "yes" : "none"));
		}

		private static ZNetView CreateTombStoneKeepEquipped(Player player)
		{
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			ZoneSystem instance = ZoneSystem.instance;
			if (instance.GetGlobalKey((GlobalKeys)23))
			{
				return null;
			}
			if (((Humanoid)player).m_hiddenLeftItem != null || ((Humanoid)player).m_hiddenRightItem != null)
			{
				((Humanoid)player).ShowHandItems(false, false);
			}
			if (instance.GetGlobalKey((GlobalKeys)20) || instance.GetGlobalKey((GlobalKeys)21))
			{
				((Humanoid)player).m_inventory.RemoveUnequipped();
			}
			List<string> list = new List<string>();
			int num = 0;
			foreach (ItemData allItem in ((Humanoid)player).m_inventory.GetAllItems())
			{
				if (allItem.m_equipped)
				{
					list.Add(allItem.m_shared.m_name);
				}
				else if (!allItem.m_shared.m_questItem)
				{
					num++;
				}
			}
			Plugin.Log.LogInfo((object)string.Format("[RevengeDeath] Ghost keeps equipped: [{0}]; {1} item(s) go to the grave", string.Join(", ", list), num));
			if (num == 0)
			{
				return null;
			}
			GameObject obj = Object.Instantiate<GameObject>(player.m_tombstone, ((Character)player).GetCenterPoint(), ((Component)player).transform.rotation);
			obj.GetComponent<Container>().GetInventory().MoveInventoryToGrave(((Humanoid)player).m_inventory);
			PlayerProfile playerProfile = Game.instance.GetPlayerProfile();
			obj.GetComponent<TombStone>().Setup(playerProfile.GetName(), playerProfile.GetPlayerID());
			return obj.GetComponent<ZNetView>();
		}

		public static void OnGhostHitTarget()
		{
			float value = Plugin.BloodlustSeconds.Value;
			if (Active && !(value <= 0f) && CanFight())
			{
				float timeLeft = TimeLeft;
				TimeLeft = Mathf.Min(TimeLeft + value, Plugin.GhostTimeout.Value);
				if (TimeLeft - timeLeft >= 0.5f)
				{
					Plugin.Log.LogInfo((object)$"[RevengeDeath] Bloodlust: +{TimeLeft - timeLeft:0.#}s (now {TimeLeft:0}s left)");
					GhostVisuals.ShowBloodlust(Ghost, TimeLeft - timeLeft);
				}
			}
		}

		public static void Win()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			Player ghost = Ghost;
			ZNetView tombstone = Tombstone;
			Vector3 deathPosition = RevengeTarget.DeathPosition;
			Plugin.Log.LogInfo((object)"[RevengeDeath] Revenge! Target killed -> reviving player");
			Cleanup(won: true);
			MovePlayerTo(ghost, deathPosition);
			float num = ((Character)ghost).GetMaxHealth() * Plugin.ReviveHealth.Value;
			((Character)ghost).SetHealth(Mathf.Max(((Character)ghost).GetHealth(), num));
			if ((Object)(object)tombstone != (Object)null && tombstone.IsValid())
			{
				((Component)tombstone).GetComponent<Container>().TakeAll((Humanoid)(object)ghost);
			}
			StartCooldown(ghost);
			Spoils.GiveVengeful(ghost, GhostVisuals.TargetIcon);
			GhostVisuals.PlayWin(ghost);
		}

		private static void MovePlayerTo(Player player, Vector3 pos)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)player == (Object)null) && !(Utils.DistanceXZ(((Component)player).transform.position, pos) <= 3f))
			{
				((Component)player).transform.position = pos;
				((Character)player).m_body.position = pos;
				((Character)player).m_body.linearVelocity = Vector3.zero;
			}
		}

		public static double CooldownLeft(Player player)
		{
			if ((Object)(object)player == (Object)null || !player.m_customData.TryGetValue("RevengeDeath_SpiritSpentUntil", out var value) || !double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return 0.0;
			}
			double num = result - ZNet.instance.GetTimeSeconds();
			if (num <= 0.0 || num > (double)Plugin.RevengeCooldown.Value)
			{
				return 0.0;
			}
			return num;
		}

		private static void StartCooldown(Player player)
		{
			float value = Plugin.RevengeCooldown.Value;
			if (!(value <= 0f))
			{
				double num = ZNet.instance.GetTimeSeconds() + (double)value;
				player.m_customData["RevengeDeath_SpiritSpentUntil"] = num.ToString("R", CultureInfo.InvariantCulture);
				GhostVisuals.ShowSpiritSpent(player, value);
			}
		}

		public static void FallBackToVanilla(string reason, string playerMessage)
		{
			Player ghost = Ghost;
			EndAsLoss(reason);
			if (!((Object)(object)ghost == (Object)null))
			{
				((Character)ghost).Message((MessageType)1, playerMessage, 0, (Sprite)null, false);
				s_forceVanillaDeath = true;
				((Character)ghost).SetHealth(0f);
			}
		}

		public static void DieNow(string reason)
		{
			Player ghost = Ghost;
			EndAsLoss(reason);
			if (!((Object)(object)ghost == (Object)null))
			{
				s_forceVanillaDeath = true;
				((Character)ghost).SetHealth(0f);
				((Character)ghost).m_isDead = true;
				((Character)ghost).OnDeath();
			}
		}

		public static void EndAsLoss(string reason)
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			Plugin.Log.LogInfo((object)("[RevengeDeath] Revenge failed: " + reason + " -> vanilla respawn"));
			Player ghost = Ghost;
			Character enemy = RevengeTarget.Enemy;
			if ((Object)(object)ghost != (Object)null)
			{
				MoveKeptGearToGrave(ghost);
				MovePlayerTo(ghost, RevengeTarget.DeathPosition);
			}
			Cleanup();
			Grudge.Apply(enemy, ghost);
		}

		private static void MoveKeptGearToGrave(Player player)
		{
			if (ZoneSystem.instance.GetGlobalKey((GlobalKeys)19) || (Object)(object)Tombstone == (Object)null || !Tombstone.IsValid() || !Tombstone.IsOwner())
			{
				return;
			}
			((Humanoid)player).UnequipAllItems();
			Inventory inventory = ((Component)Tombstone).GetComponent<Container>().GetInventory();
			Inventory inventory2 = ((Humanoid)player).GetInventory();
			int num = 0;
			int num2 = 0;
			ItemData[] array = inventory2.GetAllItems().ToArray();
			foreach (ItemData val in array)
			{
				if (!val.m_shared.m_questItem)
				{
					if (inventory.AddItem(val))
					{
						inventory2.RemoveItem(val);
						num++;
					}
					else
					{
						num2++;
					}
				}
			}
			Plugin.Log.LogInfo((object)($"[RevengeDeath] Moved {num} kept item(s) into the grave" + ((num2 > 0) ? $", {num2} didn't fit (vanilla makes a 2nd grave for them)" : "")));
		}

		public static void Cleanup(bool won = false)
		{
			GhostVisuals.End(Ghost);
			GhostSync.Unpublish(Ghost, won);
			Active = false;
			Ghost = null;
			Tombstone = null;
			TimeLeft = 0f;
			RevengeTarget.Clear();
		}

		public static bool ConsumeForceVanilla()
		{
			bool result = s_forceVanillaDeath;
			s_forceVanillaDeath = false;
			return result;
		}

		public static void Update(float dt)
		{
			if (!Active)
			{
				return;
			}
			if ((Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)Player.m_localPlayer != (Object)(object)Ghost)
			{
				Plugin.Log.LogInfo((object)"[RevengeDeath] Ghost player gone (logout or world exit), clearing ghost state");
				Cleanup();
				return;
			}
			switch (RevengeTarget.GetStatus())
			{
			case TargetStatus.Killed:
				Win();
				return;
			case TargetStatus.Despawned:
				FallBackToVanilla("target despawned", RevengeTarget.TheNameCap + " has escaped");
				return;
			}
			RevengeTarget.ControlTick(Ghost, CanFight());
			float num = RevengeTarget.DistanceFromGrave();
			if (num > Plugin.LeashRadius.Value * 2f)
			{
				FallBackToVanilla($"target is {num:0}m from the grave and couldn't be pulled back", RevengeTarget.TheNameCap + " has escaped");
				return;
			}
			TimeLeft -= dt;
			if (TimeLeft <= 0f)
			{
				FallBackToVanilla("ghost timer ran out", "Your spirit fades");
			}
			else
			{
				GhostVisuals.Tick(Ghost, TimeLeft, GhostInArena());
			}
		}
	}
	public class GhostInfo
	{
		public Player Player;

		public int Fight;

		public ZDOID Killer;

		public Character KillerChar;

		public Vector3 Grave;

		public float Radius;

		public string ProvokedRaw = "";

		public readonly HashSet<ZDOID> Provoked = new HashSet<ZDOID>();
	}
	public static class GhostSync
	{
		private class Fight
		{
			public int Id;

			public Player Player;

			public Vector3 Grave;

			public readonly List<KeyValuePair<BaseAI, Vector3>> SentAway = new List<KeyValuePair<BaseAI, Vector3>>();

			public readonly HashSet<Character> Calm = new HashSet<Character>();

			public bool Over;

			public double CalmUntil;
		}

		private static readonly int FightKey = StringExtensionMethods.GetStableHashCode("RevengeDeath_Fight");

		private static readonly KeyValuePair<int, int> KillerKey = ZDO.GetHashZDOID("RevengeDeath_Killer");

		private static readonly int GraveKey = StringExtensionMethods.GetStableHashCode("RevengeDeath_Grave");

		private static readonly int RadiusKey = StringExtensionMethods.GetStableHashCode("RevengeDeath_Radius");

		private static readonly int ProvokedKey = StringExtensionMethods.GetStableHashCode("RevengeDeath_Provoked");

		private static readonly int GraceKey = StringExtensionMethods.GetStableHashCode("RevengeDeath_GraceUntil");

		private static readonly Dictionary<Player, GhostInfo> s_ghosts = new Dictionary<Player, GhostInfo>();

		private static readonly List<Player> s_gone = new List<Player>();

		private static readonly Dictionary<int, Fight> s_fights = new Dictionary<int, Fight>();

		private static readonly HashSet<BaseAI> s_alreadySent = new HashSet<BaseAI>();

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

		private static readonly Dictionary<Player, Outline> s_remoteOutlines = new Dictionary<Player, Outline>();

		private static readonly List<Player> s_removeOutlines = new List<Player>();

		public static bool Any
		{
			get
			{
				if (s_ghosts.Count <= 0)
				{
					return s_fights.Count > 0;
				}
				return true;
			}
		}

		public static void Publish(Player player, ZDOID killer, Vector3 grave)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			ZDO val = OwnZdo(player);
			if (val != null)
			{
				val.Set(KillerKey, killer);
				val.Set(GraveKey, grave);
				val.Set(RadiusKey, Plugin.LeashRadius.Value);
				val.Set(ProvokedKey, "");
				val.Set(GraceKey, 0L);
				val.Set(FightKey, Random.Range(1, int.MaxValue), false);
				RefreshGhosts();
			}
		}

		public static void Unpublish(Player player, bool won)
		{
			ZDO val = OwnZdo(player);
			if (val != null)
			{
				long num = (won ? ((long)((ZNet.instance.GetTimeSeconds() + (double)Plugin.WinGracePeriod.Value) * 1000.0)) : 0);
				val.Set(GraceKey, num);
				val.Set(FightKey, 0, false);
				RefreshGhosts();
			}
		}

		public static void OnLocalPlayerHit(Character c)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			ZDO val = OwnZdo(localPlayer);
			if (val != null && !((Object)(object)c == (Object)null) && !((Object)(object)c.m_nview == (Object)null) && c.m_nview.GetZDO() != null && (GhostState.Active || InGrace(localPlayer)))
			{
				ZDOID zDOID = c.GetZDOID();
				string text = val.GetString(ProvokedKey, "");
				if (!ListHas(text, zDOID))
				{
					val.Set(ProvokedKey, text + $"{((ZDOID)(ref zDOID)).UserID}:{((ZDOID)(ref zDOID)).ID};");
					Plugin.Log.LogInfo((object)("[RevengeDeath] Player attacked a bystander (" + c.m_name + "); it can fight back now"));
				}
			}
		}

		private static ZDO OwnZdo(Player player)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)((Character)player).m_nview == (Object)null || !((Character)player).m_nview.IsValid() || !((Character)player).m_nview.IsOwner())
			{
				return null;
			}
			return ((Character)player).m_nview.GetZDO();
		}

		public static bool TryGet(Character c, out GhostInfo info)
		{
			info = null;
			Player val = (Player)(object)((c is Player) ? c : null);
			if (val != null)
			{
				return s_ghosts.TryGetValue(val, out info);
			}
			return false;
		}

		public static bool IsGhost(Character c)
		{
			Player val = (Player)(object)((c is Player) ? c : null);
			if (val != null)
			{
				return s_ghosts.ContainsKey(val);
			}
			return false;
		}

		public static bool IsKiller(GhostInfo g, Character c)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)c != (Object)null && (Object)(object)c.m_nview != (Object)null && c.m_nview.GetZDO() != null)
			{
				return c.GetZDOID() == g.Killer;
			}
			return false;
		}

		public static bool CanFight(GhostInfo g)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)g.Player == (Object)null || (Object)(object)g.KillerChar == (Object)null)
			{
				return false;
			}
			if (Utils.DistanceXZ(((Component)g.Player).transform.position, g.Grave) <= g.Radius)
			{
				return Utils.DistanceXZ(((Component)g.KillerChar).transform.position, g.Grave) <= g.Radius;
			}
			return false;
		}

		public static bool IsProvoked(GhostInfo g, Character c)
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)c != (Object)null && (Object)(object)c.m_nview != (Object)null && c.m_nview.GetZDO() != null)
			{
				return g.Provoked.Contains(c.GetZDOID());
			}
			return false;
		}

		private static bool InGrace(Player player)
		{
			ZDO val = (((Object)(object)player != (Object)null && (Object)(object)((Character)player).m_nview != (Object)null) ? ((Character)player).m_nview.GetZDO() : null);
			if (val != null)
			{
				return (double)val.GetLong(GraceKey, 0L) / 1000.0 > ZNet.instance.GetTimeSeconds();
			}
			return false;
		}

		public static void RefreshGhosts()
		{
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			s_gone.Clear();
			foreach (KeyValuePair<Player, GhostInfo> s_ghost in s_ghosts)
			{
				if ((Object)(object)s_ghost.Key == (Object)null || GetFight(s_ghost.Key) != s_ghost.Value.Fight)
				{
					s_gone.Add(s_ghost.Key);
				}
			}
			foreach (Player item in s_gone)
			{
				s_ghosts.Remove(item);
			}
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				int fight = GetFight(allPlayer);
				if (fight != 0)
				{
					ZDO zDO = ((Character)allPlayer).m_nview.GetZDO();
					if (!s_ghosts.TryGetValue(allPlayer, out var value))
					{
						value = new GhostInfo
						{
							Player = allPlayer,
							Fight = fight,
							Killer = zDO.GetZDOID(KillerKey),
							Grave = zDO.GetVec3(GraveKey, ((Component)allPlayer).transform.position),
							Radius = zDO.GetFloat(RadiusKey, Plugin.LeashRadius.Value)
						};
						s_ghosts[allPlayer] = value;
					}
					GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.FindInstance(value.Killer) : null);
					value.KillerChar = (((Object)(object)val != (Object)null) ? val.GetComponent<Character>() : null);
					string text = zDO.GetString(ProvokedKey, "");
					if (text != value.ProvokedRaw)
					{
						value.ProvokedRaw = text;
						ParseProvoked(text, value.Provoked);
					}
				}
			}
		}

		private static int GetFight(Player p)
		{
			ZDO val = (((Object)(object)p != (Object)null && (Object)(object)((Character)p).m_nview != (Object)null) ? ((Character)p).m_nview.GetZDO() : null);
			if (val == null)
			{
				return 0;
			}
			return val.GetInt(FightKey, 0);
		}

		private static void ParseProvoked(string raw, HashSet<ZDOID> into)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			into.Clear();
			string[] array = raw.Split(new char[1] { ';' });
			foreach (string text in array)
			{
				int num = text.IndexOf(':');
				if (num > 0 && long.TryParse(text.Substring(0, num), out var result) && uint.TryParse(text.Substring(num + 1), out var result2))
				{
					into.Add(new ZDOID(result, result2));
				}
			}
		}

		public static bool IsCalmToward(Character creature, Player player)
		{
			foreach (Fight value in s_fights.Values)
			{
				if (!((Object)(object)value.Player != (Object)(object)player) && value.Calm.Contains(creature))
				{
					return !TryGetProvokedAfter(player, creature);
				}
			}
			return false;
		}

		private static bool TryGetProvokedAfter(Player player, Character creature)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			ZDO val = (((Object)(object)player != (Object)null && (Object)(object)((Character)player).m_nview != (Object)null) ? ((Character)player).m_nview.GetZDO() : null);
			if (val == null || (Object)(object)creature.m_nview == (Object)null || creature.m_nview.GetZDO() == null)
			{
				return false;
			}
			return ListHas(val.GetString(ProvokedKey, ""), creature.GetZDOID());
		}

		private static bool ListHas(string raw, ZDOID id)
		{
			return (";" + raw).Contains($";{((ZDOID)(ref id)).UserID}:{((ZDOID)(ref id)).ID};");
		}

		private static void StartFight(GhostInfo g)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			Fight fight = new Fight
			{
				Id = g.Fight,
				Player = g.Player,
				Grave = g.Grave
			};
			s_fights[g.Fight] = fight;
			float value = Plugin.BystanderDistance.Value;
			float num = Mathf.Max(g.Radius, value) + 10f;
			foreach (BaseAI allInstance in BaseAI.GetAllInstances())
			{
				Character character = allInstance.m_character;
				if ((Object)(object)character == (Object)null || character.IsTamed() || character.IsDead() || IsKiller(g, character) || s_alreadySent.Contains(allInstance) || Utils.DistanceXZ(((Component)character).transform.position, g.Grave) > num || (Object)(object)allInstance.m_nview == (Object)null || !allInstance.m_nview.IsOwner())
				{
					continue;
				}
				GhostIsEnemyPatch.Bypass = true;
				bool num2 = BaseAI.IsEnemy(character, (Character)(object)g.Player);
				GhostIsEnemyPatch.Bypass = false;
				if (num2)
				{
					MonsterAI val = (MonsterAI)(object)((allInstance is MonsterAI) ? allInstance : null);
					if (val != null)
					{
						val.m_targetCreature = null;
						val.m_targetStatic = null;
					}
					allInstance.SetAlerted(false);
					Vector3 val2 = ((Component)character).transform.position - g.Grave;
					val2.y = 0f;
					if (((Vector3)(ref val2)).sqrMagnitude < 0.01f)
					{
						val2 = Random.insideUnitSphere;
					}
					val2.y = 0f;
					((Vector3)(ref val2)).Normalize();
					fight.SentAway.Add(new KeyValuePair<BaseAI, Vector3>(allInstance, allInstance.m_spawnPoint));
					fight.Calm.Add(character);
					s_alreadySent.Add(allInstance);
					allInstance.m_spawnPoint = g.Grave + val2 * value;
				}
			}
			if (fight.SentAway.Count > 0)
			{
				Plugin.Log.LogInfo((object)$"[RevengeDeath] Sent {fight.SentAway.Count} creature(s) away from {g.Player.GetPlayerName()}'s grave");
			}
		}

		private static void EndFight(Fight f)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			foreach (KeyValuePair<BaseAI, Vector3> item in f.SentAway)
			{
				BaseAI key = item.Key;
				if ((Object)(object)key != (Object)null && (Object)(object)key.m_nview != (Object)null && key.m_nview.IsOwner())
				{
					key.m_spawnPoint = item.Value;
				}
				s_alreadySent.Remove(key);
			}
			if (f.SentAway.Count > 0)
			{
				Plugin.Log.LogInfo((object)"[RevengeDeath] Creatures sent away can roam home again");
			}
		}

		private static void UpdateRemoteVisuals()
		{
			//IL_00ea: 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_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			foreach (GhostInfo value2 in s_ghosts.Values)
			{
				if ((Object)(object)value2.Player == (Object)(object)localPlayer)
				{
					continue;
				}
				if (!s_remoteOutlines.TryGetValue(value2.Player, out var value))
				{
					value = new Outline();
					value.Apply((Character)(object)value2.Player, value2.Player.GetPlayerName(), Plugin.GhostOutlineColor.Value, Plugin.GhostOutlineWidth.Value, GhostVisuals.GhostGlowColor);
					s_remoteOutlines[value2.Player] = value;
					if ((Object)(object)localPlayer != (Object)null && Vector3.Distance(((Component)localPlayer).transform.position, ((Component)value2.Player).transform.position) < 100f)
					{
						((Character)localPlayer).Message((MessageType)1, value2.Player.GetPlayerName() + " seeks vengeance", 0, (Sprite)null, false);
					}
				}
				else
				{
					value.Refresh(Plugin.GhostOutlineColor.Value, Plugin.GhostOutlineWidth.Value, GhostVisuals.GhostGlowColor);
				}
			}
			s_removeOutlines.Clear();
			foreach (KeyValuePair<Player, Outline> s_remoteOutline in s_remoteOutlines)
			{
				if ((Object)(object)s_remoteOutline.Key == (Object)null || !s_ghosts.ContainsKey(s_remoteOutline.Key))
				{
					s_removeOutlines.Add(s_remoteOutline.Key);
				}
			}
			foreach (Player s_removeOutline in s_removeOutlines)
			{
				s_remoteOutlines[s_removeOutline].Remove();
				s_remoteOutlines.Remove(s_removeOutline);
			}
		}

		public static void Update()
		{
			if ((Object)(object)ZNet.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return;
			}
			RefreshGhosts();
			foreach (GhostInfo value2 in s_ghosts.Values)
			{
				if (!s_fights.ContainsKey(value2.Fight))
				{
					StartFight(value2);
				}
			}
			double timeSeconds = ZNet.instance.GetTimeSeconds();
			s_finished.Clear();
			foreach (Fight value3 in s_fights.Values)
			{
				if (!((Object)(object)value3.Player != (Object)null) || !s_ghosts.TryGetValue(value3.Player, out var value) || value.Fight != value3.Id)
				{
					if (!value3.Over)
					{
						value3.Over = true;
						ZDO val = (((Object)(object)value3.Player != (Object)null && (Object)(object)((Character)value3.Player).m_nview != (Object)null) ? ((Character)value3.Player).m_nview.GetZDO() : null);
						value3.CalmUntil = ((val != null) ? ((double)val.GetLong(GraceKey, 0L) / 1000.0) : 0.0);
					}
					if (timeSeconds >= value3.CalmUntil)
					{
						s_finished.Add(value3.Id);
					}
				}
			}
			foreach (int item in s_finished)
			{
				EndFight(s_fights[item]);
				s_fights.Remove(item);
			}
			UpdateRemoteVisuals();
		}

		public static void Reset()
		{
			foreach (Fight value in s_fights.Values)
			{
				EndFight(value);
			}
			s_fights.Clear();
			s_alreadySent.Clear();
			foreach (Outline value2 in s_remoteOutlines.Values)
			{
				value2.Remove();
			}
			s_remoteOutlines.Clear();
			s_ghosts.Clear();
		}
	}
	public static class GhostVisuals
	{
		private static readonly Outline s_targetOutline = new Outline();

		private static readonly Outline s_ghostOutline = new Outline();

		public static Sprite TargetIcon;

		private static StatusEffect s_template;

		private static StatusEffect s_status;

		private static Image s_vignette;

		private static float s_nextFarMessage;

		private static bool s_tensionWarned;

		private static float s_pulsePhase;

		private static float s_tensionColorBlend;

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

		private static StatusEffect s_spentTemplate;

		private static Sprite s_vignetteSprite;

		private static float s_vignetteReach = -1f;

		public static Color GhostGlowColor
		{
			get
			{
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				Color result = Plugin.GhostOutlineColor.Value * Plugin.GhostGlow.Value;
				result.a = 1f;
				return result;
			}
		}

		public static void Begin(Player player, Character target, string targetName)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			PlayDeathEffects(player);
			PlayEffect("vfx_ghost_spawn", ((Component)player).transform.position);
			PlayEffect("sfx_ghost_alert", ((Component)player).transform.position);
			s_targetOutline.Apply(target, targetName, Plugin.KillerOutlineColor.Value, Plugin.KillerOutlineWidth.Value, Color.black);
			s_ghostOutline.Apply((Character)(object)player, "the ghost", Plugin.GhostOutlineColor.Value, Plugin.GhostOutlineWidth.Value, GhostGlowColor);
			AddStatusEffect(player, target, targetName);
			ShowVignette();
			((Character)player).Message((MessageType)2, "Avenge your death", 0, (Sprite)null, false);
			((Character)player).Message((MessageType)1, "Slay " + RevengeTarget.TheName + " to rise again", 0, (Sprite)null, false);
			s_nextFarMessage = 0f;
			s_tensionWarned = false;
		}

		public static void End(Player player)
		{
			s_targetOutline.Remove();
			s_ghostOutline.Remove();
			if ((Object)(object)s_status != (Object)null && (Object)(object)player != (Object)null)
			{
				((Character)player).GetSEMan().RemoveStatusEffect(s_status.NameHash(), true);
			}
			s_status = null;
			if ((Object)(object)s_vignette != (Object)null)
			{
				Object.Destroy((Object)(object)((Component)s_vignette).gameObject);
			}
			s_vignette = null;
		}

		public static void Tick(Player player, float timeLeft, bool inArena)
		{
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			float value = Plugin.TensionSeconds.Value;
			float num = ((value > 0f && timeLeft < value) ? (1f - timeLeft / value) : 0f);
			if ((Object)(object)s_status != (Object)null)
			{
				s_status.m_time = Mathf.Clamp(s_status.m_ttl - timeLeft, 0f, s_status.m_ttl - 0.05f);
				s_status.m_flashIcon = !inArena || num > 0f;
			}
			if (num > 0f && !s_tensionWarned)
			{
				((Character)player).Message((MessageType)2, "Your spirit is fading", 0, (Sprite)null, false);
				s_tensionWarned = true;
			}
			s_targetOutline.Refresh(Plugin.KillerOutlineColor.Value, Plugin.KillerOutlineWidth.Value, Color.black);
			s_ghostOutline.Refresh(Plugin.GhostOutlineColor.Value, Plugin.GhostOutlineWidth.Value, GhostGlowColor);
			UpdateVignette(num);
			if (!inArena && Time.time >= s_nextFarMessage)
			{
				((Character)player).Message((MessageType)2, "Return to your grave", 0, (Sprite)null, false);
				s_nextFarMessage = Time.time + 4f;
			}
		}

		public static void ShowBloodlust(Player player, float seconds)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player != (Object)null && (Object)(object)DamageText.instance != (Object)null)
			{
				DamageText.instance.ShowText((TextType)7, ((Character)player).GetTopPoint() + Vector3.up * 0.3f, $"+{Mathf.RoundToInt(seconds)}s", true);
			}
			if ((Object)(object)s_status != (Object)null)
			{
				s_status.m_isNew = true;
			}
		}

		public static void PlayWin(Player player)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			player.m_spawnEffects.Create(((Component)player).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
			((Character)player).Message((MessageType)2, "Vengeance is yours", 0, (Sprite)null, false);
		}

		private static void AddStatusEffect(Player player, Character target, string targetName)
		{
			if ((Object)(object)s_template == (Object)null)
			{
				s_template = ScriptableObject.CreateInstance<StatusEffect>();
				((Object)s_template).name = "SE_RevengeDeath_Vengeance";
				s_template.m_name = "Vengeance";
			}
			TargetIcon = FindIcon(player, target);
			s_template.m_icon = TargetIcon;
			s_template.m_ttl = Plugin.GhostTimeout.Value;
			s_template.m_tooltip = RevengeTarget.TheNameCap + " took your life. Slay it before your spirit fades to rise again with your belongings.";
			s_status = ((Character)player).GetSEMan().AddStatusEffect(s_template, true, 0, 0f, (short)(-1));
		}

		public static void ShowSpiritSpent(Player player, float seconds)
		{
			if (!((Object)(object)player == (Object)null) && !(seconds <= 0f))
			{
				if ((Object)(object)s_spentTemplate == (Object)null)
				{
					s_spentTemplate = ScriptableObject.CreateInstance<StatusEffect>();
					((Object)s_spentTemplate).name = "SE_RevengeDeath_SpiritSpent";
					s_spentTemplate.m_name = "Spirit spent";
					s_spentTemplate.m_tooltip = "Your spirit has not yet recovered from its last return. If you fall before this fades, death will be final.";
				}
				s_spentTemplate.m_icon = WispIcon() ?? FindIcon(player, null);
				s_spentTemplate.m_ttl = seconds;
				((Character)player).GetSEMan().AddStatusEffect(s_spentTemplate, true, 0, 0f, (short)(-1));
			}
		}

		private static Sprite WispIcon()
		{
			GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab("Wisp") : null);
			ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
			if (!((Object)(object)val2 != (Object)null))
			{
				return null;
			}
			return val2.m_itemData.GetIcon();
		}

		private static Sprite FindIcon(Player player, Character target)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Invalid comparison between Unknown and I4
			CharacterDrop val = (((Object)(object)target != (Object)null) ? ((Component)target).GetComponent<CharacterDrop>() : null);
			if ((Object)(object)val != (Object)null)
			{
				foreach (Drop drop in val.m_drops)
				{
					ItemDrop val2 = (((Object)(object)drop.m_prefab != (Object)null) ? drop.m_prefab.GetComponent<ItemDrop>() : null);
					if ((Object)(object)val2 != (Object)null && (int)val2.m_itemData.m_shared.m_itemType == 13)
					{
						return val2.m_itemData.GetIcon();
					}
				}
			}
			TombStone val3 = (((Object)(object)player.m_tombstone != (Object)null) ? player.m_tombstone.GetComponent<TombStone>() : null);
			if (!((Object)(object)val3 != (Object)null) || !((Object)(object)val3.m_lootStatusEffect != (Object)null))
			{
				return null;
			}
			return val3.m_lootStatusEffect.m_icon;
		}

		private static void ShowVignette()
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			Image val = (((Object)(object)Hud.instance != (Object)null) ? Hud.instance.m_damageScreen : null);
			if (!((Object)(object)val == (Object)null))
			{
				GameObject val2 = new GameObject("RevengeGhostVignette", new Type[1] { typeof(RectTransform) });
				RectTransform val3 = (RectTransform)val2.transform;
				((Transform)val3).SetParent(((Component)val).transform.parent, false);
				val3.anchorMin = Vector2.zero;
				val3.anchorMax = Vector2.one;
				val3.offsetMin = Vector2.zero;
				val3.offsetMax = Vector2.zero;
				((Transform)val3).SetSiblingIndex(((Component)val).transform.GetSiblingIndex());
				s_vignette = val2.AddComponent<Image>();
				((Graphic)s_vignette).raycastTarget = false;
				s_tensionColorBlend = 0f;
				UpdateVignette(0f);
			}
		}

		private static void UpdateVignette(float tension)
		{
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)s_vignette == (Object)null))
			{
				s_vignette.sprite = GetVignetteSprite(Plugin.EdgeReach.Value);
				float num = Mathf.Lerp(1.5f, 8f, tension);
				s_pulsePhase += Time.deltaTime * num;
				float num2 = Mathf.Lerp(Plugin.EdgePulse.Value, Mathf.Max(Plugin.EdgePulse.Value, 0.2f), tension);
				float num3 = Plugin.EdgeStrength.Value + 0.2f * tension;
				s_tensionColorBlend = Mathf.MoveTowards(s_tensionColorBlend, (tension > 0f) ? 1f : 0f, Time.deltaTime / 1.5f);
				Color color = Color.Lerp(Plugin.EdgeColor.Value, Plugin.TensionColor.Value, s_tensionColorBlend);
				color.a = ((Plugin.EdgeStrength.Value <= 0f) ? 0f : Mathf.Clamp01(num3 + num2 * Mathf.Sin(s_pulsePhase)));
				((Graphic)s_vignette).color = color;
			}
		}

		private static Sprite GetVignetteSprite(float reach)
		{
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Expected O, but got Unknown
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)s_vignetteSprite != (Object)null && Mathf.Approximately(reach, s_vignetteReach))
			{
				return s_vignetteSprite;
			}
			if ((Object)(object)s_vignetteSprite != (Object)null)
			{
				Object.Destroy((Object)(object)s_vignetteSprite.texture);
				Object.Destroy((Object)(object)s_vignetteSprite);
			}
			s_vignetteReach = reach;
			float num = Mathf.Lerp(1.25f, 0.2f, reach);
			float num2 = Mathf.Min(num + 0.7f, 1.6f);
			Texture2D val = new Texture2D(256, 256, (TextureFormat)4, false)
			{
				wrapMode = (TextureWrapMode)1,
				filterMode = (FilterMode)1
			};
			Color32[] array = (Color32[])(object)new Color32[65536];
			for (int i = 0; i < 256; i++)
			{
				for (int j = 0; j < 256; j++)
				{
					float num3 = ((float)j + 0.5f) / 256f * 2f - 1f;
					float num4 = ((float)i + 0.5f) / 256f * 2f - 1f;
					float num5 = Mathf.Sqrt(num3 * num3 + num4 * num4);
					float num6 = Mathf.SmoothStep(0f, 1f, Mathf.InverseLerp(num, num2, num5));
					array[i * 256 + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, (byte)(num6 * 255f));
				}
			}
			val.SetPixels32(array);
			val.Apply();
			s_vignetteSprite = Sprite.Create(val, new Rect(0f, 0f, 256f, 256f), new Vector2(0.5f, 0.5f));
			return s_vignetteSprite;
		}

		private static void PlayDeathEffects(Player player)
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			List<EffectData> list = new List<EffectData>();
			EffectData[] effectPrefabs = ((Character)player).m_deathEffects.m_effectPrefabs;
			foreach (EffectData val in effectPrefabs)
			{
				if (val.m_enabled && (Object)(object)val.m_prefab != (Object)null && (Object)(object)val.m_prefab.GetComponent<Ragdoll>() == (Object)null)
				{
					list.Add(val);
				}
			}
			new EffectList
			{
				m_effectPrefabs = list.ToArray()
			}.Create(((Component)player).transform.position, ((Component)player).transform.rotation, ((Component)player).transform, 1f, -1, default(ZDOID));
		}

		private static void PlayEffect(string prefabName, Vector3 position)
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefabName) : null);
			if ((Object)(object)val == (Object)null)
			{
				if (s_missingEffects.Add(prefabName))
				{
					Plugin.Log.LogWarning((object)("[RevengeDeath] Effect '" + prefabName + "' not found, skipping it"));
				}
			}
			else
			{
				EffectList val2 = new EffectList();
				val2.m_effectPrefabs = (EffectData[])(object)new EffectData[1]
				{
					new EffectData
					{
						m_prefab = val
					}
				};
				val2.Create(position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
			}
		}
	}
	public static class Grudge
	{
		private static readonly int NameKey = StringExtensionMethods.GetStableHashCode("RevengeDeath_GrudgeName");

		private static readonly int WinsKey = StringExtensionMethods.GetStableHashCode("RevengeDeath_GrudgeWins");

		public static readonly int MarkedKey = StringExtensionMethods.GetStableHashCode("RevengeDeath_Marked");

		private static readonly string[] Epithets = new string[15]
		{
			"Gravemaker", "Bonebreaker", "Skullsplitter", "Soulreaver", "Widowmaker", "Oathbreaker", "Deathbringer", "Hearthbane", "Ravenfeeder", "Shieldbreaker",
			"Corpsemaker", "Dreadborn", "Unforgiven", "Wraithmaker", "Spiritbane"
		};

		public static string GetName(Character c)
		{
			ZDO val = (((Object)(object)c != (Object)null && (Object)(object)c.m_nview != (Object)null) ? c.m_nview.GetZDO() : null);
			if (val == null)
			{
				return "";
			}
			return val.GetString(NameKey, "");
		}

		public static bool HasGrudge(Character c)
		{
			return !string.IsNullOrEmpty(GetName(c));
		}

		public static void Apply(Character enemy, Player player)
		{
			if (!Plugin.GrudgeEnabled.Value || (Object)(object)enemy == (Object)null || enemy.IsDead() || enemy.IsBoss())
			{
				return;
			}
			ZNetView nview = enemy.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid())
			{
				return;
			}
			if (!nview.IsOwner())
			{
				nview.ClaimOwnership();
			}
			if (nview.IsOwner())
			{
				ZDO zDO = nview.GetZDO();
				int num = zDO.GetInt(WinsKey, 0) + 1;
				zDO.Set(WinsKey, num, false);
				zDO.Set(MarkedKey, true);
				string text = zDO.GetString(NameKey, "");
				if (string.IsNullOrEmpty(text))
				{
					text = enemy.GetHoverName() + " the " + Epithets[Random.Range(0, Epithets.Length)];
					zDO.Set(NameKey, text);
				}
				int num2 = Plugin.GrudgeMaxStars.Value + 1;
				bool flag = enemy.GetLevel() < num2;
				if (flag)
				{
					enemy.SetLevel(enemy.GetLevel() + 1);
					enemy.SetHealth(enemy.GetMaxHealth());
				}
				if ((Object)(object)player != (Object)null)
				{
					((Character)player).Message((MessageType)1, flag ? (text + " grows stronger") : (text + " remembers you"), 0, (Sprite)null, false);
				}
				Plugin.Log.LogInfo((object)$"[RevengeDeath] Grudge: {text} has beaten a player {num} time(s), now level {enemy.GetLevel()}");
			}
		}

		public static void OnCreatureDied(Character c)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			string name = GetName(c);
			if (!string.IsNullOrEmpty(name))
			{
				Plugin.Log.LogInfo((object)("[RevengeDeath] Grudge settled: " + name + " died"));
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer != (Object)null && Vector3.Distance(((Component)localPlayer).transform.position, ((Component)c).transform.position) < 50f)
				{
					((Character)localPlayer).Message((MessageType)1, name + " has fallen. The grudge is settled", 0, (Sprite)null, false);
				}
			}
		}
	}
	public static class Spoils
	{
		private static SE_Stats s_vengeful;

		public static void AddTrophy(CharacterDrop drop, List<KeyValuePair<GameObject, int>> list)
		{
			if (!Plugin.SpoilsTrophy.Value || (Object)(object)drop == (Object)null || (Object)(object)drop.m_character == (Object)null)
			{
				return;
			}
			ZDO val = (((Object)(object)drop.m_character.m_nview != (Object)null) ? drop.m_character.m_nview.GetZDO() : null);
			if ((val == null || !val.GetBool(Grudge.MarkedKey, false)) && !RevengeTarget.IsTarget(drop.m_character))
			{
				return;
			}
			GameObject val2 = FindTrophy(drop);
			if ((Object)(object)val2 == (Object)null)
			{
				return;
			}
			foreach (KeyValuePair<GameObject, int> item in list)
			{
				if ((Object)(object)item.Key == (Object)(object)val2)
				{
					return;
				}
			}
			list.Add(new KeyValuePair<GameObject, int>(val2, 1));
			Plugin.Log.LogInfo((object)("[RevengeDeath] Spoils: guaranteed " + ((Object)val2).name));
		}

		public static GameObject FindTrophy(CharacterDrop drop)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Invalid comparison between Unknown and I4
			if ((Object)(object)drop == (Object)null)
			{
				return null;
			}
			foreach (Drop drop2 in drop.m_drops)
			{
				ItemDrop val = (((Object)(object)drop2.m_prefab != (Object)null) ? drop2.m_prefab.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val != (Object)null && (int)val.m_itemData.m_shared.m_itemType == 13)
				{
					return drop2.m_prefab;
				}
			}
			return null;
		}

		public static void GiveVengeful(Player player, Sprite icon)
		{
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			float value = Plugin.VengefulDuration.Value;
			if (!((Object)(object)player == (Object)null) && !(value <= 0f))
			{
				if ((Object)(object)s_vengeful == (Object)null)
				{
					s_vengeful = ScriptableObject.CreateInstance<SE_Stats>();
					((Object)s_vengeful).name = "SE_RevengeDeath_Vengeful";
					((StatusEffect)s_vengeful).m_name = "Vengeful";
					((StatusEffect)s_vengeful).m_tooltip = "The blood of your killer still burns in you.";
					s_vengeful.m_modifyAttackSkill = (SkillType)999;
				}
				((StatusEffect)s_vengeful).m_icon = icon;
				((StatusEffect)s_vengeful).m_ttl = value;
				s_vengeful.m_damageModifier = 1f + Plugin.VengefulDamage.Value;
				((Character)player).GetSEMan().AddStatusEffect((StatusEffect)(object)s_vengeful, true, 0, 0f, (short)(-1));
			}
		}
	}
	public class Outline
	{
		private static readonly int EmissionId = Shader.PropertyToID("_EmissionColor");

		private Color m_color;

		private float m_thickness;

		private Color m_glow;

		private Character m_target;

		private string m_label;

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

		private readonly List<Mesh> m_meshes = new List<Mesh>();

		private GameObject m_glowTarget;

		private Material m_material;

		public void Apply(Character target, string label, Color color, float thickness, Color glow, bool log = true)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			Remove();
			m_target = target;
			m_label = label;
			m_color = color;
			m_thickness = thickness;
			m_glow = glow;
			if ((Object)(object)target == (Object)null)
			{
				return;
			}
			int num = 0;
			if (thickness > 0f)
			{
				num = TryAddHulls(target);
				if (num > 0)
				{
					if (log)
					{
						Plugin.Log.LogInfo((object)$"[RevengeDeath] Outline added to {label} ({num} mesh(es))");
					}
				}
				else if (glow == Color.black)
				{
					glow = color * 0.8f;
					if (log)
					{
						Plugin.Log.LogInfo((object)("[RevengeDeath] Outline not possible for " + label + ", using a glow instead"));
					}
				}
			}
			if (glow != Color.black)
			{
				MaterialMan.instance.SetValue<Color>(((Component)target).gameObject, EmissionId, glow, true);
				m_glowTarget = ((Component)target).gameObject;
			}
		}

		public void Refresh(Color color, float thickness, Color glow)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)m_target == (Object)null)
			{
				return;
			}
			if (thickness != m_thickness || glow != m_glow)
			{
				Apply(m_target, m_label, color, thickness, glow, log: false);
			}
			else if (color != m_color)
			{
				m_color = color;
				if ((Object)(object)m_material != (Object)null)
				{
					m_material.color = color;
				}
			}
		}

		public void Remove()
		{
			m_target = null;
			foreach (GameObject hull in m_hulls)
			{
				if ((Object)(object)hull != (Object)null)
				{
					Object.Destroy((Object)(object)hull);
				}
			}
			foreach (Mesh mesh in m_meshes)
			{
				if ((Object)(object)mesh != (Object)null)
				{
					Object.Destroy((Object)(object)mesh);
				}
			}
			m_hulls.Clear();
			m_meshes.Clear();
			if ((Object)(object)m_glowTarget != (Object)null)
			{
				MaterialMan.instance.ResetValue(m_glowTarget, EmissionId);
			}
			m_glowTarget = null;
		}

		private int TryAddHulls(Character target)
		{
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0202: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: Expected O, but got Unknown
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_0248: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_029c: Unknown result type (might be due to invalid IL or missing references)
			Material material = GetMaterial();
			if ((Object)(object)material == (Object)null)
			{
				return 0;
			}
			HashSet<Renderer> hashSet = new HashSet<Renderer>();
			LODGroup componentInChildren = ((Component)target).GetComponentInChildren<LODGroup>();
			if ((Object)(object)componentInChildren != (Object)null)
			{
				LOD[] lODs = componentInChildren.GetLODs();
				HashSet<Renderer> hashSet2 = new HashSet<Renderer>((IEnumerable<Renderer>)((lODs.Length != 0) ? ((Array)lODs[0].renderers) : ((Array)new Renderer[0])));
				for (int i = 1; i < lODs.Length; i++)
				{
					Renderer[] renderers = lODs[i].renderers;
					foreach (Renderer val in renderers)
					{
						if ((Object)(object)val != (Object)null && !hashSet2.Contains(val))
						{
							hashSet.Add(val);
						}
					}
				}
			}
			int num = 0;
			SkinnedMeshRenderer[] componentsInChildren = ((Component)target).GetComponentsInChildren<SkinnedMeshRenderer>(false);
			foreach (SkinnedMeshRenderer val2 in componentsInChildren)
			{
				if (!((Renderer)val2).enabled || hashSet.Contains((Renderer)(object)val2) || ((Object)((Component)val2).gameObject).name == "RevengeOutline")
				{
					continue;
				}
				Mesh sharedMesh = val2.sharedMesh;
				if ((Object)(object)sharedMesh == (Object)null || !sharedMesh.isReadable)
				{
					continue;
				}
				Vector3[] vertices = sharedMesh.vertices;
				Vector3[] normals = sharedMesh.normals;
				if (vertices.Length == 0 || normals.Length != vertices.Length)
				{
					continue;
				}
				Bounds bounds = sharedMesh.bounds;
				Vector3 size = ((Bounds)(ref bounds)).size;
				float magnitude = ((Vector3)(ref size)).magnitude;
				bounds = ((Renderer)val2).bounds;
				size = ((Bounds)(ref bounds)).size;
				float magnitude2 = ((Vector3)(ref size)).magnitude;
				if (!(magnitude <= 0f) && !(magnitude2 <= 0f))
				{
					float num2 = m_thickness * magnitude / magnitude2;
					for (int k = 0; k < vertices.Length; k++)
					{
						ref Vector3 reference = ref vertices[k];
						reference += normals[k] * num2;
					}
					Mesh val3 = Object.Instantiate<Mesh>(sharedMesh);
					val3.vertices = vertices;
					m_meshes.Add(val3);
					GameObject val4 = new GameObject("RevengeOutline");
					val4.transform.SetParent(((Component)val2).transform.parent, false);
					val4.transform.localPosition = ((Component)val2).transform.localPosition;
					val4.transform.localRotation = ((Component)val2).transform.localRotation;
					val4.transform.localScale = ((Component)val2).transform.localScale;
					SkinnedMeshRenderer val5 = val4.AddComponent<SkinnedMeshRenderer>();
					val5.sharedMesh = val3;
					val5.bones = val2.bones;
					val5.rootBone = val2.rootBone;
					((Renderer)val5).localBounds = ((Renderer)val2).localBounds;
					val5.updateWhenOffscreen = val2.updateWhenOffscreen;
					((Renderer)val5).shadowCastingMode = (ShadowCastingMode)0;
					((Renderer)val5).receiveShadows = false;
					Material[] array = (Material[])(object)new Material[val3.subMeshCount];
					for (int l = 0; l < array.Length; l++)
					{
						array[l] = material;
					}
					((Renderer)val5).sharedMaterials = array;
					m_hulls.Add(val4);
					num++;
				}
			}
			return num;
		}

		private Material GetMaterial()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Expected O, but got Unknown
			if ((Object)(object)m_material != (Object)null)
			{
				m_material.color = m_color;
				return m_material;
			}
			Shader val = Shader.Find("Hidden/Internal-Colored");
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			m_material = new Material(val)
			{
				color = m_color
			};
			m_material.SetInt("_Cull", 1);
			m_material.SetInt("_ZWrite", 1);
			m_material.SetInt("_ZTest", 4);
			m_material.SetInt("_SrcBlend", 1);
			m_material.SetInt("_DstBlend", 0);
			return m_material;
		}
	}
	[HarmonyPatch(typeof(Player), "OnDeath")]
	public static class PlayerOnDeathPatch
	{
		private static bool Prefix(Player __instance)
		{
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || !((Character)__instance).m_nview.IsOwner())
			{
				return true;
			}
			Plugin.Log.LogInfo((object)("[RevengeDeath] Player.OnDeath fired for " + __instance.GetPlayerName()));
			if (GhostState.ConsumeForceVanilla())
			{
				return true;
			}
			if (GhostState.Active)
			{
				GhostState.EndAsLoss("ghost was killed");
				return true;
			}
			if (RevengeTarget.Capture(__instance))
			{
				GhostState.Enter(__instance);
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Game), "Shutdown")]
	public static class GhostLogoutPatch
	{
		private static void Prefix(Game __instance)
		{
			if (!__instance.m_shuttingDown)
			{
				if (GhostState.Active)
				{
					GhostState.DieNow("logged out or quit as a ghost");
				}
				GhostSync.Reset();
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnSpawned")]
	public static class SpiritSpentOnSpawnPatch
	{
		private static void Postfix(Player __instance)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer))
			{
				double num = GhostState.CooldownLeft(__instance);
				if (num > 0.0)
				{
					GhostVisuals.ShowSpiritSpent(__instance, (float)num);
				}
			}
		}
	}
	[HarmonyPatch(typeof(BaseAI), "MoveAwayAndDespawn")]
	public static class TargetNoFleePatch
	{
		private static bool Prefix(BaseAI __instance)
		{
			if (GhostState.Active)
			{
				return !RevengeTarget.IsTarget(__instance.m_character);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Character), "OnDeath")]
	public static class TargetOnDeathPatch
	{
		private static void Prefix(Character __instance)
		{
			if (RevengeTarget.IsTarget(__instance))
			{
				RevengeTarget.Killed = true;
				Plugin.Log.LogInfo((object)("[RevengeDeath] Target " + RevengeTarget.Name + " died"));
			}
			Grudge.OnCreatureDied(__instance);
		}
	}
	[HarmonyPatch(typeof(BaseAI), "IsEnemy", new Type[]
	{
		typeof(Character),
		typeof(Character)
	})]
	public static class GhostIsEnemyPatch
	{
		public static bool Bypass;

		private static void Postfix(Character a, Character b, ref bool __result)
		{
			if (!__result || Bypass || !GhostSync.Any)
			{
				return;
			}
			GhostInfo info2;
			if (GhostSync.TryGet(a, out var info))
			{
				if (GhostSync.IsKiller(info, b) && !TargetHudAlwaysPatch.DrawingBars)
				{
					__result = GhostSync.CanFight(info);
				}
			}
			else if (GhostSync.TryGet(b, out info2))
			{
				if (GhostSync.IsKiller(info2, a))
				{
					__result = GhostSync.CanFight(info2);
				}
				else if (!GhostSync.IsProvoked(info2, a))
				{
					__result = false;
				}
			}
			else
			{
				Player val = (Player)(object)((b is Player) ? b : null);
				if (val != null && GhostSync.IsCalmToward(a, val))
				{
					__result = false;
				}
			}
		}
	}
	[HarmonyPatch(typeof(Character), "Damage")]
	public static class PlayerHitPatch
	{
		private static void Prefix(Character __instance, HitData hit)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && !((Object)(object)__instance == (Object)(object)localPlayer) && !(hit.m_attacker != ((Character)localPlayer).GetZDOID()))
			{
				if (RevengeTarget.IsTarget(__instance))
				{
					GhostState.OnGhostHitTarget();
				}
				else if (!__instance.IsPlayer())
				{
					GhostSync.OnLocalPlayerHit(__instance);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Character), "RPC_Damage")]
	public static class GhostDamagePatch
	{
		private static bool Prefix(Character __instance, HitData hit)
		{
			if (!GhostSync.Any)
			{
				return true;
			}
			Character attacker = hit.GetAttacker();
			if (GhostSync.TryGet(__instance, out var info))
			{
				if ((Object)(object)attacker == (Object)null)
				{
					return true;
				}
				if (attacker.IsPlayer())
				{
					return false;
				}
				if (GhostSync.IsKiller(info, attacker))
				{
					return GhostSync.CanFight(info);
				}
				return GhostSync.IsProvoked(info, attacker);
			}
			if (__instance.IsPlayer() && (Object)(object)attacker != (Object)null && GhostSync.IsGhost(attacker))
			{
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Character), "GetHoverName")]
	public static class TargetNamePatch
	{
		private static void Postfix(Character __instance, ref string __result)
		{
			string name = Grudge.GetName(__instance);
			if (!string.IsNullOrEmpty(name))
			{
				__result = name;
			}
			if (GhostState.Active && RevengeTarget.IsTarget(__instance))
			{
				__result = "<color=#ff4040>" + __result + "</color>";
			}
		}
	}
	[HarmonyPatch(typeof(CharacterDrop), "GenerateDropList")]
	public static class SpoilsTrophyPatch
	{
		private static void Postfix(CharacterDrop __instance, List<KeyValuePair<GameObject, int>> __result)
		{
			Spoils.AddTrophy(__instance, __result);
		}
	}
	[HarmonyPatch(typeof(EnemyHud), "TestShow")]
	public static class TargetHudRangePatch
	{
		private static void Postfix(Character c, ref bool __result)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			if (!__result && GhostState.Active && RevengeTarget.IsTarget(c) && !((Object)(object)Player.m_localPlayer == (Object)null))
			{
				float num = Plugin.LeashRadius.Value * 2f;
				Vector3 val = ((Component)c).transform.position - ((Component)Player.m_localPlayer).transform.position;
				__result = ((Vector3)(ref val)).sqrMagnitude < num * num;
			}
		}
	}
	[HarmonyPatch(typeof(EnemyHud), "UpdateHuds")]
	public static class TargetHudAlwaysPatch
	{
		public static bool DrawingBars;

		private static void Prefix(EnemyHud __instance)
		{
			DrawingBars = true;
			if (GhostState.Active && (Object)(object)RevengeTarget.Enemy != (Object)null && __instance.m_huds.TryGetValue(RevengeTarget.Enemy, out var value))
			{
				value.m_hoverTimer = 0f;
			}
		}

		private static Exception Finalizer(Exception __exception)
		{
			DrawingBars = false;
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Character), "Heal")]
	public static class NoGhostHealPatch
	{
		private static bool Prefix(Character __instance)
		{
			return !GhostState.IsGhost(__instance);
		}
	}
	[HarmonyPatch(typeof(Humanoid), "Pickup")]
	public static class NoGhostPickupPatch
	{
		private static bool Prefix(Humanoid __instance, ref bool __result)
		{
			if (!GhostState.IsGhost((Character)(object)__instance))
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "AutoPickup")]
	public static class NoGhostAutoPickupPatch
	{
		private static bool Prefix(Player __instance)
		{
			return !GhostState.IsGhost((Character)(object)__instance);
		}
	}
	[HarmonyPatch(typeof(TombStone), "Interact")]
	public static class NoGhostTombstonePatch
	{
		private static bool Prefix(Humanoid character, ref bool __result)
		{
			if (!GhostState.IsGhost((Character)(object)character))
			{
				return true;
			}
			((Character)character).Message((MessageType)2, "A spirit cannot carry anything", 0, (Sprite)null, false);
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "TeleportTo")]
	public static class NoGhostTeleportPatch
	{
		private static bool Prefix(Player __instance, ref bool __result)
		{
			if (!GhostState.IsGhost((Character)(object)__instance))
			{
				return true;
			}
			((Character)__instance).Message((MessageType)2, "A spirit cannot travel", 0, (Sprite)null, false);
			__result = false;
			return false;
		}
	}
	[BepInPlugin("com.siddiq.vengeancesystem", "The Vengeance System", "1.0.0")]
	public class Plugin : BaseUnityPlugin
	{
		public const string Guid = "com.siddiq.vengeancesystem";

		public const string Name = "The Vengeance System";

		public const string Version = "1.0.0";

		internal static ManualLogSource Log;

		private Harmony _harmony;

		internal static ConfigEntry<bool> ModEnabled;

		internal static ConfigEntry<float> LeashRadius;

		internal static ConfigEntry<float> GhostTimeout;

		internal static ConfigEntry<float> GhostHealth;

		internal static ConfigEntry<bool> AllowBosses;

		internal static ConfigEntry<float> RevengeCooldown;

		internal static ConfigEntry<float> ReviveHealth;

		internal static ConfigEntry<float> BloodlustSeconds;

		internal static ConfigEntry<float> TensionSeconds;

		internal static ConfigEntry<bool> SkipAtSea;

		internal static ConfigEntry<float> SeaDepth;

		internal static ConfigEntry<bool> GrudgeEnabled;

		internal static ConfigEntry<int> GrudgeMaxStars;

		internal static ConfigEntry<bool> SpoilsTrophy;

		internal static ConfigEntry<float> VengefulDuration;

		internal static ConfigEntry<float> VengefulDamage;

		internal static ConfigEntry<float> BystanderDistance;

		internal static ConfigEntry<float> WinGracePeriod;

		internal static ConfigEntry<Color> EdgeColor;

		internal static ConfigEntry<float> EdgeStrength;

		internal static ConfigEntry<float> EdgeReach;

		internal static ConfigEntry<float> EdgePulse;

		internal static ConfigEntry<Color> TensionColor;

		internal static ConfigEntry<Color> KillerOutlineColor;

		internal static ConfigEntry<float> KillerOutlineWidth;

		internal static ConfigEntry<Color> GhostOutlineColor;

		internal static ConfigEntry<float> GhostOutlineWidth;

		internal static ConfigEntry<float> GhostGlow;

		private void Awake()
		{
			//IL_0242: Unknown result type (might be due to invalid IL or missing references)
			//IL_024c: Expected O, but got Unknown
			//IL_02ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0460: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_04ea: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			ModEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turn the revenge death mechanic on or off.");
			LeashRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "LeashRadius", 30f, "Size (meters) of the fight area around your grave. You and your killer can only hurt each other inside it; a killer that strays out walks back (and is pulled back if it goes far).");
			GhostTimeout = ((BaseUnityPlugin)this).Config.Bind<float>("General", "GhostTimeout", 120f, "Seconds you have as a ghost to kill the target before the normal respawn.");
			GhostHealth = ((BaseUnityPlugin)this).Config.Bind<float>("General", "GhostHealth", 50f, "Health of the ghost. If it hits zero, normal respawn.");
			AllowBosses = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AllowBosses", false, "Allow revenge fights when a boss kills you.");
			RevengeCooldown = ((BaseUnityPlugin)this).Config.Bind<float>("General", "RevengeCooldown", 300f, "After winning a revenge fight, your spirit is spent for this many seconds: dying again in that time is a normal death. 0 = no cooldown.");
			ReviveHealth = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ReviveHealth", 0.5f, Ranged("Health you come back with after a win, as a fraction of max health (0.5 = half). Never less than the ghost had.", 0.05f, 1f));
			BloodlustSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "BloodlustSeconds", 5f, Ranged("Seconds added to the ghost timer each time you hit your killer (never above GhostTimeout). 0 = off.", 0f, 30f));
			TensionSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("General", "TensionSeconds", 15f, Ranged("In the last this-many seconds the screen edge pulses like a heartbeat and the icon flashes. 0 = off.", 0f, 60f));
			SkipAtSea = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "SkipAtSea", true, "Dying at sea (swimming or on a boat over deep water) is always a normal death.");
			SeaDepth = ((BaseUnityPlugin)this).Config.Bind<float>("General", "SeaDepth", 4f, "How deep (meters) the water under you must be to count as 'at sea'.");
			BystanderDistance = ((BaseUnityPlugin)this).Config.Bind<float>("Bystanders", "SendAwayDistance", 40f, "Other creatures near the grave (not your killer) wander off to about this far (meters) from the grave during the fight.");
			WinGracePeriod = ((BaseUnityPlugin)this).Config.Bind<float>("Bystanders", "WinGracePeriod", 30f, "Seconds after a win that the creatures sent away keep ignoring you, so they don't swarm you right away.");
			GrudgeEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Grudge and Spoils", "Grudge", true, "If you lose a revenge fight, your killer gains a star, heals up and earns a name.");
			GrudgeMaxStars = ((BaseUnityPlugin)this).Config.Bind<int>("Grudge and Spoils", "GrudgeMaxStars", 2, new ConfigDescription("Most stars a grudge can give a creature (the game's normal maximum is 2).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 5), Array.Empty<object>()));
			SpoilsTrophy = ((BaseUnityPlugin)this).Config.Bind<bool>("Grudge and Spoils", "GuaranteedTrophy", true, "Your killer (and any grudge creature) always drops its trophy when it dies.");
			VengefulDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Grudge and Spoils", "VengefulDuration", 60f, "Seconds of the 'Vengeful' damage boost after winning a revenge fight. 0 = off.");
			VengefulDamage = ((BaseUnityPlugin)this).Config.Bind<float>("Grudge and Spoils", "VengefulDamage", 0.2f, Ranged("Extra damage while Vengeful (0.2 = +20%).", 0f, 1f));
			EdgeColor = ((BaseUnityPlugin)this).Config.Bind<Color>("Visuals - Screen edge", "Color", new Color(0.55f, 0.8f, 1f, 1f), "Color of the frost around the screen edges while you're a ghost.");
			EdgeStrength = ((BaseUnityPlugin)this).Config.Bind<float>("Visuals - Screen edge", "Strength", 0.23f, Ranged("How strong the colored edge is. 0 = off.", 0f, 1f));
			EdgeReach = ((BaseUnityPlugin)this).Config.Bind<float>("Visuals - Screen edge", "Reach", 0.25f, Ranged("How far the edge reaches toward the middle. 0 = only the very corners, 1 = most of the screen.", 0f, 1f));
			EdgePulse = ((BaseUnityPlugin)this).Config.Bind<float>("Visuals - Screen edge", "Pulse", 0.08f, Ranged("How much the edge slowly brightens and fades. 0 = steady.", 0f, 0.3f));
			TensionColor = ((BaseUnityPlugin)this).Config.Bind<Color>("Visuals - Screen edge", "TensionColor", new Color(0.85f, 0.1f, 0.1f, 1f), "Color the edge turns in the last seconds (see TensionSeconds).");
			KillerOutlineColor = ((BaseUnityPlugin)this).Config.Bind<Color>("Visuals - Outlines", "KillerColor", new Color(1f, 0.1f, 0.1f, 1f), "Outline color of the creature that killed you.");
			KillerOutlineWidth = ((BaseUnityPlugin)this).Config.Bind<float>("Visuals - Outlines", "KillerWidth", 0.015f, Ranged("Outline width (meters, roughly) on your killer. 0 = no outline.", 0f, 0.1f));
			GhostOutlineColor = ((BaseUnityPlugin)this).Config.Bind<Color>("Visuals - Outlines", "GhostColor", new Color(0.55f, 0.85f, 1f, 1f), "Outline (and glow) color on you while you're a ghost.");
			GhostOutlineWidth = ((BaseUnityPlugin)this).Config.Bind<float>("Visuals - Outlines", "GhostWidth", 0.017f, Ranged("Outline width (meters, roughly) on you as a ghost. 0 = no outline.", 0f, 0.1f));
			GhostGlow = ((BaseUnityPlugin)this).Config.Bind<float>("Visuals - Outlines", "GhostGlow", 0.35f, Ranged("How much your body glows in the ghost color. 0 = no glow.", 0f, 1f));
			_harmony = new Harmony("com.siddiq.vengeancesystem");
			_harmony.PatchAll();
			Log.LogInfo((object)"[RevengeDeath] loaded");
		}

		private static ConfigDescription Ranged(string text, float min, float max)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Expected O, but got Unknown
			return new ConfigDescription(text, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), Array.Empty<object>());
		}

		private void Update()
		{
			GhostState.Update(Time.deltaTime);
			GhostSync.Update();
		}

		private void OnDestroy()
		{
			GhostState.Cleanup();
			GhostSync.Reset();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	public enum TargetStatus
	{
		Alive,
		Killed,
		Despawned
	}
	public static class RevengeTarget
	{
		public static Character Enemy;

		public static ZDOID EnemyId;

		public static string Name = "";

		public static string TheName = "";

		public static Vector3 DeathPosition;

		public static bool HasTarget;

		public static bool Killed;

		private static bool s_homeSet;

		private static Vector3 s_originalHome;

		private static bool s_weOwnIt;

		public static string TheNameCap
		{
			get
			{
				if (TheName.Length <= 0)
				{
					return TheName;
				}
				return char.ToUpper(TheName[0]) + TheName.Substring(1);
			}
		}

		public static bool IsTarget(Character c)
		{
			if (HasTarget && (Object)(object)c != (Object)null)
			{
				return (Object)(object)c == (Object)(object)Enemy;
			}
			return false;
		}

		public static TargetStatus GetStatus()
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			if (Killed)
			{
				return TargetStatus.Killed;
			}
			if ((Object)(object)Enemy != (Object)null)
			{
				if (!Enemy.IsDead() && !(Enemy.GetHealth() <= 0f))
				{
					return TargetStatus.Alive;
				}
				return TargetStatus.Killed;
			}
			if (ZDOMan.instance.GetZDO(EnemyId) != null)
			{
				return TargetStatus.Despawned;
			}
			if (!s_weOwnIt)
			{
				return TargetStatus.Killed;
			}
			return TargetStatus.Despawned;
		}

		public static float DistanceFromGrave()
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Enemy == (Object)null)
			{
				return -1f;
			}
			return Utils.DistanceXZ(((Component)Enemy).transform.position, DeathPosition);
		}

		public static bool InArena()
		{
			float num = DistanceFromGrave();
			if (num >= 0f)
			{
				return num <= Plugin.LeashRadius.Value;
			}
			return false;
		}

		public static bool Capture(Player player)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: 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_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_018b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d3: Unknown result type (might be due to invalid IL or missing references)
			Clear();
			DeathPosition = ((Component)player).transform.position;
			if (!Plugin.ModEnabled.Value)
			{
				return Reject("mod disabled in config");
			}
			HitData lastHit = ((Character)player).m_lastHit;
			if (lastHit == null)
			{
				return Reject("no last hit recorded");
			}
			Character attacker = lastHit.GetAttacker();
			Plugin.Log.LogInfo((object)string.Format("[RevengeDeath] Death at {0}, hit type {1}, attacker: {2}", DeathPosition, lastHit.m_hitType, ((Object)(object)attacker != (Object)null) ? (attacker.GetHoverName() + " (" + attacker.m_name + ")") : "none"));
			if ((Object)(object)attacker == (Object)null)
			{
				return Reject("no attacker (environment death or attacker gone)");
			}
			if (attacker.IsPlayer())
			{
				return Reject("killed by a player");
			}
			if (attacker.IsTamed())
			{
				return Reject("killed by a tame");
			}
			if (attacker.IsDead() || attacker.GetHealth() <= 0f)
			{
				return Reject("killer died at the same moment");
			}
			if (attacker.IsBoss() && !Plugin.AllowBosses.Value)
			{
				return Reject("killer is a boss and bosses are disabled in config");
			}
			if (Plugin.SkipAtSea.Value && IsAtSea(DeathPosition))
			{
				((Character)player).Message((MessageType)1, "The sea claims your spirit", 0, (Sprite)null, false);
				return Reject("died at sea");
			}
			double num = GhostState.CooldownLeft(player);
			if (num > 0.0)
			{
				((Character)player).Message((MessageType)1, "Your spirit is too weak to linger", 0, (Sprite)null, false);
				return Reject($"spirit spent ({num:0}s of cooldown left)");
			}
			Enemy = attacker;
			EnemyId = lastHit.m_attacker;
			Name = attacker.GetHoverName();
			TheName = (Grudge.HasGrudge(attacker) ? Name : ("the " + Name));
			HasTarget = true;
			Plugin.Log.LogInfo((object)$"[RevengeDeath] Revenge target set: {Name} (id {EnemyId})");
			return true;
		}

		public static void ControlTick(Player ghost, bool canFight)
		{
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Enemy == (Object)null)
			{
				return;
			}
			ZNetView nview = Enemy.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid())
			{
				return;
			}
			if (!nview.IsOwner())
			{
				nview.ClaimOwnership();
			}
			s_weOwnIt = nview.IsOwner();
			if (!s_weOwnIt)
			{
				return;
			}
			BaseAI baseAI = Enemy.GetBaseAI();
			if (!nview.GetZDO().GetBool(Grudge.MarkedKey, false))
			{
				nview.GetZDO().Set(Grudge.MarkedKey, true);
			}
			if (!s_homeSet && (Object)(object)baseAI != (Object)null)
			{
				s_originalHome = baseAI.m_spawnPoint;
				baseAI.m_spawnPoint = DeathPosition;
				s_homeSet = true;
				Plugin.Log.LogInfo((object)("[RevengeDeath] " + Name + "'s home point moved to the grave"));
			}
			float num = DistanceFromGrave();
			if (num > Plugin.LeashRadius.Value * 1.5f)
			{
				((Component)Enemy).transform.position = DeathPosition;
				if ((Object)(object)Enemy.m_body != (Object)null)
				{
					Enemy.m_body.position = DeathPosition;
					Enemy.m_body.linearVelocity = Vector3.zero;
				}
				Plugin.Log.LogInfo((object)$"[RevengeDeath] {Name} was {num:0}m away, teleported back to the grave");
			}
			MonsterAI val = (MonsterAI)(object)((baseAI is MonsterAI) ? baseAI : null);
			if (val == null)
			{
				return;
			}
			if (canFight)
			{
				if ((Object)(object)val.m_targetCreature != (Object)(object)ghost)
				{
					val.m_targetCreature = (Character)(object)ghost;
					val.m_targetStatic = null;
				}
				if (!((BaseAI)val).IsAlerted())
				{
					((BaseAI)val).SetAlerted(true);
				}
			}
			else if ((Object)(object)val.m_targetCreature == (Object)(object)ghost)
			{
				val.m_targetCreature = null;
			}
		}

		private static void ReleaseControl()
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			if (s_homeSet && (Object)(object)Enemy != (Object)null && (Object)(object)Enemy.m_nview != (Object)null && Enemy.m_nview.IsOwner())
			{
				BaseAI baseAI = Enemy.GetBaseAI();
				if ((Object)(object)baseAI != (Object)null)
				{
					baseAI.m_spawnPoint = s_originalHome;
				}
			}
			s_homeSet = false;
		}

		public static void Clear()
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			ReleaseControl();
			Enemy = null;
			EnemyId = ZDOID.None;
			Name = "";
			TheName = "";
			HasTarget = false;
			Killed = false;
			s_weOwnIt = false;
		}

		private static bool IsAtSea(Vector3 pos)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			float waterLevel = ZoneSystem.instance.m_waterLevel;
			if (ZoneSystem.instance.GetGroundHeight(pos) < waterLevel - Plugin.SeaDepth.Value)
			{
				return pos.y < waterLevel + 10f;
			}
			return false;
		}

		private static bool Reject(string reason)
		{
			Plugin.Log.LogInfo((object)("[RevengeDeath] No revenge: " + reason + " -> vanilla respawn"));
			return false;
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}