Decompiled source of PvPOverhaul v1.2.2

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 System.Threading;
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.2.2")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.2.2.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 bool _loadedPersistence;

		private float _nextTimerTick;

		private float _nextFullSync;

		private float _nextPersistenceFlush;

		private float _nextTransientCleanup;

		private bool _persistenceDirty;

		private readonly List<string> _expiredBountyKeys = new List<string>();

		private readonly List<long> _expiredPlayerIds = new List<long>();

		private readonly List<string> _expiredPlayerNames = new List<string>();

		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 (Time.time >= _nextTransientCleanup)
			{
				_nextTransientCleanup = Time.time + 30f;
				CleanupTransientState();
			}
			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 >= _nextFullSync)
				{
					_nextFullSync = Time.time + 30f;
					BroadcastAllBounties();
				}
				if (_persistenceDirty && Time.time >= _nextPersistenceFlush)
				{
					_nextPersistenceFlush = Time.time + 30f;
					SaveBounties();
				}
			}
		}

		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);
			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;
				_persistenceDirty = true;
				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 utcNow = DateTime.UtcNow;
			_expiredBountyKeys.Clear();
			foreach (KeyValuePair<string, BountyState> activeBounty in _activeBounties)
			{
				if (utcNow >= activeBounty.Value.EndUtc)
				{
					_expiredBountyKeys.Add(activeBounty.Key);
				}
			}
			foreach (string expiredBountyKey in _expiredBountyKeys)
			{
				ClearBounty(expiredBountyKey, killed: false);
			}
		}

		private void BroadcastAllBounties()
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			DateTime utcNow = DateTime.UtcNow;
			foreach (BountyState value in _activeBounties.Values)
			{
				if (!(utcNow >= value.EndUtc))
				{
					if (!IsPlayerOnline(value.PlayerId))
					{
						BountyRpc.BroadcastBountyCircle(value.PlayerId, active: false, Vector3.zero, value.Tier, value.EndUtc);
					}
					else
					{
						BountyRpc.BroadcastBountyCircle(value.PlayerId, active: true, value.LastKnownPosition, value.Tier, value.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)
		{
			if (!_lastHits.TryGetValue(victimId, out var value))
			{
				value = new LastHit();
				_lastHits[victimId] = value;
			}
			value.VictimName = victimName;
			value.AttackerName = attackerName;
			value.Damage = damage;
			value.Time = Time.time;
			if (!string.IsNullOrWhiteSpace(victimName))
			{
				_lastHitsByName[victimName] = value;
			}
			if (PvPOverhaul.VerboseCombatLogging.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)
			{
				if (PvPOverhaul.VerboseCombatLogging.Value)
				{
					Logger.LogInfo((object)("[PvPOverhaul BOUNTY] Ignore death: godmode victim=" + victim.GetPlayerName()));
				}
				return;
			}
			if (((Character)victim).GetHealth() > 0f && !((Character)victim).IsDead())
			{
				if (PvPOverhaul.VerboseCombatLogging.Value)
				{
					Logger.LogInfo((object)$"[PvPOverhaul BOUNTY] Ignore death report: victim not dead yet {victim.GetPlayerName()} health={((Character)victim).GetHealth()} dead={((Character)victim).IsDead()}");
				}
				return;
			}
			long playerID = victim.GetPlayerID();
			if (_reportedDeaths.TryGetValue(playerID, out var value) && Time.time - value < 10f)
			{
				return;
			}
			LastHit lastHit = ResolveLastHit(victim);
			if (PvPOverhaul.VerboseCombatLogging.Value)
			{
				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;
				if (PvPOverhaul.VerboseCombatLogging.Value)
				{
					Logger.LogInfo((object)("[PvPOverhaul CLIENT] Reporting REAL kill to server: " + lastHit.AttackerName + " -> " + victim.GetPlayerName()));
				}
				PvpRuntimeMetrics.RecordBountyDeathReport();
				BountyRpc.SendKillToServer(lastHit.AttackerName, victim.GetPlayerName());
			}
		}

		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);
			_persistenceDirty = false;
			_nextPersistenceFlush = Time.time + 30f;
		}

		public void FlushPersistence()
		{
			if (_persistenceDirty && _loadedPersistence)
			{
				SaveBounties();
			}
		}

		private void CleanupTransientState()
		{
			float time = Time.time;
			_expiredPlayerIds.Clear();
			foreach (KeyValuePair<long, LastHit> lastHit in _lastHits)
			{
				if (time - lastHit.Value.Time > 30f)
				{
					_expiredPlayerIds.Add(lastHit.Key);
				}
			}
			foreach (long expiredPlayerId in _expiredPlayerIds)
			{
				if (_lastHits.TryGetValue(expiredPlayerId, out var value) && !string.IsNullOrWhiteSpace(value.VictimName) && _lastHitsByName.TryGetValue(value.VictimName, out var value2) && value == value2)
				{
					_lastHitsByName.Remove(value.VictimName);
				}
				_lastHits.Remove(expiredPlayerId);
			}
			_expiredPlayerIds.Clear();
			foreach (KeyValuePair<long, float> reportedDeath in _reportedDeaths)
			{
				if (time - reportedDeath.Value > 30f)
				{
					_expiredPlayerIds.Add(reportedDeath.Key);
				}
			}
			foreach (long expiredPlayerId2 in _expiredPlayerIds)
			{
				_reportedDeaths.Remove(expiredPlayerId2);
			}
			_expiredPlayerNames.Clear();
			foreach (KeyValuePair<string, LastHit> item in _lastHitsByName)
			{
				if (time - item.Value.Time > 30f)
				{
					_expiredPlayerNames.Add(item.Key);
				}
			}
			foreach (string expiredPlayerName in _expiredPlayerNames)
			{
				_lastHitsByName.Remove(expiredPlayerName);
			}
		}

		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();
		}
	}
	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;

		private static float _nextRewardRetryLog;

		private static float _nextRewardAttempt;

		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) || Time.unscaledTime < _nextRewardAttempt)
			{
				return;
			}
			_nextRewardAttempt = Time.unscaledTime + 1f;
			if ((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)
			{
				if (Time.unscaledTime >= _nextRewardRetryLog)
				{
					_nextRewardRetryLog = Time.unscaledTime + 10f;
					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;
			_nextRewardAttempt = 0f;
		}
	}
	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 static readonly List<string> _expiredCircleKeys = new List<string>();

		private static readonly FieldInfo PinWorldSizeField = typeof(PinData).GetField("m_worldSize", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

		private static float _nextExpirationCheck;

		private const float CircleWorldSize = 300f;

		public static void Update()
		{
			//IL_0163: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			if (Time.unscaledTime >= _nextExpirationCheck)
			{
				_nextExpirationCheck = Time.unscaledTime + 1f;
				DateTime utcNow = DateTime.UtcNow;
				_expiredCircleKeys.Clear();
				foreach (KeyValuePair<string, CircleState> circle in _circles)
				{
					if (circle.Value.EndUtc <= utcNow)
					{
						_expiredCircleKeys.Add(circle.Key);
					}
				}
				foreach (string expiredCircleKey in _expiredCircleKeys)
				{
					Remove(expiredCircleKey);
				}
			}
			if (_circles.Count == 0 || (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_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0166: 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_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0150: 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)
				{
					value2.Pin = Minimap.instance.AddPin(position, (PinType)13, text, false, false, 0L, default(PlatformUserID));
				}
				else
				{
					value2.Pin.m_pos = position;
					value2.Pin.m_name = text;
				}
				SetPinWorldSize(value2.Pin, 300f);
			}
			else
			{
				PinData pin = Minimap.instance.AddPin(position, (PinType)13, text, false, false, 0L, default(PlatformUserID));
				SetPinWorldSize(pin, 300f);
				_circles[playerName] = new CircleState
				{
					Pin = pin,
					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)
		{
			PinWorldSizeField?.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))
			{
				if (PvPOverhaul.VerboseCombatLogging.Value)
				{
					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)
			{
				if (PvPOverhaul.VerboseCombatLogging.Value)
				{
					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");

		private static Player _cachedPlayer;

		private static int _cachedFrame = -1;

		private static bool _cachedResult;

		public static bool IsInPvpCombat(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			if ((Object)(object)_cachedPlayer == (Object)(object)player && _cachedFrame == Time.frameCount)
			{
				return _cachedResult;
			}
			_cachedPlayer = player;
			_cachedFrame = Time.frameCount;
			_cachedResult = false;
			SEMan sEMan = ((Character)player).GetSEMan();
			if (sEMan != null && sEMan.HaveStatusEffect(CombatHash))
			{
				_cachedResult = true;
				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))
				{
					_cachedResult = true;
					return true;
				}
			}
			return false;
		}

		public static void ApplyPvpCombatStatus(Player player, Player templateSource = null)
		{
			if ((Object)(object)player == (Object)null)
			{
				return;
			}
			_cachedPlayer = player;
			_cachedFrame = Time.frameCount;
			_cachedResult = 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 && ((Character)player).m_nview.IsOwner() && !zDO.GetBool("VPT_PlayerInCombat", false))
				{
					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 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--;
			}
		}
	}
	internal static class PvpShieldBreakRuntime
	{
		[ThreadStatic]
		public static HitData ConsumedHit;
	}
	[HarmonyPatch]
	internal static class PvpArmorBypassRpcDamagePatch
	{
		private struct RpcDamageState
		{
			public bool ArmorBypassStarted;

			public bool IsPvpDamage;

			public bool TrackBlockLeak;

			public Player Victim;

			public Player Attacker;

			public float HealthBefore;

			public float ExpectedDamage;

			public float BlockLeakPercent;
		}

		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;
		}

		[HarmonyPriority(800)]
		private static void Prefix(Character __instance, HitData hit, out RpcDamageState __state)
		{
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Invalid comparison between Unknown and I4
			__state = default(RpcDamageState);
			Player val = (Player)(object)((__instance is Player) ? __instance : null);
			if (val == null || hit == null || (Object)(object)((Character)val).m_nview == (Object)null || !((Character)val).m_nview.IsValid() || !((Character)val).m_nview.IsOwner())
			{
				return;
			}
			Character attacker = hit.GetAttacker();
			Player val2 = (Player)(object)((attacker is Player) ? attacker : null);
			bool flag = (Object)(object)val2 == (Object)null && hit.HaveAttacker() && (int)hit.m_hitType == 2;
			if (((Object)(object)val2 == (Object)null && !flag) || (Object)(object)val2 == (Object)(object)val)
			{
				return;
			}
			if ((Object)(object)val2 != (Object)null && GroupsCompatibility.AreAllied(val2, val))
			{
				PvPDamagePatch.ZeroPvpDamage(hit);
				return;
			}
			PvpArmorBypassState.Begin();
			__state.ArmorBypassStarted = true;
			if ((Object)(object)val2 != (Object)null && PvPOverhaul.DisableSkillLossOnPvpDeath.Value)
			{
				PvpSkillLossPatch.ForceSaveSnapshot(val);
			}
			if (PvPOverhaul.EnablePvpDamagePatch.Value)
			{
				__state.IsPvpDamage = true;
				__state.Victim = val;
				__state.Attacker = val2;
				__state.HealthBefore = ((Character)val).GetHealth();
				PvpShieldBreakRuntime.ConsumedHit = null;
				PvPDamagePatch.ApplyVictimDefenses(val, hit);
				PvpRuntimeMetrics.RecordPvpHit();
				PvpRuntimeMetrics.RecordDefensiveResolution();
				float totalDamage = hit.GetTotalDamage();
				if ((Object)(object)val2 != (Object)null && totalDamage > 0f && !val.m_godMode)
				{
					BountySystem.Instance.SetLastHit(val.GetPlayerID(), val.GetPlayerName(), val2.GetPlayerName(), totalDamage);
				}
				if ((Object)(object)val2 != (Object)null && hit.m_blockable && totalDamage > 0f && ((Character)val).IsBlocking())
				{
					__state.TrackBlockLeak = true;
					__state.ExpectedDamage = totalDamage;
					__state.BlockLeakPercent = PvPDamagePatch.GetBlockLeakPercent(val2);
				}
			}
		}

		private static void Postfix(HitData hit, RpcDamageState __state)
		{
			if (__state.IsPvpDamage && !((Object)(object)__state.Victim == (Object)null))
			{
				if (__state.TrackBlockLeak && PvpShieldBreakRuntime.ConsumedHit != hit)
				{
					ApplyBlockLeak(hit, __state);
				}
				float num = Mathf.Max(0f, __state.HealthBefore - ((Character)__state.Victim).GetHealth());
				if (num > 0f && !__state.Victim.m_godMode && (Object)(object)__state.Attacker != (Object)null)
				{
					PvPTweaksCombatFilterPatch.MarkValidatedPvpHit(__state.Attacker, __state.Victim);
					PvpCombatStateCoordinator.OnDamageApplied(__state.Victim, __state.Attacker);
				}
			}
		}

		private static Exception Finalizer(RpcDamageState __state, Exception __exception)
		{
			if (__state.ArmorBypassStarted)
			{
				PvpArmorBypassState.End();
			}
			if (__state.IsPvpDamage)
			{
				PvpShieldBreakRuntime.ConsumedHit = null;
			}
			return __exception;
		}

		private static void ApplyBlockLeak(HitData originalHit, RpcDamageState state)
		{
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: 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_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Expected O, but got Unknown
			if (originalHit == null || (Object)(object)state.Attacker == (Object)null || ((Character)state.Attacker).IsStaggering())
			{
				return;
			}
			float num = Mathf.Max(0f, state.HealthBefore - ((Character)state.Victim).GetHealth());
			if (num >= state.ExpectedDamage * 0.8f)
			{
				return;
			}
			float num2 = state.ExpectedDamage * state.BlockLeakPercent;
			if (!(num >= num2))
			{
				float num3 = num2 - num;
				if (!(num3 <= 0f))
				{
					HitData val = new HitData
					{
						m_damage = new DamageTypes
						{
							m_damage = num3
						},
						m_point = ((Character)state.Victim).GetCenterPoint(),
						m_dir = Vector3.zero,
						m_blockable = false,
						m_dodgeable = false,
						m_skill = originalHit.m_skill,
						m_hitType = originalHit.m_hitType
					};
					((Character)state.Victim).Damage(val);
				}
			}
		}
	}
	[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]
	internal static class PvpPhysicalResistanceBypassPatch
	{
		private static readonly MethodInfo Target = AccessTools.DeclaredMethod(typeof(HitData), "ApplyResistance", new Type[2]
		{
			typeof(DamageModifiers),
			typeof(DamageModifier).MakeByRefType()
		}, (Type[])null);

		private static bool Prepare()
		{
			if (Target == null)
			{
				Logger.LogWarning((object)"[PvPOverhaul] HitData.ApplyResistance(DamageModifiers, out DamageModifier) not found, PvP physical resistance bypass disabled.");
				return false;
			}
			return true;
		}

		private static MethodBase TargetMethod()
		{
			return Target;
		}

		[HarmonyPriority(800)]
		private static void Prefix(ref DamageModifiers __0)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			if (PvpArmorBypassState.IsActive)
			{
				__0.m_blunt = (DamageModifier)0;
				__0.m_slash = (DamageModifier)0;
				__0.m_pierce = (DamageModifier)0;
			}
		}
	}
	[HarmonyPatch(typeof(Character), "Damage")]
	internal static class PvPDamagePatch
	{
		private class MagicBurstWindow
		{
			public float StartTime;

			public float Damage;
		}

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

		private static readonly List<PlayerPairKey> ExpiredBurstWindowKeys = new List<PlayerPairKey>();

		private static float _nextBurstWindowCleanup;

		internal static int MagicBurstWindowCount => MagicBurstWindows.Count;

		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void Prefix(Character __instance, HitData hit)
		{
			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);
				if (PvPOverhaul.VerboseCombatLogging.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)(object)val))
			{
				if (GroupsCompatibility.AreAllied(val2, val))
				{
					ZeroDamage(hit);
				}
				else
				{
					NormalizePvpDamage(val, val2, hit);
				}
			}
		}

		private static void NormalizePvpDamage(Player victim, Player attacker, HitData hit)
		{
			//IL_0003: 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_0058: Invalid comparison between Unknown and I4
			hit.m_hitType = (HitType)2;
			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)
			{
				PlayerPairKey key = new PlayerPairKey(attacker.GetPlayerID(), victim.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);
			if (flag2)
			{
				PlayerPairKey key2 = new PlayerPairKey(attacker.GetPlayerID(), victim.GetPlayerID());
				if (MagicBurstWindows.TryGetValue(key2, out var value3))
				{
					value3.Damage += hit.GetTotalDamage();
				}
			}
		}

		internal static void ApplyVictimDefenses(Player victim, HitData hit)
		{
			if ((Object)(object)victim == (Object)null || hit == null)
			{
				return;
			}
			ApplyPvPResilience(victim, hit);
			if (PvpShieldStateSyncPatch.HasAuthoritativeShield(victim))
			{
				float num = Mathf.Clamp01(PvPOverhaul.PvpShieldEquippedDamageReduction.Value);
				if (!(num <= 0f))
				{
					((DamageTypes)(ref hit.m_damage)).Modify(1f - num);
				}
			}
		}

		internal 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 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;
		}

		internal static float GetBlockLeakPercent(Player attacker)
		{
			ItemData weapon = ((attacker != null) ? ((Humanoid)attacker).GetCurrentWeapon() : null);
			return IsTwoHandedMeleeWeapon(weapon) ? PvPOverhaul.PvpTwoHandedBlockedDamageLeakPercent.Value : PvPOverhaul.PvpBlockedDamageLeakPercent.Value;
		}

		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 ZeroDamage(HitData hit)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			hit.m_damage = default(DamageTypes);
		}

		internal static void ZeroPvpDamage(HitData hit)
		{
			if (hit != null)
			{
				ZeroDamage(hit);
			}
		}

		private static void ApplyPvPResilience(Player victim, HitData hit)
		{
			if ((Object)(object)victim == (Object)null || hit == null)
			{
				return;
			}
			float totalDamage = hit.GetTotalDamage();
			float authoritativeResilience = PvpResilienceAPI.GetAuthoritativeResilience(victim);
			if (PvPOverhaul.VerboseCombatLogging.Value)
			{
				Logger.LogInfo((object)$"[PvPOverhaul Resilience] victim={victim.GetPlayerName()} rating={authoritativeResilience} before={totalDamage}");
			}
			if (authoritativeResilience <= 0f)
			{
				return;
			}
			float num = authoritativeResilience / PvPOverhaul.ResilienceRatingPerPercent.Value / 100f;
			num = Mathf.Clamp(num, 0f, PvPOverhaul.MaxResilienceReduction.Value);
			if (!(num <= 0f))
			{
				((DamageTypes)(ref hit.m_damage)).Modify(1f - num);
				if (PvPOverhaul.VerboseCombatLogging.Value)
				{
					Logger.LogInfo((object)$"[PvPOverhaul Resilience] reduction={num:P1} after={hit.GetTotalDamage()}");
				}
			}
		}

		internal static void UpdateRuntimeState()
		{
			if (Time.unscaledTime < _nextBurstWindowCleanup)
			{
				return;
			}
			_nextBurstWindowCleanup = Time.unscaledTime + 1f;
			float num = Mathf.Max(1f, PvPOverhaul.PvpMagicBurstWindowSeconds.Value * 4f);
			ExpiredBurstWindowKeys.Clear();
			foreach (KeyValuePair<PlayerPairKey, MagicBurstWindow> magicBurstWindow in MagicBurstWindows)
			{
				if (Time.time - magicBurstWindow.Value.StartTime > num)
				{
					ExpiredBurstWindowKeys.Add(magicBurstWindow.Key);
				}
			}
			foreach (PlayerPairKey expiredBurstWindowKey in ExpiredBurstWindowKeys)
			{
				MagicBurstWindows.Remove(expiredBurstWindowKey);
			}
		}
	}
	[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_00e9: 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);
			}
			if (PvPOverhaul.ShieldBreakConsumesHit.Value)
			{
				hit.m_damage = default(DamageTypes);
				PvpShieldBreakRuntime.ConsumedHit = hit;
			}
			return 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), "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 long _cachedOwnerId;

		private static bool _cachedOwnerHasShield;

		private static bool _hasOwnerCache;

		private static void Postfix(Player __instance)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !(Time.time < _nextUpdate))
			{
				_nextUpdate = Time.time + 1f;
				PvpResilienceAPI.SyncLocalPlayerResilience(__instance);
				SyncLocalPlayerShield(__instance);
			}
		}

		internal static void SyncLocalPlayerShield(Player player)
		{
			if (!((Object)(object)((Character)(player?)).m_nview == (Object)null) && ((Character)player).m_nview.IsValid() && ((Character)player).m_nview.IsOwner())
			{
				bool flag = PvPDamagePatch.GetEquippedShield(player) != null;
				_cachedOwnerId = player.GetPlayerID();
				_cachedOwnerHasShield = flag;
				_hasOwnerCache = true;
				ZDO zDO = ((Character)player).m_nview.GetZDO();
				if (zDO != null && zDO.GetBool("PvpOverhaul.HasShieldEquipped", false) != flag)
				{
					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);
		}

		internal static bool HasAuthoritativeShield(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			if ((Object)(object)player == (Object)(object)Player.m_localPlayer || ((Object)(object)((Character)player).m_nview != (Object)null && ((Character)player).m_nview.IsValid() && ((Character)player).m_nview.IsOwner()))
			{
				long playerID = player.GetPlayerID();
				if (_hasOwnerCache && _cachedOwnerId == playerID)
				{
					return _cachedOwnerHasShield;
				}
				_cachedOwnerId = playerID;
				_cachedOwnerHasShield = PvPDamagePatch.GetEquippedShield(player) != null;
				_hasOwnerCache = true;
				return _cachedOwnerHasShield;
			}
			return HasShield(player);
		}
	}
	[BepInPlugin("dzk.pvpoverhaul", "PvpOverhaul", "1.2.2")]
	[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.2.2";

		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;

		internal static ConfigEntry<bool> VerboseCombatLogging;

		internal static ConfigEntry<bool> EnablePerformanceMetrics;

		private FileSystemWatcher _configWatcher;

		private int _configReloadPending;

		private long _configReloadAfterUtcTicks;

		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()
		{
			ReloadConfigOnMainThread();
			BountyRpc.Update();
			BountySystem.Instance.Update();
			BountyMapCircle.Update();
			BountyStatusEffectController.UpdateRegistration();
			BountyRpc.UpdateReward();
			PvpCombatStateCoordinator.Update();
			PvPTweaksCombatFilterPatch.UpdateRuntimeState();
			PvPDamagePatch.UpdateRuntimeState();
			PvpSkillLossPatch.UpdateRuntimeState();
			PvpRuntimeMetrics.Update();
		}

		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.",