Decompiled source of EpicLootCustomEffects v1.0.11

plugins/EpicLootCustomEffects.dll

Decompiled 2 weeks 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.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using EpicLoot;
using EpicLoot.MagicItemEffects;
using EpicLoot.src.Magic.MagicItemEffects.Helpers;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyCompany("EpicLootCustomEffects")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.11.0")]
[assembly: AssemblyInformationalVersion("1.0.11")]
[assembly: AssemblyProduct("EpicLootCustomEffects")]
[assembly: AssemblyTitle("EpicLootCustomEffects")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("1.0.11.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 EpicLootCustomEffects
{
	internal sealed class SettingsBundle
	{
		public RuntimeOptions Runtime;

		public TargetRules Rules;

		public List<MagicItemEffectDefinition> Effects;
	}
	internal static class LiveSettings
	{
		internal static RuntimeOptions Current = new RuntimeOptions();

		private static readonly string[] EffectNames = new string[3] { "Eclipse", "Awakening", "PrecisionStrike" }.Concat(KillerRuntime.Effects).ToArray();

		private static readonly string[] EffectFiles = EffectNames;

		internal static readonly string[] Files = new string[4] { "runtime.json", "classification.json", "localizations/Korean.json", "localizations/English.json" }.Concat(EffectFiles.Select((string x) => "effects/" + x + ".json")).ToArray();

		internal static TargetRules Rules;

		private const string RequestRpc = "ECE_SettingsRequest_v2";

		private const string SyncRpc = "ECE_SettingsSync_v2";

		private static string directory;

		private static string signature;

		private static string rejectedSignature;

		private static string serialized;

		private static string digest = "";

		private static Func<string, string> read;

		private static CustomLocalization localization;

		private static Dictionary<string, string> korean;

		private static Dictionary<string, string> english;

		private static SettingsBundle local;

		private static SettingsBundle remote;

		private static ZRoutedRpc router;

		private static readonly MethodInfo ServerPeerMethod = AccessTools.Method(typeof(ZRoutedRpc), "GetServerPeerID", (Type[])null, (Type[])null);

		private static float nextPoll;

		private static float nextRequest;

		private static bool routerSeen;

		internal static bool HasServerSettings => remote != null;

		internal static bool IsRemote
		{
			get
			{
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					return !ZNet.instance.IsServer();
				}
				return false;
			}
		}

		private static long ServerPeer
		{
			get
			{
				if (router != null)
				{
					return (long)ServerPeerMethod.Invoke(router, null);
				}
				return 0L;
			}
		}

		internal static bool CanEditRules
		{
			get
			{
				if (!IsRemote)
				{
					return true;
				}
				if (remote == null)
				{
					return false;
				}
				MethodInfo methodInfo = AccessTools.Method(typeof(ZNet), "LocalPlayerIsAdminOrHost", (Type[])null, (Type[])null);
				if (methodInfo != null)
				{
					return (bool)methodInfo.Invoke(ZNet.instance, null);
				}
				return false;
			}
		}

		internal static void Initialize(string path, Func<string, string> reader)
		{
			directory = path;
			read = reader;
			localization = LocalizationManager.Instance.GetLocalization();
			ReloadDisk(initial: true);
			signature = Signature();
		}

		private static string Signature()
		{
			return string.Join("|", Files.Where((string name) => !name.StartsWith("localizations/", StringComparison.Ordinal)).Select(delegate(string name)
			{
				FileInfo fileInfo = new FileInfo(Path.Combine(directory, name));
				return name + ":" + (fileInfo.Exists ? (fileInfo.LastWriteTimeUtc.Ticks + ":" + fileInfo.Length) : "embedded");
			}));
		}

		private static void ValidateEffect(MagicItemEffectDefinition effect, string expected)
		{
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_014e: Invalid comparison between Unknown and I4
			if (effect != null)
			{
				effect.Type = EffectIdentity.Canonical(effect.Type);
			}
			if (effect == null || effect.Type != expected)
			{
				throw new InvalidDataException("Invalid Type for " + expected);
			}
			JObject val = JObject.FromObject((object)effect);
			double num = Extensions.Value<double>((IEnumerable<JToken>)val["SelectionWeight"]);
			if (!WoundLedger.Finite(num) || num < 0.0)
			{
				throw new InvalidDataException(expected + ": SelectionWeight must be finite and nonnegative");
			}
			if (expected == "PrecisionStrike")
			{
				if (effect.Requirements == null)
				{
					throw new InvalidDataException("PrecisionStrike: missing Requirements");
				}
				if (effect.ValuesPerRarity == null)
				{
					throw new InvalidDataException("PrecisionStrike: use an empty ValuesPerRarity object, not null");
				}
				FieldInfo[] fields = ((object)effect.ValuesPerRarity).GetType().GetFields();
				for (int i = 0; i < fields.Length; i++)
				{
					if (fields[i].GetValue(effect.ValuesPerRarity) != null)
					{
						throw new InvalidDataException("PrecisionStrike is a valueless effect; omit rarity values");
					}
				}
				return;
			}
			string[] array = new string[6] { "Magic", "Rare", "Epic", "Legendary", "Mythic", "Ancient" };
			foreach (string text in array)
			{
				JToken obj = val["ValuesPerRarity"];
				JToken val2 = ((obj != null) ? obj[(object)text] : null);
				if (val2 == null || (int)val2.Type == 10)
				{
					throw new InvalidDataException(expected + ": missing rarity " + text);
				}
				double num2 = Extensions.Value<double>((IEnumerable<JToken>)val2[(object)"MinValue"]);
				double num3 = Extensions.Value<double>((IEnumerable<JToken>)val2[(object)"MaxValue"]);
				double num4 = Extensions.Value<double>((IEnumerable<JToken>)val2[(object)"Increment"]);
				if (!WoundLedger.Finite(num2) || !WoundLedger.Finite(num3) || !WoundLedger.Finite(num4) || num2 < 0.0 || num3 < num2 || num4 <= 0.0)
				{
					throw new InvalidDataException(expected + ": invalid range for " + text);
				}
			}
		}

		private static void Validate(SettingsBundle bundle)
		{
			if (bundle == null || bundle.Runtime == null || bundle.Rules == null || bundle.Effects == null || bundle.Effects.Count != EffectNames.Length)
			{
				throw new InvalidDataException("Incomplete settings bundle");
			}
			bundle.Runtime.Validate();
			bundle.Rules.Validate();
			for (int i = 0; i < EffectNames.Length; i++)
			{
				ValidateEffect(bundle.Effects[i], EffectNames[i]);
			}
		}

		private static Dictionary<string, string> ReadLanguage(string name)
		{
			Dictionary<string, string> dictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(read(name));
			if (dictionary == null || dictionary.Any((KeyValuePair<string, string> p) => p.Value == null))
			{
				throw new InvalidDataException("Invalid localization: " + name);
			}
			return dictionary;
		}

		private static void ReloadDisk(bool initial)
		{
			SettingsBundle bundle = new SettingsBundle
			{
				Runtime = JsonConvert.DeserializeObject<RuntimeOptions>(read("runtime.json")),
				Rules = JsonConvert.DeserializeObject<TargetRules>(read("classification.json")),
				Effects = EffectFiles.Select((string n) => JsonConvert.DeserializeObject<MagicItemEffectDefinition>(read("effects/" + n + ".json"))).ToList()
			};
			Validate(bundle);
			if (initial)
			{
				korean = ReadLanguage("localizations/Korean.json");
				english = ReadLanguage("localizations/English.json");
			}
			local = bundle;
			bool flag = (Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer() && remote != null;
			Apply(flag ? remote : local);
			if (initial)
			{
				UpdateLanguages();
			}
			if (!initial)
			{
				Plugin.Log.LogInfo((object)(flag ? "JSON reloaded: local display settings updated; gameplay settings remain server-authoritative." : "JSON reloaded: effect definitions, runtime settings and classification applied."));
				if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
				{
					Send(0L);
				}
			}
		}

		private static void RefreshWords(Dictionary<string, string> ko, Dictionary<string, string> en)
		{
			Type type = AccessTools.TypeByName("Localization");
			if (type == null)
			{
				return;
			}
			object obj = AccessTools.Property(type, "instance")?.GetValue(null, null);
			if (obj == null)
			{
				return;
			}
			string text = AccessTools.Method(type, "GetSelectedLanguage", (Type[])null, (Type[])null)?.Invoke(obj, null) as string;
			MethodInfo methodInfo = AccessTools.Method(type, "AddWord", new Type[2]
			{
				typeof(string),
				typeof(string)
			}, (Type[])null);
			if (methodInfo == null)
			{
				return;
			}
			Dictionary<string, string> dictionary = new Dictionary<string, string>(en);
			if (text == "Korean")
			{
				foreach (KeyValuePair<string, string> item in ko)
				{
					dictionary[item.Key] = item.Value;
				}
			}
			foreach (KeyValuePair<string, string> item2 in dictionary)
			{
				methodInfo.Invoke(obj, new object[2] { item2.Key, item2.Value });
			}
		}

		private static void UpdateLanguages()
		{
			if (korean != null && english != null)
			{
				Func<string, string> expand = (string text) => text.Replace("{eclipse_duration}", Current.Eclipse.DurationSeconds.ToString("0.###", CultureInfo.InvariantCulture)).Replace("{awakening_refresh}", "2.5").Replace("{awakening_duration}", Current.Awakening.DurationSeconds.ToString("0.###", CultureInfo.InvariantCulture))
					.Replace("{awakening_conversion}", Current.Awakening.DamageConversionPercent.ToString("0.###", CultureInfo.InvariantCulture));
				Dictionary<string, string> dictionary = korean.ToDictionary((KeyValuePair<string, string> p) => p.Key, (KeyValuePair<string, string> p) => expand(p.Value));
				Dictionary<string, string> dictionary2 = english.ToDictionary((KeyValuePair<string, string> p) => p.Key, (KeyValuePair<string, string> p) => expand(p.Value));
				localization.ClearAll();
				localization.AddJsonFile("Korean", JsonConvert.SerializeObject((object)dictionary));
				localization.AddJsonFile("English", JsonConvert.SerializeObject((object)dictionary2));
				RefreshWords(dictionary, dictionary2);
			}
		}

		private static void Apply(SettingsBundle bundle)
		{
			Validate(bundle);
			Current = local.Runtime;
			Rules = bundle.Rules;
			Plugin.Definitions.Clear();
			Plugin.Definitions.AddRange(bundle.Effects);
			if (Plugin.Ready)
			{
				Plugin.RegisterEffect();
			}
			serialized = JsonConvert.SerializeObject((object)bundle);
			using (SHA256 sHA = SHA256.Create())
			{
				digest = BitConverter.ToString(sHA.ComputeHash(Encoding.UTF8.GetBytes(serialized))).Replace("-", "");
			}
			if (Plugin.Ready && (Object)(object)Player.m_localPlayer != (Object)null)
			{
				AccessTools.Method(AccessTools.TypeByName("EpicLoot.EquipmentEffectCache"), "Reset", (Type[])null, (Type[])null)?.Invoke(null, new object[1] { Player.m_localPlayer });
			}
		}

		private static void Send(long peer)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			if (router != null && serialized != null)
			{
				ZPackage val = new ZPackage();
				val.Write(serialized);
				router.InvokeRoutedRPC(peer, "ECE_SettingsSync_v2", new object[1] { val });
			}
		}

		private static void EnsureRouter()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance == router)
			{
				return;
			}
			router = instance;
			remote = null;
			nextRequest = 0f;
			if (routerSeen)
			{
				Apply(local);
			}
			routerSeen = true;
			if (router == null)
			{
				return;
			}
			router.Register<ZPackage>("ECE_ClassificationEdit_v1", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
			{
				if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
				{
					return;
				}
				try
				{
					if (!IsAdmin(sender))
					{
						Send(sender);
						Plugin.Log.LogWarning((object)"Classification edit denied: sender is not a server administrator.");
					}
					else
					{
						string text = pkg.ReadString();
						string text2 = pkg.ReadString();
						if (text2.Length > 200000)
						{
							throw new InvalidDataException("Classification edit too large");
						}
						if (Signature() != signature)
						{
							ReloadDisk(initial: false);
							signature = Signature();
						}
						if (text != digest)
						{
							Send(sender);
							Plugin.Log.LogWarning((object)"Classification edit rejected: settings changed; refresh and retry.");
						}
						else
						{
							TargetRules targetRules = JsonConvert.DeserializeObject<TargetRules>(text2);
							targetRules.Validate();
							SaveRules(targetRules);
						}
					}
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Classification edit rejected: " + ex.Message));
					Send(sender);
				}
			});
			router.Register<string>("ECE_SettingsRequest_v2", (Action<long, string>)delegate(long sender, string known)
			{
				if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && known != digest)
				{
					Send(sender);
				}
			});
			router.Register<ZPackage>("ECE_SettingsSync_v2", (Action<long, ZPackage>)delegate(long sender, ZPackage pkg)
			{
				if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer() || sender != ServerPeer)
				{
					return;
				}
				try
				{
					SettingsBundle bundle = JsonConvert.DeserializeObject<SettingsBundle>(pkg.ReadString());
					Validate(bundle);
					remote = bundle;
					Apply(bundle);
					Plugin.Log.LogInfo((object)"Authoritative server effect settings applied.");
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Server settings rejected; keeping last valid values: " + ex.Message));
				}
			});
		}

		private static bool IsAdmin(long sender)
		{
			ZNet instance = ZNet.instance;
			ZNetPeer peer = instance.GetPeer(sender);
			if (peer == null || peer.m_rpc == null)
			{
				return false;
			}
			object value = AccessTools.Field(typeof(ZNet), "m_adminList").GetValue(instance);
			return (bool)AccessTools.Method(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null).Invoke(instance, new object[2]
			{
				value,
				peer.m_rpc.GetSocket().GetHostName()
			});
		}

		private static void WriteJson(string name, object value)
		{
			string text = Path.Combine(directory, name);
			string text2 = text + ".ece-tmp";
			File.WriteAllText(text2, JsonConvert.SerializeObject(value, (Formatting)1) + Environment.NewLine);
			if (File.Exists(text))
			{
				File.Replace(text2, text, null);
			}
			else
			{
				File.Move(text2, text);
			}
		}

		private static void SaveRules(TargetRules rules)
		{
			rules.Validate();
			WriteJson("classification.json", rules);
			ReloadDisk(initial: false);
			signature = Signature();
		}

		internal static void EditRules(TargetRules rules)
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			rules.Validate();
			if (!CanEditRules)
			{
				throw new InvalidOperationException("Only the host or a server administrator can change classification.");
			}
			if (IsRemote)
			{
				ZPackage val = new ZPackage();
				val.Write(digest);
				val.Write(JsonConvert.SerializeObject((object)rules));
				router.InvokeRoutedRPC(ServerPeer, "ECE_ClassificationEdit_v1", new object[1] { val });
				Plugin.Log.LogInfo((object)"Classification change requested; waiting for authoritative server settings.");
			}
			else
			{
				SaveRules(rules);
			}
		}

		internal static void EditDisplay(RuntimeOptions options)
		{
			options.Validate();
			WriteJson("runtime.json", options);
			ReloadDisk(initial: false);
			signature = Signature();
		}

		internal static void Update()
		{
			EnsureRouter();
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (realtimeSinceStartup >= nextPoll)
			{
				nextPoll = realtimeSinceStartup + 0.5f;
				string text = Signature();
				if (text != signature)
				{
					try
					{
						ReloadDisk(initial: false);
						signature = text;
						rejectedSignature = null;
					}
					catch (Exception ex)
					{
						if (rejectedSignature != text)
						{
							Plugin.Log.LogWarning((object)("JSON reload rejected; keeping last valid values: " + ex.Message));
						}
						rejectedSignature = text;
					}
				}
			}
			if (router != null && (Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer() && realtimeSinceStartup >= nextRequest)
			{
				nextRequest = realtimeSinceStartup + 2f;
				long serverPeer = ServerPeer;
				if (serverPeer != 0L)
				{
					router.InvokeRoutedRPC(serverPeer, "ECE_SettingsRequest_v2", new object[1] { (remote == null) ? "" : digest });
				}
			}
		}
	}
	public sealed class VitalityState
	{
		public const double Conversion = 0.2;

		public const double Duration = 5.0;

		public const double RefreshDuration = 2.5;

		public double Bonus { get; private set; }

		public double Cap { get; private set; }

		public double Expires { get; private set; }

		public bool Locked { get; private set; }

		public void Reset()
		{
			Bonus = 0.0;
			Cap = 0.0;
			Expires = 0.0;
			Locked = false;
		}

		public bool Expire(double now)
		{
			if (Bonus > 0.0 && now >= Expires)
			{
				Reset();
				return true;
			}
			return false;
		}

		public bool AddDamage(double now, double damage, double weaponCap, double conversion = 0.2, double duration = 5.0, double maxCap = double.MaxValue, double refreshDuration = 2.5)
		{
			Expire(now);
			if (!WoundLedger.Finite(now) || !WoundLedger.Finite(damage) || !WoundLedger.Finite(weaponCap) || damage <= 0.0 || weaponCap <= 0.0 || !WoundLedger.Finite(conversion) || conversion < 0.0 || !WoundLedger.Finite(duration) || duration <= 0.0 || !WoundLedger.Finite(maxCap) || maxCap <= 0.0 || !WoundLedger.Finite(refreshDuration) || refreshDuration <= 0.0 || Locked)
			{
				return false;
			}
			double num = Math.Min(maxCap, weaponCap);
			Cap = ((Bonus > 0.0) ? Math.Min(Cap, num) : num);
			Bonus = Math.Min(Cap, Bonus + damage * conversion);
			Locked = Bonus >= Cap;
			Expires = now + (Locked ? duration : refreshDuration);
			return true;
		}
	}
	public sealed class ReceiptState
	{
		public readonly double Deadline;

		public bool Resolved { get; private set; }

		public ReceiptState(double now)
		{
			Deadline = now + 8.0;
		}

		public bool Resolve()
		{
			if (Resolved)
			{
				return false;
			}
			Resolved = true;
			return true;
		}

		public bool Timeout(double now)
		{
			if (now >= Deadline)
			{
				return Resolve();
			}
			return false;
		}
	}
	public sealed class EclipseOptions
	{
		public bool ShowDamageText = true;

		public string DamageTextColor = "#C77DFF";

		public string DamageTextFormat = "HP -{damage}";

		public int DamageTextDecimals = 1;

		public double TextVerticalOffset = 0.8;

		public double TextHorizontalOffset = 0.65;

		[JsonIgnore]
		public double DurationSeconds => 3.0;

		[JsonIgnore]
		public double TickIntervalSeconds => 0.25;

		public string FormatDamage(double amount)
		{
			string text = "0" + ((DamageTextDecimals > 0) ? ("." + new string('#', DamageTextDecimals)) : "");
			string text2 = amount.ToString(text, CultureInfo.InvariantCulture);
			if (amount > 0.0 && text2 == "0")
			{
				text2 = amount.ToString("0.###", CultureInfo.InvariantCulture);
			}
			if (amount > 0.0 && text2 == "0")
			{
				text2 = amount.ToString("0.###E+0", CultureInfo.InvariantCulture);
			}
			return DamageTextFormat.Replace("{damage}", text2);
		}
	}
	public sealed class AwakeningOptions
	{
		[JsonIgnore]
		public double DamageConversionPercent => 20.0;

		[JsonIgnore]
		public double DurationSeconds => 5.0;
	}
	public sealed class RuntimeOptions
	{
		public bool Diagnostics;

		public EclipseOptions Eclipse = new EclipseOptions();

		[JsonIgnore]
		public AwakeningOptions Awakening = new AwakeningOptions();

		public void Validate()
		{
			if (Eclipse == null || Awakening == null)
			{
				throw new ArgumentException("Eclipse/Awakening sections are required");
			}
			Range(Eclipse.DurationSeconds, 0.1, 120.0, "Eclipse.DurationSeconds");
			Range(Eclipse.TickIntervalSeconds, 0.05, 3.0, "Eclipse.TickIntervalSeconds");
			if (Eclipse.TickIntervalSeconds > Eclipse.DurationSeconds)
			{
				throw new ArgumentException("TickIntervalSeconds exceeds duration");
			}
			Range(Eclipse.TextVerticalOffset, -10.0, 10.0, "TextVerticalOffset");
			Range(Eclipse.TextHorizontalOffset, -10.0, 10.0, "TextHorizontalOffset");
			if (Eclipse.DamageTextDecimals < 0 || Eclipse.DamageTextDecimals > 3)
			{
				throw new ArgumentException("DamageTextDecimals must be 0..3");
			}
			if (Eclipse.DamageTextColor == null || !Regex.IsMatch(Eclipse.DamageTextColor, "^#[0-9a-fA-F]{6}([0-9a-fA-F]{2})?$"))
			{
				throw new ArgumentException("DamageTextColor must be #RRGGBB or #RRGGBBAA");
			}
			if (Eclipse.DamageTextFormat == null || Eclipse.DamageTextFormat.Length > 80 || !Eclipse.DamageTextFormat.Contains("{damage}"))
			{
				throw new ArgumentException("DamageTextFormat must contain {damage}, max 80 characters");
			}
			Range(Awakening.DamageConversionPercent, 0.0, 1000.0, "DamageConversionPercent");
			Range(Awakening.DurationSeconds, 0.1, 120.0, "Awakening.DurationSeconds");
		}

		private static void Range(double value, double min, double max, string label)
		{
			if (!WoundLedger.Finite(value) || value < min || value > max)
			{
				throw new ArgumentException(label + " must be in " + min + ".." + max);
			}
		}
	}
	public static class LedgerCodec
	{
		public static byte[] Write(WoundLedger ledger)
		{
			if (ledger.Windows.Count == 0)
			{
				return new byte[0];
			}
			using MemoryStream memoryStream = new MemoryStream();
			using BinaryWriter binaryWriter = new BinaryWriter(memoryStream);
			binaryWriter.Write(3);
			binaryWriter.Write(ledger.Windows.Count);
			foreach (WoundWindow window in ledger.Windows)
			{
				binaryWriter.Write(window.UserId);
				binaryWriter.Write(window.ObjectId);
				binaryWriter.Write(window.End);
				binaryWriter.Write(window.IntegratedThrough);
				binaryWriter.Write(window.Remaining);
				binaryWriter.Write(window.Pending);
			}
			return memoryStream.ToArray();
		}

		public static WoundLedger Read(byte[] bytes)
		{
			WoundLedger woundLedger = new WoundLedger();
			if (bytes == null || bytes.Length == 0)
			{
				return woundLedger;
			}
			using BinaryReader binaryReader = new BinaryReader(new MemoryStream(bytes));
			int num = binaryReader.ReadInt32();
			if (num != 2 && num != 3)
			{
				throw new InvalidDataException("Unknown snapshot version");
			}
			int num2 = binaryReader.ReadInt32();
			if (num2 < 0 || num2 > (bytes.Length - 8) / 44)
			{
				throw new InvalidDataException("Invalid snapshot size");
			}
			for (int i = 0; i < num2; i++)
			{
				WoundWindow woundWindow = new WoundWindow
				{
					UserId = binaryReader.ReadInt64(),
					ObjectId = binaryReader.ReadUInt32(),
					End = binaryReader.ReadDouble(),
					IntegratedThrough = binaryReader.ReadDouble(),
					Remaining = binaryReader.ReadDouble(),
					Pending = binaryReader.ReadDouble()
				};
				if (!WoundLedger.Finite(woundWindow.End) || !WoundLedger.Finite(woundWindow.IntegratedThrough) || !WoundLedger.Finite(woundWindow.Remaining) || !WoundLedger.Finite(woundWindow.Pending) || woundWindow.Remaining < 0.0 || woundWindow.Pending < 0.0 || woundWindow.IntegratedThrough > woundWindow.End)
				{
					throw new InvalidDataException("Invalid wound window");
				}
				if (woundWindow.IntegratedThrough == woundWindow.End && woundWindow.Remaining > 0.0)
				{
					throw new InvalidDataException("Expired wound contains unpaid future damage");
				}
				woundLedger.Windows.Add(woundWindow);
			}
			if (binaryReader.BaseStream.Position != binaryReader.BaseStream.Length)
			{
				throw new InvalidDataException("Trailing snapshot bytes");
			}
			return woundLedger;
		}
	}
	[BepInPlugin("local.epicloot.customeffects", "EpicLoot Custom Effects", "1.0.11")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string Guid = "local.epicloot.customeffects";

		public const string Version = "1.0.11";

		internal const string Effect = "Eclipse";

		internal const string Rpc = "ECE_DirectHit_v5";

		internal const string SnapshotKey = "cw_windows_v2";

		internal static ManualLogSource Log;

		internal static bool Ready;

		internal static readonly List<MagicItemEffectDefinition> Definitions = new List<MagicItemEffectDefinition>();

		internal static readonly ConditionalWeakTable<HitData, HitTag> Tags = new ConditionalWeakTable<HitData, HitTag>();

		internal static readonly ConditionalWeakTable<Character, Tracker> Trackers = new ConditionalWeakTable<Character, Tracker>();

		internal static readonly MethodInfo ReceiveDamage = AccessTools.Method(typeof(Character), "RPC_Damage", (Type[])null, (Type[])null);

		internal static readonly MethodInfo FindWeakSpot = AccessTools.Method(typeof(Character), "FindWeakSpotIndex", (Type[])null, (Type[])null);

		internal static readonly FieldInfo HitCollider = AccessTools.Field(typeof(HitData), "m_hitCollider");

		private Harmony harmony;

		private string settingsDirectory;

		internal static double Now
		{
			get
			{
				if (!((Object)(object)ZNet.instance != (Object)null))
				{
					return Time.time;
				}
				return ZNet.instance.GetTimeSeconds();
			}
		}

		private void Awake()
		{
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			try
			{
				settingsDirectory = Path.Combine(Paths.ConfigPath, "EpicLootCustomEffects");
				ConfigStorage.Initialize(settingsDirectory, Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), LiveSettings.Files, ReadEmbedded);
				LocalizationDefaults.AddPrecisionKeys(settingsDirectory, ReadEmbedded);
				LiveSettings.Initialize(settingsDirectory, ReadText);
				((BaseUnityPlugin)this).Logger.LogDebug((object)("Settings directory: " + settingsDirectory));
				try
				{
					ConfigStorage.ArchiveLegacy(settingsDirectory, Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location), LiveSettings.Files);
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Legacy settings loaded, but archiving was incomplete: " + ex.Message));
				}
				ConfigBridge.Initialize(((BaseUnityPlugin)this).Config);
				if (ReceiveDamage == null || FindWeakSpot == null || HitCollider == null)
				{
					throw new MissingMethodException("Required Valheim damage methods were not found");
				}
				harmony = new Harmony("local.epicloot.customeffects");
				harmony.PatchAll(typeof(Plugin).Assembly);
				MagicItemEffectDefinitions.OnSetupMagicItemEffectDefinitions += RegisterEffect;
				RegisterEffect();
				Ready = true;
			}
			catch (Exception ex2)
			{
				Ready = false;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
				((BaseUnityPlugin)this).Logger.LogError((object)("EpicLoot Custom Effects could not start; gameplay patches were rolled back. " + ex2));
			}
		}

		private string ReadText(string name)
		{
			return File.ReadAllText(Path.Combine(settingsDirectory, name));
		}

		private string ReadEmbedded(string name)
		{
			string text = (name.StartsWith("localizations/", StringComparison.Ordinal) ? Path.GetFileName(name) : name);
			using Stream stream = typeof(Plugin).Assembly.GetManifestResourceStream("EpicLootCustomEffects." + text.Replace('/', '.'));
			if (stream == null)
			{
				throw new FileNotFoundException("Missing embedded default: " + name);
			}
			using StreamReader streamReader = new StreamReader(stream);
			return streamReader.ReadToEnd();
		}

		internal static void RegisterEffect()
		{
			foreach (MagicItemEffectDefinition definition in Definitions)
			{
				MagicItemEffectDefinition val = definition;
				if (definition.Type.StartsWith("MinorKiller", StringComparison.Ordinal))
				{
					string key = definition.Type.Substring("MinorKiller".Length);
					if (LiveSettings.Rules.Biomes[key].Length == 0)
					{
						val = JsonConvert.DeserializeObject<MagicItemEffectDefinition>(JsonConvert.SerializeObject((object)definition));
						val.Requirements.NoRoll = true;
					}
				}
				MagicItemEffectDefinitions.Add(val);
			}
		}

		private void Update()
		{
			if (Ready)
			{
				EclipseDisplay.Update();
				LiveSettings.Update();
				ConfigBridge.Update();
				VitalityRuntime.Update();
			}
		}

		private void OnDestroy()
		{
			VitalityRuntime.Shutdown();
			Ready = false;
			MagicItemEffectDefinitions.OnSetupMagicItemEffectDefinitions -= RegisterEffect;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}

		internal static ZNetView View(Character c)
		{
			if (!((Object)(object)c == (Object)null))
			{
				return ((Component)c).GetComponent<ZNetView>();
			}
			return null;
		}

		internal static bool EligibleTarget(Character c)
		{
			if ((Object)(object)c != (Object)null && !c.IsPlayer() && !c.IsTamed() && !c.IsDead())
			{
				return c.GetHealth() > 0f;
			}
			return false;
		}

		internal static bool Direct(HitData h)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Invalid comparison between Unknown and I4
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Invalid comparison between Unknown and I4
			if (h != null)
			{
				if ((int)h.m_hitType != 2)
				{
					return (int)h.m_hitType == 0;
				}
				return true;
			}
			return false;
		}

		internal static bool IsWound(HitData h)
		{
			if (h != null && Tags.TryGetValue(h, out var value))
			{
				return value.Wound;
			}
			return false;
		}

		public static void ApplyModifierUnlessWound(HitData hit, float multiplier)
		{
			if (!IsWound(hit))
			{
				hit.ApplyModifier(multiplier);
			}
		}

		public static float MinimumDamage(HitData hit)
		{
			if (!IsWound(hit))
			{
				return 0.1f;
			}
			return 0f;
		}

		internal static void Register(Character c)
		{
			if (!Ready)
			{
				return;
			}
			ZNetView val = View(c);
			if ((Object)(object)val == (Object)null || !val.IsValid())
			{
				return;
			}
			Player val2 = (Player)(object)((c is Player) ? c : null);
			if ((Object)(object)val2 != (Object)null)
			{
				VitalityRuntime.Register(val2, val);
			}
			if (!Trackers.TryGetValue(c, out var _))
			{
				Tracker tracker = new Tracker(c, val);
				Trackers.Add(c, tracker);
				val.Register<ZPackage>("ECE_DirectHit_v5", (Action<long, ZPackage>)delegate(long sender, ZPackage package)
				{
					tracker.Receive(sender, package);
				});
			}
		}
	}
	public sealed class WoundWindow
	{
		public long UserId;

		public uint ObjectId;

		public double End;

		public double IntegratedThrough;

		public double Remaining;

		public double Pending;

		public string Key => UserId + ":" + ObjectId;

		public void Advance(double now)
		{
			double num = Math.Min(now, End);
			if (!(num <= IntegratedThrough))
			{
				double num2 = Remaining * Math.Min(1.0, (num - IntegratedThrough) / (End - IntegratedThrough));
				Pending += num2;
				Remaining -= num2;
				IntegratedThrough = num;
			}
		}
	}
	public struct WoundPayment
	{
		public long UserId;

		public uint ObjectId;

		public double Damage;

		public WoundPayment(WoundWindow w)
		{
			UserId = w.UserId;
			ObjectId = w.ObjectId;
			Damage = w.Pending;
		}
	}
	public sealed class WoundLedger
	{
		public const double Duration = 3.0;

		public readonly List<WoundWindow> Windows = new List<WoundWindow>();

		public static bool Finite(double v)
		{
			if (!double.IsNaN(v))
			{
				return !double.IsInfinity(v);
			}
			return false;
		}

		public void AddHit(long uid, uint id, double now, double damage, double percent, double duration = 3.0)
		{
			if (Finite(now) && Finite(damage) && Finite(percent) && !(damage <= 0.0) && !(percent <= 0.0) && Finite(duration) && !(duration <= 0.0))
			{
				double num = damage * (percent / 100.0);
				double num2 = now + duration;
				if (Finite(num) && !(num <= 0.0) && Finite(num2) && !(num2 <= now))
				{
					Windows.Add(new WoundWindow
					{
						UserId = uid,
						ObjectId = id,
						End = num2,
						IntegratedThrough = now,
						Remaining = num
					});
				}
			}
		}

		public List<WoundPayment> Drain(double now)
		{
			return Drain(now, null);
		}

		public List<WoundPayment> Drain(double now, string attackerKey)
		{
			List<WoundPayment> list = new List<WoundPayment>();
			if (!Finite(now))
			{
				return list;
			}
			Dictionary<string, int> dictionary = new Dictionary<string, int>();
			foreach (WoundWindow window in Windows)
			{
				if (attackerKey != null && window.Key != attackerKey)
				{
					continue;
				}
				window.Advance(now);
				if (window.Pending > 0.0)
				{
					if (dictionary.TryGetValue(window.Key, out var value))
					{
						WoundPayment value2 = list[value];
						value2.Damage += window.Pending;
						list[value] = value2;
					}
					else
					{
						dictionary.Add(window.Key, list.Count);
						list.Add(new WoundPayment(window));
					}
				}
				window.Pending = 0.0;
			}
			Windows.RemoveAll((WoundWindow w) => (attackerKey == null || w.Key == attackerKey) && now >= w.End);
			return list;
		}
	}
	internal sealed class HitTag
	{
		internal bool Wound;

		internal bool Bonus;

		internal bool Precision;

		internal bool Consumed;

		internal float Percent;

		internal float VitalityCap;

		internal long RequestId;

		internal double ActualDamage;

		internal ZDOID Attacker;
	}
	internal sealed class Tracker
	{
		private readonly Character target;

		private readonly ZNetView view;

		private WoundLedger ledger = new WoundLedger();

		private bool owned;

		private readonly WoundClock clock = new WoundClock();

		internal Tracker(Character c, ZNetView v)
		{
			target = c;
			view = v;
		}

		private bool EnsureOwner()
		{
			if ((Object)(object)view == (Object)null || !view.IsValid() || !view.IsOwner())
			{
				owned = false;
				return false;
			}
			if (!owned)
			{
				owned = true;
				clock.Reset();
				byte[] byteArray = view.GetZDO().GetByteArray("cw_windows_v2", (byte[])null);
				try
				{
					ledger = LedgerCodec.Read(byteArray);
				}
				catch (Exception ex)
				{
					ledger = new WoundLedger();
					Plugin.Log.LogWarning((object)("Discarding invalid wound snapshot: " + ex.Message));
				}
			}
			return true;
		}

		private void Save()
		{
			view.GetZDO().Set("cw_windows_v2", LedgerCodec.Write(ledger));
		}

		internal void Receive(long sender, ZPackage package)
		{
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.Ready || !EnsureOwner())
			{
				return;
			}
			HitData val2;
			HitTag hitTag;
			try
			{
				bool flag = package.ReadBool();
				bool flag2 = package.ReadBool();
				float num = package.ReadSingle();
				float num2 = package.ReadSingle();
				long requestId = package.ReadLong();
				float[] array = new float[KillerRuntime.Effects.Length];
				for (int i = 0; i < array.Length; i++)
				{
					array[i] = package.ReadSingle();
					if (!WoundLedger.Finite(array[i]) || array[i] < 0f || array[i] > 1000f)
					{
						return;
					}
				}
				ZPackage val = package.ReadPackage();
				val2 = new HitData();
				val2.Deserialize(ref val);
				Character attacker = val2.GetAttacker();
				Player val3 = (Player)(object)((attacker is Player) ? attacker : null);
				ZNetView val4 = Plugin.View((Character)(object)val3);
				if ((Object)(object)val3 == (Object)null || (Object)(object)val4 == (Object)null || !val4.IsValid() || val4.GetZDO().GetOwner() != sender || !WoundLedger.Finite(num) || num < 0f || num > 1000f || !WoundLedger.Finite(num2) || num2 < 0f || num2 > 100000f || !Plugin.Direct(val2))
				{
					return;
				}
				hitTag = new HitTag
				{
					Bonus = flag,
					Precision = (flag2 && !flag),
					Percent = num,
					VitalityCap = num2,
					RequestId = requestId,
					Attacker = val2.m_attacker
				};
				Plugin.Tags.Add(val2, hitTag);
				if (!flag)
				{
					KillerRuntime.Apply(target, val3, val2, array);
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Rejected malformed wound hit: " + ex.Message));
				return;
			}
			try
			{
				using (BonusDamageContext.Enter(hitTag.Bonus))
				{
					Plugin.ReceiveDamage.Invoke(target, new object[2] { sender, val2 });
				}
			}
			finally
			{
				VitalityRuntime.SendReceipt(hitTag, target);
			}
		}

		internal unsafe void Record(ZDOID attacker, double amount, double percent)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: 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 (EnsureOwner() && Plugin.EligibleTarget(target))
			{
				if (ConfigBridge.Diagnostics)
				{
					ManualLogSource log = Plugin.Log;
					string[] obj = new string[8]
					{
						"[EclipseRecord] target=",
						((object)target.GetZDOID()/*cast due to .constrained prefix*/).ToString(),
						" attacker=",
						null,
						null,
						null,
						null,
						null
					};
					ZDOID val = attacker;
					obj[3] = ((object)(*(ZDOID*)(&val))/*cast due to .constrained prefix*/).ToString();
					obj[4] = " damage=";
					obj[5] = amount.ToString();
					obj[6] = " percent=";
					obj[7] = percent.ToString();
					log.LogInfo((object)string.Concat(obj));
				}
				ledger.AddHit(((ZDOID)(ref attacker)).UserID, ((ZDOID)(ref attacker)).ID, Plugin.Now, amount, percent, LiveSettings.Current.Eclipse.DurationSeconds);
				Save();
			}
		}

		internal void Tick()
		{
			if (!Plugin.Ready || !EnsureOwner())
			{
				return;
			}
			if (!Plugin.EligibleTarget(target))
			{
				if (ledger.Windows.Count > 0)
				{
					ledger.Windows.Clear();
					Save();
				}
				return;
			}
			double now = Plugin.Now;
			if (ledger.Windows.Count == 0)
			{
				clock.Reset();
				return;
			}
			List<WoundPayment> list = clock.DrainDue(ledger, now, LiveSettings.Current.Eclipse.TickIntervalSeconds);
			if (list.Count == 0)
			{
				return;
			}
			Save();
			foreach (WoundPayment item in list)
			{
				if (!Plugin.EligibleTarget(target) || !view.IsOwner())
				{
					break;
				}
				Pay(item);
			}
		}

		private unsafe void Pay(WoundPayment payment)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: 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_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: 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_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			if (!(payment.Damage <= 0.0) && WoundLedger.Finite(payment.Damage) && view.IsOwner() && Plugin.EligibleTarget(target))
			{
				HitData val = new HitData();
				val.m_damage.m_damage = (float)Math.Min(payment.Damage, 3.4028234663852886E+38);
				val.m_attacker = new ZDOID(payment.UserId, payment.ObjectId);
				val.m_point = target.GetCenterPoint();
				val.m_dodgeable = false;
				val.m_blockable = false;
				val.m_staggerMultiplier = 0f;
				val.m_pushForce = 0f;
				val.m_backstabBonus = 1f;
				val.m_hitType = (HitType)0;
				Plugin.Tags.Add(val, new HitTag
				{
					Wound = true
				});
				float health = target.GetHealth();
				target.ApplyDamage(val, false, false, (DamageModifier)0);
				double actual = Math.Max(0f, health - Math.Max(0f, target.GetHealth()));
				EclipseDisplay.Show(val.m_point, actual, val.m_attacker);
				if (ConfigBridge.Diagnostics)
				{
					ManualLogSource log = Plugin.Log;
					string[] obj = new string[8]
					{
						"[EclipsePay] target=",
						((object)target.GetZDOID()/*cast due to .constrained prefix*/).ToString(),
						" attacker=",
						null,
						null,
						null,
						null,
						null
					};
					ZDOID attacker = val.m_attacker;
					obj[3] = ((object)(*(ZDOID*)(&attacker))/*cast due to .constrained prefix*/).ToString();
					obj[4] = " scheduled=";
					obj[5] = payment.Damage.ToString();
					obj[6] = " actual=";
					obj[7] = actual.ToString();
					log.LogInfo((object)string.Concat(obj));
				}
			}
		}
	}
	[HarmonyPatch(typeof(Character), "Awake")]
	internal static class RegisterCharacterPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Character __instance)
		{
			Plugin.Register(__instance);
		}
	}
	[HarmonyPatch(typeof(Character), "CustomFixedUpdate")]
	internal static class TickPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Character __instance)
		{
			if (Plugin.Ready)
			{
				if (!Plugin.Trackers.TryGetValue(__instance, out var value))
				{
					Plugin.Register(__instance);
					Plugin.Trackers.TryGetValue(__instance, out value);
				}
				value?.Tick();
			}
		}
	}
	[HarmonyPatch(typeof(Character), "Damage")]
	internal static class RouteHitPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(0)]
		private static bool Prefix(Character __instance, HitData hit)
		{
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Expected O, but got Unknown
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Expected O, but got Unknown
			if (!Plugin.Ready || !Plugin.EligibleTarget(__instance) || !Plugin.Direct(hit) || Plugin.IsWound(hit))
			{
				return true;
			}
			Character attacker = hit.GetAttacker();
			Player val = (Player)(object)((attacker is Player) ? attacker : null);
			if ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			float num = PlayerExtensions.GetTotalActiveMagicEffectValue(val, "Eclipse", 1f, (ItemData)null);
			bool isBonusHit = HitSource.IsBonusHit;
			if (!WoundLedger.Finite(num))
			{
				num = 0f;
			}
			bool flag = !isBonusHit && PrecisionStrikeRuntime.HasEffect(val);
			float num2 = (isBonusHit ? 0f : VitalityRuntime.WeaponCap(val));
			if (ConfigBridge.Diagnostics)
			{
				KillerRuntime.Classify(__instance, val);
			}
			float[] array = (isBonusHit ? new float[KillerRuntime.Effects.Length] : KillerRuntime.Values(val));
			if (!flag && num <= 0f && num2 <= 0f && !array.Any((float x) => x > 0f))
			{
				return true;
			}
			ZNetView val2 = Plugin.View(__instance);
			if ((Object)(object)val2 == (Object)null || !val2.IsValid())
			{
				return true;
			}
			Plugin.Register(__instance);
			hit.m_weakSpot = (short)Plugin.FindWeakSpot.Invoke(__instance, new object[1] { Plugin.HitCollider.GetValue(hit) });
			ZPackage val3 = new ZPackage();
			hit.Serialize(ref val3);
			ZPackage val4 = new ZPackage();
			long num3 = VitalityRuntime.Begin(val, __instance, hit, num2);
			val4.Write(isBonusHit);
			val4.Write(flag);
			val4.Write(Math.Min(Math.Max(num, 0f), 1000f));
			val4.Write(num2);
			val4.Write(num3);
			float[] array2 = array;
			foreach (float num5 in array2)
			{
				val4.Write(num5);
			}
			val4.Write(val3);
			val2.InvokeRPC("ECE_DirectHit_v5", new object[1] { val4 });
			return false;
		}
	}
	[HarmonyPatch(typeof(Character), "ApplyDamage")]
	internal static class CaptureDamagePatch
	{
		internal sealed class State
		{
			internal bool Capture;

			internal float Health;

			internal HitTag Tag;

			internal Character Target;

			internal double Nested;
		}

		[ThreadStatic]
		private static List<State> active;

		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void Prefix(Character __instance, HitData hit, out State __state)
		{
			__state = new State
			{
				Health = __instance.GetHealth(),
				Target = __instance
			};
			if (active == null)
			{
				active = new List<State>();
			}
			active.Add(__state);
			if (Plugin.Ready && Plugin.Tags.TryGetValue(hit, out var value) && !value.Wound && !value.Consumed && (!(value.Percent <= 0f) || !(value.VitalityCap <= 0f)))
			{
				ZNetView val = Plugin.View(__instance);
				if (!((Object)(object)val == (Object)null) && val.IsValid() && val.IsOwner() && Plugin.EligibleTarget(__instance))
				{
					value.Consumed = true;
					__state.Capture = true;
					__state.Tag = value;
				}
			}
		}

		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void Postfix(Character __instance, State __state)
		{
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			if (__state == null)
			{
				return;
			}
			double num = Math.Max(0f, __state.Health - Math.Max(0f, __instance.GetHealth()));
			for (int num2 = active.IndexOf(__state) - 1; num2 >= 0; num2--)
			{
				if ((Object)(object)active[num2].Target == (Object)(object)__instance)
				{
					active[num2].Nested += num;
					break;
				}
			}
			active.Remove(__state);
			if (__state.Capture)
			{
				double num3 = Math.Max(0.0, num - __state.Nested);
				__state.Tag.ActualDamage = num3;
				if (!(num3 <= 0.0) && !(__state.Tag.Percent <= 0f) && Plugin.EligibleTarget(__instance) && Plugin.Trackers.TryGetValue(__instance, out var value))
				{
					value.Record(__state.Tag.Attacker, num3, __state.Tag.Percent);
				}
			}
		}

		[HarmonyFinalizer]
		private static Exception Finalizer(Exception __exception, State __state)
		{
			if (__state != null && active != null)
			{
				active.Remove(__state);
			}
			return __exception;
		}

		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Expected O, but got Unknown
			List<CodeInstruction> list = instructions.ToList();
			MethodInfo methodInfo = AccessTools.Method(typeof(HitData), "ApplyModifier", new Type[1] { typeof(float) }, (Type[])null);
			MethodInfo operand = AccessTools.Method(typeof(Plugin), "ApplyModifierUnlessWound", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(Plugin), "MinimumDamage", (Type[])null, (Type[])null);
			int num = 0;
			int num2 = 0;
			List<CodeInstruction> list2 = new List<CodeInstruction>();
			foreach (CodeInstruction item in list)
			{
				if (CodeInstructionExtensions.Calls(item, methodInfo))
				{
					item.opcode = OpCodes.Call;
					item.operand = operand;
					num++;
				}
				if (item.opcode == OpCodes.Ldc_R4 && item.operand is float && (float)item.operand == 0.1f)
				{
					item.opcode = OpCodes.Ldarg_1;
					item.operand = null;
					list2.Add(item);
					list2.Add(new CodeInstruction(OpCodes.Call, (object)methodInfo2));
					num2++;
				}
				else
				{
					list2.Add(item);
				}
			}
			if (num != 3 || num2 != 1)
			{
				throw new InvalidOperationException("Unsupported ApplyDamage layout: expected 3 scale calls and 1 minimum threshold, found " + num + " / " + num2);
			}
			return list2;
		}
	}
	internal sealed class VitalityTracker
	{
		internal readonly Player Player;

		internal readonly ZNetView View;

		internal readonly VitalityState State = new VitalityState();

		internal float BaseHealth;

		internal const string BonusKey = "ece_vitality_applied";

		private static readonly int MaxHealthKey = (int)AccessTools.Field(typeof(ZDOVars), "s_maxHealth").GetValue(null);

		internal bool Owner
		{
			get
			{
				if ((Object)(object)View != (Object)null && View.IsValid())
				{
					return View.IsOwner();
				}
				return false;
			}
		}

		internal VitalityTracker(Player player, ZNetView view)
		{
			Player = player;
			View = view;
			float num = view.GetZDO().GetFloat("ece_vitality_applied", 0f);
			if (!WoundLedger.Finite(num) || num < 0f || num > 100000f)
			{
				num = 0f;
			}
			BaseHealth = Math.Max(1f, ((Character)player).GetMaxHealth() - num);
			if (num > 0f && view.IsOwner())
			{
				Apply();
			}
		}

		internal void Apply()
		{
			if (Owner)
			{
				float num = Math.Max(1f, BaseHealth + (float)State.Bonus);
				View.GetZDO().Set(MaxHealthKey, num);
				View.GetZDO().Set("ece_vitality_applied", (float)State.Bonus);
				if (((Character)Player).GetHealth() > num)
				{
					((Character)Player).SetHealth(num);
				}
			}
		}

		internal void Expire(double now)
		{
			if (State.Expire(now))
			{
				Apply();
			}
		}

		internal void Confirm(double damage, double cap)
		{
			if (Owner && !((Character)Player).IsDead() && !(((Character)Player).GetHealth() <= 0f))
			{
				double now = Plugin.Now;
				Expire(now);
				if (State.AddDamage(now, damage, cap))
				{
					Apply();
				}
			}
		}

		internal void Reset()
		{
			State.Reset();
			Apply();
		}

		internal void SetIncomingBase(ref float health)
		{
			if (Owner && WoundLedger.Finite(health))
			{
				State.Expire(Plugin.Now);
				BaseHealth = health;
				health += (float)State.Bonus;
				View.GetZDO().Set("ece_vitality_applied", (float)State.Bonus);
			}
		}
	}
	internal struct DeferredHeal
	{
		internal float Amount;

		internal bool Show;
	}
	internal sealed class PendingVitalityHit
	{
		internal long Id;

		internal Player Player;

		internal Character Target;

		internal ZDOID TargetId;

		internal long TargetOwner;

		internal float Cap;

		internal ReceiptState Receipt;

		internal readonly List<DeferredHeal> Heals = new List<DeferredHeal>();

		internal void Flush()
		{
			DeferredHeal[] array = Heals.ToArray();
			Heals.Clear();
			if (!((Object)(object)Player == (Object)null) && !((Object)(object)Player != (Object)(object)Player.m_localPlayer) && !((Character)Player).IsDead() && !(((Character)Player).GetHealth() <= 0f))
			{
				DeferredHeal[] array2 = array;
				for (int i = 0; i < array2.Length; i++)
				{
					DeferredHeal deferredHeal = array2[i];
					((Character)Player).Heal(deferredHeal.Amount, deferredHeal.Show);
				}
			}
		}
	}
	internal static class VitalityRuntime
	{
		internal const string Effect = "Awakening";

		internal const string ReceiptRpc = "ECE_VitalityReceipt_v1";

		private static readonly ConditionalWeakTable<Player, VitalityTracker> Trackers = new ConditionalWeakTable<Player, VitalityTracker>();

		private static readonly ConditionalWeakTable<HitData, PendingVitalityHit> Outgoing = new ConditionalWeakTable<HitData, PendingVitalityHit>();

		private static readonly Dictionary<long, PendingVitalityHit> Pending = new Dictionary<long, PendingVitalityHit>();

		private static long sequence = DateTime.UtcNow.Ticks;

		private static double nextUpdate;

		internal static void Register(Player player, ZNetView view)
		{
			if (!Trackers.TryGetValue(player, out var _))
			{
				Trackers.Add(player, new VitalityTracker(player, view));
				view.Register<ZPackage>("ECE_VitalityReceipt_v1", (Action<long, ZPackage>)delegate(long sender, ZPackage package)
				{
					ReceiveReceipt(player, sender, package);
				});
			}
		}

		internal static VitalityTracker Get(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return null;
			}
			ZNetView val = Plugin.View((Character)(object)player);
			if ((Object)(object)val == (Object)null || !val.IsValid())
			{
				return null;
			}
			Register(player, val);
			Trackers.TryGetValue(player, out var value);
			return value;
		}

		internal static float WeaponCap(Player player)
		{
			if (HitSource.IsBonusHit)
			{
				return 0f;
			}
			ItemData activeWeapon = MagicEffectsHelper.GetActiveWeapon(player);
			MagicItem val = ((activeWeapon == null) ? null : ItemDataExtensions.GetMagicItem(activeWeapon));
			if (val == null)
			{
				return 0f;
			}
			float totalEffectValue = val.GetTotalEffectValue("Awakening", 1f, true);
			if (!WoundLedger.Finite(totalEffectValue) || totalEffectValue <= 0f)
			{
				return 0f;
			}
			return totalEffectValue;
		}

		internal static long Begin(Player player, Character target, HitData hit, float cap)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			if (cap <= 0f)
			{
				return 0L;
			}
			Get(player);
			ZNetView val = Plugin.View(target);
			PendingVitalityHit pendingVitalityHit = new PendingVitalityHit
			{
				Id = ++sequence,
				Player = player,
				Target = target,
				TargetId = target.GetZDOID(),
				TargetOwner = val.GetZDO().GetOwner(),
				Cap = cap,
				Receipt = new ReceiptState(Plugin.Now)
			};
			Pending.Add(pendingVitalityHit.Id, pendingVitalityHit);
			Outgoing.Remove(hit);
			Outgoing.Add(hit, pendingVitalityHit);
			return pendingVitalityHit.Id;
		}

		internal static PendingVitalityHit ForHeal(HitData hit, Character attacker)
		{
			if (hit == null || !Outgoing.TryGetValue(hit, out var value) || !((Object)(object)value.Player == (Object)(object)attacker) || value.Receipt.Resolved)
			{
				return null;
			}
			return value;
		}

		public static void HealAfterConfirmation(Character attacker, float amount, bool show, HitData hit)
		{
			PendingVitalityHit pendingVitalityHit = (Plugin.Ready ? ForHeal(hit, attacker) : null);
			if (pendingVitalityHit != null && WoundLedger.Finite(amount) && amount > 0f)
			{
				pendingVitalityHit.Heals.Add(new DeferredHeal
				{
					Amount = amount,
					Show = show
				});
			}
			else
			{
				attacker.Heal(amount, show);
			}
		}

		internal static void SendReceipt(HitTag tag, Character target)
		{
			//IL_0029: 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_0070: Expected O, but got Unknown
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			if (tag.RequestId != 0L && !(tag.VitalityCap <= 0f))
			{
				GameObject val = (((Object)(object)ZNetScene.instance == (Object)null) ? null : ZNetScene.instance.FindInstance(tag.Attacker));
				Player val2 = (((Object)(object)val == (Object)null) ? null : val.GetComponent<Player>());
				ZNetView val3 = Plugin.View((Character)(object)val2);
				if (!((Object)(object)val3 == (Object)null) && val3.IsValid())
				{
					Get(val2);
					ZPackage val4 = new ZPackage();
					val4.Write(tag.RequestId);
					val4.Write(target.GetZDOID());
					val4.Write(tag.ActualDamage);
					val3.InvokeRPC("ECE_VitalityReceipt_v1", new object[1] { val4 });
				}
			}
		}

		private static void ReceiveReceipt(Player player, long sender, ZPackage package)
		{
			//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_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			ZNetView val = Plugin.View((Character)(object)player);
			if (!Plugin.Ready || (Object)(object)val == (Object)null || !val.IsValid() || !val.IsOwner() || (Object)(object)player != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			double num;
			PendingVitalityHit value;
			try
			{
				long key = package.ReadLong();
				ZDOID val2 = package.ReadZDOID();
				num = package.ReadDouble();
				if (!Pending.TryGetValue(key, out value) || (Object)(object)value.Player != (Object)(object)player || value.TargetId != val2)
				{
					return;
				}
				ZNetView val3 = Plugin.View(value.Target);
				if ((sender != value.TargetOwner && (!((Object)(object)val3 != (Object)null) || !val3.IsValid() || val3.GetZDO().GetOwner() != sender)) || !WoundLedger.Finite(num) || num < 0.0)
				{
					return;
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Invalid vitality receipt: " + ex.Message));
				return;
			}
			if (value.Receipt.Timeout(Plugin.Now))
			{
				Pending.Remove(value.Id);
				value.Flush();
			}
			else if (value.Receipt.Resolve())
			{
				Pending.Remove(value.Id);
				Get(player)?.Confirm(num, value.Cap);
				value.Flush();
			}
		}

		internal static void Update()
		{
			double now = Plugin.Now;
			if (now < nextUpdate)
			{
				return;
			}
			nextUpdate = now + 0.05;
			Player localPlayer = Player.m_localPlayer;
			VitalityTracker vitalityTracker = Get(localPlayer);
			if (vitalityTracker != null && vitalityTracker.Owner)
			{
				if (((Character)localPlayer).IsDead() || ((Character)localPlayer).GetHealth() <= 0f)
				{
					Cancel(localPlayer);
					if (vitalityTracker.State.Bonus > 0.0)
					{
						vitalityTracker.Reset();
					}
				}
				else
				{
					vitalityTracker.Expire(now);
				}
			}
			List<long> list = new List<long>();
			foreach (KeyValuePair<long, PendingVitalityHit> item in Pending)
			{
				if (item.Value.Receipt.Timeout(now))
				{
					list.Add(item.Key);
				}
			}
			foreach (long item2 in list)
			{
				PendingVitalityHit pendingVitalityHit = Pending[item2];
				Pending.Remove(item2);
				pendingVitalityHit.Flush();
				Plugin.Log.LogWarning((object)"Vitality damage confirmation timed out; original life steal released without a new health bonus.");
			}
		}

		internal static void Cancel(Player player)
		{
			List<long> list = new List<long>();
			foreach (KeyValuePair<long, PendingVitalityHit> item in Pending)
			{
				if ((Object)(object)item.Value.Player == (Object)(object)player)
				{
					list.Add(item.Key);
				}
			}
			foreach (long item2 in list)
			{
				Pending[item2].Receipt.Resolve();
				Pending[item2].Heals.Clear();
				Pending.Remove(item2);
			}
		}

		internal static void Shutdown()
		{
			Get(Player.m_localPlayer)?.Reset();
			Pending.Clear();
		}
	}
	[HarmonyPatch(typeof(Character), "SetMaxHealth", new Type[] { typeof(float) })]
	internal static class VitalityMaxHealthPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(0)]
		private static void Prefix(Character __instance, ref float health)
		{
			if (Plugin.Ready)
			{
				Player val = (Player)(object)((__instance is Player) ? __instance : null);
				if (!((Object)(object)val == (Object)null))
				{
					VitalityRuntime.Get(val)?.SetIncomingBase(ref health);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnDeath")]
	internal static class VitalityDeathPatch
	{
		[HarmonyPrefix]
		private static void Prefix(Player __instance)
		{
			if (Plugin.Ready)
			{
				VitalityRuntime.Cancel(__instance);
				VitalityRuntime.Get(__instance)?.Reset();
			}
		}
	}
	[HarmonyPatch(typeof(AddLifeSteal), "CheckAndDoLifeSteal")]
	internal static class VitalityLifeStealOrderPatch
	{
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(Character), "Heal", new Type[2]
			{
				typeof(float),
				typeof(bool)
			}, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(VitalityRuntime), "HealAfterConfirmation", (Type[])null, (Type[])null);
			List<CodeInstruction> list = new List<CodeInstruction>();
			int num = 0;
			foreach (CodeInstruction instruction in instructions)
			{
				if (CodeInstructionExtensions.Calls(instruction, methodInfo))
				{
					instruction.opcode = OpCodes.Ldarg_0;
					instruction.operand = null;
					list.Add(instruction);
					list.Add(new CodeInstruction(OpCodes.Call, (object)methodInfo2));
					num++;
				}
				else
				{
					list.Add(instruction);
				}
			}
			if (num != 1)
			{
				throw new InvalidOperationException("Unsupported EpicLoot LifeSteal layout: expected one Heal call, found " + num);
			}
			return list;
		}
	}
	internal static class EclipseDisplay
	{
		private sealed class Opacity
		{
			internal object Field;

			internal PropertyInfo ColorProperty;

			internal float Scale;
		}

		private const string Rpc = "ECE_EclipseText_v2";

		private static ZRoutedRpc router;

		private static bool sent;

		private static bool received;

		private static bool rendered;

		private static float nextWarning;

		private static readonly ConditionalWeakTable<object, Opacity> opacities = new ConditionalWeakTable<object, Opacity>();

		private static readonly FieldInfo MaxDistance = AccessTools.Field(typeof(DamageText), "m_maxTextDistance");

		internal static void Update()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null && instance != router)
			{
				instance.Register<ZPackage>("ECE_EclipseText_v2", (Action<long, ZPackage>)Receive);
				router = instance;
				sent = (received = (rendered = false));
			}
		}

		internal static void Show(Vector3 point, double actual, ZDOID attacker)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			_ = LiveSettings.Current.Eclipse;
			if (!WoundLedger.Finite(actual) || actual <= 0.0)
			{
				return;
			}
			try
			{
				Update();
				if (router == null)
				{
					Warn("No routed RPC available for Eclipse text", null);
					return;
				}
				ZPackage val = new ZPackage();
				val.Write(((ZDOID)(ref attacker)).UserID);
				val.Write(((ZDOID)(ref attacker)).ID);
				val.Write(point);
				val.Write((float)actual);
				router.InvokeRoutedRPC(0L, "ECE_EclipseText_v2", new object[1] { val });
				if (!sent)
				{
					sent = true;
					Plugin.Log.LogDebug((object)("[EclipseText] sent first damage event: " + actual));
				}
			}
			catch (Exception ex)
			{
				Warn("Could not send Eclipse text", ex);
			}
		}

		private static void Receive(long sender, ZPackage package)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_024f: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				long attackerUser = package.ReadLong();
				uint attackerObject = package.ReadUInt();
				Vector3 val = package.ReadVector3();
				float num = package.ReadSingle();
				if (!WoundLedger.Finite(num) || num <= 0f || !Finite(val))
				{
					return;
				}
				if (!received)
				{
					received = true;
					Plugin.Log.LogDebug((object)"[EclipseText] received first damage event");
				}
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null)
				{
					return;
				}
				ZDOID zDOID = ((Character)localPlayer).GetZDOID();
				bool own = EclipseAudience.IsOwnDamage(attackerUser, attackerObject, ((ZDOID)(ref zDOID)).UserID, ((ZDOID)(ref zDOID)).ID);
				if (!LiveSettings.Current.Eclipse.ShowDamageText)
				{
					return;
				}
				EclipseOptions eclipse = LiveSettings.Current.Eclipse;
				val += new Vector3((float)eclipse.TextHorizontalOffset, (float)eclipse.TextVerticalOffset, 0f);
				DamageText instance = DamageText.instance;
				Camera mainCamera = Utils.GetMainCamera();
				if ((Object)(object)instance == (Object)null || (Object)(object)mainCamera == (Object)null || Hud.IsUserHidden())
				{
					return;
				}
				float num2 = Vector3.Distance(((Component)mainCamera).transform.position, val);
				float num3 = ((MaxDistance == null) ? 60f : ((float)MaxDistance.GetValue(instance)));
				if (num2 > num3)
				{
					return;
				}
				IList list = (IList)AccessTools.Field(typeof(DamageText), "m_worldTexts").GetValue(instance);
				int count = list.Count;
				EclipseOriginalRenderer.Add(instance, (TextType)0, val, num2, LiveSettings.Current.Eclipse.FormatDamage(num), mySelf: false);
				if (list.Count > count)
				{
					object obj = list[list.Count - 1];
					object value = AccessTools.Field(obj.GetType(), "m_textField").GetValue(obj);
					PropertyInfo propertyInfo = AccessTools.Property(value.GetType(), "color");
					string hex = LiveSettings.Current.Eclipse.DamageTextColor.Substring(1);
					Func<int, float> func = (int i) => (float)(int)byte.Parse(hex.Substring(i, 2), NumberStyles.HexNumber, CultureInfo.InvariantCulture) / 255f;
					float num4 = EclipseAudience.OpacityScale(own, (hex.Length == 8) ? func(6) : 1f);
					propertyInfo.SetValue(value, (object)new Color(func(0), func(2), func(4), num4), null);
					opacities.Add(obj, new Opacity
					{
						Field = value,
						ColorProperty = propertyInfo,
						Scale = num4
					});
				}
				if (!rendered)
				{
					rendered = true;
					Plugin.Log.LogDebug((object)"[EclipseText] rendered first damage event (1.0.11)");
				}
			}
			catch (Exception ex)
			{
				Warn("Could not render Eclipse text", ex);
			}
		}

		internal static void ApplyOpacity(DamageText ui)
		{
			//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_0072: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				foreach (object item in (IList)AccessTools.Field(typeof(DamageText), "m_worldTexts").GetValue(ui))
				{
					if (opacities.TryGetValue(item, out var value))
					{
						Color val = (Color)value.ColorProperty.GetValue(value.Field, null);
						val.a *= value.Scale;
						value.ColorProperty.SetValue(value.Field, val, null);
					}
				}
			}
			catch (Exception ex)
			{
				Warn("Could not update Eclipse opacity", ex);
			}
		}

		private static bool Finite(Vector3 p)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			if (WoundLedger.Finite(p.x) && WoundLedger.Finite(p.y))
			{
				return WoundLedger.Finite(p.z);
			}
			return false;
		}

		private static void Warn(string message, Exception ex)
		{
			if (!(Time.realtimeSinceStartup < nextWarning))
			{
				nextWarning = Time.realtimeSinceStartup + 10f;
				Plugin.Log.LogWarning((object)("[EclipseText] " + message + ((ex == null) ? "" : (" : " + ex))));
			}
		}
	}
	[HarmonyPatch(typeof(DamageText), "UpdateWorldTexts")]
	internal static class EclipseOpacityPatch
	{
		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void Postfix(DamageText __instance)
		{
			EclipseDisplay.ApplyOpacity(__instance);
		}
	}
	[HarmonyPatch(typeof(DamageText), "AddInworldText")]
	internal static class EclipseOriginalRenderer
	{
		[HarmonyReversePatch(/*Could not decode attribute arguments.*/)]
		internal static void Add(DamageText instance, TextType type, Vector3 pos, float distance, string text, bool mySelf)
		{
			throw new NotImplementedException("Original damage text renderer was not bound");
		}
	}
	[HarmonyPatch(/*Could not decode attribute arguments.*/)]
	internal static class EclipseTextRegistration
	{
		[HarmonyPostfix]
		private static void Postfix()
		{
			EclipseDisplay.Update();
		}
	}
	internal static class EffectIdentity
	{
		internal static string Canonical(string type)
		{
			if (type == "CumulativeWounds")
			{
				return "Eclipse";
			}
			if (type == "VitalityAccumulation")
			{
				return "Awakening";
			}
			return type;
		}
	}
	[HarmonyPatch(typeof(ItemDataExtensions), "GetMagicItem")]
	internal static class LegacyEffectIdentityPatch
	{
		[HarmonyPostfix]
		private static void Postfix(MagicItem __result)
		{
			if (__result == null || __result.Effects == null)
			{
				return;
			}
			foreach (MagicItemEffect effect in __result.Effects)
			{
				if (effect != null)
				{
					effect.EffectType = EffectIdentity.Canonical(effect.EffectType);
				}
			}
		}
	}
	public sealed class TargetRules
	{
		public static readonly string[] BiomeNames = new string[9] { "Meadows", "BlackForest", "Swamp", "Mountain", "Plains", "Mistlands", "Ashlands", "DeepNorth", "Ocean" };

		public bool UseGameBossFlag = true;

		public string[] AdditionalBosses = new string[0];

		public string[] ExcludedBosses = new string[0];

		public Dictionary<string, string[]> Biomes = new Dictionary<string, string[]>();

		[JsonIgnore]
		private HashSet<string> added;

		[JsonIgnore]
		private HashSet<string> excluded;

		[JsonIgnore]
		private Dictionary<string, string> membership;

		public void Validate()
		{
			if (AdditionalBosses == null || ExcludedBosses == null || Biomes == null)
			{
				throw new ArgumentException("Missing target lists");
			}
			added = List(AdditionalBosses);
			excluded = List(ExcludedBosses);
			membership = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
			foreach (KeyValuePair<string, string[]> biome in Biomes)
			{
				if (!BiomeNames.Contains(biome.Key))
				{
					throw new ArgumentException("Unknown biome: " + biome.Key);
				}
				foreach (string item in List(biome.Value))
				{
					if (membership.ContainsKey(item))
					{
						throw new ArgumentException("Prefab belongs to multiple biomes: " + item);
					}
					membership.Add(item, biome.Key);
				}
			}
			string[] biomeNames = BiomeNames;
			foreach (string text in biomeNames)
			{
				if (!Biomes.ContainsKey(text))
				{
					throw new ArgumentException("Missing biome: " + text);
				}
			}
		}

		private static HashSet<string> List(string[] values)
		{
			if (values == null || values.Length > 2048)
			{
				throw new ArgumentException("Invalid prefab list (maximum 2048 names)");
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (string text in values)
			{
				string text2 = ((text == null) ? "" : text.Trim());
				if (text2.Length == 0 || text2.Length > 160 || !Regex.IsMatch(text2, "^[\\w.\\-]+$"))
				{
					throw new ArgumentException("Use exact prefab IDs, not display names: " + text2);
				}
				hashSet.Add(text2);
			}
			return hashSet;
		}

		public string EffectFor(string prefab, bool gameBoss, bool hostile, bool excludedTarget)
		{
			if (excludedTarget)
			{
				return null;
			}
			if (added == null)
			{
				Validate();
			}
			if (!excluded.Contains(prefab) && (added.Contains(prefab) || (UseGameBossFlag && gameBoss)))
			{
				return "GiantKiller";
			}
			if (!hostile)
			{
				return null;
			}
			if (!membership.TryGetValue(prefab, out var value))
			{
				return null;
			}
			return "MinorKiller" + value;
		}

		public static double Multiplier(double percent)
		{
			if (!WoundLedger.Finite(percent) || !(percent > 0.0) || !(percent <= 1000.0))
			{
				return 1.0;
			}
			return 1.0 + percent / 100.0;
		}
	}
	internal static class KillerRuntime
	{
		internal static readonly string[] Effects = new string[1] { "GiantKiller" }.Concat(TargetRules.BiomeNames.Select((string x) => "MinorKiller" + x)).ToArray();

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

		internal static string Classify(Character target, Player player)
		{
			string text = ((Object)((Component)target).gameObject).name;
			if (text.EndsWith("(Clone)", StringComparison.Ordinal))
			{
				text = text.Substring(0, text.Length - 7);
			}
			text = text.Trim();
			bool hostile = target.GetBaseAI() is MonsterAI && BaseAI.IsEnemy(target, (Character)(object)player);
			string text2 = LiveSettings.Rules.EffectFor(text, target.IsBoss(), hostile, target.IsPlayer() || target.IsTamed() || target.IsDead());
			if (ConfigBridge.Diagnostics && reported.Count < 4096 && reported.Add(text + ":" + text2))
			{
				Plugin.Log.LogInfo((object)("[TargetClassification] " + text + " => " + (text2 ?? "unclassified/non-hostile") + "; gameBoss=" + target.IsBoss() + "; hostile=" + hostile));
			}
			return text2;
		}

		internal static float[] Values(Player player)
		{
			return Effects.Select(delegate(string effect)
			{
				float totalActiveMagicEffectValue = PlayerExtensions.GetTotalActiveMagicEffectValue(player, effect, 1f, (ItemData)null);
				return (!WoundLedger.Finite(totalActiveMagicEffectValue)) ? 0f : Math.Min(1000f, Math.Max(0f, totalActiveMagicEffectValue));
			}).ToArray();
		}

		internal static void Apply(Character target, Player player, HitData hit, float[] values)
		{
			if (LiveSettings.IsRemote && !LiveSettings.HasServerSettings)
			{
				return;
			}
			string value = Classify(target, player);
			int num = Array.IndexOf(Effects, value);
			if (num >= 0)
			{
				double num2 = TargetRules.Multiplier(values[num]);
				if (num2 > 1.0)
				{
					hit.ApplyModifier((float)num2);
				}
			}
		}
	}
	internal sealed class ConfigurationManagerAttributes
	{
		public bool ReadOnly;

		public int Order;
	}
	internal static class ConfigBridge
	{
		private static ConfigFile config;

		private static bool pushing;

		private static float next;

		private static float lastEdit;

		private static bool collecting;

		private static TargetRules draftRules;

		private static RuntimeOptions draftDisplay;

		private static readonly Dictionary<string, Action> pending = new Dictionary<string, Action>();

		private static readonly List<Action> refresh = new List<Action>();

		private static readonly List<ConfigurationManagerAttributes> ruleTags = new List<ConfigurationManagerAttributes>();

		internal static bool Diagnostics => LiveSettings.Current.Diagnostics;

		private static T Copy<T>(T value)
		{
			return JsonConvert.DeserializeObject<T>(JsonConvert.SerializeObject((object)value));
		}

		internal static void Initialize(ConfigFile file)
		{
			config = file;
			config.SaveOnConfigSet = false;
			Bind("01. 서버 몬스터 분류", "기본 보스 판정 사용", initial: true, server: true, () => LiveSettings.Rules.UseGameBossFlag, delegate(bool v)
			{
				Rule(delegate(TargetRules r)
				{
					r.UseGameBossFlag = v;
				});
			}, "게임의 보스 플래그를 사용합니다. 제외 목록이 추가 목록보다 우선합니다.");
			Bind("01. 서버 몬스터 분류", "보스 추가 목록", "", server: true, () => Join(LiveSettings.Rules.AdditionalBosses), delegate(string v)
			{
				Rule(delegate(TargetRules r)
				{
					r.AdditionalBosses = Split(v);
				});
			}, "보스로 취급할 정확한 프리팹 ID. 쉼표로 구분합니다. 번역된 이름은 사용하지 마세요.");
			Bind("01. 서버 몬스터 분류", "보스 제외 목록", "", server: true, () => Join(LiveSettings.Rules.ExcludedBosses), delegate(string v)
			{
				Rule(delegate(TargetRules r)
				{
					r.ExcludedBosses = Split(v);
				});
			}, "보스에서 제외할 프리팹 ID. 해당 바이옴에 등록되어 있고 적대적이면 마이너 킬러 대상입니다.");
			string[] array = new string[9] { "초원", "검은숲", "늪", "산", "평원", "안개 지대", "잿가루 지대", "심층 북부", "바다" };
			for (int num = 0; num < TargetRules.BiomeNames.Length; num++)
			{
				string biome = TargetRules.BiomeNames[num];
				Bind("02. 서버 바이옴 소속", array[num], "", server: true, () => Join(LiveSettings.Rules.Biomes[biome]), delegate(string v)
				{
					Rule(delegate(TargetRules r)
					{
						r.Biomes[biome] = Split(v);
					});
				}, "쉼표로 구분한 프리팹 ID. 전투 장소와 무관하게 이 바이옴 소속으로 판정합니다. 한 몬스터는 한 바이옴만 허용합니다. 빈 목록은 해당 효과의 새 추첨을 비활성화합니다.");
			}
			Bind("03. 개인 이클립스 표시", "피해 표시", initial: true, server: false, () => LiveSettings.Current.Eclipse.ShowDamageText, delegate(bool v)
			{
				Display(delegate(RuntimeOptions o)
				{
					o.Eclipse.ShowDamageText = v;
				});
			}, "나에게 보이는 이클립스 피해 텍스트 표시 여부입니다.");
			Bind("03. 개인 이클립스 표시", "색상", "#C77DFF", server: false, () => LiveSettings.Current.Eclipse.DamageTextColor, delegate(string v)
			{
				Display(delegate(RuntimeOptions o)
				{
					o.Eclipse.DamageTextColor = v;
				});
			}, "#RRGGBB 형식. 예: #C77DFF, #00FF00.");
			Bind("03. 개인 이클립스 표시", "표시 문구", "HP -{damage}", server: false, () => LiveSettings.Current.Eclipse.DamageTextFormat, delegate(string v)
			{
				Display(delegate(RuntimeOptions o)
				{
					o.Eclipse.DamageTextFormat = v;
				});
			}, "{damage}를 반드시 포함하세요. 색상 태그 없이 일반 텍스트를 입력합니다.");
			Bind("03. 개인 이클립스 표시", "소수 자릿수", 1, server: false, () => LiveSettings.Current.Eclipse.DamageTextDecimals, delegate(int v)
			{
				Display(delegate(RuntimeOptions o)
				{
					o.Eclipse.DamageTextDecimals = v;
				});
			}, "0~3자리", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 3));
			Bind("03. 개인 이클립스 표시", "세로 위치", 0.8, server: false, () => LiveSettings.Current.Eclipse.TextVerticalOffset, delegate(double v)
			{
				Display(delegate(RuntimeOptions o)
				{
					o.Eclipse.TextVerticalOffset = v;
				});
			}, "적의 중심에서 위아래 이동", (AcceptableValueBase)(object)new AcceptableValueRange<double>(-10.0, 10.0));
			Bind("03. 개인 이클립스 표시", "가로 위치", 0.65, server: false, () => LiveSettings.Current.Eclipse.TextHorizontalOffset, delegate(double v)
			{
				Display(delegate(RuntimeOptions o)
				{
					o.Eclipse.TextHorizontalOffset = v;
				});
			}, "표시의 좌우 이동", (AcceptableValueBase)(object)new AcceptableValueRange<double>(-10.0, 10.0));
			Bind("04. 진단", "몬스터 분류 로그", initial: false, server: false, () => LiveSettings.Current.Diagnostics, delegate(bool v)
			{
				Display(delegate(RuntimeOptions o)
				{
					o.Diagnostics = v;
				});
			}, "공격한 몬스터의 프리팹 ID와 분류를 BepInEx/LogOutput.log에 기록합니다. 같은 분류는 한 번만 기록됩니다.");
			Update();
		}

		private static string Join(string[] x)
		{
			return string.Join(", ", x);
		}

		private static string[] Split(string x)
		{
			return (from v in (x ?? "").Split(new char[4] { ',', ';', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries)
				select v.Trim() into v
				where v.Length > 0
				select v).ToArray();
		}

		private static void Rule(Action<TargetRules> edit)
		{
			if (!collecting)
			{
				throw new InvalidOperationException("Edit outside transaction");
			}
			if (draftRules == null)
			{
				draftRules = Copy(LiveSettings.Rules);
			}
			edit(draftRules);
		}

		private static void Display(Action<RuntimeOptions> edit)
		{
			if (!collecting)
			{
				throw new InvalidOperationException("Edit outside transaction");
			}
			if (draftDisplay == null)
			{
				draftDisplay = Copy(LiveSettings.Current);
			}
			edit(draftDisplay);
		}

		private static void Bind<T>(string section, string key, T initial, bool server, Func<T> get, Action<T> set, string description, AcceptableValueBase range = null)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Expected O, but got Unknown
			ConfigurationManagerAttributes configurationManagerAttributes = new ConfigurationManagerAttributes
			{
				ReadOnly = (server && !LiveSettings.CanEditRules),
				Order = 0
			};
			if (server)
			{
				ruleTags.Add(configurationManagerAttributes);
			}
			ConfigEntry<T> entry = config.Bind<T>(section, key, initial, new ConfigDescription(description, range, new object[1] { configurationManagerAttributes }));
			Action action = delegate
			{
				pushing = true;
				try
				{
					entry.Value = get();
				}
				finally
				{
					pushing = false;
				}
			};
			refresh.Add(action);
			action();
			entry.SettingChanged += delegate
			{
				if (!pushing)
				{
					T value = entry.Value;
					pending[section + "/" + key] = delegate
					{
						set(value);
					};
					lastEdit = Time.realtimeSinceStartup;
				}
			};
		}

		internal static void Update()
		{
			if (config == null || Time.realtimeSinceStartup < next)
			{
				return;
			}
			next = Time.realtimeSinceStartup + 0.5f;
			foreach (ConfigurationManagerAttributes ruleTag in ruleTags)
			{
				ruleTag.ReadOnly = !LiveSettings.CanEditRules;
			}
			if (pending.Count > 0)
			{
				if (Time.realtimeSinceStartup - lastEdit < 1f)
				{
					return;
				}
				try
				{
					collecting = true;
					draftRules = null;
					draftDisplay = null;
					foreach (Action value in pending.Values)
					{
						value();
					}
					if (draftRules != null)
					{
						draftRules.Validate();
					}
					if (draftDisplay != null)
					{
						draftDisplay.Validate();
					}
					if (draftRules != null)
					{
						LiveSettings.EditRules(draftRules);
					}
					if (draftDisplay != null)
					{
						LiveSettings.EditDisplay(draftDisplay);
					}
				}
				catch (Exception ex)
				{
					Plugin.Log.LogWarning((object)("Configuration changes rejected: " + ex.Message));
				}
				finally
				{
					collecting = false;
					pending.Clear();
					draftRules = null;
					draftDisplay = null;
				}
			}
			foreach (Action item in refresh)
			{
				item();
			}
		}
	}
	internal static class ConfigStorage
	{
		internal static void Initialize(string root, string legacy, IEnumerable<string> files, Func<string, string> embedded)
		{
			Directory.CreateDirectory(root);
			foreach (string file in files)
			{
				string text = Path.Combine(root, file);
				if (File.Exists(text))
				{
					continue;
				}
				Directory.CreateDirectory(Path.GetDirectoryName(text));
				string text2 = Candidates(root, legacy, file).FirstOrDefault(File.Exists);
				byte[] bytes = ((text2 == null) ? new UTF8Encoding(encoderShouldEmitUTF8Identifier: false).GetBytes(embedded(file)) : File.ReadAllBytes(text2));
				string text3 = text + "." + Guid.NewGuid().ToString("N") + ".tmp";
				try
				{
					File.WriteAllBytes(text3, bytes);
					File.Move(text3, text);
				}
				finally
				{
					if (File.Exists(text3))
					{
						File.Delete(text3);
					}
				}
			}
		}

		internal static void ArchiveLegacy(string root, string legacy, IEnumerable<string> files)
		{
			string[] array = files.Concat(new string[4] { "Korean.json", "English.json", "effects/CumulativeWounds.json", "effects/VitalityAccumulation.json" }).Distinct().ToArray();
			string path = Path.Combine(root, "migration-backup", DateTime.UtcNow.ToString("yyyyMMdd-HHmmss") + "-" + Guid.NewGuid().ToString("N").Substring(0, 8));
			string[] array2 = array;
			foreach (string path2 in array2)
			{
				string text = Path.Combine(legacy, path2);
				if (File.Exists(text))
				{
					string text2 = Path.Combine(path, path2);
					Directory.CreateDirectory(Path.GetDirectoryName(text2));
					File.Move(text, text2);
				}
			}
			array2 = new string[2] { "effects", "localizations" };
			foreach (string path3 in array2)
			{
				string path4 = Path.Combine(legacy, path3);
				if (Directory.Exists(path4) && !Directory.EnumerateFileSystemEntries(path4).Any())
				{
					Directory.Delete(path4);
				}
			}
		}

		private static IEnumerable<string> Candidates(string root, string legacy, string name)
		{
			if (name.StartsWith("localizations/", StringComparison.Ordinal))
			{
				yield return Path.Combine(root, Path.GetFileName(name));
			}
			yield return Path.Combine(legacy, name);
			if (name.StartsWith("localizations/", StringComparison.Ordinal))
			{
				yield return Path.Combine(legacy, Path.GetFileName(name));
			}
			string old = ((name == "effects/Eclipse.json") ? "effects/CumulativeWounds.json" : ((name == "effects/Awakening.json") ? "effects/VitalityAccumulation.json" : null));
			if (old != null)
			{
				yield return Path.Combine(root, old);
				yield return Path.Combine(legacy, old);
			}
		}
	}
	internal sealed class BonusDamageContext : IDisposable
	{
		private static readonly FieldInfo Depth = AccessTools.Field(typeof(HitSource), "_bonusDepth");

		private readonly int previous;

		private readonly bool enabled;

		private BonusDamageContext(bool bonus)
		{
			enabled = bonus;
			if (enabled)
			{
				if (Depth == null)
				{
					throw new InvalidOperationException("Unsupported EpicLoot bonus-hit context.");
				}
				previous = (int)Depth.GetValue(null);
				Depth.SetValue(null, previous + 1);
			}
		}

		internal static IDisposable Enter(bool bonus)
		{
			return new BonusDamageContext(bonus);
		}

		public void Dispose()
		{
			if (enabled)
			{
				Depth.SetValue(null, previous);
			}
		}
	}
	[HarmonyPatch]
	internal static class ShardLightningAttributionPatch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(AccessTools.TypeByName("EpicLoot.MagicItemEffects.Shards.StrikeCausesLightning"), "OnDamageDealt", (Type[])null, (Type[])null);
		}

		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Expected O, but got Unknown
			List<CodeInstruction> list = instructions.ToList();
			MethodInfo original = AccessTools.Method(typeof(DamageInRadius), "DamageEnemiesInRadius", new Type[4]
			{
				typeof(Player),
				typeof(Vector3),
				typeof(float),
				typeof(DamageTypes)
			}, (Type[])null);
			MethodInfo methodInfo = AccessTools.Method(typeof(ShardLightningAttributionPatch), "DamageWithAttacker", (Type[])null, (Type[])null);
			int num = list.Count((CodeInstruction x) => CodeInstructionExtensions.Calls(x, original));
			if (num != 1)
			{
				throw new InvalidOperationException("Unsupported EpicLoot shard-lightning call signature: expected one DamageEnemiesInRadius call, found " + num);
			}
			List<CodeInstruction> list2 = new List<CodeInstruction>();
			foreach (CodeInstruction item in list)
			{
				if (CodeInstructionExtensions.Calls(item, original))
				{
					item.opcode = OpCodes.Ldarg_2;
					item.operand = null;
					list2.Add(item);
					list2.Add(new CodeInstruction(OpCodes.Call, (object)methodInfo));
				}
				else
				{
					list2.Add(item);
				}
			}
			return list2;
		}

		private static void DamageWithAttacker(Player originalAttacker, Vector3 center, float radius, DamageTypes damage, Character callbackAttacker)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			DamageInRadius.DamageEnemiesInRadius((Player)(((Object)(object)originalAttacker != (Object)null) ? ((object)originalAttacker) : ((object)((callbackAttacker is Player) ? callbackAttacker : null))), center, radius, damage);
		}
	}
	internal static class PrecisionStrikeRuntime
	{
		internal const string Effect = "PrecisionStrike";

		internal static bool HasEffect(Player player)
		{
			if ((Object)(object)player == (Object)null || HitSource.IsBonusHit)
			{
				return false;
			}
			ItemData activeWeapon = MagicEffectsHelper.GetActiveWeapon(player);
			MagicItem val = ((activeWeapon == null) ? null : ItemDataExtensions.GetMagicItem(activeWeapon));
			if (val != null)
			{
				return val.HasEffect("PrecisionStrike", false, false);
			}
			return false;
		}

		internal static bool Applies(HitData hit)
		{
			if (Plugin.Ready && hit != null && Plugin.Tags.TryGetValue(hit, out var value) && value.Precision && !value.Bonus)
			{
				return !value.Wound;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(HitData), "ApplyResistance")]
	internal static class PrecisionStrikeResistancePatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(0)]
		private static void Prefix(HitData __instance, ref DamageModifiers modifiers)
		{
			if (PrecisionStrikeRuntime.Applies(__instance))
			{
				PrecisionStrikePolicy.Promote(ref modifiers);
			}
		}
	}
	internal static class LocalizationDefaults
	{
		internal static void AddPrecisionKeys(string root, Func<string, string> embedded)
		{
			string[] array = new string[2] { "Korean", "English" };
			foreach (string text in array)
			{
				string text2 = "localizations/" + text + ".json";
				string text3 = Path.Combine(root, text2);
				Dictionary<string, string> dictionary = JsonConvert.DeserializeObject<Dictionary<string, string>>(File.ReadAllText(text3));
				Dictionary<string, string> dictionary2 = JsonConvert.DeserializeObject<Dictionary<string, string>>(embedded(text2));
				if (dictionary == null || dictionary2 == null)
				{
					throw new InvalidDataException("Invalid localization: " + text2);
				}
				bool flag = false;
				foreach (KeyValuePair<string, string> item in dictionary2)
				{
					if (item.Key.StartsWith("ece_precisionstrike_", StringComparison.Ordinal) && !dictionary.ContainsKey(item.Key))
					{
						dictionary.Add(item.Key, item.Value);
						flag = true;
					}
				}
				if (!flag)
				{
					continue;
				}
				string text4 = text3 + "." + Guid.NewGuid().ToString("N") + ".tmp";
				try
				{
					File.WriteAllText(text4, JsonConvert.SerializeObject((object)dictionary, (Formatting)1) + Environment.NewLine);
					File.Replace(text4, text3, null);
				}
				finally
				{
					if (File.Exists(text4))
					{
						File.Delete(text4);
					}
				}
			}
		}
	}
	internal static class PrecisionStrikePolicy
	{
		internal static DamageModifier Weakness(DamageModifier original)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Invalid comparison between Unknown and I4
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			if ((int)original != 6)
			{
				return (DamageModifier)2;
			}
			return original;
		}

		internal static void Promote(ref DamageModifiers mods)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: 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_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			mods.m_blunt = Weakness(mods.m_blunt);
			mods.m_slash = Weakness(mods.m_slash);
			mods.m_pierce = Weakness(mods.m_pierce);
			mods.m_chop = Weakness(mods.m_chop);
			mods.m_pickaxe = Weakness(mods.m_pickaxe);
			mods.m_fire = Weakness(mods.m_fire);
			mods.m_frost = Weakness(mods.m_frost);
			mods.m_lightning = Weakness(mods.m_lightning);
			mods.m_poison = Weakness(mods.m_poison);
			mods.m_spirit = Weakness(mods.m_spirit);
		}
	}
	internal static class EclipseAudience
	{
		internal static float OpacityScale(bool own, float configuredAlpha)
		{
			return configuredAlpha * (own ? 1f : 0.45f);
		}

		internal static bool IsOwnDamage(long attackerUser, uint attackerObject, long localUser, uint localObject)
		{
			if (attackerObject != 0 && localObject != 0 && attackerUser == localUser)
			{
				return attackerObject == localObject;
			}
			return false;
		}
	}
	public sealed class WoundClock
	{
		private readonly Dictionary<string, double> next = new Dictionary<string, double>();

		public void Reset()
		{
			next.Clear();
		}

		public List<WoundPayment> DrainDue(WoundLedger ledger, double now, double interval)
		{
			List<WoundPayment> list = new List<WoundPayment>();
			if (!WoundLedger.Finite(now) || !WoundLedger.Finite(interval) || interval <= 0.0)
			{
				return list;
			}
			HashSet<string> hashSet = new HashSet<string>(ledger.Windows.Select((WoundWindow w) => w.Key));
			string[] array = next.Keys.ToArray();
			foreach (string text in array)
			{
				if (!hashSet.Contains(text))
				{
					next.Remove(text);
				}
			}
			foreach (string key in hashSet)
			{
				if (next.TryGetValue(key, out var value) && now < value)
				{
					continue;
				}
				list.AddRange(ledger.Drain(now, key));
				WoundWindow[] array2 = ledger.Windows.Where((WoundWindow w) => w.Key == key).ToArray();
				if (array2.Length == 0)
				{
					next.Remove(key);
					continue;
				}
				next[key] = Math.Min(now + interval, array2.Min((WoundWindow w) => w.End));
			}
			return list;
		}
	}
}