Decompiled source of PvPOverhaul v1.1.1

PvpOverhaul.dll

Decompiled 3 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.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn;
using Jotunn.Entities;
using Jotunn.Extensions;
using Jotunn.Managers;
using Jotunn.Utils;
using PvpOverhaul.API;
using PvpOverhaul.Compatibility;
using Splatform;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("PvpOverhaul")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("PvpOverhaul")]
[assembly: AssemblyCopyright("Copyright ©  2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("1.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.0.0")]
namespace PvPOverhaul
{
	[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[]
	{
		typeof(ItemData),
		typeof(int),
		typeof(bool),
		typeof(float),
		typeof(int)
	})]
	public static class PvPResilienceTooltipPatch
	{
		private static void Postfix(ItemData item, ref string __result)
		{
			if (item?.m_customData != null)
			{
				float resilience = PvpResilienceAPI.GetResilience(item);
				if (!(resilience <= 0f))
				{
					__result += $"\n<color=#66ccff>Résilience : {resilience:0}</color>";
				}
			}
		}
	}
}
namespace PvpOverhaul
{
	internal class BountySystem
	{
		private class KillRecord
		{
			public string KillerId;

			public string VictimId;

			public DateTime TimestampUtc;
		}

		public class BountyState
		{
			public string PlayerId;

			public DateTime StartUtc;

			public DateTime EndUtc;

			public int Tier;

			public Vector3 LastKnownPosition;
		}

		public class LastHit
		{
			public string VictimName;

			public string AttackerName;

			public float Damage;

			public float Time;
		}

		private readonly List<KillRecord> _kills = new List<KillRecord>();

		private readonly Dictionary<string, BountyState> _activeBounties = new Dictionary<string, BountyState>();

		private readonly Dictionary<long, LastHit> _lastHits = new Dictionary<long, LastHit>();

		private readonly Dictionary<string, LastHit> _lastHitsByName = new Dictionary<string, LastHit>();

		private readonly Dictionary<long, float> _reportedDeaths = new Dictionary<long, float>();

		private readonly Dictionary<long, float> _ignoredDeaths = new Dictionary<long, float>();

		private bool _loadedPersistence;

		private float _nextTimerTick;

		private float _nextPositionSync;

		private float _nextFullSync;

		public static BountySystem Instance { get; } = new BountySystem();

		private string SaveDir => Path.Combine(Paths.ConfigPath, "PvPOverhaul");

		private string SaveFile => Path.Combine(SaveDir, "bounties.txt");

		private string LogDir => Path.Combine(Paths.ConfigPath, "PvPOverhaul", "Logs");

		private TimeSpan KillWindow => TimeSpan.FromMinutes(PvPOverhaul.BountyKillWindowMinutes.Value);

		private TimeSpan BountyDuration => TimeSpan.FromMinutes(PvPOverhaul.BountyDurationMinutes.Value);

		public void Update()
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				if (!_loadedPersistence)
				{
					_loadedPersistence = true;
					LoadBounties();
				}
				if (Time.time >= _nextTimerTick)
				{
					_nextTimerTick = Time.time + 1f;
					UpdateTimers();
				}
				if (Time.time >= _nextPositionSync)
				{
					_nextPositionSync = Time.time + 10f;
					SyncBountyPositions();
				}
				if (Time.time >= _nextFullSync)
				{
					_nextFullSync = Time.time + 30f;
					BroadcastAllBounties();
				}
			}
		}

		private void WriteServerLog(string message)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				Directory.CreateDirectory(LogDir);
				string path = Path.Combine(LogDir, $"{DateTime.UtcNow:yyyy-MM-dd}.log");
				string text = $"[{DateTime.UtcNow:HH:mm:ss} UTC] {message}";
				File.AppendAllText(path, text + Environment.NewLine);
			}
		}

		private bool IsPlayerOnline(string playerName)
		{
			//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_0030: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				return false;
			}
			foreach (PlayerInfo player in ZNet.instance.GetPlayerList())
			{
				if (player.m_name == playerName)
				{
					return true;
				}
			}
			return false;
		}

		private static string FormatMessage(string template, string killer = "", string victim = "", string player = "", int tier = 0, int duration = 0, int amount = 0, string item = "")
		{
			return template.Replace("{killer}", killer).Replace("{victim}", victim).Replace("{player}", player)
				.Replace("{tier}", tier.ToString())
				.Replace("{duration}", duration.ToString())
				.Replace("{amount}", amount.ToString())
				.Replace("{item}", item);
		}

		public void RegisterKill(string killerName, string victimName)
		{
			if (string.IsNullOrWhiteSpace(killerName) || string.IsNullOrWhiteSpace(victimName) || killerName == victimName)
			{
				return;
			}
			DateTime now = DateTime.UtcNow;
			BountyState bounty = GetBounty(victimName);
			ClearBounty(victimName, killed: true);
			_kills.RemoveAll((KillRecord x) => x.KillerId == victimName);
			SaveBounties();
			if (bounty != null)
			{
				int num = Math.Max(1, bounty.Tier) * PvPOverhaul.BountyRewardAmountPerTier.Value;
				string value = PvPOverhaul.BountyRewardItem.Value;
				BountyRpc.SendRewardToPlayer(killerName, value, num);
				_kills.RemoveAll((KillRecord x) => x.KillerId == killerName);
				string message = FormatMessage(PvPOverhaul.MsgBountyReward.Value, killerName, victimName, "", 0, 0, num, value);
				BountyRpc.BroadcastCenterMessage(message);
				WriteServerLog($"REWARD | {killerName} claimed bounty on {victimName} | tier={bounty.Tier} | {num}x {value}");
			}
			_kills.Add(new KillRecord
			{
				KillerId = killerName,
				VictimId = victimName,
				TimestampUtc = now
			});
			CleanupOldKills(now);
			int num2 = _kills.Count((KillRecord x) => x.KillerId == killerName && now - x.TimestampUtc <= KillWindow);
			Logger.LogInfo((object)$"[PvPOverhaul SERVER] Kill registered: {killerName} -> {victimName} | kills={num2}");
			WriteServerLog($"KILL | {killerName} killed {victimName} | kills={num2}");
			BountyRpc.BroadcastCenterMessage(FormatMessage(PvPOverhaul.MsgKillBroadcast.Value, killerName, victimName));
			if (bounty == null && num2 >= PvPOverhaul.BountyTriggerKillCount.Value)
			{
				ActivateOrRefreshBounty(killerName, num2, now);
			}
		}

		private int GetTierFromKills(int kills)
		{
			return (int)Math.Floor(Math.Sqrt(kills));
		}

		private void ActivateOrRefreshBounty(string playerName, int killCount, DateTime now)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: 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_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: 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)
			int tierFromKills = GetTierFromKills(killCount);
			DateTime dateTime = now.Add(BountyDuration);
			Vector3 playerPosition = GetPlayerPosition(playerName);
			if (!_activeBounties.TryGetValue(playerName, out var value))
			{
				value = new BountyState
				{
					PlayerId = playerName,
					StartUtc = now,
					EndUtc = dateTime,
					Tier = tierFromKills,
					LastKnownPosition = playerPosition
				};
				_activeBounties[playerName] = value;
				Logger.LogInfo((object)("[PvPOverhaul SERVER] BOUNTY ACTIVATED: " + playerName));
				WriteServerLog($"BOUNTY_ACTIVATED | {playerName} | tier={tierFromKills} | end={dateTime:o}");
				BountyRpc.BroadcastCenterMessage(FormatMessage(PvPOverhaul.MsgBountyActivated.Value, "", "", playerName, tierFromKills));
			}
			else
			{
				value.EndUtc = dateTime;
				value.Tier = tierFromKills;
				if (playerPosition != Vector3.zero)
				{
					value.LastKnownPosition = playerPosition;
				}
				Logger.LogInfo((object)("[PvPOverhaul SERVER] BOUNTY REFRESHED: " + playerName));
				WriteServerLog($"BOUNTY_REFRESHED | {playerName} | tier={tierFromKills} | end={dateTime:o}");
				BountyRpc.BroadcastCenterMessage(FormatMessage(PvPOverhaul.MsgBountyRefreshed.Value, "", "", playerName, tierFromKills, PvPOverhaul.BountyDurationMinutes.Value));
			}
			SaveBounties();
			if (IsPlayerOnline(playerName))
			{
				BountyRpc.BroadcastBountyCircle(playerName, active: true, value.LastKnownPosition, tierFromKills, dateTime);
			}
		}

		public void UpdateBountyPositionFromClient(string playerName, Vector3 position)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			if (_activeBounties.TryGetValue(playerName, out var value) && !(position == Vector3.zero))
			{
				value.LastKnownPosition = position;
				SaveBounties();
				BountyRpc.BroadcastBountyCircle(playerName, active: true, position, value.Tier, value.EndUtc);
			}
		}

		public void ClearBounty(string playerName, bool killed)
		{
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			if (_activeBounties.Remove(playerName))
			{
				Logger.LogInfo((object)("[PvPOverhaul SERVER] Bounty cleared: " + playerName));
				WriteServerLog($"BOUNTY_CLEARED | {playerName} | killed={killed}");
				if (killed)
				{
					BountyRpc.BroadcastCenterMessage(FormatMessage(PvPOverhaul.MsgBountyCleared.Value, "", "", playerName));
				}
				else
				{
					BountyRpc.BroadcastCenterMessage(FormatMessage(PvPOverhaul.MsgBountyExpired.Value, "", "", playerName));
				}
				SaveBounties();
				BountyRpc.BroadcastBountyCircle(playerName, active: false, Vector3.zero, 0, DateTime.UtcNow);
			}
		}

		public void ResetPlayer(string playerName)
		{
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			_activeBounties.Remove(playerName);
			_kills.RemoveAll((KillRecord x) => x.KillerId == playerName || x.VictimId == playerName);
			Logger.LogInfo((object)("[PvPOverhaul SERVER] Full reset: " + playerName));
			WriteServerLog("ADMIN_RESET | " + playerName);
			SaveBounties();
			BountyRpc.BroadcastBountyCircle(playerName, active: false, Vector3.zero, 0, DateTime.UtcNow);
		}

		public BountyState GetBounty(string playerName)
		{
			if (!_activeBounties.TryGetValue(playerName, out var value))
			{
				return null;
			}
			if (DateTime.UtcNow >= value.EndUtc)
			{
				ClearBounty(playerName, killed: false);
				return null;
			}
			return value;
		}

		private void UpdateTimers()
		{
			DateTime now = DateTime.UtcNow;
			foreach (string item in (from x in _activeBounties
				where now >= x.Value.EndUtc
				select x.Key).ToList())
			{
				ClearBounty(item, killed: false);
			}
		}

		private void SyncBountyPositions()
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			DateTime utcNow = DateTime.UtcNow;
			foreach (BountyState item in _activeBounties.Values.ToList())
			{
				if (!(utcNow >= item.EndUtc))
				{
					if (!IsPlayerOnline(item.PlayerId))
					{
						BountyRpc.BroadcastBountyCircle(item.PlayerId, active: false, Vector3.zero, item.Tier, item.EndUtc);
					}
					else
					{
						BountyRpc.BroadcastBountyCircle(item.PlayerId, active: true, item.LastKnownPosition, item.Tier, item.EndUtc);
					}
				}
			}
		}

		private void BroadcastAllBounties()
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			DateTime utcNow = DateTime.UtcNow;
			foreach (BountyState item in _activeBounties.Values.ToList())
			{
				if (!(utcNow >= item.EndUtc))
				{
					if (!IsPlayerOnline(item.PlayerId))
					{
						BountyRpc.BroadcastBountyCircle(item.PlayerId, active: false, Vector3.zero, item.Tier, item.EndUtc);
					}
					else
					{
						BountyRpc.BroadcastBountyCircle(item.PlayerId, active: true, item.LastKnownPosition, item.Tier, item.EndUtc);
					}
				}
			}
		}

		private Vector3 GetPlayerPosition(string playerName)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if (allPlayer.GetPlayerName() == playerName)
				{
					return ((Component)allPlayer).transform.position;
				}
			}
			return Vector3.zero;
		}

		private void CleanupOldKills(DateTime now)
		{
			_kills.RemoveAll((KillRecord x) => now - x.TimestampUtc > KillWindow);
		}

		public void SetLastHit(long victimId, string victimName, string attackerName, float damage)
		{
			LastHit value = new LastHit
			{
				VictimName = victimName,
				AttackerName = attackerName,
				Damage = damage,
				Time = Time.time
			};
			_lastHits[victimId] = value;
			if (!string.IsNullOrWhiteSpace(victimName))
			{
				_lastHitsByName[victimName] = value;
			}
			Logger.LogInfo((object)$"[PvPOverhaul BOUNTY] LastHit set: victim={victimName} id={victimId} attacker={attackerName} dmg={damage}");
		}

		public LastHit ResolveLastHit(long victimId)
		{
			if (!_lastHits.TryGetValue(victimId, out var value))
			{
				return null;
			}
			if (Time.time - value.Time > 10f)
			{
				return null;
			}
			return value;
		}

		private LastHit ResolveLastHit(Player victim)
		{
			if ((Object)(object)victim == (Object)null)
			{
				return null;
			}
			long playerID = victim.GetPlayerID();
			LastHit value = ResolveLastHit(playerID);
			if (value != null)
			{
				return value;
			}
			string playerName = victim.GetPlayerName();
			if (string.IsNullOrWhiteSpace(playerName))
			{
				return null;
			}
			if (!_lastHitsByName.TryGetValue(playerName, out value))
			{
				return null;
			}
			if (Time.time - value.Time > 10f)
			{
				return null;
			}
			return value;
		}

		public void TryReportDeath(Player victim)
		{
			if ((Object)(object)victim == (Object)null)
			{
				return;
			}
			if (victim.m_godMode)
			{
				Logger.LogInfo((object)("[PvPOverhaul BOUNTY] Ignore death: godmode victim=" + victim.GetPlayerName()));
				return;
			}
			if (((Character)victim).GetHealth() > 0f && !((Character)victim).IsDead())
			{
				Logger.LogInfo((object)$"[PvPOverhaul BOUNTY] Ignore death report: victim not dead yet {victim.GetPlayerName()} health={((Character)victim).GetHealth()} dead={((Character)victim).IsDead()}");
				return;
			}
			if (RecentlyIgnoredDeath(victim))
			{
				Logger.LogInfo((object)("[PvPOverhaul BOUNTY] Ignore death: recently ignored victim=" + victim.GetPlayerName()));
				return;
			}
			long playerID = victim.GetPlayerID();
			if (!_reportedDeaths.TryGetValue(playerID, out var value) || !(Time.time - value < 10f))
			{
				LastHit lastHit = ResolveLastHit(victim);
				Logger.LogInfo((object)($"[PvPOverhaul BOUNTY] TryReportDeath victim={victim.GetPlayerName()} id={playerID} " + "lastHit=" + ((lastHit != null) ? lastHit.AttackerName : "NULL") + " " + $"health={((Character)victim).GetHealth()} dead={((Character)victim).IsDead()}"));
				if (lastHit != null && !string.IsNullOrWhiteSpace(lastHit.AttackerName) && !(lastHit.AttackerName == victim.GetPlayerName()))
				{
					_reportedDeaths[playerID] = Time.time;
					Logger.LogInfo((object)("[PvPOverhaul CLIENT] Reporting REAL kill to server: " + lastHit.AttackerName + " -> " + victim.GetPlayerName()));
					BountyRpc.SendKillToServer(lastHit.AttackerName, victim.GetPlayerName());
				}
			}
		}

		public void MarkIgnoredDeath(Player victim)
		{
			if (!((Object)(object)victim == (Object)null))
			{
				_ignoredDeaths[victim.GetPlayerID()] = Time.time;
			}
		}

		private bool RecentlyIgnoredDeath(Player victim)
		{
			if ((Object)(object)victim == (Object)null)
			{
				return false;
			}
			if (!_ignoredDeaths.TryGetValue(victim.GetPlayerID(), out var value))
			{
				return false;
			}
			return Time.time - value < 1f;
		}

		private void SaveBounties()
		{
			Directory.CreateDirectory(SaveDir);
			List<string> list = new List<string>();
			foreach (BountyState value in _activeBounties.Values)
			{
				list.Add(string.Join("|", value.PlayerId, value.StartUtc.Ticks, value.EndUtc.Ticks, value.Tier, value.LastKnownPosition.x.ToString(CultureInfo.InvariantCulture), value.LastKnownPosition.y.ToString(CultureInfo.InvariantCulture), value.LastKnownPosition.z.ToString(CultureInfo.InvariantCulture)));
			}
			File.WriteAllLines(SaveFile, list);
		}

		private void LoadBounties()
		{
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			if (!File.Exists(SaveFile))
			{
				return;
			}
			DateTime utcNow = DateTime.UtcNow;
			string[] array = File.ReadAllLines(SaveFile);
			foreach (string text in array)
			{
				string[] array2 = text.Split(new char[1] { '|' });
				if (array2.Length < 7)
				{
					continue;
				}
				string text2 = array2[0];
				if (!long.TryParse(array2[1], out var result) || !long.TryParse(array2[2], out var result2) || !int.TryParse(array2[3], out var result3) || !float.TryParse(array2[4], NumberStyles.Float, CultureInfo.InvariantCulture, out var result4) || !float.TryParse(array2[5], NumberStyles.Float, CultureInfo.InvariantCulture, out var result5) || !float.TryParse(array2[6], NumberStyles.Float, CultureInfo.InvariantCulture, out var result6))
				{
					continue;
				}
				DateTime dateTime = new DateTime(result2, DateTimeKind.Utc);
				if (!(utcNow >= dateTime))
				{
					BountyState bountyState = new BountyState
					{
						PlayerId = text2,
						StartUtc = new DateTime(result, DateTimeKind.Utc),
						EndUtc = dateTime,
						Tier = result3,
						LastKnownPosition = new Vector3(result4, result5, result6)
					};
					_activeBounties[text2] = bountyState;
					Logger.LogInfo((object)("[PvPOverhaul SERVER] Loaded persistent bounty: " + text2));
					WriteServerLog($"BOUNTY_LOADED | {text2} | tier={result3} | end={dateTime:o}");
					if (IsPlayerOnline(text2))
					{
						BountyRpc.BroadcastBountyCircle(text2, active: true, bountyState.LastKnownPosition, result3, dateTime);
					}
				}
			}
			SaveBounties();
		}

		public void ScheduleDeathCheck(Player victim)
		{
			if (!((Object)(object)victim == (Object)null) && !((Object)(object)PvPOverhaul.Instance == (Object)null))
			{
				((MonoBehaviour)PvPOverhaul.Instance).StartCoroutine(DelayedDeathCheck(victim));
			}
		}

		private IEnumerator DelayedDeathCheck(Player victim)
		{
			if ((Object)(object)victim == (Object)null)
			{
				yield break;
			}
			string victimName = victim.GetPlayerName();
			long victimId = victim.GetPlayerID();
			for (int i = 0; i < 30; i++)
			{
				yield return (object)new WaitForSeconds(0.1f);
				if ((Object)(object)victim == (Object)null)
				{
					break;
				}
				LastHit lastHit = ResolveLastHit(victimId);
				if (i == 0 || ((Character)victim).IsDead() || ((Character)victim).GetHealth() <= 0f)
				{
					Logger.LogInfo((object)($"[PvPOverhaul BOUNTY] DelayedDeathCheck {i} victim={victimName} " + $"health={((Character)victim).GetHealth()} dead={((Character)victim).IsDead()} " + "lastHit=" + ((lastHit != null) ? lastHit.AttackerName : "NULL")));
				}
				if (((Character)victim).GetHealth() <= 0f || ((Character)victim).IsDead())
				{
					TryReportDeath(victim);
					break;
				}
			}
		}
	}
	internal static class BountyRpc
	{
		private const string RpcRegisterKill = "PvpModes_RegisterKill";

		private const string RpcBroadcastMessage = "PvpModes_BroadcastMessage";

		private const string RpcBountyCircle = "PvpModes_BountyCircle";

		private const string RpcBountyPosition = "PvpModes_BountyPosition";

		private const string RpcBountyStatusRequest = "PvpModes_BountyStatusRequest";

		private const string RpcBountyStatusResponse = "PvpModes_BountyStatusResponse";

		private const string RpcBountyResetRequest = "PvpModes_BountyResetRequest";

		private const string RpcBountyResetResponse = "PvpModes_BountyResetResponse";

		private const string RpcBountyReward = "PvpModes_BountyReward";

		private static bool _registered;

		private static ZRoutedRpc _registeredInstance;

		private static string _pendingRewardItem;

		private static int _pendingRewardAmount;

		public static void Update()
		{
			EnsureRegistered();
		}

		public static bool EnsureRegistered()
		{
			if (ZRoutedRpc.instance == null)
			{
				_registered = false;
				_registeredInstance = null;
				return false;
			}
			if (_registered && _registeredInstance == ZRoutedRpc.instance)
			{
				return true;
			}
			_registered = false;
			_registeredInstance = ZRoutedRpc.instance;
			ZRoutedRpc.instance.Register<ZPackage>("PvpModes_RegisterKill", (Action<long, ZPackage>)RPC_RegisterKill);
			ZRoutedRpc.instance.Register<ZPackage>("PvpModes_BroadcastMessage", (Action<long, ZPackage>)RPC_BroadcastMessage);
			ZRoutedRpc.instance.Register<ZPackage>("PvpModes_BountyCircle", (Action<long, ZPackage>)RPC_BountyCircle);
			ZRoutedRpc.instance.Register<ZPackage>("PvpModes_BountyPosition", (Action<long, ZPackage>)RPC_BountyPosition);
			ZRoutedRpc.instance.Register<ZPackage>("PvpModes_BountyStatusRequest", (Action<long, ZPackage>)RPC_BountyStatusRequest);
			ZRoutedRpc.instance.Register<ZPackage>("PvpModes_BountyStatusResponse", (Action<long, ZPackage>)RPC_BountyStatusResponse);
			ZRoutedRpc.instance.Register<ZPackage>("PvpModes_BountyResetRequest", (Action<long, ZPackage>)RPC_BountyResetRequest);
			ZRoutedRpc.instance.Register<ZPackage>("PvpModes_BountyResetResponse", (Action<long, ZPackage>)RPC_BountyResetResponse);
			ZRoutedRpc.instance.Register<ZPackage>("PvpModes_BountyReward", (Action<long, ZPackage>)RPC_BountyReward);
			_registered = true;
			Logger.LogInfo((object)"[PvPOverhaul] RPC registered successfully");
			return true;
		}

		public static void RequestBountyStatus(string playerName)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			if (!EnsureRegistered())
			{
				Console.instance.Print("[PvPOverhaul] RPC not ready");
				return;
			}
			ZPackage val = new ZPackage();
			val.Write(playerName);
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				SendBountyStatusResponse(ZRoutedRpc.instance.GetServerPeerID(), playerName);
				return;
			}
			ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "PvpModes_BountyStatusRequest", new object[1] { val });
		}

		public static void RequestBountyReset(string playerName)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Expected O, but got Unknown
			if (!EnsureRegistered())
			{
				Console.instance.Print("[PvPOverhaul] RPC not ready");
				return;
			}
			ZPackage val = new ZPackage();
			val.Write(playerName);
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				BountySystem.Instance.ResetPlayer(playerName);
				Console.instance.Print("Reset bounty for " + playerName);
			}
			else
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "PvpModes_BountyResetRequest", new object[1] { val });
			}
		}

		private static void RPC_BountyStatusRequest(long sender, ZPackage pkg)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				string text = pkg.ReadString();
				ZPackage val = BuildBountyStatusResponse(text);
				Logger.LogInfo((object)$"[PvPOverhaul SERVER] Bounty status request: {text} sender={sender}");
				ZRoutedRpc.instance.InvokeRoutedRPC(sender, "PvpModes_BountyStatusResponse", new object[1] { val });
			}
		}

		private static void RPC_BountyStatusResponse(long sender, ZPackage pkg)
		{
			string text = pkg.ReadString();
			string text2;
			if (!pkg.ReadBool())
			{
				text2 = "No bounty for " + text;
			}
			else
			{
				int num = pkg.ReadInt();
				long ticks = pkg.ReadLong();
				TimeSpan timeSpan = new DateTime(ticks, DateTimeKind.Utc) - DateTime.UtcNow;
				if (timeSpan < TimeSpan.Zero)
				{
					timeSpan = TimeSpan.Zero;
				}
				text2 = $"{text} → Tier {num} | Time left: {timeSpan:hh\\:mm\\:ss}";
			}
			Console.instance.Print(text2);
			if ((Object)(object)MessageHud.instance != (Object)null)
			{
				MessageHud.instance.ShowMessage((MessageType)2, text2, 0, (Sprite)null, false);
			}
		}

		private static void RPC_BountyResetRequest(long sender, ZPackage pkg)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				string text = pkg.ReadString();
				BountySystem.Instance.ResetPlayer(text);
				ZPackage val = new ZPackage();
				val.Write(text);
				ZRoutedRpc.instance.InvokeRoutedRPC(sender, "PvpModes_BountyResetResponse", new object[1] { val });
			}
		}

		private static void RPC_BountyResetResponse(long sender, ZPackage pkg)
		{
			string text = pkg.ReadString();
			Console.instance.Print("Reset bounty for " + text);
		}

		private static ZPackage BuildBountyStatusResponse(string playerName)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			BountySystem.BountyState bounty = BountySystem.Instance.GetBounty(playerName);
			val.Write(playerName);
			val.Write(bounty != null);
			if (bounty != null)
			{
				val.Write(bounty.Tier);
				val.Write(bounty.EndUtc.Ticks);
			}
			return val;
		}

		private static void SendBountyStatusResponse(long targetPeerId, string playerName)
		{
			ZPackage val = BuildBountyStatusResponse(playerName);
			ZRoutedRpc.instance.InvokeRoutedRPC(targetPeerId, "PvpModes_BountyStatusResponse", new object[1] { val });
		}

		public static void SendKillToServer(string killerName, string victimName)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			if (EnsureRegistered())
			{
				ZPackage val = new ZPackage();
				val.Write(killerName);
				val.Write(victimName);
				if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
				{
					BountySystem.Instance.RegisterKill(killerName, victimName);
					return;
				}
				ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "PvpModes_RegisterKill", new object[1] { val });
			}
		}

		public static void SendBountyPositionToServer(string playerName, Vector3 position)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			if (EnsureRegistered() && (!((Object)(object)ZNet.instance != (Object)null) || !ZNet.instance.IsServer()))
			{
				ZPackage val = new ZPackage();
				val.Write(playerName);
				val.Write(position);
				ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "PvpModes_BountyPosition", new object[1] { val });
			}
		}

		public static void BroadcastCenterMessage(string message)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			if (EnsureRegistered())
			{
				ZPackage val = new ZPackage();
				val.Write(message);
				Logger.LogInfo((object)("[PvPOverhaul SERVER] Broadcast message: " + message));
				ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "PvpModes_BroadcastMessage", new object[1] { val });
			}
		}

		public static void BroadcastBountyCircle(string playerName, bool active, Vector3 position, int tier, DateTime endUtc)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			if (EnsureRegistered())
			{
				ZPackage val = new ZPackage();
				val.Write(playerName);
				val.Write(active);
				val.Write(position);
				val.Write(tier);
				val.Write(endUtc.Ticks);
				ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "PvpModes_BountyCircle", new object[1] { val });
			}
		}

		private static void RPC_RegisterKill(long sender, ZPackage pkg)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				string text = pkg.ReadString();
				string text2 = pkg.ReadString();
				Logger.LogInfo((object)("[PvPOverhaul SERVER] Kill RPC received: " + text + " -> " + text2));
				BountySystem.Instance.RegisterKill(text, text2);
			}
		}

		private static void RPC_BountyPosition(long sender, ZPackage pkg)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				string playerName = pkg.ReadString();
				Vector3 position = pkg.ReadVector3();
				BountySystem.Instance.UpdateBountyPositionFromClient(playerName, position);
			}
		}

		private static void RPC_BroadcastMessage(long sender, ZPackage pkg)
		{
			string text = pkg.ReadString();
			if ((Object)(object)MessageHud.instance != (Object)null)
			{
				MessageHud.instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false);
			}
		}

		private static void RPC_BountyCircle(long sender, ZPackage pkg)
		{
			//IL_0010: 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)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			string playerName = pkg.ReadString();
			bool active = pkg.ReadBool();
			Vector3 position = pkg.ReadVector3();
			int tier = pkg.ReadInt();
			long ticks = pkg.ReadLong();
			DateTime endUtc = new DateTime(ticks, DateTimeKind.Utc);
			BountyStatusEffectController.SetLocalBountyStatus(playerName, active, endUtc);
			BountyMapCircle.Set(playerName, active, position, tier, endUtc);
		}

		public static void SendRewardToPlayer(string playerName, string itemPrefabName, int amount)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Expected O, but got Unknown
			if (EnsureRegistered())
			{
				ZPackage val = new ZPackage();
				val.Write(playerName);
				val.Write(itemPrefabName);
				val.Write(amount);
				Logger.LogInfo((object)$"[PvPOverhaul SERVER] Sending reward RPC to {playerName}: {amount}x {itemPrefabName}");
				ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "PvpModes_BountyReward", new object[1] { val });
			}
		}

		private static void RPC_BountyReward(long sender, ZPackage pkg)
		{
			string text = pkg.ReadString();
			string text2 = pkg.ReadString();
			int num = pkg.ReadInt();
			Logger.LogInfo((object)$"[PvPOverhaul CLIENT] Reward RPC received for {text}: {num}x {text2}");
			if (!((Object)(object)Player.m_localPlayer == (Object)null) && !(Player.m_localPlayer.GetPlayerName() != text))
			{
				Logger.LogInfo((object)("[PvPOverhaul CLIENT] Reward accepted by local player " + Player.m_localPlayer.GetPlayerName()));
				_pendingRewardItem = text2;
				_pendingRewardAmount += num;
				TryGivePendingReward();
			}
		}

		public static void UpdateReward()
		{
			TryGivePendingReward();
		}

		private static void TryGivePendingReward()
		{
			if (string.IsNullOrWhiteSpace(_pendingRewardItem) || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return;
			}
			GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(_pendingRewardItem);
			if ((Object)(object)itemPrefab == (Object)null)
			{
				Logger.LogWarning((object)("[PvPOverhaul] Reward prefab not found yet: " + _pendingRewardItem));
				return;
			}
			((Humanoid)Player.m_localPlayer).GetInventory().AddItem(itemPrefab, _pendingRewardAmount);
			if ((Object)(object)MessageHud.instance != (Object)null)
			{
				MessageHud.instance.ShowMessage((MessageType)2, $"Récompense de prime : {_pendingRewardAmount}x {_pendingRewardItem}", 0, (Sprite)null, false);
			}
			_pendingRewardItem = null;
			_pendingRewardAmount = 0;
		}
	}
	internal static class BountyMapCircle
	{
		private class CircleState
		{
			public PinData Pin;

			public bool Active;

			public int Tier;

			public DateTime EndUtc;

			public float NextPositionSend;
		}

		private static readonly Dictionary<string, CircleState> _circles = new Dictionary<string, CircleState>();

		private const float CircleWorldSize = 300f;

		public static void Update()
		{
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			DateTime utcNow = DateTime.UtcNow;
			foreach (KeyValuePair<string, CircleState> item in _circles.ToList())
			{
				if (item.Value.EndUtc <= utcNow)
				{
					Remove(item.Key);
				}
			}
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			string playerName = Player.m_localPlayer.GetPlayerName();
			if (_circles.TryGetValue(playerName, out var value) && value.Active && !(Time.time < value.NextPositionSend))
			{
				value.NextPositionSend = Time.time + 10f;
				Vector3 position = ((Component)Player.m_localPlayer).transform.position;
				if (value.Pin != null)
				{
					value.Pin.m_pos = position;
					value.Pin.m_name = "RECHERCHÉ: " + playerName;
					SetPinWorldSize(value.Pin, 300f);
				}
				BountyRpc.SendBountyPositionToServer(playerName, position);
			}
		}

		public static void Set(string playerName, bool active, Vector3 position, int tier, DateTime endUtc)
		{
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)Minimap.instance == (Object)null)
			{
				return;
			}
			bool flag = (Object)(object)Player.m_localPlayer != (Object)null && Player.m_localPlayer.GetPlayerName() == playerName;
			if (!active)
			{
				Remove(playerName);
				return;
			}
			if (flag)
			{
				if (_circles.TryGetValue(playerName, out var value))
				{
					if (value.Pin != null)
					{
						Minimap.instance.RemovePin(value.Pin);
					}
					value.Pin = null;
					value.Active = true;
					value.Tier = tier;
					value.EndUtc = endUtc;
				}
				else
				{
					_circles[playerName] = new CircleState
					{
						Pin = null,
						Active = true,
						Tier = tier,
						EndUtc = endUtc,
						NextPositionSend = 0f
					};
				}
				return;
			}
			string text = "RECHERCHÉ: " + playerName;
			if (_circles.TryGetValue(playerName, out var value2))
			{
				value2.Active = true;
				value2.Tier = tier;
				value2.EndUtc = endUtc;
				if (value2.Pin != null)
				{
					Minimap.instance.RemovePin(value2.Pin);
				}
				PinData pin = Minimap.instance.AddPin(position, (PinType)13, text, false, false, 0L, default(PlatformUserID));
				SetPinWorldSize(pin, 300f);
				value2.Pin = pin;
			}
			else
			{
				PinData pin2 = Minimap.instance.AddPin(position, (PinType)13, text, false, false, 0L, default(PlatformUserID));
				SetPinWorldSize(pin2, 300f);
				_circles[playerName] = new CircleState
				{
					Pin = pin2,
					Active = true,
					Tier = tier,
					EndUtc = endUtc,
					NextPositionSend = 0f
				};
			}
		}

		private static void Remove(string playerName)
		{
			if (_circles.TryGetValue(playerName, out var value))
			{
				if ((Object)(object)Minimap.instance != (Object)null && value.Pin != null)
				{
					Minimap.instance.RemovePin(value.Pin);
				}
				_circles.Remove(playerName);
			}
		}

		private static void SetPinWorldSize(PinData pin, float size)
		{
			FieldInfo field = typeof(PinData).GetField("m_worldSize", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				field.SetValue(pin, size);
			}
		}
	}
	internal static class BountyStatusEffectController
	{
		private const string EffectName = "SE_PvpModes_Bounty";

		private static readonly int EffectHash = StringExtensionMethods.GetStableHashCode("SE_PvpModes_Bounty");

		private static DateTime _currentEndUtc;

		private static SE_Stats _effectPrefab;

		private static bool _registered;

		private static bool _pendingActive;

		private static DateTime _pendingEndUtc;

		private static string _pendingPlayerName;

		public static void UpdateRegistration()
		{
			if (_registered || (Object)(object)ObjectDB.instance == (Object)null)
			{
				return;
			}
			StatusEffect statusEffect = ObjectDB.instance.GetStatusEffect(EffectHash);
			if ((Object)(object)statusEffect != (Object)null)
			{
				_effectPrefab = (SE_Stats)(object)((statusEffect is SE_Stats) ? statusEffect : null);
				_registered = true;
				return;
			}
			SE_Stats val = (_effectPrefab = ScriptableObject.CreateInstance<SE_Stats>());
			((Object)val).name = "SE_PvpModes_Bounty";
			((StatusEffect)val).m_name = "Recherché";
			((StatusEffect)val).m_tooltip = "Votre position approximative est révélée aux autres joueurs.";
			((StatusEffect)val).m_ttl = 60f;
			Sprite sprite = GetSprite("SoftDeath");
			if ((Object)(object)sprite != (Object)null)
			{
				((StatusEffect)val).m_icon = sprite;
			}
			else
			{
				Logger.LogWarning((object)"[PvPOverhaul] SoftDeath icon not found");
			}
			ObjectDB.instance.m_StatusEffects.Add((StatusEffect)(object)val);
			_registered = true;
			Logger.LogInfo((object)"[PvPOverhaul] Bounty StatusEffect registered");
			if (!string.IsNullOrEmpty(_pendingPlayerName))
			{
				string pendingPlayerName = _pendingPlayerName;
				bool pendingActive = _pendingActive;
				DateTime pendingEndUtc = _pendingEndUtc;
				_pendingPlayerName = null;
				SetLocalBountyStatus(pendingPlayerName, pendingActive, pendingEndUtc);
			}
		}

		public static void SetLocalBountyStatus(string playerName, bool active, DateTime endUtc)
		{
			if (!_registered || (Object)(object)Player.m_localPlayer == (Object)null)
			{
				_pendingPlayerName = playerName;
				_pendingActive = active;
				_pendingEndUtc = endUtc;
			}
			UpdateRegistration();
			if (!_registered || (Object)(object)Player.m_localPlayer == (Object)null || Player.m_localPlayer.GetPlayerName() != playerName)
			{
				return;
			}
			SEMan sEMan = ((Character)Player.m_localPlayer).GetSEMan();
			if (!active)
			{
				sEMan.RemoveStatusEffect(EffectHash, false);
				return;
			}
			float num = (float)(endUtc - DateTime.UtcNow).TotalSeconds;
			if (num <= 0f)
			{
				sEMan.RemoveStatusEffect(EffectHash, false);
				return;
			}
			bool flag = sEMan.HaveStatusEffect(EffectHash);
			if (!(_currentEndUtc == endUtc && flag))
			{
				_currentEndUtc = endUtc;
				if (flag)
				{
					sEMan.RemoveStatusEffect(EffectHash, false);
				}
				if ((Object)(object)_effectPrefab != (Object)null)
				{
					((StatusEffect)_effectPrefab).m_ttl = num;
				}
				StatusEffect val = (((Object)(object)_effectPrefab != (Object)null) ? sEMan.AddStatusEffect((StatusEffect)(object)_effectPrefab, true, 0, 0f) : sEMan.AddStatusEffect(EffectHash, true, 0, 0f));
				if ((Object)(object)val != (Object)null)
				{
					val.m_ttl = num;
					val.m_time = 0f;
				}
			}
		}

		private static Sprite GetSprite(string name)
		{
			Sprite[] array = Resources.FindObjectsOfTypeAll<Sprite>();
			foreach (Sprite val in array)
			{
				if (((Object)val).name == name)
				{
					return val;
				}
			}
			return null;
		}
	}
	internal class BountyStatusCommand : ConsoleCommand
	{
		public override string Name => "pvp_bounty";

		public override string Help => "Affiche la bounty d'un joueur. Usage: pvp_bounty nom";

		public override void Run(string[] args)
		{
			if (args.Length < 1)
			{
				Console.instance.Print("Usage: pvp_bounty nom");
			}
			else if (!SynchronizationManager.Instance.PlayerIsAdmin)
			{
				Console.instance.Print("Only server admins can use this command.");
			}
			else
			{
				BountyRpc.RequestBountyStatus(args[0]);
			}
		}
	}
	internal class BountyResetCommand : ConsoleCommand
	{
		public override string Name => "pvp_resetbounty";

		public override string Help => "Reset la bounty et les kills d'un joueur. Usage: pvp_resetbounty nom";

		public override void Run(string[] args)
		{
			if (args.Length < 1)
			{
				Console.instance.Print("Usage: pvp_resetbounty nom");
			}
			else if (!SynchronizationManager.Instance.PlayerIsAdmin)
			{
				Console.instance.Print("Only server admins can use this command.");
			}
			else
			{
				BountyRpc.RequestBountyReset(args[0]);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnDeath")]
	internal static class PlayerDeathBountyPatch
	{
		private static void Postfix(Player __instance)
		{
			if (!((Object)(object)__instance == (Object)null))
			{
				Logger.LogInfo((object)("[PvPOverhaul BOUNTY] Player.OnDeath detected: " + __instance.GetPlayerName()));
				BountySystem.Instance.TryReportDeath(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Character), "OnDeath")]
	internal static class CharacterDeathBountyPatch
	{
		private static void Postfix(Character __instance)
		{
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (val != null)
			{
				Logger.LogInfo((object)("[PvPOverhaul BOUNTY] Character.OnDeath detected: " + val.GetPlayerName()));
				BountySystem.Instance.TryReportDeath(val);
			}
		}
	}
	internal static class PvpCombatHelper
	{
		private static readonly int CombatHash = StringExtensionMethods.GetStableHashCode("SE_Combat");

		public static bool IsInPvpCombat(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			SEMan sEMan = ((Character)player).GetSEMan();
			if (sEMan != null && sEMan.HaveStatusEffect(CombatHash))
			{
				return true;
			}
			if ((Object)(object)((Character)player).m_nview != (Object)null && ((Character)player).m_nview.IsValid())
			{
				ZDO zDO = ((Character)player).m_nview.GetZDO();
				if (zDO != null && zDO.GetBool("VPT_PlayerInCombat", false))
				{
					return true;
				}
			}
			return false;
		}

		public static void ApplyPvpCombatStatus(Player player, Player templateSource = null)
		{
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			if ((Object)(object)((Character)player).m_nview != (Object)null && ((Character)player).m_nview.IsValid())
			{
				ZDO zDO = ((Character)player).m_nview.GetZDO();
				if (zDO != null)
				{
					zDO.Set("VPT_PlayerInCombat", true);
				}
			}
			SEMan sEMan = ((Character)player).GetSEMan();
			if (sEMan == null)
			{
				return;
			}
			if (sEMan.HaveStatusEffect(CombatHash))
			{
				sEMan.AddStatusEffect(CombatHash, true, 0, 0f);
				return;
			}
			ObjectDB instance = ObjectDB.instance;
			StatusEffect val = ((instance != null) ? instance.GetStatusEffect(CombatHash) : null);
			if ((Object)(object)val == (Object)null && (Object)(object)templateSource != (Object)null)
			{
				SEMan sEMan2 = ((Character)templateSource).GetSEMan();
				if (sEMan2 != null)
				{
					foreach (StatusEffect statusEffect in sEMan2.GetStatusEffects())
					{
						if ((Object)(object)statusEffect != (Object)null && statusEffect.NameHash() == CombatHash)
						{
							val = statusEffect;
							break;
						}
					}
				}
			}
			if ((Object)(object)val != (Object)null)
			{
				sEMan.AddStatusEffect(val, true, 0, 0f);
			}
		}
	}
	internal static class PlayerCombatHandler
	{
		public static void EnterCombat(Character character)
		{
			if (!((Object)(object)character == (Object)null) && !((Object)(object)character.m_nview == (Object)null) && character.m_nview.IsValid())
			{
				ZDO zDO = character.m_nview.GetZDO();
				if (zDO != null)
				{
					zDO.Set("VPT_PlayerInCombat", true);
				}
				character.m_nview.InvokeRPC("EnterCombatRpc", Array.Empty<object>());
			}
		}
	}
	internal static class PvpArmorBypassState
	{
		[ThreadStatic]
		private static int _activeDepth;

		public static bool IsActive => _activeDepth > 0;

		public static void Begin()
		{
			_activeDepth++;
		}

		public static void End()
		{
			if (_activeDepth > 0)
			{
				_activeDepth--;
			}
		}
	}
	[HarmonyPatch]
	internal static class PvpArmorBypassRpcDamagePatch
	{
		private static readonly MethodInfo Target = AccessTools.DeclaredMethod(typeof(Character), "RPC_Damage", new Type[2]
		{
			typeof(long),
			typeof(HitData)
		}, (Type[])null);

		private static bool Prepare()
		{
			if (Target == null)
			{
				Logger.LogWarning((object)"[PvPOverhaul] Character.RPC_Damage(long, HitData) not found, PvP armor bypass disabled.");
				return false;
			}
			return true;
		}

		private static MethodBase TargetMethod()
		{
			return Target;
		}

		private static void Prefix(Character __instance, HitData hit)
		{
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (val != null && hit != null)
			{
				Character attacker = hit.GetAttacker();
				Player val2 = (Player)(object)((attacker is Player) ? attacker : null);
				if (val2 != null && !((Object)(object)val2 == (Object)(object)val))
				{
					PvpArmorBypassState.Begin();
				}
			}
		}

		private static void Finalizer()
		{
			PvpArmorBypassState.End();
		}
	}
	[HarmonyPatch]
	internal static class PvpArmorBypassApplyArmorPatch
	{
		private static readonly MethodInfo Target = AccessTools.DeclaredMethod(typeof(HitData), "ApplyArmor", new Type[1] { typeof(float) }, (Type[])null);

		private static bool Prepare()
		{
			if (Target == null)
			{
				Logger.LogWarning((object)"[PvPOverhaul] HitData.ApplyArmor(float) not found, PvP armor bypass disabled.");
				return false;
			}
			return true;
		}

		private static MethodBase TargetMethod()
		{
			return Target;
		}

		private static void Prefix(ref float __0)
		{
			if (PvpArmorBypassState.IsActive)
			{
				__0 = 0f;
			}
		}
	}
	[HarmonyPatch(typeof(Character), "Damage")]
	internal static class PvPDamagePatch
	{
		private class MagicBurstWindow
		{
			public float StartTime;

			public float Damage;
		}

		internal class PendingBlockLeak
		{
			public float ExpectedDamage;

			public float HealthBefore;

			public float Time;

			public DamageTypes LeakDamage;

			public float BlockLeakPercent;
		}

		private class PendingPvpCombatHit
		{
			public Player Attacker;

			public float HealthBefore;
		}

		internal static readonly Dictionary<long, PendingBlockLeak> PendingLeaks = new Dictionary<long, PendingBlockLeak>();

		private static readonly Dictionary<string, MagicBurstWindow> MagicBurstWindows = new Dictionary<string, MagicBurstWindow>();

		private static readonly Dictionary<long, string> PendingBountyAttackers = new Dictionary<long, string>();

		private static readonly Dictionary<long, PendingPvpCombatHit> PendingPvpCombatHits = new Dictionary<long, PendingPvpCombatHit>();

		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void Prefix(Character __instance, HitData hit)
		{
			//IL_0171: Unknown result type (might be due to invalid IL or missing references)
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			if (!PvPOverhaul.EnablePvpDamagePatch.Value)
			{
				return;
			}
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (val == null || hit == null)
			{
				return;
			}
			if (IsTherzieBleedTick(val, hit))
			{
				ScaleDamage(hit, PvPOverhaul.TherzieBleedNerf.Value);
				Logger.LogInfo((object)$"[PvPOverhaul PvPDMG] Therzie bleed nerfed on {val.GetPlayerName()} | dmg={hit.GetTotalDamage()}");
				return;
			}
			Character attacker = hit.GetAttacker();
			Player val2 = (Player)(object)((attacker is Player) ? attacker : null);
			if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2 == (Object)null) && !((Object)(object)val2 == (Object)(object)val))
			{
				PendingPvpCombatHits[val.GetPlayerID()] = new PendingPvpCombatHit
				{
					Attacker = val2,
					HealthBefore = ((Character)val).GetHealth()
				};
				PendingBountyAttackers[val.GetPlayerID()] = val2.GetPlayerName();
				float totalDamage = hit.GetTotalDamage();
				if (totalDamage > 0f && !val.m_godMode)
				{
					BountySystem.Instance.SetLastHit(val.GetPlayerID(), val.GetPlayerName(), val2.GetPlayerName(), totalDamage);
				}
				float health = ((Character)val).GetHealth();
				NormalizePvpDamage(val, val2, hit);
				float totalDamage2 = hit.GetTotalDamage();
				if (hit.m_blockable && totalDamage2 > 0f && ((Character)val).IsBlocking())
				{
					DamageTypes damage = hit.m_damage;
					float num = (IsTwoHandedMeleeWeapon(((Humanoid)val2).GetCurrentWeapon()) ? PvPOverhaul.PvpTwoHandedBlockedDamageLeakPercent.Value : PvPOverhaul.PvpBlockedDamageLeakPercent.Value);
					ScaleDamageTypes(ref damage, num);
					PendingLeaks[val.GetPlayerID()] = new PendingBlockLeak
					{
						ExpectedDamage = totalDamage2,
						HealthBefore = health,
						LeakDamage = damage,
						Time = Time.time,
						BlockLeakPercent = num
					};
				}
			}
		}

		private static Player TryResolveRecentPvpAttacker(Player victim)
		{
			if ((Object)(object)victim == (Object)null)
			{
				return null;
			}
			BountySystem.LastHit lastHit = BountySystem.Instance.ResolveLastHit(victim.GetPlayerID());
			if (lastHit == null)
			{
				return null;
			}
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if ((Object)(object)allPlayer != (Object)null && allPlayer.GetPlayerName() == lastHit.AttackerName)
				{
					return allPlayer;
				}
			}
			return null;
		}

		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void Postfix(Character __instance, HitData hit)
		{
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (val == null)
			{
				return;
			}
			long playerID = val.GetPlayerID();
			if (!PendingBountyAttackers.TryGetValue(playerID, out var value))
			{
				return;
			}
			PendingBountyAttackers.Remove(playerID);
			if (string.IsNullOrWhiteSpace(value) || val.m_godMode)
			{
				return;
			}
			if (PendingPvpCombatHits.TryGetValue(val.GetPlayerID(), out var value2))
			{
				PendingPvpCombatHits.Remove(val.GetPlayerID());
				float num = Mathf.Max(0f, value2.HealthBefore - ((Character)val).GetHealth());
				if (num > 0f && (Object)(object)value2.Attacker != (Object)null && (Object)(object)value2.Attacker != (Object)(object)val && !val.m_godMode)
				{
					PvpCombatHelper.ApplyPvpCombatStatus(value2.Attacker);
					PvpCombatHelper.ApplyPvpCombatStatus(val, value2.Attacker);
					PlayerCombatHandler.EnterCombat((Character)(object)value2.Attacker);
					PlayerCombatHandler.EnterCombat((Character)(object)val);
				}
			}
			if (!(value == val.GetPlayerName()))
			{
				Logger.LogInfo((object)$"[PvPOverhaul BOUNTY] PvP hit completed, scheduling death check: {value} -> {val.GetPlayerName()} health={((Character)val).GetHealth()} dead={((Character)val).IsDead()}");
				BountySystem.Instance.ScheduleDeathCheck(val);
			}
		}

		private static void NormalizePvpDamage(Player victim, Player attacker, HitData hit)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Invalid comparison between Unknown and I4
			float maxHealth = ((Character)victim).GetMaxHealth();
			if (maxHealth <= 0f)
			{
				return;
			}
			float totalDamage = hit.GetTotalDamage();
			if (totalDamage <= 0f)
			{
				return;
			}
			bool flag = IsLikelyDotTick(hit);
			bool flag2 = IsLikelyMagicBurstFragment(hit);
			bool flag3 = (int)hit.m_skill == 9 && IsLikelyMagicSecondary(hit);
			float value = PvPOverhaul.PvpMaxDirectHitPercentHp.Value;
			value *= GetWeaponPvpMultiplier(attacker);
			float num = (flag ? (maxHealth * PvPOverhaul.PvpMaxDotTickPercentHp.Value) : (maxHealth * value));
			if (flag2)
			{
				num = Mathf.Min(num, maxHealth * PvPOverhaul.PvpMaxMagicMultiHitPercentHp.Value);
			}
			if (flag3)
			{
				num = Mathf.Min(num, maxHealth * PvPOverhaul.PvpMaxMagicSecondaryPercentHp.Value);
			}
			if (flag2)
			{
				string key = $"{victim.GetPlayerID()}:{attacker.GetPlayerID()}";
				float num2 = Mathf.Max(0.05f, PvPOverhaul.PvpMagicBurstWindowSeconds.Value);
				float num3 = maxHealth * PvPOverhaul.PvpMaxMagicBurstPercentHp.Value;
				if (!MagicBurstWindows.TryGetValue(key, out var value2) || Time.time - value2.StartTime > num2)
				{
					value2 = new MagicBurstWindow
					{
						StartTime = Time.time,
						Damage = 0f
					};
					MagicBurstWindows[key] = value2;
				}
				float num4 = num3 - value2.Damage;
				if (num4 <= 0f)
				{
					ZeroDamage(hit);
					return;
				}
				num = Mathf.Min(num, num4);
			}
			if (num <= 0f)
			{
				ZeroDamage(hit);
				return;
			}
			if (totalDamage > num)
			{
				float scale = num / totalDamage;
				ScaleDamage(hit, scale);
			}
			ApplyElementalPvpMultipliers(hit);
			ApplyTherzieSpecialNerfs(victim, attacker, hit);
			ApplyPvPResilience(victim, hit);
			ApplyShieldEquippedPvpReduction(victim, hit);
			if (flag2)
			{
				string key2 = $"{victim.GetPlayerID()}:{attacker.GetPlayerID()}";
				if (MagicBurstWindows.TryGetValue(key2, out var value3))
				{
					value3.Damage += hit.GetTotalDamage();
				}
			}
		}

		private static void ApplyShieldEquippedPvpReduction(Player victim, HitData hit)
		{
			if (!((Object)(object)victim == (Object)null) && hit != null && PvpShieldStateSyncPatch.HasShield(victim))
			{
				float num = Mathf.Clamp01(PvPOverhaul.PvpShieldEquippedDamageReduction.Value);
				if (!(num <= 0f))
				{
					((DamageTypes)(ref hit.m_damage)).Modify(1f - num);
				}
			}
		}

		private static ItemData GetEquippedShield(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return null;
			}
			ItemData leftItem = ((Humanoid)player).GetLeftItem();
			if (IsShield(leftItem))
			{
				return leftItem;
			}
			ItemData currentBlocker = ((Humanoid)player).GetCurrentBlocker();
			if (IsShield(currentBlocker))
			{
				return currentBlocker;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return null;
			}
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (allItem == null || allItem.m_shared == null || !allItem.m_equipped || !IsShield(allItem))
				{
					continue;
				}
				return allItem;
			}
			return null;
		}

		private static bool IsShield(ItemData item)
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Invalid comparison between Unknown and I4
			if (item == null || item.m_shared == null)
			{
				return false;
			}
			if ((int)item.m_shared.m_itemType == 5)
			{
				return true;
			}
			string text = (((Object)(object)item.m_dropPrefab != (Object)null) ? ((Object)item.m_dropPrefab).name : "");
			string text2 = item.m_shared.m_name ?? "";
			return text.IndexOf("shield", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("shield", StringComparison.OrdinalIgnoreCase) >= 0 || text2.IndexOf("bouclier", StringComparison.OrdinalIgnoreCase) >= 0;
		}

		private static void ConvertPvpDamageToTrueDamage(HitData hit)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (hit != null)
			{
				float totalDamage = hit.GetTotalDamage();
				if (!(totalDamage <= 0f))
				{
					hit.m_damage = new DamageTypes
					{
						m_damage = totalDamage
					};
				}
			}
		}

		private static void ApplyElementalPvpMultipliers(HitData hit)
		{
			if (hit != null)
			{
				hit.m_damage.m_lightning *= PvPOverhaul.PvpLightningDamageMultiplier.Value;
			}
		}

		private static void ApplyTherzieSpecialNerfs(Player victim, Player attacker, HitData hit)
		{
			if (hit != null)
			{
				if (IsTherzieBleedTick(victim, hit))
				{
					ScaleDamage(hit, PvPOverhaul.TherzieBleedNerf.Value);
				}
				else if (IsTherzieDualWeapon(attacker))
				{
					ScaleDamage(hit, PvPOverhaul.TherzieDualWeapNerf.Value);
				}
			}
		}

		private static bool IsTherzieDualWeapon(Player attacker)
		{
			if ((Object)(object)attacker == (Object)null)
			{
				return false;
			}
			ItemData currentWeapon = ((Humanoid)attacker).GetCurrentWeapon();
			if (currentWeapon == null || currentWeapon.m_shared == null)
			{
				return false;
			}
			string text = "";
			if ((Object)(object)currentWeapon.m_dropPrefab != (Object)null)
			{
				text = text + ((Object)currentWeapon.m_dropPrefab).name + " ";
			}
			text += currentWeapon.m_shared.m_name;
			return text.IndexOf("Dual", StringComparison.OrdinalIgnoreCase) >= 0;
		}

		private static bool IsTherzieBleedTick(Player victim, HitData hit)
		{
			if ((Object)(object)victim == (Object)null || hit == null)
			{
				return false;
			}
			if ((Object)(object)hit.GetAttacker() != (Object)null)
			{
				return false;
			}
			if (hit.GetTotalDamage() <= 0f)
			{
				return false;
			}
			SEMan sEMan = ((Character)victim).GetSEMan();
			if (sEMan == null)
			{
				return false;
			}
			foreach (StatusEffect statusEffect in sEMan.GetStatusEffects())
			{
				if (!((Object)(object)statusEffect == (Object)null))
				{
					string text = ((Object)statusEffect).name ?? "";
					string text2 = statusEffect.m_name ?? "";
					string text3 = ((object)statusEffect).GetType().FullName ?? "";
					if (text.StartsWith("SE_Warfare", StringComparison.OrdinalIgnoreCase))
					{
						return true;
					}
					if (text.StartsWith("SE_WarfareFireAndIce", StringComparison.OrdinalIgnoreCase))
					{
						return true;
					}
					if (text3.IndexOf("SE_Warfare", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						return true;
					}
					if (text3.IndexOf("SE_WarfareFireAndIce", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						return true;
					}
					if (text.IndexOf("Warfare_Bleeding", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						return true;
					}
					if (text2.IndexOf("Bleeding", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						return true;
					}
				}
			}
			return false;
		}

		private static float GetWeaponPvpMultiplier(Player attacker)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: 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_0078: Expected I4, but got Unknown
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			ItemData currentWeapon = ((Humanoid)attacker).GetCurrentWeapon();
			if (currentWeapon == null || currentWeapon.m_shared == null)
			{
				return 1f;
			}
			SkillType skillType = currentWeapon.m_shared.m_skillType;
			SkillType val = skillType;
			float num = (val - 1) switch
			{
				0 => PvPOverhaul.PvpSwordDamageMultiplier.Value, 
				6 => PvPOverhaul.PvpAxeDamageMultiplier.Value, 
				2 => PvPOverhaul.PvpClubDamageMultiplier.Value, 
				1 => PvPOverhaul.PvpKnifeDamageMultiplier.Value, 
				4 => PvPOverhaul.PvpSpearDamageMultiplier.Value, 
				3 => PvPOverhaul.PvpPolearmDamageMultiplier.Value, 
				10 => PvPOverhaul.PvpUnarmedDamageMultiplier.Value, 
				7 => PvPOverhaul.PvpBowDamageMultiplier.Value, 
				13 => PvPOverhaul.PvpCrossbowDamageMultiplier.Value, 
				_ => 1f, 
			};
			if (IsMeleeSkill(currentWeapon.m_shared.m_skillType) && IsSecondaryAttack(attacker, currentWeapon))
			{
				num *= PvPOverhaul.PvpMeleeSecondaryDamageMultiplier.Value;
			}
			if (IsTwoHandedMeleeWeapon(currentWeapon))
			{
				num *= PvPOverhaul.PvpTwoHandedDamageMultiplier.Value;
			}
			return num;
		}

		private static bool IsMeleeSkill(SkillType skill)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Invalid comparison between Unknown and I4
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Invalid comparison between Unknown and I4
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Invalid comparison between Unknown and I4
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			return (int)skill == 1 || (int)skill == 7 || (int)skill == 3 || (int)skill == 2 || (int)skill == 5 || (int)skill == 4 || (int)skill == 11;
		}

		private static bool IsTwoHandedMeleeWeapon(ItemData weapon)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Invalid comparison between Unknown and I4
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Invalid comparison between Unknown and I4
			if (weapon == null || weapon.m_shared == null)
			{
				return false;
			}
			if (!IsMeleeSkill(weapon.m_shared.m_skillType))
			{
				return false;
			}
			return (int)weapon.m_shared.m_itemType == 14 || (int)weapon.m_shared.m_itemType == 22;
		}

		private static bool IsSecondaryAttack(Player attacker, ItemData weapon)
		{
			if ((Object)(object)attacker == (Object)null || weapon?.m_shared == null)
			{
				return false;
			}
			Attack currentAttack = ((Humanoid)attacker).m_currentAttack;
			Attack secondaryAttack = weapon.m_shared.m_secondaryAttack;
			if (currentAttack == null || secondaryAttack == null)
			{
				return false;
			}
			if (currentAttack == secondaryAttack)
			{
				return true;
			}
			return currentAttack.m_attackAnimation == secondaryAttack.m_attackAnimation || currentAttack.m_attackChainLevels == secondaryAttack.m_attackChainLevels || Math.Abs(currentAttack.m_damageMultiplier - secondaryAttack.m_damageMultiplier) < 0.01f;
		}

		private static bool IsLikelyMagicBurstFragment(HitData hit)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Invalid comparison between Unknown and I4
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Invalid comparison between Unknown and I4
			if (hit == null)
			{
				return false;
			}
			float totalDamage = hit.GetTotalDamage();
			if (totalDamage <= 0f || totalDamage > 100f)
			{
				return false;
			}
			if ((int)hit.m_skill != 9 && (int)hit.m_skill != 10)
			{
				return false;
			}
			int num = 0;
			if (hit.m_damage.m_damage > 0f)
			{
				num++;
			}
			if (hit.m_damage.m_blunt > 0f)
			{
				num++;
			}
			if (hit.m_damage.m_slash > 0f)
			{
				num++;
			}
			if (hit.m_damage.m_pierce > 0f)
			{
				num++;
			}
			if (hit.m_damage.m_chop > 0f)
			{
				num++;
			}
			if (hit.m_damage.m_pickaxe > 0f)
			{
				num++;
			}
			if (hit.m_damage.m_fire > 0f)
			{
				num++;
			}
			if (hit.m_damage.m_frost > 0f)
			{
				num++;
			}
			if (hit.m_damage.m_lightning > 0f)
			{
				num++;
			}
			if (hit.m_damage.m_poison > 0f)
			{
				num++;
			}
			if (hit.m_damage.m_spirit > 0f)
			{
				num++;
			}
			return num <= 3;
		}

		private static bool IsLikelyMagicSecondary(HitData hit)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Invalid comparison between Unknown and I4
			if ((int)hit.m_skill != 9)
			{
				return false;
			}
			float totalDamage = hit.GetTotalDamage();
			if (totalDamage <= 0f)
			{
				return false;
			}
			return hit.m_damage.m_fire >= 50f || hit.m_damage.m_frost >= 50f || hit.m_damage.m_lightning >= 50f || hit.m_damage.m_poison >= 50f || hit.m_damage.m_spirit >= 50f || totalDamage >= 100f;
		}

		private static bool IsLikelyDotTick(HitData hit)
		{
			if (!(hit.m_damage.m_fire > 0f) && !(hit.m_damage.m_poison > 0f) && !(hit.m_damage.m_spirit > 0f))
			{
				return false;
			}
			bool flag = hit.m_damage.m_damage > 0f || hit.m_damage.m_blunt > 0f || hit.m_damage.m_slash > 0f || hit.m_damage.m_pierce > 0f || hit.m_damage.m_chop > 0f || hit.m_damage.m_pickaxe > 0f || hit.m_damage.m_frost > 0f || hit.m_damage.m_lightning > 0f;
			return !flag;
		}

		private static void ScaleDamage(HitData hit, float scale)
		{
			hit.m_damage.m_damage *= scale;
			hit.m_damage.m_blunt *= scale;
			hit.m_damage.m_slash *= scale;
			hit.m_damage.m_pierce *= scale;
			hit.m_damage.m_chop *= scale;
			hit.m_damage.m_pickaxe *= scale;
			hit.m_damage.m_fire *= scale;
			hit.m_damage.m_frost *= scale;
			hit.m_damage.m_lightning *= scale;
			hit.m_damage.m_poison *= scale;
			hit.m_damage.m_spirit *= scale;
		}

		private static void ScaleDamageTypes(ref DamageTypes damage, float scale)
		{
			damage.m_damage *= scale;
			damage.m_blunt *= scale;
			damage.m_slash *= scale;
			damage.m_pierce *= scale;
			damage.m_chop *= scale;
			damage.m_pickaxe *= scale;
			damage.m_fire *= scale;
			damage.m_frost *= scale;
			damage.m_lightning *= scale;
			damage.m_poison *= scale;
			damage.m_spirit *= scale;
		}

		private static void ZeroDamage(HitData hit)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			hit.m_damage = default(DamageTypes);
		}

		private static void ApplyPvPResilience(Player victim, HitData hit)
		{
			if ((Object)(object)victim == (Object)null || hit == null)
			{
				return;
			}
			float totalDamage = hit.GetTotalDamage();
			float resilience = PvpResilienceAPI.GetResilience(victim);
			Logger.LogInfo((object)$"[PvPOverhaul Resilience] victim={victim.GetPlayerName()} rating={resilience} before={totalDamage}");
			if (!(resilience <= 0f))
			{
				float num = resilience / PvPOverhaul.ResilienceRatingPerPercent.Value / 100f;
				num = Mathf.Clamp(num, 0f, PvPOverhaul.MaxResilienceReduction.Value);
				if (!(num <= 0f))
				{
					((DamageTypes)(ref hit.m_damage)).Modify(1f - num);
					Logger.LogInfo((object)$"[PvPOverhaul Resilience] reduction={num:P1} after={hit.GetTotalDamage()}");
				}
			}
		}
	}
	[HarmonyPatch(typeof(Character), "Heal", new Type[]
	{
		typeof(float),
		typeof(bool)
	})]
	internal static class PvpCombatNoHealingPatch
	{
		private static bool Prefix(Character __instance, ref float hp)
		{
			if (!PvPOverhaul.DisableHealthRegenInPvpBattle.Value)
			{
				return true;
			}
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (val == null)
			{
				return true;
			}
			if (!PvpCombatHelper.IsInPvpCombat(val))
			{
				return true;
			}
			hp = 0f;
			return false;
		}
	}
	[HarmonyPatch(typeof(SE_Shield), "OnDamaged")]
	internal static class PvPShieldBreakOnDamagedPatch
	{
		private static bool Prefix(SE_Shield __instance, HitData hit, Character attacker)
		{
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			if (!PvPOverhaul.BreakShieldOnPvpHit.Value)
			{
				return true;
			}
			Character obj = ((StatusEffect)(__instance?)).m_character;
			Player val = (Player)(object)((obj is Player) ? obj : null);
			if (val == null)
			{
				return true;
			}
			if (hit == null || hit.GetTotalDamage() <= 0f)
			{
				return true;
			}
			Character val2 = (((Object)(object)attacker != (Object)null) ? attacker : hit.GetAttacker());
			Player val3 = (Player)(object)((val2 is Player) ? val2 : null);
			if (val3 == null)
			{
				return true;
			}
			if ((Object)(object)val3 == (Object)(object)val)
			{
				return true;
			}
			__instance.m_absorbDamage = 0f;
			__instance.m_absorbDamagePerSkillLevel = 0f;
			SEMan sEMan = ((Character)val).GetSEMan();
			if (sEMan != null)
			{
				sEMan.RemoveStatusEffect(((StatusEffect)__instance).NameHash(), true);
			}
			BountySystem.Instance.MarkIgnoredDeath(val);
			if (PvPOverhaul.ShieldBreakConsumesHit.Value)
			{
				hit.m_damage = default(DamageTypes);
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Character), "Damage")]
	internal static class PvPBlockLeakPatch
	{
		private static bool _applyingLeak;

		private static void Postfix(Character __instance, HitData hit)
		{
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0134: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0142: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_017a: Expected O, but got Unknown
			if (_applyingLeak)
			{
				return;
			}
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (val == null)
			{
				return;
			}
			long playerID = val.GetPlayerID();
			if (!PvPDamagePatch.PendingLeaks.TryGetValue(playerID, out var value))
			{
				return;
			}
			PvPDamagePatch.PendingLeaks.Remove(playerID);
			if (Time.time - value.Time > 1f)
			{
				return;
			}
			Character val2 = ((hit != null) ? hit.GetAttacker() : null);
			if ((Object)(object)val2 != (Object)null && val2.IsStaggering())
			{
				return;
			}
			float health = ((Character)val).GetHealth();
			float num = Mathf.Max(0f, value.HealthBefore - health);
			if (num >= value.ExpectedDamage * 0.8f)
			{
				return;
			}
			float blockLeakPercent = value.BlockLeakPercent;
			float num2 = value.ExpectedDamage * blockLeakPercent;
			if (!(num >= num2))
			{
				float num3 = num2 - num;
				if (!(num3 <= 0f))
				{
					HitData val3 = new HitData
					{
						m_damage = new DamageTypes
						{
							m_damage = num3
						},
						m_point = ((Character)val).GetCenterPoint(),
						m_dir = Vector3.zero,
						m_blockable = false,
						m_dodgeable = false,
						m_skill = hit.m_skill,
						m_hitType = hit.m_hitType
					};
					_applyingLeak = true;
					((Character)val).Damage(val3);
					_applyingLeak = false;
				}
			}
		}
	}
	[HarmonyPatch(typeof(Aoe), "OnHit")]
	internal static class DisablePvpAoePatch
	{
		private static bool Prefix(Aoe __instance, Collider collider)
		{
			if (!PvPOverhaul.DisablePvpAoeSplash.Value)
			{
				return true;
			}
			if ((Object)(object)__instance == (Object)null || (Object)(object)collider == (Object)null)
			{
				return true;
			}
			if (((DamageTypes)(ref __instance.m_damage)).GetTotalDamage() <= 0f)
			{
				return true;
			}
			Player componentInParent = ((Component)collider).GetComponentInParent<Player>();
			if ((Object)(object)componentInParent == (Object)null)
			{
				return true;
			}
			Character owner = __instance.m_owner;
			Player val = (Player)(object)((owner is Player) ? owner : null);
			if (val == null)
			{
				return true;
			}
			if ((Object)(object)val == (Object)(object)componentInParent)
			{
				return true;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "FixedUpdate")]
	internal static class PvpCombatVelocityClampPatch
	{
		private static void Postfix(Player __instance)
		{
			//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_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)__instance == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && PvpCombatHelper.IsInPvpCombat(__instance) && !((Object)(object)((Character)__instance).m_body == (Object)null))
			{
				Vector3 linearVelocity = ((Character)__instance).m_body.linearVelocity;
				Vector3 val = default(Vector3);
				((Vector3)(ref val))..ctor(linearVelocity.x, 0f, linearVelocity.z);
				float value = PvPOverhaul.PvpCombatMaxRunSpeedFactor.Value;
				if (!(((Vector3)(ref val)).magnitude <= value))
				{
					val = ((Vector3)(ref val)).normalized * value;
					linearVelocity.x = val.x;
					linearVelocity.z = val.z;
					((Character)__instance).m_body.linearVelocity = linearVelocity;
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "GetJogSpeedFactor")]
	internal static class PvpCombatJogSpeedClampPatch
	{
		private static void Postfix(Player __instance, ref float __result)
		{
			if (PvpCombatHelper.IsInPvpCombat(__instance))
			{
				__result = Mathf.Min(__result, PvPOverhaul.PvpCombatMaxJogSpeedFactor.Value);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "GetRunSpeedFactor")]
	internal static class PvpCombatRunSpeedClampPatch
	{
		private static void Postfix(Player __instance, ref float __result)
		{
			if (PvpCombatHelper.IsInPvpCombat(__instance))
			{
				__result = Mathf.Min(__result, PvPOverhaul.PvpCombatMaxRunSpeedFactor.Value);
			}
		}
	}
	[HarmonyPatch]
	internal static class PvpCombatJumpForceClampPatch
	{
		private static MethodBase TargetMethod()
		{
			return typeof(Character).GetMethod("Jump", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		}

		private static void Postfix(Character __instance)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (val != null && PvpCombatHelper.IsInPvpCombat(val) && !((Object)(object)((Character)val).m_body == (Object)null))
			{
				Vector3 linearVelocity = ((Character)val).m_body.linearVelocity;
				if (linearVelocity.y > PvPOverhaul.PvpCombatMaxJumpForce.Value)
				{
					linearVelocity.y = PvPOverhaul.PvpCombatMaxJumpForce.Value;
					((Character)val).m_body.linearVelocity = linearVelocity;
				}
			}
		}
	}
	[HarmonyPatch(typeof(Character), "UseStamina")]
	internal static class PvpPercentStaminaCostPatch
	{
		private static void Prefix(Character __instance, ref float stamina)
		{
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (val != null && PvpCombatHelper.IsInPvpCombat(val))
			{
				float maxStamina = ((Character)val).GetMaxStamina();
				if (!(maxStamina <= 0f))
				{
					float value = PvPOverhaul.PvpGenericStaminaPercent.Value;
					value = Mathf.Clamp01(value);
					stamina = maxStamina * value;
				}
			}
		}
	}
	[HarmonyPatch(typeof(Player), "Update")]
	internal static class PvpShieldStateSyncPatch
	{
		private const string ShieldKey = "PvpOverhaul.HasShieldEquipped";

		private static float _nextUpdate;

		private static void Postfix(Player __instance)
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Invalid comparison between Unknown and I4
			if ((Object)(object)__instance != (Object)(object)Player.m_localPlayer || Time.time < _nextUpdate)
			{
				return;
			}
			_nextUpdate = Time.time + 1f;
			bool flag = false;
			Inventory inventory = ((Humanoid)__instance).GetInventory();
			if (inventory != null)
			{
				foreach (ItemData allItem in inventory.GetAllItems())
				{
					if (allItem?.m_shared == null || !allItem.m_equipped || (int)allItem.m_shared.m_itemType != 5)
					{
						continue;
					}
					flag = true;
					break;
				}
			}
			if ((Object)(object)((Character)__instance).m_nview != (Object)null && ((Character)__instance).m_nview.IsValid())
			{
				ZDO zDO = ((Character)__instance).m_nview.GetZDO();
				if (zDO != null)
				{
					zDO.Set("PvpOverhaul.HasShieldEquipped", flag);
				}
			}
		}

		public static bool HasShield(Player player)
		{
			if ((Object)(object)player == (Object)null || (Object)(object)((Character)player).m_nview == (Object)null || !((Character)player).m_nview.IsValid())
			{
				return false;
			}
			ZDO zDO = ((Character)player).m_nview.GetZDO();
			return zDO != null && zDO.GetBool("PvpOverhaul.HasShieldEquipped", false);
		}
	}
	[BepInPlugin("dzk.pvpoverhaul", "PvpOverhaul", "1.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	internal class PvPOverhaul : BaseUnityPlugin
	{
		public const string PluginGUID = "dzk.pvpoverhaul";

		public const string PluginName = "PvpOverhaul";

		public const string PluginVersion = "1.1.0";

		public static PvPOverhaul Instance;

		public static CustomLocalization Localization;

		public static ConfigEntry<int> BountyTriggerKillCount;

		public static ConfigEntry<int> BountyKillWindowMinutes;

		public static ConfigEntry<int> BountyDurationMinutes;

		public static ConfigEntry<string> BountyRewardItem;

		public static ConfigEntry<int> BountyRewardAmountPerTier;

		public static ConfigEntry<string> MsgKillBroadcast;

		public static ConfigEntry<string> MsgBountyActivated;

		public static ConfigEntry<string> MsgBountyRefreshed;

		public static ConfigEntry<string> MsgBountyCleared;

		public static ConfigEntry<string> MsgBountyExpired;

		public static ConfigEntry<string> MsgBountyReward;

		public static ConfigEntry<bool> EnablePvpDamagePatch;

		public static ConfigEntry<float> PvpMaxDirectHitPercentHp;

		public static ConfigEntry<float> PvpMaxDotTickPercentHp;

		public static ConfigEntry<bool> BreakShieldOnPvpHit;

		public static ConfigEntry<bool> ShieldBreakConsumesHit;

		public static ConfigEntry<string> ShieldStatusEffectNames;

		public static ConfigEntry<bool> DisableHealthRegenInPvpBattle;

		public static ConfigEntry<string> PvpBattleStatusEffectNames;

		public static ConfigEntry<float> PvpBlockedDamageLeakPercent;

		public static ConfigEntry<float> PvpMaxMagicMultiHitPercentHp;

		public static ConfigEntry<bool> DisablePvpAoeSplash;

		public static ConfigEntry<float> PvpMaxMagicBurstPercentHp;

		public static ConfigEntry<float> PvpMagicBurstWindowSeconds;

		public static ConfigEntry<float> PvpMaxMagicSecondaryPercentHp;

		public static ConfigEntry<float> PvpMeleeDamageMultiplier;

		public static ConfigEntry<float> PvpMeleeSecondaryDamageMultiplier;

		public static ConfigEntry<float> PvpSwordDamageMultiplier;

		public static ConfigEntry<float> PvpAxeDamageMultiplier;

		public static ConfigEntry<float> PvpClubDamageMultiplier;

		public static ConfigEntry<float> PvpKnifeDamageMultiplier;

		public static ConfigEntry<float> PvpSpearDamageMultiplier;

		public static ConfigEntry<float> PvpPolearmDamageMultiplier;

		public static ConfigEntry<float> PvpUnarmedDamageMultiplier;

		public static ConfigEntry<float> PvpBowDamageMultiplier;

		public static ConfigEntry<float> PvpCrossbowDamageMultiplier;

		public static ConfigEntry<float> PvpLightningDamageMultiplier;

		public static ConfigEntry<float> PvpTwoHandedDamageMultiplier;

		public static ConfigEntry<float> PvpTwoHandedBlockedDamageLeakPercent;

		public static ConfigEntry<float> TherzieDualWeapNerf;

		public static ConfigEntry<float> TherzieBleedNerf;

		public static ConfigEntry<bool> DisableSkillLossOnPvpDeath;

		public static ConfigEntry<float> ResilienceRatingPerPercent;

		public static ConfigEntry<float> MaxResilienceReduction;

		internal static ConfigEntry<float> PvpCombatMaxJogSpeedFactor;

		internal static ConfigEntry<float> PvpCombatMaxRunSpeedFactor;

		internal static ConfigEntry<float> PvpCombatMaxJumpForce;

		internal static ConfigEntry<float> PvpShieldEquippedDamageReduction;

		internal static ConfigEntry<float> PvpGenericStaminaPercent;

		private FileSystemWatcher _configWatcher;

		private DateTime _lastConfigReloadUtc;

		private void Awake()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			Instance = this;
			Localization = LocalizationManager.Instance.GetLocalization();
			SetupConfig();
			SetupConfigWatcher();
			Logger.LogInfo((object)"PvpOverhaul is loading...");
			Harmony val = new Harmony("dzk.pvpoverhaul");
			val.PatchAll();
			Compat_ShudnalInfinityHammer.TryInstall(val);
			PvpSkillLossPatch.PatchFortifySkillsRedux(val);
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new BountyStatusCommand());
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new BountyResetCommand());
			SynchronizationManager.OnConfigurationSynchronized += delegate(object _, ConfigurationSynchronizationEventArgs args)
			{
				Logger.LogInfo((object)(args.InitialSynchronization ? "[PvPOverhaul] Initial server config sync received" : "[PvPOverhaul] Server config sync received"));
			};
			Logger.LogInfo((object)"PvpOverhaul loaded successfully.");
		}

		private void Update()
		{
			BountyRpc.Update();
			BountySystem.Instance.Update();
			BountyMapCircle.Update();
			BountyStatusEffectController.UpdateRegistration();
			BountyRpc.UpdateReward();
		}

		private void SetupConfig()
		{
			BountyTriggerKillCount = ConfigFileExtensions.BindConfig<int>(((BaseUnityPlugin)this).Config, "Bounty", "TriggerKillCount", 3, "Number of player kills required to activate a bounty.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			BountyKillWindowMinutes = ConfigFileExtensions.BindConfig<int>(((BaseUnityPlugin)this).Config, "Bounty", "KillWindowMinutes", 180, "Time window in minutes during which kills are counted for bounty activation.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			BountyDurationMinutes = ConfigFileExtensions.BindConfig<int>(((BaseUnityPlugin)this).Config, "Bounty", "DurationMinutes", 60, "How long the bounty stays active, in minutes.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			BountyRewardItem = ConfigFileExtensions.BindConfig<string>(((BaseUnityPlugin)this).Config, "Bounty", "RewardItem", "Coins", "Prefab name of the item rewarded when killing a bounty player.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			BountyRewardAmountPerTier = ConfigFileExtensions.BindConfig<int>(((BaseUnityPlugin)this).Config, "Bounty", "RewardAmountPerTier", 100, "Reward amount per bounty tier.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			MsgKillBroadcast = ConfigFileExtensions.BindConfig<string>(((BaseUnityPlugin)this).Config, "Messages", "KillBroadcast", "{killer} a tué {victim}", "Broadcast when a player kills another player.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			MsgBountyActivated = ConfigFileExtensions.BindConfig<string>(((BaseUnityPlugin)this).Config, "Messages", "BountyActivated", "{player} est maintenant recherché ! Tier {tier}", "Broadcast when a bounty starts.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			MsgBountyRefreshed = ConfigFileExtensions.BindConfig<string>(((BaseUnityPlugin)this).Config, "Messages", "BountyRefreshed", "{player} reste recherché ! Tier {tier} | Timer remis à {duration} min.", "Broadcast when bounty timer refreshes.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			MsgBountyCleared = ConfigFileExtensions.BindConfig<string>(((BaseUnityPlugin)this).Config, "Messages", "BountyCleared", "{player} n'est plus recherché.", "Broadcast when a bounty is cleared.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			MsgBountyExpired = ConfigFileExtensions.BindConfig<string>(((BaseUnityPlugin)this).Config, "Messages", "BountyExpired", "La prime de {player} a expiré.", "Broadcast when a bounty expires.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			MsgBountyReward = ConfigFileExtensions.BindConfig<string>(((BaseUnityPlugin)this).Config, "Messages", "BountyReward", "{killer} a éliminé le joueur recherché {victim} et gagne {amount}x {item} !", "Broadcast when a bounty is claimed.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			EnablePvpDamagePatch = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "PvP Damage", "EnablePvpDamagePatch", true, "Enable global PvP damage normalization.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpMaxDirectHitPercentHp = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Damage", "MaxDirectHitPercentHp", 0.08f, "Maximum PvP direct hit damage as percent of victim max HP.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpMaxDotTickPercentHp = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Damage", "MaxDotTickPercentHp", 0.015f, "Maximum PvP fire/poison/spirit tick damage as percent of victim max HP.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			BreakShieldOnPvpHit = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "PvP Shields", "BreakShieldOnPvpHit", true, "If true, PvP hits instantly remove configured shield status effects.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ShieldBreakConsumesHit = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "PvP Shields", "ShieldBreakConsumesHit", true, "If true, the hit that breaks a shield deals 0 damage.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ShieldStatusEffectNames = ConfigFileExtensions.BindConfig<string>(((BaseUnityPlugin)this).Config, "PvP Shields", "ShieldStatusEffectNames", "Staff_shield,SE_StaffShield,SE_Shield,Shield,Bubble,Barrier,Protection", "Comma-separated status effect names/hashes to remove on PvP hit.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			DisableHealthRegenInPvpBattle = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "PvP Battle", "DisableHealthRegenInPvpBattle", true, "If true, health regen is set to 0 while the player has the PvP battle status.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpBattleStatusEffectNames = ConfigFileExtensions.BindConfig<string>(((BaseUnityPlugin)this).Config, "PvP Battle", "BattleStatusEffectNames", "SE_Combat", "Comma-separated status effect names used by Valheim PvP Tweaks.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpBlockedDamageLeakPercent = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Damage", "BlockedDamageLeakPercent", 0.25f, "Minimum percent of normalized PvP damage that still goes through block.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpMaxMagicMultiHitPercentHp = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Damage", "MaxMagicMultiHitPercentHp", 0.015f, "Maximum damage per hit for magic multi-hit projectiles, as percent of victim max HP.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			DisablePvpAoeSplash = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "PvP Damage", "DisablePvpAoeSplash", true, "If true, damaging AoE effects owned by players do not damage other players.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpMaxMagicBurstPercentHp = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Damage", "MaxMagicBurstPercentHp", 0.08f, "Maximum total PvP damage from magic multi-hit burst per attacker/victim over a short window.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpMagicBurstWindowSeconds = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Damage", "MagicBurstWindowSeconds", 0.2f, "Short time window used to group instant magic fragment hits from the same attacker to the same victim.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpMaxMagicSecondaryPercentHp = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Damage", "MaxMagicSecondaryPercentHp", 0.02f, "Maximum PvP damage for magic secondary attacks, as percent of victim max HP.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpMeleeDamageMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Damage", "MeleeDamageMultiplier", 1.25f, "Multiplier applied to melee PvP direct hit cap.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpMeleeSecondaryDamageMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Damage", "MeleeSecondaryDamageMultiplier", 1.5f, "Multiplier applied to melee secondary attack PvP damage cap.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpSwordDamageMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Weapon Multipliers", "Swords", 1.25f, "PvP damage cap multiplier for swords.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpAxeDamageMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Weapon Multipliers", "Axes", 1.25f, "PvP damage cap multiplier for axes.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpClubDamageMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Weapon Multipliers", "Clubs", 1.25f, "PvP damage cap multiplier for clubs.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpKnifeDamageMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Weapon Multipliers", "Knives", 0.9f, "PvP damage cap multiplier for knives.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpSpearDamageMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Weapon Multipliers", "Spears", 1f, "PvP damage cap multiplier for spears.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpPolearmDamageMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Weapon Multipliers", "Polearms", 1.15f, "PvP damage cap multiplier for polearms.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpUnarmedDamageMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Weapon Multipliers", "Unarmed", 1f, "PvP damage cap multiplier for unarmed.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpBowDamageMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Weapon Multipliers", "Bows", 1f, "PvP damage cap multiplier for bows.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpCrossbowDamageMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Weapon Multipliers", "Crossbows", 1.25f, "PvP damage cap multiplier for crossbows.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpLightningDamageMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Damage", "LightningDamageMultiplier", 0.25f, "Multiplier applied to PvP lightning damage after normalization. 0.25 = -75%.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpTwoHandedDamageMultiplier = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Damage", "TwoHandedDamageMultiplier", 1.25f, "Extra PvP damage cap multiplier for two-handed melee weapons.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpTwoHandedBlockedDamageLeakPercent = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Damage", "TwoHandedBlockedDamageLeakPercent", 0.75f, "Minimum percent of normalized two-handed PvP damage that still goes through block.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			TherzieDualWeapNerf = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Therzie Balance", "TherzieDualWeapNerf", 0.5f, "Multiplier applied to PvP damage from Therzie dual weapons detected by prefab/name containing 'Dual'. 0.50 = -50%.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			TherzieBleedNerf = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Therzie Balance", "TherzieSENerf", 0.25f, "Multiplier applied to Therzie SE that proc on 4th or 5th. 0.25 = -75%.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			DisableSkillLossOnPvpDeath = ConfigFileExtensions.BindConfig<bool>(((BaseUnityPlugin)this).Config, "PvP Death", "DisableSkillLossOnPvpDeath", true, "If true, PvP deaths restore all skill levels after death, overriding vanilla and other mods.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			ResilienceRatingPerPercent = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Resilience", "ResilienceRatingPerPercent", 10f, "How much resilience rating equals 1% PvP damage reduction.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			MaxResilienceReduction = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Resilience", "MaxResilienceReduction", 0.25f, "Maximum PvP damage reduction from resilience. 0.25 = 25%.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpCombatMaxJogSpeedFactor = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Combat Movement", "MaxJogSpeedFactor", 1.25f, "Maximum jog speed factor while in PvP combat.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpCombatMaxRunSpeedFactor = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Combat Movement", "MaxRunSpeedFactor", 1.25f, "Maximum run speed factor while in PvP combat.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpCombatMaxJumpForce = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Combat Movement", "MaxJumpForce", 8f, "Maximum jump force while in PvP combat.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpShieldEquippedDamageReduction = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Shield", "ShieldEquippedDamageReduction", 0.15f, "PvP damage reduction while a shield is equipped in the left hand. 0.15 = 15%.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
			PvpGenericStaminaPercent = ConfigFileExtensions.BindConfig<float>(((BaseUnityPlugin)this).Config, "PvP Stamina", "GenericStaminaPercent", 0.1f, "Toute dépense de stamina en combat PvP coûte ce pourcentage de stamina max. 0.10 = 10%.", true, (int?)null, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null);
		}

		private void SetupConfigWatcher()
		{
			string configFilePath = ((BaseUnityPlugin)this).Config.Con