Decompiled source of SoulAnchor v1.0.0

SoulAnchor.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Extensions;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using Splatform;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("xNeliox")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © xNeliox 2026")]
[assembly: AssemblyDescription("Configurable self and ally revival for Valheim.")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Soul Anchor")]
[assembly: AssemblyTitle("SoulAnchor")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Seelenanker
{
	[BepInPlugin("xNeliox.valheim.soulanchor", "Soul Anchor", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Id = "xNeliox.valheim.soulanchor";

		public const string Totem = "SoulAnchor";

		public const string BrokenTotem = "BrokenSoulAnchor";

		public const string Potion = "EirsBreath";

		public const string MeadBase = "EirsBrew";

		internal static Plugin Instance;

		private Harmony harmony;

		private CustomItem totemItem;

		private CustomItem brokenTotemItem;

		private CustomItem meadItem;

		private CustomItemConversion fermenterConversion;

		private ItemConversion runtimeFermenterConversion;

		private string appliedRecipeSettings;

		private void Awake()
		{
			//IL_03f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0401: Expected O, but got Unknown
			Instance = this;
			ModSettings.Bind(((BaseUnityPlugin)this).Config);
			CustomLocalization localization = LocalizationManager.Instance.GetLocalization();
			string text = "English";
			localization.AddTranslation(ref text, new Dictionary<string, string>
			{
				{ "sa_totem", "Soul Anchor" },
				{ "sa_totem_desc", "An old wooden anchor marked with a soul sign. Breaks on a lethal blow and calls you back to life." },
				{ "sa_broken_totem", "Broken Soul Anchor" },
				{ "sa_broken_totem_desc", "A Soul Anchor split by the force of revival. It can be reforged at a black forge." },
				{ "sa_potion", "Eir's Breath" },
				{ "sa_potion_desc", "A potent draught carrying Eir's breath. Use it from the hotbar on a nearby downed ally to call them back to life." },
				{ "sa_base", "Mead mixture: Eir's Brew" },
				{ "sa_base_desc", "A rich mead mixture prepared at the mead ketill. Ferments into Eir's Breath." },
				{ "sa_downed", "Downed – {0} s left to be rescued" },
				{ "sa_giveup_progress", "Giving up: {0}%" },
				{ "sa_downed_cooldown", "Revival available again in {0} s" },
				{ "sa_cooldown", "Eir's call is not ready yet ({0} s)." },
				{ "sa_hint", "Use Eir's Breath from your hotbar (max. 3 m)" },
				{ "sa_giveup_hint", "Give up: [R] / {0}, or hold Interact for 2 s" },
				{ "sa_rescued", "Revived! Protected for {0} seconds. Revival cooldown: {1} seconds." },
				{ "sa_totem_used", "Your Soul Anchor breaks and saves you! Protected for {0} seconds; revival cooldown: {1} seconds." },
				{ "sa_invalid", "Aim at a downed ally within 3 metres." },
				{ "sa_sent", "Confirming rescue …" },
				{ "sa_success", "Ally revived!" },
				{ "sa_refund", "Rescue unavailable. Potion refunded." },
				{ "sa_pending", "The previous rescue is still awaiting the other player's reply." },
				{ "sa_grace", "Back among the living" },
				{ "sa_grace_desc", "Protected from damage after revival." },
				{ "sa_totem_grace", "Soul Anchor's Aegis" },
				{ "sa_totem_grace_desc", "The Soul Anchor shields you from damage after pulling you back to life." },
				{ "sa_revival_cooldown", "Revival Exhaustion" },
				{ "sa_revival_cooldown_desc", "You cannot be revived again until this countdown ends." },
				{ "sa_warning", "Only {0} seconds remain!" },
				{ "sa_last_death", "Last final death" }
			});
			text = "German";
			localization.AddTranslation(ref text, new Dictionary<string, string>
			{
				{ "sa_totem", "Seelenanker" },
				{ "sa_totem_desc", "Ein mit einem Seelenzeichen versehener Anker aus altem Holz. Zerbricht bei einem tödlichen Treffer und ruft dich ins Leben zurück." },
				{ "sa_broken_totem", "Gebrochener Seelenanker" },
				{ "sa_broken_totem_desc", "Ein von der Kraft der Wiederbelebung gespaltener Seelenanker. Er kann an einer Schwarzschmiede neu gebunden werden." },
				{ "sa_potion", "Eirs Atem" },
				{ "sa_potion_desc", "Ein kräftiger Trank mit dem Atem Eirs. Benutze ihn aus der Schnellleiste auf einen kampfunfähigen Verbündeten in Reichweite, um ihn ins Leben zurückzurufen." },
				{ "sa_base", "Metmischung: Eirs Sud" },
				{ "sa_base_desc", "Eine süße, kräftige Metmischung aus dem Metkocher. Im Gärer entsteht daraus Eirs Atem." },
				{ "sa_downed", "Kampfunfähig – Rettung noch {0} s möglich" },
				{ "sa_giveup_hint", "Aufgeben: [R] / {0} oder Interagieren 2 s halten" },
				{ "sa_giveup_progress", "Aufgeben: {0}%" },
				{ "sa_downed_cooldown", "Wiederbelebung wieder möglich in {0} s" },
				{ "sa_cooldown", "Eirs Ruf ist noch nicht bereit ({0} s)." },
				{ "sa_hint", "Eirs Atem aus der Schnellleiste anwenden (max. 3 m)" },
				{ "sa_rescued", "Wiederbelebt! {0} Sekunden Schutz. Erneute Wiederbelebung in {1} Sekunden möglich." },
				{ "sa_totem_used", "Dein Seelenanker zerbricht und rettet dich! {0} Sekunden Schutz; {1} Sekunden Abklingzeit." },
				{ "sa_invalid", "Sieh einen kampfunfähigen Gefährten aus höchstens 3 Metern an." },
				{ "sa_sent", "Rettung wird bestätigt …" },
				{ "sa_success", "Gefährte gerettet!" },
				{ "sa_refund", "Rettung nicht mehr möglich. Trank zurückerstattet." },
				{ "sa_pending", "Die vorherige Rettung wartet noch auf die Antwort des Mitspielers." },
				{ "sa_grace", "Zurück im Leben" },
				{ "sa_grace_desc", "Nach der Wiederbelebung vor Schaden geschützt." },
				{ "sa_totem_grace", "Schutz des Seelenankers" },
				{ "sa_totem_grace_desc", "Der Seelenanker schützt dich vor Schaden, nachdem er dich ins Leben zurückgerufen hat." },
				{ "sa_revival_cooldown", "Wiederbelebungs-Erschöpfung" },
				{ "sa_revival_cooldown_desc", "Bis dieser Countdown endet, kannst du nicht erneut wiederbelebt werden." },
				{ "sa_warning", "Nur noch {0} Sekunden!" },
				{ "sa_last_death", "Letzter endgültiger Tod" }
			});
			PrefabManager.OnVanillaPrefabsAvailable += RegisterItems;
			harmony = new Harmony("xNeliox.valheim.soulanchor");
			harmony.PatchAll();
			((MonoBehaviour)this).StartCoroutine(RevivalEffects.LoadAudio());
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Soul Anchor 1.0.0 by xNeliox loaded. Rescue window: {LifeWindow.RescueSeconds}s; revival cooldown: {LifeWindow.RevivalCooldownSeconds}s; synchronized server config enabled.");
		}

		private void Update()
		{
			ModSettings.ApplyRuntime();
			RescueNetwork.EnsureRegistered();
			DownedSignals.EnsureRegistered();
			DownedSignals.Tick();
			ApplyRecipeSettings();
		}

		private void OnDestroy()
		{
			PrefabManager.OnVanillaPrefabsAvailable -= RegisterItems;
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}

		private static Sprite Icon(string name)
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Expected O, but got Unknown
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Seelenanker." + name + ".png");
			if (stream == null)
			{
				throw new InvalidOperationException("Missing embedded icon: " + name);
			}
			byte[] array = new byte[stream.Length];
			int num;
			for (int i = 0; i < array.Length; i += num)
			{
				num = stream.Read(array, i, array.Length - i);
				if (num == 0)
				{
					throw new EndOfStreamException();
				}
			}
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false);
			if (!ImageConversion.LoadImage(val, array))
			{
				throw new InvalidOperationException("Invalid icon: " + name);
			}
			((Object)val).name = "SA_" + name;
			((Texture)val).filterMode = (FilterMode)1;
			return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
		}

		private static CustomItem Item(string id, string clone, ItemConfig config, int stack)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Expected O, but got Unknown
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			CustomItem val = new CustomItem(id, clone, config);
			SharedData shared = val.ItemPrefab.GetComponent<ItemDrop>().m_itemData.m_shared;
			shared.m_maxStackSize = stack;
			shared.m_maxQuality = 1;
			shared.m_itemType = (ItemType)1;
			shared.m_consumeStatusEffect = null;
			shared.m_food = 0f;
			shared.m_foodStamina = 0f;
			shared.m_foodEitr = 0f;
			shared.m_questItem = false;
			shared.m_teleportable = true;
			switch (id)
			{
			case "SoulAnchor":
			case "BrokenSoulAnchor":
			case "EirsBreath":
			case "EirsBrew":
				ItemModels.Apply(val.ItemPrefab, id == "SoulAnchor" || id == "BrokenSoulAnchor", id == "EirsBrew", id == "BrokenSoulAnchor");
				break;
			}
			if (!ItemManager.Instance.AddItem(val))
			{
				throw new InvalidOperationException("Item registration failed: " + id);
			}
			return val;
		}

		private void RegisterItems()
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: 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_007b: 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)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Expected O, but got Unknown
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: 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_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Expected O, but got Unknown
			//IL_01df: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_020a: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Expected O, but got Unknown
			//IL_0228: Unknown result type (might be due to invalid IL or missing references)
			//IL_022d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: Unknown result type (might be due to invalid IL or missing references)
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0269: Unknown result type (might be due to invalid IL or missing references)
			//IL_0270: Unknown result type (might be due to invalid IL or missing references)
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Expected O, but got Unknown
			//IL_02a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e2: Expected O, but got Unknown
			//IL_02dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e7: Expected O, but got Unknown
			Sprite val = Icon("totem_buff");
			try
			{
				RevivalEffects.Register(Icon("potion"), val, Icon("cooldown"));
				RevivalUi.Register(val);
			}
			catch (Exception ex)
			{
				Log("Optional revival effect registration failed: " + ex.Message);
			}
			totemItem = Item("SoulAnchor", "TrophyGreydwarfShaman", new ItemConfig
			{
				Name = "$sa_totem",
				Description = "$sa_totem_desc",
				Icon = Icon("totem"),
				Weight = 1f,
				CraftingStation = "blackforge",
				MinStationLevel = 1,
				Enabled = ModSettings.TotemEnabled.Value,
				Amount = ModSettings.TotemCraftAmount.Value,
				Requirements = ModSettings.ParseRecipe(ModSettings.TotemRecipe.Value, "TrophyGreydwarfShaman:1,TrophyWolf:1,TrophyLox:1,FineWood:10")
			}, 1);
			ItemDrop component = totemItem.ItemPrefab.GetComponent<ItemDrop>();
			component.m_itemData.m_shared.m_useDurability = true;
			component.m_itemData.m_shared.m_maxDurability = 100f;
			component.m_itemData.m_shared.m_canBeReparied = false;
			component.m_itemData.m_durability = 100f;
			brokenTotemItem = Item("BrokenSoulAnchor", "TrophyGreydwarfShaman", new ItemConfig
			{
				Name = "$sa_broken_totem",
				Description = "$sa_broken_totem_desc",
				Icon = Icon("broken_totem"),
				Weight = 1f
			}, 1);
			ItemDrop component2 = brokenTotemItem.ItemPrefab.GetComponent<ItemDrop>();
			component2.m_itemData.m_shared.m_useDurability = true;
			component2.m_itemData.m_shared.m_maxDurability = 100f;
			component2.m_itemData.m_shared.m_canBeReparied = true;
			component2.m_itemData.m_durability = 0f;
			Item("EirsBreath", "MeadHealthMedium", new ItemConfig
			{
				Name = "$sa_potion",
				Description = "$sa_potion_desc",
				Icon = Icon("potion"),
				Weight = 0.5f
			}, 10);
			meadItem = Item("EirsBrew", "MeadBaseHealthMedium", new ItemConfig
			{
				Name = "$sa_base",
				Description = "$sa_base_desc",
				Icon = Icon("base"),
				Weight = 1f,
				CraftingStation = "piece_MeadCauldron",
				MinStationLevel = 1,
				Enabled = ModSettings.PotionEnabled.Value,
				Requirements = ModSettings.ParseRecipe(ModSettings.MeadRecipe.Value, "Honey:20,Cloudberry:20,Bloodbag:10,FreezeGland:5,Thistle:10")
			}, 10);
			fermenterConversion = new CustomItemConversion((ConversionConfig)new FermenterConversionConfig
			{
				FromItem = "EirsBrew",
				ToItem = "EirsBreath",
				Station = "fermenter",
				ProducedItems = ModSettings.FermenterYield.Value
			});
			ItemManager.Instance.AddItemConversion(fermenterConversion);
			appliedRecipeSettings = null;
			PrefabManager.OnVanillaPrefabsAvailable -= RegisterItems;
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Registered 4 items, configurable recipes, and fermentation output of {ModSettings.FermenterYield.Value} potions.");
		}

		private void ApplyRecipeSettings()
		{
			CustomItem obj = totemItem;
			object obj2;
			if (obj == null)
			{
				obj2 = null;
			}
			else
			{
				CustomRecipe recipe = obj.Recipe;
				obj2 = ((recipe != null) ? recipe.Recipe : null);
			}
			if ((Object)obj2 == (Object)null)
			{
				return;
			}
			CustomItem obj3 = meadItem;
			object obj4;
			if (obj3 == null)
			{
				obj4 = null;
			}
			else
			{
				CustomRecipe recipe2 = obj3.Recipe;
				obj4 = ((recipe2 != null) ? recipe2.Recipe : null);
			}
			if ((Object)obj4 == (Object)null || fermenterConversion == null || !Object.op_Implicit((Object)(object)ObjectDB.instance))
			{
				return;
			}
			string text = string.Join("|", ModSettings.TotemEnabled.Value, ModSettings.PotionEnabled.Value, ModSettings.TotemRecipe.Value, ModSettings.TotemCraftAmount.Value, ModSettings.MeadRecipe.Value, ModSettings.FermenterYield.Value);
			if (text == appliedRecipeSettings)
			{
				return;
			}
			ApplyRecipe(totemItem.Recipe.Recipe, ModSettings.TotemEnabled.Value, ModSettings.TotemCraftAmount.Value, ModSettings.ParseRecipe(ModSettings.TotemRecipe.Value, "TrophyGreydwarfShaman:1,TrophyWolf:1,TrophyLox:1,FineWood:10"));
			ApplyRecipe(meadItem.Recipe.Recipe, ModSettings.PotionEnabled.Value, 1, ModSettings.ParseRecipe(ModSettings.MeadRecipe.Value, "Honey:20,Cloudberry:20,Bloodbag:10,FreezeGland:5,Thistle:10"));
			GameObject prefab = PrefabManager.Instance.GetPrefab("fermenter");
			Fermenter val = ((prefab != null) ? prefab.GetComponent<Fermenter>() : null);
			if ((Object)(object)val != (Object)null)
			{
				if (runtimeFermenterConversion == null)
				{
					foreach (ItemConversion item in val.m_conversion)
					{
						if (Object.op_Implicit((Object)(object)item.m_from) && ((Object)((Component)item.m_from).gameObject).name == "EirsBrew")
						{
							runtimeFermenterConversion = item;
							break;
						}
					}
				}
				if (runtimeFermenterConversion != null)
				{
					runtimeFermenterConversion.m_producedItems = ModSettings.FermenterYield.Value;
					val.m_conversion.RemoveAll((ItemConversion x) => (Object)(object)x.m_from == (Object)(object)runtimeFermenterConversion.m_from);
					if (ModSettings.PotionEnabled.Value)
					{
						val.m_conversion.Add(runtimeFermenterConversion);
					}
				}
			}
			if (runtimeFermenterConversion != null)
			{
				appliedRecipeSettings = text;
				if (Object.op_Implicit((Object)(object)Player.m_localPlayer))
				{
					AccessTools.Method(typeof(Player), "UpdateKnownRecipesList", (Type[])null, (Type[])null)?.Invoke(Player.m_localPlayer, null);
				}
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Applied synchronized Soul Anchor recipe settings.");
			}
		}

		private static void ApplyRecipe(Recipe recipe, bool enabled, int amount, RequirementConfig[] configured)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Expected O, but got Unknown
			recipe.m_enabled = enabled;
			recipe.m_amount = amount;
			List<Requirement> list = new List<Requirement>();
			foreach (RequirementConfig val in configured)
			{
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(val.Item);
				ItemDrop val2 = (Object.op_Implicit((Object)(object)itemPrefab) ? itemPrefab.GetComponent<ItemDrop>() : null);
				if (!Object.op_Implicit((Object)(object)val2))
				{
					Log("Unknown recipe item ignored: " + val.Item);
					continue;
				}
				list.Add(new Requirement
				{
					m_resItem = val2,
					m_amount = val.Amount,
					m_amountPerLevel = val.AmountPerLevel
				});
			}
			if (list.Count > 0)
			{
				recipe.m_resources = list.ToArray();
			}
		}

		internal static void GiveBrokenTotem(Player player)
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = (Object.op_Implicit((Object)(object)ObjectDB.instance) ? ObjectDB.instance.GetItemPrefab("BrokenSoulAnchor") : null);
			if (!Object.op_Implicit((Object)(object)player) || !Object.op_Implicit((Object)(object)val))
			{
				Log("Broken Soul Anchor could not be created.");
				return;
			}
			ItemData val2 = val.GetComponent<ItemDrop>().m_itemData.Clone();
			val2.m_dropPrefab = val;
			val2.m_stack = 1;
			val2.m_worldLevel = (byte)Game.m_worldLevel;
			val2.m_durability = 0f;
			if (!((Humanoid)player).GetInventory().AddItem(val2))
			{
				GameObject obj = Object.Instantiate<GameObject>(val, ((Component)player).transform.position + Vector3.up, Quaternion.identity);
				obj.GetComponent<ItemDrop>().m_itemData.m_stack = 1;
				obj.GetComponent<ItemDrop>().m_itemData.m_durability = 0f;
			}
		}

		internal static void NormalizeBrokenTotems(Player player)
		{
			if (!Object.op_Implicit((Object)(object)player))
			{
				return;
			}
			foreach (ItemData allItem in ((Humanoid)player).GetInventory().GetAllItems())
			{
				if (Object.op_Implicit((Object)(object)allItem?.m_dropPrefab) && ((Object)allItem.m_dropPrefab).name == "BrokenSoulAnchor")
				{
					allItem.m_durability = 0f;
				}
			}
		}

		internal static bool TryRepairBrokenTotem()
		{
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			CraftingStation val = (Object.op_Implicit((Object)(object)localPlayer) ? localPlayer.GetCurrentCraftingStation() : null);
			if (!Object.op_Implicit((Object)(object)localPlayer) || !IsBlackForge(val))
			{
				return false;
			}
			ItemData val2 = ((IEnumerable<ItemData>)((Humanoid)localPlayer).GetInventory().GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => Object.op_Implicit((Object)(object)i?.m_dropPrefab) && ((Object)i.m_dropPrefab).name == "BrokenSoulAnchor" && i.m_durability < i.GetMaxDurability()));
			if (val2 == null)
			{
				return false;
			}
			((Humanoid)localPlayer).GetInventory().RemoveItem(val2, 1);
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("SoulAnchor");
			if (Object.op_Implicit((Object)(object)itemPrefab) && !((Humanoid)localPlayer).GetInventory().AddItem(itemPrefab, 1))
			{
				Object.Instantiate<GameObject>(itemPrefab, ((Component)localPlayer).transform.position + Vector3.up, Quaternion.identity);
			}
			val.m_repairItemDoneEffects.Create(((Component)val).transform.position, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
			((Character)localPlayer).Message((MessageType)2, Localization.instance.Localize("$msg_repaired", new string[1] { "$sa_totem" }), 0, (Sprite)null, false);
			return true;
		}

		internal static bool IsBlackForge(CraftingStation station)
		{
			if (Object.op_Implicit((Object)(object)station))
			{
				if (!((Object)station).name.StartsWith("blackforge", StringComparison.OrdinalIgnoreCase) && !((Object)station).name.StartsWith("piece_blackforge", StringComparison.OrdinalIgnoreCase))
				{
					return station.m_name == "$piece_blackforge";
				}
				return true;
			}
			return false;
		}

		internal static void Log(string text)
		{
			((BaseUnityPlugin)Instance).Logger.LogWarning((object)text);
		}

		internal static void LogInfo(string text)
		{
			((BaseUnityPlugin)Instance).Logger.LogInfo((object)text);
		}

		internal static void Say(Player player, string key)
		{
			string text = Localization.instance.Localize(key);
			if (key == "$sa_rescued" || key == "$sa_totem_used")
			{
				text = string.Format(text, LifeWindow.GraceSeconds, LifeWindow.RevivalCooldownSeconds);
			}
			((Character)player).Message((MessageType)2, text, 0, (Sprite)null, false);
		}
	}
	internal static class ModSettings
	{
		internal static ConfigEntry<float> RescueTime;

		internal static ConfigEntry<float> Cooldown;

		internal static ConfigEntry<float> RestoredHealth;

		internal static ConfigEntry<float> ProtectionTime;

		internal static ConfigEntry<float> RevivalDuration;

		internal static ConfigEntry<bool> TotemEnabled;

		internal static ConfigEntry<bool> PotionEnabled;

		internal static ConfigEntry<bool> SkillLossOnFinalDeath;

		internal static ConfigEntry<float> EffectVolume;

		internal static ConfigEntry<float> EffectStrength;

		internal static ConfigEntry<string> TotemRecipe;

		internal static ConfigEntry<int> TotemCraftAmount;

		internal static ConfigEntry<string> MeadRecipe;

		internal static ConfigEntry<int> FermenterYield;

		internal static void Bind(ConfigFile config)
		{
			AcceptableValueBase val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 600f);
			RescueTime = ConfigFileExtensions.BindConfig<float>(config, "Gameplay", "Rescue time (seconds)", 60f, "How long a downed player can be rescued.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 600f);
			Cooldown = ConfigFileExtensions.BindConfig<float>(config, "Gameplay", "Revival cooldown (seconds)", 30f, "Minimum time before the same player can be revived again.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f);
			RestoredHealth = ConfigFileExtensions.BindConfig<float>(config, "Gameplay", "Restored health (percent)", 40f, "Percentage of maximum health restored by either revival method.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f);
			ProtectionTime = ConfigFileExtensions.BindConfig<float>(config, "Gameplay", "Protection duration (seconds)", 5f, "Damage immunity and revival-effect duration after revival.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f);
			RevivalDuration = ConfigFileExtensions.BindConfig<float>(config, "Gameplay", "Ally revival duration (seconds)", 1.5f, "How long Eir's Breath must be applied before the rescue request is sent.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			SkillLossOnFinalDeath = ConfigFileExtensions.BindConfig<bool>(config, "Gameplay", "Skill loss on final death", true, "If disabled, final death still creates a tombstone but does not lower skills.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			TotemEnabled = ConfigFileExtensions.BindConfig<bool>(config, "Items", "Enable Soul Anchor", true, "Enables automatic self-revival and its crafting recipe.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PotionEnabled = ConfigFileExtensions.BindConfig<bool>(config, "Items", "Enable Eirs Breath", true, "Enables ally revival and the mead-mixture recipe.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			TotemRecipe = ConfigFileExtensions.BindConfig<string>(config, "Recipes", "Soul Anchor ingredients", "TrophyGreydwarfShaman:1,TrophyWolf:1,TrophyLox:1,FineWood:10", "Comma-separated prefab names and amounts, for example FineWood:10.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100);
			TotemCraftAmount = ConfigFileExtensions.BindConfig<int>(config, "Recipes", "Soul Anchor crafted amount", 1, "Number of Soul Anchors created per craft.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			MeadRecipe = ConfigFileExtensions.BindConfig<string>(config, "Recipes", "Eirs Brew ingredients", "Honey:20,Cloudberry:20,Bloodbag:10,FreezeGland:5,Thistle:10", "Comma-separated prefab names and amounts used at the mead ketill.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			val = (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100);
			FermenterYield = ConfigFileExtensions.BindConfig<int>(config, "Recipes", "Eirs Breath fermenter yield", 6, "Number of Eir's Breath potions produced from one mixture.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f);
			EffectVolume = ConfigFileExtensions.BindConfig<float>(config, "Effects", "Revival effect volume", 1f, "Volume multiplier for revival sounds. 0 is silent.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			val = (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 3f);
			EffectStrength = ConfigFileExtensions.BindConfig<float>(config, "Effects", "Revival effect strength", 1f, "Multiplier for particles and revival light. 0 disables the visual burst and sparks.", true, (int?)null, val, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ApplyRuntime();
		}

		internal static void ApplyRuntime()
		{
			LifeWindow.RescueSeconds = RescueTime.Value;
			LifeWindow.RevivalCooldownSeconds = Cooldown.Value;
			LifeWindow.RestoredHealthFraction = RestoredHealth.Value / 100f;
			LifeWindow.GraceSeconds = ProtectionTime.Value;
			LifeWindow.TotemEnabled = TotemEnabled.Value;
			LifeWindow.PotionEnabled = PotionEnabled.Value;
			LifeWindow.SkillLossOnFinalDeath = SkillLossOnFinalDeath.Value;
		}

		internal static RequirementConfig[] ParseRecipe(string text, string fallback)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Expected O, but got Unknown
			List<RequirementConfig> list = new List<RequirementConfig>();
			string[] array = (string.IsNullOrWhiteSpace(text) ? fallback : text).Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string[] array2 = array[i].Trim().Split(':');
				if (array2.Length == 2 && array2[0].Length > 0 && int.TryParse(array2[1], out var result) && result > 0)
				{
					list.Add(new RequirementConfig(array2[0].Trim(), result, 0, true));
				}
			}
			if (list.Count == 0 && text != fallback)
			{
				return ParseRecipe(fallback, fallback);
			}
			return list.ToArray();
		}
	}
	public static class LifeWindow
	{
		public const float RescueDistance = 3f;

		public static double RescueSeconds { get; internal set; } = 60.0;

		public static double GraceSeconds { get; internal set; } = 5.0;

		public static double RevivalCooldownSeconds { get; internal set; } = 30.0;

		public static float RestoredHealthFraction { get; internal set; } = 0.4f;

		public static bool TotemEnabled { get; internal set; } = true;

		public static bool PotionEnabled { get; internal set; } = true;

		public static bool SkillLossOnFinalDeath { get; internal set; } = true;

		public static bool CanRescue(double deadline, double now, float distance, bool rescuerAlive)
		{
			return deadline > now && distance <= 3f && rescuerAlive;
		}

		public static bool Expired(double deadline, double now)
		{
			if (deadline > 0.0)
			{
				return now >= deadline;
			}
			return false;
		}

		public static int Remaining(double deadline, double now)
		{
			return Math.Max(0, (int)Math.Ceiling(deadline - now));
		}
	}
	public sealed class RevivalState : MonoBehaviour
	{
		internal const string DeadlineKey = "SA_deadline_v1";

		internal const string ZdoDeadline = "SA_deadline";

		internal const string EpochKey = "SA_epoch_v1";

		internal const string CooldownKey = "SA_cooldown_v1";

		internal const string ZdoCooldown = "SA_cooldown";

		internal Player Player;

		internal ZNetView View;

		internal double Deadline;

		internal double GraceUntil;

		internal double CooldownUntil;

		internal bool Finalizing;

		internal double GiveUpHoldStarted;

		private bool restored;

		private int lastSecond = -1;

		private int lastGiveUpProgress = -1;

		private bool cooldownStatusApplied;

		private double nextSignalBroadcast;

		internal const double GiveUpHoldSeconds = 2.0;

		internal static double Now => (double)DateTime.UtcNow.Ticks / 10000000.0;

		internal bool Owned
		{
			get
			{
				if (Object.op_Implicit((Object)(object)View) && View.IsValid())
				{
					return View.IsOwner();
				}
				return false;
			}
		}

		internal bool Downed
		{
			get
			{
				if (!Finalizing)
				{
					if (!(Deadline > 0.0))
					{
						if ((Object)(object)Player != (Object)null)
						{
							return Player.m_customData.ContainsKey("SA_deadline_v1");
						}
						return false;
					}
					return true;
				}
				return false;
			}
		}

		internal bool Protected
		{
			get
			{
				if (!Downed)
				{
					return Now < GraceUntil;
				}
				return true;
			}
		}

		internal bool RevivalReady => Now >= CooldownUntil;

		internal static RevivalState Of(Character p)
		{
			if (!Object.op_Implicit((Object)(object)p))
			{
				return null;
			}
			return ((Component)p).GetComponent<RevivalState>();
		}

		internal static bool IsDown(Character p)
		{
			RevivalState revivalState = Of(p);
			if (revivalState != null && revivalState.Downed)
			{
				return true;
			}
			Player val = (Player)(object)((p is Player) ? p : null);
			if (Object.op_Implicit((Object)(object)val))
			{
				return RemoteDeadline(val) > Now;
			}
			return false;
		}

		internal void RestoreAfterLoad()
		{
			restored = false;
			Deadline = 0.0;
			GraceUntil = 0.0;
			CooldownUntil = 0.0;
			Finalizing = false;
			GiveUpHoldStarted = 0.0;
			lastSecond = -1;
			lastGiveUpProgress = -1;
			cooldownStatusApplied = false;
			nextSignalBroadcast = 0.0;
			Restore();
		}

		private void Awake()
		{
			Player = ((Component)this).GetComponent<Player>();
			View = ((Component)this).GetComponent<ZNetView>();
		}

		private void Restore()
		{
			if (restored || !Owned || (Object)(object)Player != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			restored = true;
			if (Player.m_customData.TryGetValue("SA_cooldown_v1", out var value) && double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && !double.IsNaN(result) && !double.IsInfinity(result) && result > Now && result <= Now + LifeWindow.RevivalCooldownSeconds)
			{
				CooldownUntil = result;
			}
			else
			{
				Player.m_customData.Remove("SA_cooldown_v1");
			}
			if (CooldownUntil > Now)
			{
				cooldownStatusApplied = RevivalEffects.PlayCooldown(Player, CooldownUntil - Now);
			}
			if (Player.m_customData.TryGetValue("SA_deadline_v1", out var value2))
			{
				if (!double.TryParse(value2, NumberStyles.Float, CultureInfo.InvariantCulture, out Deadline) || double.IsNaN(Deadline) || double.IsInfinity(Deadline) || Deadline <= 0.0 || Deadline > Now + LifeWindow.RescueSeconds)
				{
					Deadline = Now - 1.0;
				}
				Publish();
				((Character)Player).SetHealth(1f);
				StopActions();
				Pose();
			}
		}

		private void Update()
		{
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			Restore();
			((Character)Player).m_aiSkipTarget = IsDown((Character)(object)Player);
			if (!Owned || (Object)(object)Player != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			RescueNetwork.Retry(Player);
			if (CooldownUntil > Now && !cooldownStatusApplied)
			{
				cooldownStatusApplied = RevivalEffects.PlayCooldown(Player, CooldownUntil - Now);
			}
			if (GraceUntil > 0.0 && Now >= GraceUntil)
			{
				GraceUntil = 0.0;
				RevivalEffects.Stop(Player);
				Plugin.LogInfo("Revival protection ended and its effects were removed.");
			}
			if (CooldownUntil > 0.0 && Now >= CooldownUntil)
			{
				CooldownUntil = 0.0;
				cooldownStatusApplied = false;
				Player.m_customData.Remove("SA_cooldown_v1");
				RevivalEffects.StopCooldown(Player);
				Publish();
			}
			if (!Downed || Finalizing)
			{
				return;
			}
			if (Now >= nextSignalBroadcast)
			{
				nextSignalBroadcast = Now + 5.0;
				DownedSignals.Broadcast(Player, active: true, Math.Max(0.0, Deadline - Now));
			}
			if (ZInput.GetKeyDown((KeyCode)114, true) || ZInput.GetButtonDown("JoyButtonY"))
			{
				FinalDeath();
				return;
			}
			if (ZInput.GetButton("Use") || ZInput.GetButton("JoyUse"))
			{
				if (GiveUpHoldStarted <= 0.0)
				{
					GiveUpHoldStarted = Now;
				}
				if (Now - GiveUpHoldStarted >= 2.0)
				{
					FinalDeath();
					return;
				}
			}
			else if (GiveUpHoldStarted > 0.0)
			{
				GiveUpHoldStarted = 0.0;
				lastGiveUpProgress = -1;
			}
			if (LifeWindow.Expired(Deadline, Now))
			{
				FinalDeath();
			}
			else
			{
				if (LifeWindow.TotemEnabled && RevivalReady && TryConsumeTotem())
				{
					return;
				}
				((Character)Player).SetMoveDir(Vector3.zero);
				int num = LifeWindow.Remaining(Deadline, Now);
				int num2 = ((GiveUpHoldStarted > 0.0) ? Math.Min(99, (int)((Now - GiveUpHoldStarted) / 2.0 * 100.0)) : (-1));
				if (num != lastSecond || num2 != lastGiveUpProgress)
				{
					lastSecond = num;
					lastGiveUpProgress = num2;
					if (!((Character)Player).InEmote())
					{
						Pose();
					}
					string text = string.Format(Localization.instance.Localize("$sa_downed"), num);
					text = text + "\n" + RevivalUi.GiveUpHint();
					if (num2 >= 0)
					{
						text = text + "\n" + string.Format(Localization.instance.Localize("$sa_giveup_progress"), num2);
					}
					int num3 = Math.Max(0, (int)Math.Ceiling(CooldownUntil - Now));
					if (num3 > 0)
					{
						text = text + "\n" + string.Format(Localization.instance.Localize("$sa_downed_cooldown"), num3);
					}
					((Character)Player).Message((MessageType)2, text, 0, (Sprite)null, false);
					if (num == 10 || num == 5 || num == 3)
					{
						RevivalUi.Warning(Player, num);
					}
				}
			}
		}

		internal bool InterceptDeath()
		{
			if (!Owned || Finalizing)
			{
				return false;
			}
			Restore();
			if (Downed)
			{
				AccessTools.Field(typeof(Character), "m_isDead").SetValue(Player, false);
				return true;
			}
			if (Now < GraceUntil)
			{
				((Character)Player).SetHealth(Mathf.Max(1f, ((Character)Player).GetHealth()));
				return true;
			}
			if (LifeWindow.TotemEnabled && RevivalReady && TryConsumeTotem())
			{
				return true;
			}
			Deadline = Now + LifeWindow.RescueSeconds;
			Player.m_customData["SA_epoch_v1"] = Guid.NewGuid().ToString("N");
			Player.m_customData["SA_deadline_v1"] = Deadline.ToString("R", CultureInfo.InvariantCulture);
			Publish();
			((Character)Player).SetHealth(1f);
			lastSecond = -1;
			lastGiveUpProgress = -1;
			GiveUpHoldStarted = 0.0;
			StopActions();
			((Character)Player).m_aiSkipTarget = true;
			Pose();
			DownedSignals.Broadcast(Player, active: true, LifeWindow.RescueSeconds);
			nextSignalBroadcast = Now + 5.0;
			return true;
		}

		private bool TryConsumeTotem()
		{
			ItemData val = ((IEnumerable<ItemData>)((Humanoid)Player).GetInventory().GetAllItems()).FirstOrDefault((Func<ItemData, bool>)((ItemData i) => Object.op_Implicit((Object)(object)i.m_dropPrefab) && ((Object)i.m_dropPrefab).name == "SoulAnchor"));
			if (val == null || !((Humanoid)Player).GetInventory().RemoveItem(val, 1))
			{
				return false;
			}
			Plugin.GiveBrokenTotem(Player);
			Revive(fromTotem: true);
			Plugin.Say(Player, "$sa_totem_used");
			return true;
		}

		private void StopActions()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			((Character)Player).SetMoveDir(Vector3.zero);
			AccessTools.Field(typeof(Player), "m_autoRun").SetValue(Player, false);
			AccessTools.Field(typeof(Character), "m_isDead").SetValue(Player, false);
			((Character)Player).m_aiSkipTarget = true;
			FieldInfo fieldInfo = AccessTools.Field(typeof(Humanoid), "m_currentAttack");
			object? value = fieldInfo.GetValue(Player);
			object? obj = ((value is Attack) ? value : null);
			if (obj != null)
			{
				((Attack)obj).Stop();
			}
			fieldInfo.SetValue(Player, null);
			string[] array = new string[6] { "m_attack", "m_attackHold", "m_secondaryAttack", "m_secondaryAttackHold", "m_blocking", "m_run" };
			foreach (string text in array)
			{
				AccessTools.Field(typeof(Character), text).SetValue(Player, false);
			}
			AccessTools.Field(typeof(Humanoid), "m_attackDrawTime").SetValue(Player, 0f);
			AccessTools.Field(typeof(Player), "m_queuedDodgeTimer").SetValue(Player, 0f);
			Player.StopDoodadControl();
			((Character)Player).AttachStop();
			if (Object.op_Implicit((Object)(object)InventoryGui.instance))
			{
				InventoryGui.instance.Hide();
			}
		}

		private void Pose()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			((Character)Player).SetMoveDir(Vector3.zero);
			Player.StartEmote("sit", false);
		}

		private void Publish()
		{
			if (Owned)
			{
				double num = ((Deadline > 0.0) ? (ZNet.instance.GetTimeSeconds() + Math.Max(0.0, Deadline - Now)) : 0.0);
				View.GetZDO().Set("SA_deadline", (long)(num * 10000000.0));
				double num2 = ((CooldownUntil > 0.0) ? (ZNet.instance.GetTimeSeconds() + Math.Max(0.0, CooldownUntil - Now)) : 0.0);
				View.GetZDO().Set("SA_cooldown", (long)(num2 * 10000000.0));
				if (Player.m_customData.TryGetValue("SA_epoch_v1", out var value))
				{
					View.GetZDO().Set("SA_epoch_v1", value);
				}
			}
		}

		internal void Revive(bool fromTotem = false)
		{
			GiveUpHoldStarted = 0.0;
			lastGiveUpProgress = -1;
			Deadline = 0.0;
			Player.m_customData.Remove("SA_deadline_v1");
			Publish();
			GraceUntil = Now + LifeWindow.GraceSeconds;
			CooldownUntil = Now + LifeWindow.RevivalCooldownSeconds;
			Player.m_customData["SA_cooldown_v1"] = CooldownUntil.ToString("R", CultureInfo.InvariantCulture);
			Publish();
			AccessTools.Field(typeof(Character), "m_isDead").SetValue(Player, false);
			AccessTools.Method(typeof(Player), "StopEmote", (Type[])null, (Type[])null).Invoke(Player, null);
			((Character)Player).m_aiSkipTarget = false;
			((Character)Player).SetHealth(Mathf.Max(1f, ((Character)Player).GetMaxHealth() * LifeWindow.RestoredHealthFraction));
			RevivalEffects.Play(Player, fromTotem);
			cooldownStatusApplied = RevivalEffects.PlayCooldown(Player, LifeWindow.RevivalCooldownSeconds);
			DownedSignals.Broadcast(Player, active: false, 0.0);
			Plugin.LogInfo($"Revival protection started for {LifeWindow.GraceSeconds:0.##} seconds; health restored to {((Character)Player).GetHealth():0.##}.");
			Plugin.Say(Player, "$sa_rescued");
		}

		private void FinalDeath()
		{
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Expected O, but got Unknown
			Finalizing = true;
			GiveUpHoldStarted = 0.0;
			lastGiveUpProgress = -1;
			Deadline = 0.0;
			Player.m_customData.Remove("SA_deadline_v1");
			Publish();
			AccessTools.Method(typeof(Player), "StopEmote", (Type[])null, (Type[])null).Invoke(Player, null);
			((Character)Player).m_aiSkipTarget = false;
			DownedSignals.Broadcast(Player, active: false, 0.0);
			RevivalUi.MarkLastDeath(Player);
			((Character)Player).SetHealth(0f);
			AccessTools.Field(typeof(Character), "m_isDead").SetValue(Player, true);
			FieldInfo fieldInfo = AccessTools.Field(typeof(Character), "m_lastHit");
			if (fieldInfo.GetValue(Player) == null)
			{
				fieldInfo.SetValue(Player, (object?)new HitData());
			}
			OptionalSkillLoss.Suppress = !LifeWindow.SkillLossOnFinalDeath;
			try
			{
				((Character)Player).OnDeath();
			}
			finally
			{
				OptionalSkillLoss.Suppress = false;
			}
		}

		internal static double RemoteDeadline(Player p)
		{
			ZNetView val = (Object.op_Implicit((Object)(object)p) ? ((Component)p).GetComponent<ZNetView>() : null);
			if (!Object.op_Implicit((Object)(object)val) || !val.IsValid() || !Object.op_Implicit((Object)(object)ZNet.instance))
			{
				return 0.0;
			}
			long num = val.GetZDO().GetLong("SA_deadline", 0L);
			if (num <= 0)
			{
				return 0.0;
			}
			return Now + (double)num / 10000000.0 - ZNet.instance.GetTimeSeconds();
		}

		internal static double RemoteCooldown(Player p)
		{
			ZNetView val = (Object.op_Implicit((Object)(object)p) ? ((Component)p).GetComponent<ZNetView>() : null);
			if (!Object.op_Implicit((Object)(object)val) || !val.IsValid() || !Object.op_Implicit((Object)(object)ZNet.instance))
			{
				return 0.0;
			}
			long num = val.GetZDO().GetLong("SA_cooldown", 0L);
			if (num <= 0)
			{
				return 0.0;
			}
			return Now + (double)num / 10000000.0 - ZNet.instance.GetTimeSeconds();
		}
	}
	internal static class RescueNetwork
	{
		private const string Rpc = "SA_Rescue_v1";

		private const string PendingKey = "SA_pending_v1";

		private const string ReceiptPrefix = "SA_receipt_v1_";

		private static ZRoutedRpc registered;

		private static double nextRetry;

		internal static void EnsureRegistered()
		{
			if (ZRoutedRpc.instance != null && registered != ZRoutedRpc.instance)
			{
				registered = ZRoutedRpc.instance;
				registered.Register<ZPackage>("SA_Rescue_v1", (Action<long, ZPackage>)Receive);
			}
		}

		internal static void Begin(Player user, Player target, ItemData item)
		{
			if (!LifeWindow.PotionEnabled)
			{
				return;
			}
			EnsureRegistered();
			if (registered == null)
			{
				return;
			}
			if (user.m_customData.ContainsKey("SA_pending_v1"))
			{
				Plugin.Say(user, "$sa_pending");
				return;
			}
			string text = ((Component)target).GetComponent<ZNetView>().GetZDO().GetString("SA_epoch_v1", "");
			if (text.Length != 32)
			{
				Plugin.Say(user, "$sa_invalid");
				return;
			}
			string text2 = Guid.NewGuid().ToString("N");
			if (((Humanoid)user).GetInventory().RemoveItem(item, 1))
			{
				user.m_customData["SA_pending_v1"] = target.GetPlayerID().ToString(CultureInfo.InvariantCulture) + "|" + ((Character)target).GetOwner().ToString(CultureInfo.InvariantCulture) + "|" + text2 + "|" + text;
				Plugin.Say(user, "$sa_sent");
				nextRetry = 0.0;
				Retry(user);
			}
		}

		internal static void Retry(Player user)
		{
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Expected O, but got Unknown
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: 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)
			if (!user.m_customData.TryGetValue("SA_pending_v1", out var value) || registered == null)
			{
				return;
			}
			if (value == "refund")
			{
				if (((Character)user).IsDead())
				{
					return;
				}
				GameObject val = (Object.op_Implicit((Object)(object)ObjectDB.instance) ? ObjectDB.instance.GetItemPrefab("EirsBreath") : null);
				if (Object.op_Implicit((Object)(object)val))
				{
					if (!((Humanoid)user).GetInventory().AddItem(val, 1))
					{
						Object.Instantiate<GameObject>(val, ((Component)user).transform.position + Vector3.up, Quaternion.identity).GetComponent<ItemDrop>().m_itemData.m_stack = 1;
					}
					user.m_customData.Remove("SA_pending_v1");
					Plugin.Say(user, "$sa_refund");
				}
			}
			else
			{
				if (RevivalState.Now < nextRetry)
				{
					return;
				}
				nextRetry = RevivalState.Now + 2.0;
				string[] array = value.Split('|');
				if (array.Length == 4 && long.TryParse(array[0], out var result))
				{
					Player player = Player.GetPlayer(result);
					if (Object.op_Implicit((Object)(object)player))
					{
						long owner = ((Character)player).GetOwner();
						array[1] = owner.ToString(CultureInfo.InvariantCulture);
						user.m_customData["SA_pending_v1"] = string.Join("|", array);
						ZPackage val2 = new ZPackage();
						val2.Write(0);
						val2.Write(result);
						val2.Write(user.GetPlayerID());
						val2.Write(array[2]);
						val2.Write(array[3]);
						registered.InvokeRoutedRPC(owner, "SA_Rescue_v1", new object[1] { val2 });
					}
				}
			}
		}

		private static void Reply(long peer, long targetId, string token, bool accepted)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			val.Write(1);
			val.Write(targetId);
			val.Write(token);
			val.Write(accepted);
			registered.InvokeRoutedRPC(peer, "SA_Rescue_v1", new object[1] { val });
		}

		private static void Receive(long sender, ZPackage pkg)
		{
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)localPlayer))
			{
				return;
			}
			try
			{
				switch (pkg.ReadInt())
				{
				case 0:
				{
					long num2 = pkg.ReadLong();
					long num3 = pkg.ReadLong();
					string text2 = pkg.ReadString();
					string text3 = pkg.ReadString();
					if (num2 != localPlayer.GetPlayerID() || text2.Length != 32 || text3.Length != 32 || !Guid.TryParseExact(text2, "N", out var result) || !Guid.TryParseExact(text3, "N", out result))
					{
						break;
					}
					Player player = Player.GetPlayer(num3);
					if (!Object.op_Implicit((Object)(object)player) || (Object)(object)player == (Object)(object)localPlayer || ((Character)player).GetOwner() != sender)
					{
						break;
					}
					string key = "SA_receipt_v1_" + num3.ToString(CultureInfo.InvariantCulture) + "_" + text2;
					if (localPlayer.m_customData.TryGetValue(key, out var value2))
					{
						Reply(sender, num2, text2, value2 == "1");
						break;
					}
					RevivalState revivalState = RevivalState.Of((Character)(object)localPlayer);
					string value3;
					bool flag2 = LifeWindow.PotionEnabled && (Object)(object)revivalState != (Object)null && revivalState.Owned && revivalState.Downed && revivalState.RevivalReady && localPlayer.m_customData.TryGetValue("SA_epoch_v1", out value3) && value3 == text3 && LifeWindow.CanRescue(revivalState.Deadline, RevivalState.Now, Vector3.Distance(((Component)localPlayer).transform.position, ((Component)player).transform.position), !((Character)player).IsDead() && RevivalState.RemoteDeadline(player) <= 0.0) && HasLineOfSight(player, localPlayer);
					localPlayer.m_customData[key] = (flag2 ? "1" : "0");
					if (flag2)
					{
						revivalState.Revive();
					}
					Reply(sender, num2, text2, flag2);
					break;
				}
				case 1:
				{
					long num = pkg.ReadLong();
					string text = pkg.ReadString();
					bool flag = pkg.ReadBool();
					if (!localPlayer.m_customData.TryGetValue("SA_pending_v1", out var value))
					{
						break;
					}
					string[] array = value.Split('|');
					if (array.Length == 4 && !(array[0] != num.ToString(CultureInfo.InvariantCulture)) && !(array[1] != sender.ToString(CultureInfo.InvariantCulture)) && !(array[2] != text))
					{
						if (flag)
						{
							localPlayer.m_customData.Remove("SA_pending_v1");
							RevivalEffects.PlayApplication(localPlayer, Player.GetPlayer(num));
							Plugin.Say(localPlayer, "$sa_success");
						}
						else
						{
							localPlayer.m_customData["SA_pending_v1"] = "refund";
							Retry(localPlayer);
						}
					}
					break;
				}
				}
			}
			catch (Exception ex)
			{
				Plugin.Log("Rejected rescue message: " + ex.Message);
			}
		}

		private static bool HasLineOfSight(Player from, Player to)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			int mask = LayerMask.GetMask(new string[5] { "Default", "static_solid", "Default_small", "piece", "terrain" });
			return !Physics.Linecast(((Component)from).transform.position + Vector3.up, ((Component)to).transform.position + Vector3.up, mask, (QueryTriggerInteraction)1);
		}
	}
	[HarmonyPatch(typeof(Player), "Awake")]
	internal static class PlayerSetup
	{
		private static void Postfix(Player __instance)
		{
			if (!Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<RevivalState>()))
			{
				((Component)__instance).gameObject.AddComponent<RevivalState>();
			}
			if (!Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<DownedIndicator>()))
			{
				((Component)__instance).gameObject.AddComponent<DownedIndicator>();
			}
			if (!Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<RescueChannel>()))
			{
				((Component)__instance).gameObject.AddComponent<RescueChannel>();
			}
		}
	}
	[HarmonyPatch(typeof(Player), "Load")]
	internal static class LoadedPlayer
	{
		private static void Postfix(Player __instance)
		{
			RevivalState.Of((Character)(object)__instance)?.RestoreAfterLoad();
			Plugin.NormalizeBrokenTotems(__instance);
		}
	}
	[HarmonyPatch(typeof(Character), "CheckDeath")]
	internal static class CheckDeath
	{
		[HarmonyPriority(800)]
		private static bool Prefix(Character __instance)
		{
			RevivalState revivalState = RevivalState.Of(__instance);
			if (!((Object)(object)revivalState == (Object)null) && !revivalState.Finalizing && !(__instance.GetHealth() > 0f))
			{
				return !revivalState.InterceptDeath();
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Player), "OnDeath")]
	internal static class DirectDeath
	{
		[HarmonyPriority(800)]
		private static bool Prefix(Player __instance)
		{
			RevivalState revivalState = RevivalState.Of((Character)(object)__instance);
			if (revivalState == null)
			{
				return true;
			}
			return !revivalState.InterceptDeath();
		}
	}
	[HarmonyPatch(typeof(Character), "SetHealth")]
	internal static class HealthGuard
	{
		private static void Prefix(Character __instance, ref float health)
		{
			RevivalState revivalState = RevivalState.Of(__instance);
			if (!((Object)(object)revivalState == (Object)null) && !revivalState.Finalizing && revivalState.Owned)
			{
				if (revivalState.Downed)
				{
					health = 1f;
				}
				else if (revivalState.Protected)
				{
					health = Mathf.Max(new float[3]
					{
						health,
						__instance.GetHealth(),
						1f
					});
				}
			}
		}
	}
	[HarmonyPatch(typeof(Character), "RPC_Damage")]
	internal static class DamageGuard
	{
		private static bool Prefix(Character __instance)
		{
			RevivalState revivalState = RevivalState.Of(__instance);
			if (revivalState == null)
			{
				return true;
			}
			return !revivalState.Protected;
		}
	}
	[HarmonyPatch(typeof(Character), "ApplyDamage")]
	internal static class DirectDamageGuard
	{
		private static bool Prefix(Character __instance)
		{
			RevivalState revivalState = RevivalState.Of(__instance);
			if (revivalState == null)
			{
				return true;
			}
			return !revivalState.Protected;
		}
	}
	[HarmonyPatch(typeof(Skills), "OnDeath")]
	internal static class OptionalSkillLoss
	{
		internal static bool Suppress;

		private static bool Prefix()
		{
			if (!Suppress)
			{
				return LifeWindow.SkillLossOnFinalDeath;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Skills), "Clear")]
	internal static class OptionalFullSkillReset
	{
		private static bool Prefix()
		{
			return !OptionalSkillLoss.Suppress;
		}
	}
	[HarmonyPatch(typeof(Player), "SetControls")]
	internal static class Controls
	{
		private static bool Prefix(Player __instance)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			if (!RevivalState.IsDown((Character)(object)__instance))
			{
				return true;
			}
			((Character)__instance).SetMoveDir(Vector3.zero);
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "TakeInput")]
	internal static class InputGuard
	{
		private static void Postfix(Player __instance, ref bool __result)
		{
			if (RevivalState.IsDown((Character)(object)__instance))
			{
				__result = false;
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "StartAttack")]
	internal static class AttackGuard
	{
		private static bool Prefix(Humanoid __instance, ref bool __result)
		{
			if (!RevivalState.IsDown((Character)(object)__instance))
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "Interact")]
	internal static class InteractGuard
	{
		private static bool Prefix(Player __instance)
		{
			return !RevivalState.IsDown((Character)(object)__instance);
		}
	}
	[HarmonyPatch(typeof(Player), "AutoPickup")]
	internal static class PickupGuard
	{
		private static bool Prefix(Player __instance)
		{
			return !RevivalState.IsDown((Character)(object)__instance);
		}
	}
	[HarmonyPatch(typeof(MonsterAI), "UpdateAI")]
	internal static class DownedTargetGuard
	{
		private static void Prefix(MonsterAI __instance)
		{
			FieldInfo fieldInfo = AccessTools.Field(typeof(MonsterAI), "m_targetCreature");
			object? value = fieldInfo.GetValue(__instance);
			if (RevivalState.IsDown((Character)((value is Character) ? value : null)))
			{
				fieldInfo.SetValue(__instance, null);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "InGhostMode")]
	internal static class DownedHuntTargetGuard
	{
		private static void Postfix(Player __instance, ref bool __result)
		{
			if (RevivalState.IsDown((Character)(object)__instance))
			{
				__result = true;
			}
		}
	}
	[HarmonyPatch(typeof(Player), "StartGuardianPower")]
	internal static class PowerStartGuard
	{
		private static bool Prefix(Player __instance, ref bool __result)
		{
			if (!RevivalState.IsDown((Character)(object)__instance))
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "ActivateGuardianPower")]
	internal static class PowerActivationGuard
	{
		private static bool Prefix(Player __instance, ref bool __result)
		{
			if (!RevivalState.IsDown((Character)(object)__instance))
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "TeleportTo")]
	internal static class TeleportGuard
	{
		private static bool Prefix(Player __instance, ref bool __result)
		{
			if (!RevivalState.IsDown((Character)(object)__instance))
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Show")]
	internal static class InventoryGuard
	{
		private static bool Prefix()
		{
			if (RevivalState.IsDown((Character)(object)Player.m_localPlayer))
			{
				return false;
			}
			Plugin.NormalizeBrokenTotems(Player.m_localPlayer);
			return true;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "CanRepair")]
	internal static class BrokenAnchorRepairEligibility
	{
		private static void Postfix(ItemData item, ref bool __result)
		{
			if (Object.op_Implicit((Object)(object)item?.m_dropPrefab) && ((Object)item.m_dropPrefab).name == "BrokenSoulAnchor")
			{
				Player localPlayer = Player.m_localPlayer;
				if (Plugin.IsBlackForge((localPlayer != null) ? localPlayer.GetCurrentCraftingStation() : null))
				{
					__result = true;
				}
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "RepairOneItem")]
	internal static class BrokenAnchorRepairAction
	{
		private static bool Prefix()
		{
			return !Plugin.TryRepairBrokenTotem();
		}
	}
	[HarmonyPatch(typeof(Player), "GetHoverText")]
	internal static class Hover
	{
		private static void Postfix(Player __instance, ref string __result)
		{
			double num = RevivalState.RemoteDeadline(__instance);
			if (!(num <= 0.0))
			{
				__result = __instance.GetPlayerName() + "\n<color=#70e4cb>" + string.Format(Localization.instance.Localize("$sa_downed"), LifeWindow.Remaining(num, RevivalState.Now)) + "</color>\n" + Localization.instance.Localize("$sa_hint");
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "UseItem")]
	internal static class PotionUse
	{
		private static bool Prefix(Humanoid __instance, Inventory inventory, ItemData item, bool fromInventoryGui)
		{
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			if (RevivalState.IsDown((Character)(object)__instance))
			{
				return false;
			}
			if (item == null || !Object.op_Implicit((Object)(object)item.m_dropPrefab) || ((Object)item.m_dropPrefab).name != "EirsBreath")
			{
				return true;
			}
			if (!LifeWindow.PotionEnabled)
			{
				return false;
			}
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (!Object.op_Implicit((Object)(object)val) || (Object)(object)val != (Object)(object)Player.m_localPlayer || ((Character)val).IsDead())
			{
				return false;
			}
			GameObject hoverObject = ((Humanoid)val).GetHoverObject();
			Player val2 = (Object.op_Implicit((Object)(object)hoverObject) ? hoverObject.GetComponentInParent<Player>() : null);
			double num = (Object.op_Implicit((Object)(object)val2) ? RevivalState.RemoteCooldown(val2) : 0.0);
			if (num > RevivalState.Now)
			{
				((Character)val).Message((MessageType)2, string.Format(Localization.instance.Localize("$sa_cooldown"), Math.Max(1, (int)Math.Ceiling(num - RevivalState.Now))), 0, (Sprite)null, false);
				return false;
			}
			if (fromInventoryGui || !Object.op_Implicit((Object)(object)val2) || (Object)(object)val2 == (Object)(object)val || (inventory != null && inventory != ((Humanoid)val).GetInventory()) || !((Humanoid)val).GetInventory().ContainsItem(item) || !LifeWindow.CanRescue(RevivalState.RemoteDeadline(val2), RevivalState.Now, Vector3.Distance(((Component)val).transform.position, ((Component)val2).transform.position), !((Character)val).IsDead()))
			{
				Plugin.Say(val, "$sa_invalid");
				return false;
			}
			((Component)val).GetComponent<RescueChannel>().Begin(val2, item, inventory);
			return false;
		}
	}
	internal static class RevivalEffects
	{
		internal const string StatusName = "SA_RevivalGlow";

		internal const string TotemStatusName = "SA_TotemRevivalGlow";

		internal const string CooldownStatusName = "SA_RevivalCooldown";

		private static Material sparkMaterial;

		private static StatusEffect glowEffect;

		private static StatusEffect totemGlowEffect;

		private static StatusEffect cooldownEffect;

		private static AudioClip revivalClip;

		internal static AudioClip RevivalAudioClip => revivalClip;

		internal static IEnumerator LoadAudio()
		{
			string text = Path.Combine(Paths.CachePath, "SoulAnchor");
			string text2 = Path.Combine(text, "elemental-magic-spell-cast-d-228349.mp3");
			try
			{
				Directory.CreateDirectory(text);
				using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Seelenanker.revival_sound.mp3");
				if (stream == null)
				{
					throw new InvalidOperationException("Embedded revival sound is missing.");
				}
				using FileStream destination = File.Create(text2);
				stream.CopyTo(destination);
			}
			catch (Exception ex)
			{
				Plugin.Log("Revival sound extraction failed: " + ex.Message);
				yield break;
			}
			UnityWebRequest request = UnityWebRequestMultimedia.GetAudioClip(new Uri(text2).AbsoluteUri, (AudioType)13);
			try
			{
				yield return request.SendWebRequest();
				if ((int)request.result != 1)
				{
					Plugin.Log("Revival sound loading failed: " + request.error);
					yield break;
				}
				revivalClip = DownloadHandlerAudioClip.GetContent(request);
				if (Object.op_Implicit((Object)(object)revivalClip))
				{
					((Object)revivalClip).name = "SoulAnchor_ElementalMagicSpellCastD";
				}
			}
			finally
			{
				((IDisposable)request)?.Dispose();
			}
		}

		internal static void Register(Sprite potionIcon, Sprite totemIcon, Sprite cooldownIcon)
		{
			glowEffect = CreateStatus("SA_RevivalGlow", "$sa_grace", "$sa_grace_desc", potionIcon, (float)LifeWindow.GraceSeconds);
			totemGlowEffect = CreateStatus("SA_TotemRevivalGlow", "$sa_totem_grace", "$sa_totem_grace_desc", totemIcon, (float)LifeWindow.GraceSeconds);
			cooldownEffect = CreateStatus("SA_RevivalCooldown", "$sa_revival_cooldown", "$sa_revival_cooldown_desc", cooldownIcon, (float)LifeWindow.RevivalCooldownSeconds);
		}

		private static StatusEffect CreateStatus(string id, string name, string tooltip, Sprite icon, float duration)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			StatusEffect val = ScriptableObject.CreateInstance<StatusEffect>();
			((Object)val).name = id;
			val.m_name = name;
			val.m_tooltip = tooltip;
			val.m_icon = icon;
			val.m_ttl = duration;
			val.m_cooldownIcon = id == "SA_RevivalCooldown";
			if (!ItemManager.Instance.AddStatusEffect(new CustomStatusEffect(val, false)))
			{
				Plugin.Log("Could not register status " + id + ". Gameplay remains functional.");
			}
			return val;
		}

		internal static void Play(Player player, bool fromTotem)
		{
			if (!Object.op_Implicit((Object)(object)player))
			{
				return;
			}
			try
			{
				StatusEffect val = (fromTotem ? totemGlowEffect : glowEffect);
				if (Object.op_Implicit((Object)(object)val) && LifeWindow.GraceSeconds > 0.0)
				{
					val.m_ttl = (float)LifeWindow.GraceSeconds;
					((Character)player).GetSEMan().RemoveStatusEffect(val, true);
					((Character)player).GetSEMan().AddStatusEffect(val, true, 0, 0f, (short)(-1));
					StatusEffect statusEffect = ((Character)player).GetSEMan().GetStatusEffect(val.NameHash());
					Plugin.LogInfo(string.Format("Revival status active={0}, icon={1}.", (Object)(object)statusEffect != (Object)null, (Object.op_Implicit((Object)(object)statusEffect) && Object.op_Implicit((Object)(object)statusEffect.m_icon)) ? "ready" : "missing"));
				}
				else
				{
					Plugin.Log("Revival status object is unavailable; protection remains active.");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log("Revival status icon skipped: " + ex);
			}
			try
			{
				RevivalSparkController revivalSparkController = ((Component)player).GetComponent<RevivalSparkController>();
				if (!Object.op_Implicit((Object)(object)revivalSparkController))
				{
					revivalSparkController = ((Component)player).gameObject.AddComponent<RevivalSparkController>();
				}
				revivalSparkController.Begin(LifeWindow.GraceSeconds, ResolveSparkMaterial(), ModSettings.EffectStrength.Value, ModSettings.EffectVolume.Value, fromTotem);
			}
			catch (Exception ex2)
			{
				Plugin.Log("Revival visual skipped: " + ex2);
			}
		}

		internal static bool PlayCooldown(Player player, double seconds)
		{
			if (!Object.op_Implicit((Object)(object)player) || !Object.op_Implicit((Object)(object)cooldownEffect) || seconds <= 0.0)
			{
				return false;
			}
			try
			{
				cooldownEffect.m_ttl = (float)seconds;
				((Character)player).GetSEMan().RemoveStatusEffect(cooldownEffect, true);
				((Character)player).GetSEMan().AddStatusEffect(cooldownEffect, true, 0, 0f, (short)(-1));
				return (Object)(object)((Character)player).GetSEMan().GetStatusEffect(cooldownEffect.NameHash()) != (Object)null;
			}
			catch (Exception ex)
			{
				Plugin.Log("Cooldown status skipped: " + ex.Message);
				return false;
			}
		}

		internal static void StopCooldown(Player player)
		{
			if (!Object.op_Implicit((Object)(object)player))
			{
				return;
			}
			try
			{
				((Character)player).GetSEMan().RemoveStatusEffect(StringExtensionMethods.GetStableHashCode("SA_RevivalCooldown"), true);
			}
			catch (Exception ex)
			{
				Plugin.Log("Cooldown icon cleanup skipped: " + ex.Message);
			}
		}

		internal static void Stop(Player player)
		{
			if (!Object.op_Implicit((Object)(object)player))
			{
				return;
			}
			try
			{
				((Character)player).GetSEMan().RemoveStatusEffect(StringExtensionMethods.GetStableHashCode("SA_RevivalGlow"), true);
				((Character)player).GetSEMan().RemoveStatusEffect(StringExtensionMethods.GetStableHashCode("SA_TotemRevivalGlow"), true);
			}
			catch (Exception ex)
			{
				Plugin.Log("Revival icon cleanup skipped: " + ex.Message);
			}
			try
			{
				((Component)player).GetComponent<RevivalSparkController>()?.Finish();
			}
			catch (Exception ex2)
			{
				Plugin.Log("Revival spark cleanup skipped: " + ex2.Message);
			}
		}

		private static Material ResolveSparkMaterial()
		{
			if (Object.op_Implicit((Object)(object)sparkMaterial))
			{
				return sparkMaterial;
			}
			string[] array = new string[4] { "fx_chainlightning_hit", "ChainLightning", "ChainLightningRed", "vfx_Potion_health_medium" };
			foreach (string text in array)
			{
				GameObject prefab = PrefabManager.Instance.GetPrefab(text);
				ParticleSystemRenderer val = (Object.op_Implicit((Object)(object)prefab) ? prefab.GetComponentInChildren<ParticleSystemRenderer>(true) : null);
				if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)((Renderer)val).sharedMaterial))
				{
					sparkMaterial = ((Renderer)val).sharedMaterial;
					return sparkMaterial;
				}
			}
			array = new string[2] { "Lightning", "MeadHealthMedium" };
			foreach (string text2 in array)
			{
				object obj;
				if (!(text2 == "Lightning"))
				{
					GameObject prefab2 = PrefabManager.Instance.GetPrefab(text2);
					obj = ((prefab2 == null) ? null : prefab2.GetComponent<ItemDrop>()?.m_itemData.m_shared.m_consumeStatusEffect);
				}
				else
				{
					ObjectDB instance = ObjectDB.instance;
					obj = ((instance != null) ? instance.GetStatusEffect(StringExtensionMethods.GetStableHashCode(text2)) : null);
				}
				StatusEffect val2 = (StatusEffect)obj;
				if (val2?.m_startEffects?.m_effectPrefabs == null)
				{
					continue;
				}
				EffectData[] effectPrefabs = val2.m_startEffects.m_effectPrefabs;
				foreach (EffectData val3 in effectPrefabs)
				{
					ParticleSystemRenderer val4 = (Object.op_Implicit((Object)(object)val3.m_prefab) ? val3.m_prefab.GetComponentInChildren<ParticleSystemRenderer>(true) : null);
					if (Object.op_Implicit((Object)(object)val4) && Object.op_Implicit((Object)(object)((Renderer)val4).sharedMaterial))
					{
						sparkMaterial = ((Renderer)val4).sharedMaterial;
						return sparkMaterial;
					}
				}
			}
			return null;
		}

		internal static void PlayApplication(Player helper, Player target)
		{
			try
			{
				if (Object.op_Implicit((Object)(object)helper) && Object.op_Implicit((Object)(object)target) && !((Character)helper).IsDead() && !RevivalState.IsDown((Character)(object)helper) && !((Character)helper).InAttack() && !((Character)helper).InDodge())
				{
					AccessTools.Method(typeof(Humanoid), "DoInteractAnimation", (Type[])null, (Type[])null).Invoke(helper, new object[1] { ((Component)target).gameObject });
				}
			}
			catch (Exception ex)
			{
				Plugin.Log("Revival gesture skipped: " + ex.Message);
			}
		}
	}
	public sealed class RevivalSparkController : MonoBehaviour
	{
		private GameObject holder;

		private readonly List<LineRenderer> bolts = new List<LineRenderer>();

		private Light aura;

		private ParticleSystem auraParticles;

		private double end;

		private float nextFlash;

		private float strength;

		internal void Begin(double seconds, Material material, float effectStrength, float volume, bool fromTotem)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			//IL_0047: 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_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0258: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a7: Expected O, but got Unknown
			//IL_02d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0308: Unknown result type (might be due to invalid IL or missing references)
			//IL_031e: Unknown result type (might be due to invalid IL or missing references)
			//IL_033e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0357: Unknown result type (might be due to invalid IL or missing references)
			//IL_035c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0393: Unknown result type (might be due to invalid IL or missing references)
			//IL_0398: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0409: Unknown result type (might be due to invalid IL or missing references)
			//IL_040e: Unknown result type (might be due to invalid IL or missing references)
			//IL_042c: Unknown result type (might be due to invalid IL or missing references)
			//IL_043c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0441: Unknown result type (might be due to invalid IL or missing references)
			//IL_044b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0452: Expected O, but got Unknown
			//IL_046b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0475: Unknown result type (might be due to invalid IL or missing references)
			//IL_047a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0490: Unknown result type (might be due to invalid IL or missing references)
			//IL_049a: Unknown result type (might be due to invalid IL or missing references)
			//IL_049f: Unknown result type (might be due to invalid IL or missing references)
			//IL_04b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_04bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_04db: 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_04f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0507: Unknown result type (might be due to invalid IL or missing references)
			//IL_050c: Unknown result type (might be due to invalid IL or missing references)
			//IL_051d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0522: Unknown result type (might be due to invalid IL or missing references)
			//IL_0530: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Expected O, but got Unknown
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: 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_01cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			Finish();
			end = RevivalState.Now + seconds;
			strength = effectStrength;
			holder = new GameObject("SA_RevivalSparks");
			holder.layer = 0;
			holder.transform.position = ((Component)this).transform.position + Vector3.up * 0.9f;
			PlayRevivalSound(fromTotem, volume);
			int num = Mathf.Max(1, Mathf.RoundToInt(11f * strength));
			for (int i = 0; i < num; i++)
			{
				if (!(strength > 0f))
				{
					break;
				}
				GameObject val = new GameObject("SA_BodyLightning")
				{
					layer = 0
				};
				val.transform.SetParent(holder.transform, false);
				LineRenderer val2 = val.AddComponent<LineRenderer>();
				val2.useWorldSpace = false;
				val2.positionCount = 5;
				val2.alignment = (LineAlignment)0;
				val2.numCapVertices = 2;
				val2.widthMultiplier = Mathf.Lerp(0.007f, 0.018f, Mathf.Clamp01(strength / 3f));
				val2.textureMode = (LineTextureMode)0;
				if (Object.op_Implicit((Object)(object)material))
				{
					((Renderer)val2).sharedMaterial = material;
				}
				Gradient val3 = new Gradient();
				val3.SetKeys((GradientColorKey[])(object)new GradientColorKey[3]
				{
					new GradientColorKey(new Color(0.65f, 0.4f, 1f), 0f),
					new GradientColorKey(new Color(0.96f, 0.88f, 1f), 0.4f),
					new GradientColorKey(Color.white, 1f)
				}, (GradientAlphaKey[])(object)new GradientAlphaKey[4]
				{
					new GradientAlphaKey(0.35f, 0f),
					new GradientAlphaKey(1f, 0.12f),
					new GradientAlphaKey(1f, 0.86f),
					new GradientAlphaKey(0.1f, 1f)
				});
				val2.colorGradient = val3;
				bolts.Add(val2);
			}
			RefreshBolts();
			aura = holder.AddComponent<Light>();
			aura.type = (LightType)2;
			aura.color = new Color(0.68f, 0.42f, 1f);
			aura.range = 2.9f * strength;
			aura.intensity = 1.8f * strength;
			aura.shadows = (LightShadows)0;
			GameObject val4 = new GameObject("SA_RevivalAura");
			val4.transform.SetParent(holder.transform, false);
			auraParticles = val4.AddComponent<ParticleSystem>();
			MainModule main = auraParticles.main;
			((MainModule)(ref main)).loop = true;
			((MainModule)(ref main)).playOnAwake = false;
			((MainModule)(ref main)).startLifetime = new MinMaxCurve(0.9f, 1.55f);
			((MainModule)(ref main)).startSpeed = new MinMaxCurve(0.045f, 0.15f);
			((MainModule)(ref main)).startSize = new MinMaxCurve(0.085f, 0.19f);
			((MainModule)(ref main)).startColor = new MinMaxGradient(new Color(0.6f, 0.34f, 1f, 0.24f), new Color(1f, 0.92f, 1f, 0.58f));
			((MainModule)(ref main)).maxParticles = Mathf.Max(18, Mathf.RoundToInt(42f * strength));
			((MainModule)(ref main)).simulationSpace = (ParticleSystemSimulationSpace)0;
			EmissionModule emission = auraParticles.emission;
			((EmissionModule)(ref emission)).rateOverTime = MinMaxCurve.op_Implicit(Mathf.Max(9f, 23f * strength));
			ShapeModule shape = auraParticles.shape;
			((ShapeModule)(ref shape)).shapeType = (ParticleSystemShapeType)0;
			((ShapeModule)(ref shape)).radius = 0.72f;
			((ShapeModule)(ref shape)).radiusThickness = 0.12f;
			((ShapeModule)(ref shape)).scale = new Vector3(0.82f, 1.48f, 0.72f);
			VelocityOverLifetimeModule velocityOverLifetime = auraParticles.velocityOverLifetime;
			((VelocityOverLifetimeModule)(ref velocityOverLifetime)).enabled = true;
			((VelocityOverLifetimeModule)(ref velocityOverLifetime)).space = (ParticleSystemSimulationSpace)0;
			((VelocityOverLifetimeModule)(ref velocityOverLifetime)).y = new MinMaxCurve(0.11f, 0.28f);
			ColorOverLifetimeModule colorOverLifetime = auraParticles.colorOverLifetime;
			((ColorOverLifetimeModule)(ref colorOverLifetime)).enabled = true;
			Gradient val5 = new Gradient();
			val5.SetKeys((GradientColorKey[])(object)new GradientColorKey[3]
			{
				new GradientColorKey(new Color(0.62f, 0.38f, 1f), 0f),
				new GradientColorKey(new Color(0.96f, 0.88f, 1f), 0.55f),
				new GradientColorKey(new Color(0.58f, 0.3f, 1f), 1f)
			}, (GradientAlphaKey[])(object)new GradientAlphaKey[4]
			{
				new GradientAlphaKey(0f, 0f),
				new GradientAlphaKey(0.62f, 0.18f),
				new GradientAlphaKey(0.38f, 0.72f),
				new GradientAlphaKey(0f, 1f)
			});
			((ColorOverLifetimeModule)(ref colorOverLifetime)).color = MinMaxGradient.op_Implicit(val5);
			ParticleSystemRenderer component = ((Component)auraParticles).GetComponent<ParticleSystemRenderer>();
			component.renderMode = (ParticleSystemRenderMode)0;
			((Renderer)component).sortingOrder = 2;
			if (Object.op_Implicit((Object)(object)material))
			{
				((Renderer)component).sharedMaterial = material;
			}
			auraParticles.Play();
		}

		private void PlayRevivalSound(bool fromTotem, float volume)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (!(volume <= 0f))
			{
				if (!Object.op_Implicit((Object)(object)RevivalEffects.RevivalAudioClip))
				{
					Plugin.Log("Revival sound is not loaded yet.");
					return;
				}
				GameObject val = new GameObject("SA_RevivalAudio");
				val.transform.SetParent(holder.transform, false);
				AudioSource obj = val.AddComponent<AudioSource>();
				obj.clip = RevivalEffects.RevivalAudioClip;
				obj.spatialBlend = 0.82f;
				obj.rolloffMode = (AudioRolloffMode)1;
				obj.minDistance = 1.5f;
				obj.maxDistance = 24f;
				obj.volume = Mathf.Clamp01(0.5f * volume);
				obj.Play();
			}
		}

		private static Vector3 BodyPoint()
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			float num = Random.Range(0f, MathF.PI * 2f);
			float num2 = Random.Range(0.12f, 0.62f);
			return new Vector3(Mathf.Cos(num) * num2, Random.Range(-0.98f, 1.02f), Mathf.Sin(num) * num2);
		}

		private void RefreshBolts()
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: 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_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: 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_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			nextFlash = Time.time + Random.Range(0.045f, 0.11f);
			foreach (LineRenderer bolt in bolts)
			{
				if (!(((Renderer)bolt).enabled = Random.value > 0.18f))
				{
					continue;
				}
				Vector3 val = BodyPoint();
				Vector3 val2 = BodyPoint();
				for (int i = 0; i < bolt.positionCount; i++)
				{
					float num = (float)i / (float)(bolt.positionCount - 1);
					Vector3 val3 = Vector3.Lerp(val, val2, num);
					if (i > 0 && i < bolt.positionCount - 1)
					{
						val3 += Random.insideUnitSphere * 0.13f;
					}
					bolt.SetPosition(i, val3);
				}
			}
		}

		private void Update()
		{
			//IL_0043: 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_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			if (end > 0.0 && RevivalState.Now >= end)
			{
				Finish();
				return;
			}
			if (Object.op_Implicit((Object)(object)holder))
			{
				holder.transform.position = ((Component)this).transform.position + Vector3.up * 0.9f;
			}
			if (Time.time >= nextFlash)
			{
				RefreshBolts();
			}
			if (Object.op_Implicit((Object)(object)aura))
			{
				aura.intensity = (1.15f + Mathf.Abs(Mathf.Sin(Time.time * 18f)) * 1.25f) * strength;
			}
		}

		internal void Finish()
		{
			end = 0.0;
			bolts.Clear();
			aura = null;
			auraParticles = null;
			if (Object.op_Implicit((Object)(object)holder))
			{
				Object.Destroy((Object)(object)holder);
			}
			holder = null;
		}

		private void OnDestroy()
		{
			Finish();
		}
	}
	internal static class RevivalUi
	{
		private static Sprite circleSprite;

		private static AudioClip warningClip;

		internal static Sprite SoulIcon { get; private set; }

		internal static void Register(Sprite soulIcon)
		{
			SoulIcon = soulIcon;
		}

		internal static string GiveUpHint()
		{
			string text = ((ZInput.instance != null) ? ZInput.instance.GetBoundKeyString("JoyButtonY", false) : "");
			if (string.IsNullOrWhiteSpace(text))
			{
				text = Localization.instance.Localize("$settings_none");
			}
			return string.Format(Localization.instance.Localize("$sa_giveup_hint"), text);
		}

		internal static Sprite CircleSprite()
		{
			//IL_0018: 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)
			//IL_0029: Expected O, but got Unknown
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)circleSprite))
			{
				return circleSprite;
			}
			Texture2D val = new Texture2D(96, 96, (TextureFormat)4, false)
			{
				name = "SA_ProgressCircle"
			};
			Color32[] array = (Color32[])(object)new Color32[9216];
			for (int i = 0; i < 96; i++)
			{
				for (int j = 0; j < 96; j++)
				{
					float num = ((float)j + 0.5f) / 96f * 2f - 1f;
					float num2 = ((float)i + 0.5f) / 96f * 2f - 1f;
					float num3 = Mathf.Sqrt(num * num + num2 * num2);
					byte b = (byte)((num3 <= 1f && num3 >= 0.68f) ? byte.MaxValue : 0);
					array[i * 96 + j] = new Color32(byte.MaxValue, byte.MaxValue, byte.MaxValue, b);
				}
			}
			val.SetPixels32(array);
			val.Apply(false, true);
			circleSprite = Sprite.Create(val, new Rect(0f, 0f, 96f, 96f), new Vector2(0.5f, 0.5f));
			return circleSprite;
		}

		internal static void Warning(Player player, int seconds)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)player) && !((Object)(object)player != (Object)(object)Player.m_localPlayer))
			{
				((Character)player).Message((MessageType)2, string.Format(Localization.instance.Localize("$sa_warning"), seconds), 0, (Sprite)null, false);
				if (!Object.op_Implicit((Object)(object)warningClip))
				{
					warningClip = CreateWarningClip();
				}
				AudioSource.PlayClipAtPoint(warningClip, ((Component)player).transform.position, Mathf.Clamp01(ModSettings.EffectVolume.Value));
			}
		}

		private static AudioClip CreateWarningClip()
		{
			int num = Mathf.CeilToInt(4851f);
			float[] array = new float[num];
			for (int i = 0; i < num; i++)
			{
				float num2 = (float)i / 22050f;
				float num3 = Mathf.Clamp01(num2 / 0.012f) * Mathf.Clamp01((0.22f - num2) / 0.09f);
				array[i] = Mathf.Sin(MathF.PI * 2f * (680f + num2 * 520f) * num2) * num3 * 0.42f;
			}
			AudioClip obj = AudioClip.Create("SoulAnchor_RescueWarning", num, 1, 22050, false);
			obj.SetData(array, 0);
			return obj;
		}

		internal static void MarkLastDeath(Player player)
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)player) || (Object)(object)player != (Object)(object)Player.m_localPlayer || !Object.op_Implicit((Object)(object)Minimap.instance))
			{
				return;
			}
			try
			{
				if (AccessTools.Field(typeof(Minimap), "m_pins").GetValue(Minimap.instance) is List<PinData> collection)
				{
					foreach (PinData item in new List<PinData>(collection))
					{
						if (item.m_name == "$sa_last_death")
						{
							Minimap.instance.RemovePin(item);
						}
					}
				}
				PinData obj = Minimap.instance.AddPin(((Component)player).transform.position, (PinType)4, "$sa_last_death", true, false, player.GetPlayerID(), default(PlatformUserID));
				obj.m_doubleSize = false;
				obj.m_animate = false;
			}
			catch (Exception ex)
			{
				Plugin.Log("Last-death marker skipped: " + ex.Message);
			}
		}
	}
	public sealed class DownedIndicator : MonoBehaviour
	{
		private Player player;

		private GameObject marker;

		private SpriteRenderer icon;

		private TextMesh label;

		private void Awake()
		{
			player = ((Component)this).GetComponent<Player>();
		}

		private void Update()
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: 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_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: 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_01aa: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)player) || (Object)(object)player == (Object)(object)Player.m_localPlayer)
			{
				Hide();
				return;
			}
			double num = RevivalState.RemoteDeadline(player);
			if (num <= RevivalState.Now)
			{
				Hide();
				return;
			}
			Ensure();
			if (!Object.op_Implicit((Object)(object)icon.sprite))
			{
				icon.sprite = RevivalUi.SoulIcon;
			}
			marker.SetActive(true);
			marker.transform.position = ((Component)player).transform.position + Vector3.up * 2.35f;
			if (Object.op_Implicit((Object)(object)Camera.main))
			{
				marker.transform.rotation = Quaternion.LookRotation(marker.transform.position - ((Component)Camera.main).transform.position);
			}
			int num2 = LifeWindow.Remaining(num, RevivalState.Now);
			int num3 = (Object.op_Implicit((Object)(object)Player.m_localPlayer) ? Mathf.CeilToInt(Vector3.Distance(((Component)Player.m_localPlayer).transform.position, ((Component)player).transform.position)) : 0);
			label.text = ((num3 > 0) ? (num2 + " s  •  " + num3 + " m") : (num2 + " s"));
			float num4 = 0.78f + Mathf.Abs(Mathf.Sin(Time.time * 4.5f)) * 0.22f;
			icon.color = new Color(0.78f, 0.52f, 1f, num4);
		}

		private void Ensure()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//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_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Expected O, but got Unknown
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: 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.op_Implicit((Object)(object)marker))
			{
				marker = new GameObject("SA_DownedSoulMarker");
				GameObject val = new GameObject("Soul");
				val.transform.SetParent(marker.transform, false);
				val.transform.localPosition = Vector3.up * 0.26f;
				val.transform.localScale = Vector3.one * 0.34f;
				icon = val.AddComponent<SpriteRenderer>();
				icon.sprite = RevivalUi.SoulIcon;
				((Renderer)icon).sortingOrder = 80;
				GameObject val2 = new GameObject("RescueTimeDistance");
				val2.transform.SetParent(marker.transform, false);
				val2.transform.localPosition = Vector3.down * 0.16f;
				label = val2.AddComponent<TextMesh>();
				label.anchor = (TextAnchor)4;
				label.alignment = (TextAlignment)1;
				label.characterSize = 0.027f;
				label.fontSize = 42;
				label.color = new Color(0.84f, 0.72f, 1f, 1f);
				label.text = "";
			}
		}

		private void Hide()
		{
			if (Object.op_Implicit((Object)(object)marker))
			{
				marker.SetActive(false);
			}
		}

		private void OnDestroy()
		{
			if (Object.op_Implicit((Object)(object)marker))
			{
				Object.Destroy((Object)(object)marker);
			}
		}
	}
	public sealed class RescueChannel : MonoBehaviour
	{
		private Player user;

		private Player target;

		private ItemData item;

		private Inventory inventory;

		private double started;

		private GameObject canvasObject;

		private Image fill;

		private void Awake()
		{
			user = ((Component)this).GetComponent<Player>();
		}

		internal void Begin(Player rescueTarget, ItemData potion, Inventory source)
		{
			if (Object.op_Implicit((Object)(object)user) && !((Object)(object)user != (Object)(object)Player.m_localPlayer) && !Object.op_Implicit((Object)(object)target))
			{
				target = rescueTarget;
				item = potion;
				inventory = source;
				started = RevivalState.Now;
				RevivalEffects.PlayApplication(user, target);
				ShowProgress();
			}
		}

		private void Update()
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)target))
			{
				return;
			}
			double deadline = RevivalState.RemoteDeadline(target);
			if (!Object.op_Implicit((Object)(object)user) || ((Character)user).IsDead() || RevivalState.IsDown((Character)(object)user) || inventory != ((Humanoid)user).GetInventory() || !inventory.ContainsItem(item) || !LifeWindow.CanRescue(deadline, RevivalState.Now, Vector3.Distance(((Component)user).transform.position, ((Component)target).transform.position), rescuerAlive: true) || ZInput.GetKeyDown((KeyCode)27, true))
			{
				Cancel();
				return;
			}
			float num = Mathf.Clamp01((float)((RevivalState.Now - started) / (double)ModSettings.RevivalDuration.Value));
			if (Object.op_Implicit((Object)(object)fill))
			{
				fill.fillAmount = num;
			}
			if (!(num < 1f))
			{
				Player val = target;
				ItemData val2 = item;
				Cancel();
				RescueNetwork.Begin(user, val, val2);
			}
		}

		private void ShowProgress()
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Expected O, but got Unknown
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			canvasObject = new GameObject("SA_RevivalProgress");
			Canvas obj = canvasObject.AddComponent<Canvas>();
			obj.renderMode = (RenderMode)0;
			obj.sortingOrder = 400;
			canvasObject.AddComponent<CanvasScaler>();
			CreateCircle("Background", new Color(0.05f, 0.03f, 0.09f, 0.72f));
			fill = CreateCircle("Progress", new Color(0.76f, 0.48f, 1f, 0.95f));
			fill.type = (Type)3;
			fill.fillMethod = (FillMethod)4;
			fill.fillOrigin = 2;
			fill.fillClockwise = true;
			fill.fillAmount = 0f;
		}

		private Image CreateCircle(string name, Color color)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: 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_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject(name);
			val.transform.SetParent(canvasObject.transform, false);
			Image obj = val.AddComponent<Image>();
			obj.sprite = RevivalUi.CircleSprite();
			((Graphic)obj).color = color;
			RectTransform rectTransform = ((Graphic)obj).rectTransform;
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor(0.5f, 0.5f);
			rectTransform.anchorMax = val2;
			rectTransform.anchorMin = val2;
			rectTransform.sizeDelta = new Vector2(84f, 84f);
			rectTransform.anchoredPosition = new Vector2(0f, -72f);
			return obj;
		}

		private void Cancel()
		{
			target = null;
			item = null;
			inventory = null;
			if (Object.op_Implicit((Object)(object)canvasObject))
			{
				Object.Destroy((Object)(object)canvasObject);
			}
			canvasObject = null;
			fill = null;
		}

		private void OnDestroy()
		{
			Cancel();
		}
	}
	internal static class DownedSignals
	{
		private sealed class Signal
		{
			internal string Name;

			internal Vector3 Position;

			internal double Deadline;

			internal PinData Pin;
		}

		private const string Rpc = "SA_DownedSignal_v1";

		private static ZRoutedRpc registered;

		private static readonly Dictionary<long, Signal> signals = new Dictionary<long, Signal>();

		internal static void EnsureRegistered()
		{
			if (ZRoutedRpc.instance != null && registered != ZRoutedRpc.instance)
			{
				registered = ZRoutedRpc.instance;
				registered.Register<ZPackage>("SA_DownedSignal_v1", (Action<long, ZPackage>)Receive);
				signals.Clear();
			}
		}

		internal static void Broadcast(Player player, bool active, double seconds)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			EnsureRegistered();
			if (registered != null && Object.op_Implicit((Object)(object)player))
			{
				ZPackage val = new ZPackage();
				val.Write(player.GetPlayerID());
				val.Write(active);
				val.Write(player.GetPlayerName());
				val.Write(((Component)player).transform.position.x);
				val.Write(((Component)player).transform.position.y);
				val.Write(((Component)player).transform.position.z);
				val.Write(Math.Max(0.0, Math.Min(seconds, LifeWindow.RescueSeconds)));
				registered.InvokeRoutedRPC(0L, "SA_DownedSignal_v1", new object[1] { val });
			}
		}

		private static void Receive(long sender, ZPackage package)
		{
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				long num = package.ReadLong();
				bool flag =