Decompiled source of PvPOverhaul v1.0.1

PvpOverhaul.dll

Decompiled 5 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn;
using Jotunn.Entities;
using Jotunn.Extensions;
using Jotunn.Managers;
using Jotunn.Utils;
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.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
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;
	}

	[CompilerGenerated]
	private sealed class <DelayedDeathCheck>d__51 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public Player victim;

		public BountySystem <>4__this;

		private string <victimName>5__1;

		private long <victimId>5__2;

		private int <i>5__3;

		private LastHit <lastHit>5__4;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <DelayedDeathCheck>d__51(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<victimName>5__1 = null;
			<lastHit>5__4 = null;
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				if ((Object)(object)victim == (Object)null)
				{
					return false;
				}
				<victimName>5__1 = victim.GetPlayerName();
				<victimId>5__2 = victim.GetPlayerID();
				<i>5__3 = 0;
				break;
			case 1:
				<>1__state = -1;
				if ((Object)(object)victim == (Object)null)
				{
					return false;
				}
				<lastHit>5__4 = <>4__this.ResolveLastHit(<victimId>5__2);
				if (<i>5__3 == 0 || ((Character)victim).IsDead() || ((Character)victim).GetHealth() <= 0f)
				{
					Logger.LogInfo((object)($"[PvPOverhaul BOUNTY] DelayedDeathCheck {<i>5__3} victim={<victimName>5__1} " + $"health={((Character)victim).GetHealth()} dead={((Character)victim).IsDead()} " + "lastHit=" + ((<lastHit>5__4 != null) ? <lastHit>5__4.AttackerName : "NULL")));
				}
				if (((Character)victim).GetHealth() <= 0f || ((Character)victim).IsDead())
				{
					<>4__this.TryReportDeath(victim);
					return false;
				}
				<lastHit>5__4 = null;
				<i>5__3++;
				break;
			}
			if (<i>5__3 < 30)
			{
				<>2__current = (object)new WaitForSeconds(0.1f);
				<>1__state = 1;
				return true;
			}
			return false;
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

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

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

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

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

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

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

	private bool _loadedPersistence;

	private float _nextTimerTick;

	private float _nextPositionSync;

	private float _nextFullSync;

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


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

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

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

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

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

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

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

	private bool IsPlayerOnline(string playerName)
	{
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)ZNet.instance == (Object)null)
		{
			return false;
		}
		foreach (PlayerInfo player in ZNet.instance.GetPlayerList())
		{
			if (player.m_name == playerName)
			{
				return true;
			}
		}
		return false;
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	[IteratorStateMachine(typeof(<DelayedDeathCheck>d__51))]
	private IEnumerator DelayedDeathCheck(Player victim)
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <DelayedDeathCheck>d__51(0)
		{
			<>4__this = this,
			victim = victim
		};
	}
}
internal static class BountyRpc
{
	private const string RpcRegisterKill = "PvpModes_RegisterKill";

	private const string RpcBroadcastMessage = "PvpModes_BroadcastMessage";

	private const string RpcBountyCircle = "PvpModes_BountyCircle";

	private const string RpcBountyPosition = "PvpModes_BountyPosition";

	private const string RpcBountyStatusRequest = "PvpModes_BountyStatusRequest";

	private const string RpcBountyStatusResponse = "PvpModes_BountyStatusResponse";

	private const string RpcBountyResetRequest = "PvpModes_BountyResetRequest";

	private const string RpcBountyResetResponse = "PvpModes_BountyResetResponse";

	private const string RpcBountyReward = "PvpModes_BountyReward";

	private static bool _registered;

	private static ZRoutedRpc _registeredInstance;

	private static string _pendingRewardItem;

	private static int _pendingRewardAmount;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

		public bool Active;

		public int Tier;

		public DateTime EndUtc;

		public float NextPositionSend;
	}

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

	private const float CircleWorldSize = 300f;

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

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

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

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

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

	private static DateTime _currentEndUtc;

	private static SE_Stats _effectPrefab;

	private static bool _registered;

	private static bool _pendingActive;

	private static DateTime _pendingEndUtc;

	private static string _pendingPlayerName;

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

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

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

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

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

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

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

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

	[HarmonyPatch(typeof(Character), "Damage")]
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	private static void CharacterDamagePrefix(Character __instance, HitData hit)
	{
		Player val = (Player)(object)((__instance is Player) ? __instance : null);
		if (val != null && hit != null)
		{
			Character attacker = hit.GetAttacker();
			Player val2 = (Player)(object)((attacker is Player) ? attacker : null);
			if (val2 != null && !((Object)(object)val2 == (Object)(object)val))
			{
				float time = Time.time;
				RecentRealPvpHit[val.GetPlayerID()] = time;
				RecentRealPvpHit[val2.GetPlayerID()] = time;
			}
		}
	}

	[HarmonyPatch(typeof(SEMan), "AddStatusEffect", new Type[]
	{
		typeof(int),
		typeof(bool),
		typeof(int),
		typeof(float)
	})]
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	private static bool AddStatusEffectHashPrefix(SEMan __instance, int nameHash)
	{
		if (nameHash != CombatHash)
		{
			return true;
		}
		return AllowCombatStatus(__instance);
	}

	[HarmonyPatch(typeof(SEMan), "AddStatusEffect", new Type[]
	{
		typeof(StatusEffect),
		typeof(bool),
		typeof(int),
		typeof(float)
	})]
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	private static bool AddStatusEffectObjectPrefix(SEMan __instance, StatusEffect statusEffect)
	{
		if ((Object)(object)statusEffect == (Object)null)
		{
			return true;
		}
		if (statusEffect.NameHash() != CombatHash)
		{
			return true;
		}
		return AllowCombatStatus(__instance);
	}

	private static bool AllowCombatStatus(SEMan seMan)
	{
		if (seMan == null)
		{
			return true;
		}
		Character character = seMan.m_character;
		Player val = (Player)(object)((character is Player) ? character : null);
		if (val == null)
		{
			return true;
		}
		long playerID = val.GetPlayerID();
		if (RecentRealPvpHit.TryGetValue(playerID, out var value) && Time.time - value <= 2f)
		{
			return true;
		}
		if (BountySystem.Instance.ResolveLastHit(playerID) != null)
		{
			return true;
		}
		Logger.LogInfo((object)("[PvPOverhaul Compat] Blocked PvPTweaks SE_Combat on " + val.GetPlayerName() + " | no real PvP hit"));
		return false;
	}
}
[HarmonyPatch(typeof(Character), "Damage")]
internal static class PvPDamagePatch
{
	private class MagicBurstWindow
	{
		public float StartTime;

		public float Damage;
	}

	internal class PendingBlockLeak
	{
		public float ExpectedDamage;

		public float HealthBefore;

		public float Time;

		public DamageTypes LeakDamage;

		public float BlockLeakPercent;
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	private static void ZeroDamage(HitData hit)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		hit.m_damage = default(DamageTypes);
	}
}
[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 (!IsInPvpCombat(val))
		{
			return true;
		}
		hp = 0f;
		return false;
	}

	private static bool IsInPvpCombat(Player player)
	{
		SEMan sEMan = ((Character)player).GetSEMan();
		if (sEMan != null && sEMan.HaveStatusEffect(StringExtensionMethods.GetStableHashCode("SE_Combat")))
		{
			return true;
		}
		if ((Object)(object)((Character)player).m_nview != (Object)null && ((Character)player).m_nview.IsValid())
		{
			ZDO zDO = ((Character)player).m_nview.GetZDO();
			if (zDO != null && zDO.GetBool("VPT_PlayerInCombat", false))
			{
				return true;
			}
		}
		return false;
	}
}
[HarmonyPatch(typeof(SE_Shield), "OnDamaged")]
internal static class PvPShieldBreakOnDamagedPatch
{
	private static bool Prefix(SE_Shield __instance, HitData hit, Character attacker)
	{
		//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
		if (!PvPOverhaul.BreakShieldOnPvpHit.Value)
		{
			return true;
		}
		Character obj = ((StatusEffect)(__instance?)).m_character;
		Player val = (Player)(object)((obj is Player) ? obj : null);
		if (val == null)
		{
			return true;
		}
		if (hit == null || hit.GetTotalDamage() <= 0f)
		{
			return true;
		}
		Character val2 = (((Object)(object)attacker != (Object)null) ? attacker : hit.GetAttacker());
		Player val3 = (Player)(object)((val2 is Player) ? val2 : null);
		if (val3 == null)
		{
			return true;
		}
		if ((Object)(object)val3 == (Object)(object)val)
		{
			return true;
		}
		__instance.m_absorbDamage = 0f;
		__instance.m_absorbDamagePerSkillLevel = 0f;
		SEMan sEMan = ((Character)val).GetSEMan();
		if (sEMan != null)
		{
			sEMan.RemoveStatusEffect(((StatusEffect)__instance).NameHash(), true);
		}
		BountySystem.Instance.MarkIgnoredDeath(val);
		if (PvPOverhaul.ShieldBreakConsumesHit.Value)
		{
			hit.m_damage = default(DamageTypes);
		}
		return false;
	}
}
[HarmonyPatch(typeof(Character), "Damage")]
internal static class PvPBlockLeakPatch
{
	private static bool _applyingLeak;

	private static void Postfix(Character __instance, HitData hit)
	{
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0108: Unknown result type (might be due to invalid IL or missing references)
		//IL_010a: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0111: Unknown result type (might be due to invalid IL or missing references)
		//IL_0116: Unknown result type (might be due to invalid IL or missing references)
		//IL_011b: Unknown result type (might be due to invalid IL or missing references)
		//IL_011c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_0126: Unknown result type (might be due to invalid IL or missing references)
		//IL_012d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0134: Unknown result type (might be due to invalid IL or missing references)
		//IL_0136: Unknown result type (might be due to invalid IL or missing references)
		//IL_013b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0140: Unknown result type (might be due to invalid IL or missing references)
		//IL_0142: Unknown result type (might be due to invalid IL or missing references)
		//IL_0147: Unknown result type (might be due to invalid IL or missing references)
		//IL_014e: Expected O, but got Unknown
		if (_applyingLeak)
		{
			return;
		}
		Player val = (Player)(object)((__instance is Player) ? __instance : null);
		if (val == null)
		{
			return;
		}
		long playerID = val.GetPlayerID();
		if (!PvPDamagePatch.PendingLeaks.TryGetValue(playerID, out var value))
		{
			return;
		}
		PvPDamagePatch.PendingLeaks.Remove(playerID);
		if (Time.time - value.Time > 1f)
		{
			return;
		}
		float health = ((Character)val).GetHealth();
		float num = Mathf.Max(0f, value.HealthBefore - health);
		if (num >= value.ExpectedDamage * 0.8f)
		{
			return;
		}
		float blockLeakPercent = value.BlockLeakPercent;
		float num2 = value.ExpectedDamage * blockLeakPercent;
		if (!(num >= num2))
		{
			float num3 = num2 - num;
			if (!(num3 <= 0f))
			{
				HitData val2 = new HitData
				{
					m_damage = new DamageTypes
					{
						m_damage = num3
					},
					m_point = ((Character)val).GetCenterPoint(),
					m_dir = Vector3.zero,
					m_blockable = false,
					m_dodgeable = false,
					m_skill = hit.m_skill,
					m_hitType = hit.m_hitType
				};
				_applyingLeak = true;
				((Character)val).Damage(val2);
				_applyingLeak = false;
			}
		}
	}
}
[HarmonyPatch(typeof(Aoe), "OnHit")]
internal static class DisablePvpAoePatch
{
	private static bool Prefix(Aoe __instance, Collider collider)
	{
		if (!PvPOverhaul.DisablePvpAoeSplash.Value)
		{
			return true;
		}
		if ((Object)(object)__instance == (Object)null || (Object)(object)collider == (Object)null)
		{
			return true;
		}
		if (((DamageTypes)(ref __instance.m_damage)).GetTotalDamage() <= 0f)
		{
			return true;
		}
		Player componentInParent = ((Component)collider).GetComponentInParent<Player>();
		if ((Object)(object)componentInParent == (Object)null)
		{
			return true;
		}
		Character owner = __instance.m_owner;
		Player val = (Player)(object)((owner is Player) ? owner : null);
		if (val == null)
		{
			return true;
		}
		if ((Object)(object)val == (Object)(object)componentInParent)
		{
			return true;
		}
		return false;
	}
}
[BepInPlugin("dzk.pvpoverhaul", "PvpOverhaul", "1.0.0")]
[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.0.0";

	public static PvPOverhaul Instance;

	public static CustomLocalization Localization;

	public static ConfigEntry<int> BountyTriggerKillCount;

	public static ConfigEntry<int> BountyKillWindowMinutes;

	public static ConfigEntry<int> BountyDurationMinutes;

	public static ConfigEntry<string> BountyRewardItem;

	public static ConfigEntry<int> BountyRewardAmountPerTier;

	public static ConfigEntry<string> MsgKillBroadcast;

	public static ConfigEntry<string> MsgBountyActivated;

	public static ConfigEntry<string> MsgBountyRefreshed;

	public static ConfigEntry<string> MsgBountyCleared;

	public static ConfigEntry<string> MsgBountyExpired;

	public static ConfigEntry<string> MsgBountyReward;

	public static ConfigEntry<bool> EnablePvpDamagePatch;

	public static ConfigEntry<float> PvpMaxDirectHitPercentHp;

	public static ConfigEntry<float> PvpMaxDotTickPercentHp;

	public static ConfigEntry<bool> BreakShieldOnPvpHit;

	public static ConfigEntry<bool> ShieldBreakConsumesHit;

	public static ConfigEntry<string> ShieldStatusEffectNames;

	public static ConfigEntry<bool> DisableHealthRegenInPvpBattle;

	public static ConfigEntry<string> PvpBattleStatusEffectNames;

	public static ConfigEntry<float> PvpBlockedDamageLeakPercent;

	public static ConfigEntry<float> PvpMaxMagicMultiHitPercentHp;

	public static ConfigEntry<bool> DisablePvpAoeSplash;

	public static ConfigEntry<float> PvpMaxMagicBurstPercentHp;

	public static ConfigEntry<float> PvpMagicBurstWindowSeconds;

	public static ConfigEntry<float> PvpMaxMagicSecondaryPercentHp;

	public static ConfigEntry<float> PvpMeleeDamageMultiplier;

	public static ConfigEntry<float> PvpMeleeSecondaryDamageMultiplier;

	public static ConfigEntry<float> PvpSwordDamageMultiplier;

	public static ConfigEntry<float> PvpAxeDamageMultiplier;

	public static ConfigEntry<float> PvpClubDamageMultiplier;

	public static ConfigEntry<float> PvpKnifeDamageMultiplier;

	public static ConfigEntry<float> PvpSpearDamageMultiplier;

	public static ConfigEntry<float> PvpPolearmDamageMultiplier;

	public static ConfigEntry<float> PvpUnarmedDamageMultiplier;

	public static ConfigEntry<float> PvpBowDamageMultiplier;

	public static ConfigEntry<float> PvpCrossbowDamageMultiplier;

	public static ConfigEntry<float> PvpLightningDamageMultiplier;

	public static ConfigEntry<float> PvpTwoHandedDamageMultiplier;

	public static ConfigEntry<float> PvpTwoHandedBlockedDamageLeakPercent;

	public static ConfigEntry<float> TherzieDualWeapNerf;

	public static ConfigEntry<float> TherzieBleedNerf;

	public static ConfigEntry<bool> DisableSkillLossOnPvpDeath;

	private FileSystemWatcher _configWatcher;

	private DateTime _lastConfigReloadUtc;

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

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

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

	private void SetupConfigWatcher()
	{
		string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath;
		string directoryName = Path.GetDirectoryName(configFilePath);
		string fileName = Path.GetFileName(configFilePath);
		if (!string.IsNullOrWhiteSpace(directoryName) && !string.IsNullOrWhiteSpace(fileName))
		{
			_configWatcher = new FileSystemWatcher(directoryName, fileName)
			{
				NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite),
				EnableRaisingEvents = true
			};
			_configWatcher.Changed += OnConfigFileChanged;
			_configWatcher.Created += OnConfigFileChanged;
			_configWatcher.Renamed += OnConfigFileChanged;
			Logger.LogInfo((object)"[PvPOverhaul] Config watcher enabled");
		}
	}

	private void OnConfigFileChanged(object sender, FileSystemEventArgs e)
	{
		DateTime utcNow = DateTime.UtcNow;
		if ((utcNow - _lastConfigReloadUtc).TotalMilliseconds < 500.0)
		{
			return;
		}
		_lastConfigReloadUtc = utcNow;
		try
		{
			((BaseUnityPlugin)this).Config.Reload();
			Logger.LogInfo((object)"[PvPOverhaul] Config reloaded");
		}
		catch (Exception arg)
		{
			Logger.LogWarning((object)$"[PvPOverhaul] Failed to reload config: {arg}");
		}
	}

	private void OnDestroy()
	{
		if (_configWatcher != null)
		{
			_configWatcher.Changed -= OnConfigFileChanged;
			_configWatcher.Created -= OnConfigFileChanged;
			_configWatcher.Renamed -= OnConfigFileChanged;
			_configWatcher.Dispose();
			_configWatcher = null;
		}
	}
}
internal static class PvpSkillLossPatch
{
	private class SkillSnapshot
	{
		public float LastPvpHitTime;

		public readonly Dictionary<SkillType, SkillData> Skills = new Dictionary<SkillType, SkillData>();

		public readonly Dictionary<object, FortifyData> FortifySkills = new Dictionary<object, FortifyData>();
	}

	private class SkillData
	{
		public float Level;

		public float Accumulator;
	}

	private class FortifyData
	{
		public float Level;

		public float Accumulator;
	}

	[CompilerGenerated]
	private sealed class <DelayedRestore>d__11 : IEnumerator<object>, IDisposable, IEnumerator
	{
		private int <>1__state;

		private object <>2__current;

		public Player player;

		private long <playerId>5__1;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <DelayedRestore>d__11(int <>1__state)
		{
			this.<>1__state = <>1__state;
		}

		[DebuggerHidden]
		void IDisposable.Dispose()
		{
			<>1__state = -2;
		}

		private bool MoveNext()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			switch (<>1__state)
			{
			default:
				return false;
			case 0:
				<>1__state = -1;
				<>2__current = null;
				<>1__state = 1;
				return true;
			case 1:
				<>1__state = -1;
				RestoreSnapshot(player);
				<>2__current = (object)new WaitForSeconds(0.5f);
				<>1__state = 2;
				return true;
			case 2:
				<>1__state = -1;
				RestoreSnapshot(player);
				if ((Object)(object)player != (Object)null)
				{
					<playerId>5__1 = player.GetPlayerID();
					Snapshots.Remove(<playerId>5__1);
					DelayedRestoreRunning.Remove(<playerId>5__1);
				}
				return false;
			}
		}

		bool IEnumerator.MoveNext()
		{
			//ILSpy generated this explicit interface implementation from .override directive in MoveNext
			return this.MoveNext();
		}

		[DebuggerHidden]
		void IEnumerator.Reset()
		{
			throw new NotSupportedException();
		}
	}

	private const float SnapshotDuration = 30f;

	private static readonly Dictionary<long, SkillSnapshot> Snapshots = new Dictionary<long, SkillSnapshot>();

	private static readonly HashSet<long> DelayedRestoreRunning = new HashSet<long>();

	internal static bool ShouldProtectSkills(Player player)
	{
		if (!PvPOverhaul.DisableSkillLossOnPvpDeath.Value)
		{
			return false;
		}
		if ((Object)(object)player == (Object)null || (Object)(object)player.m_skills == (Object)null)
		{
			return false;
		}
		if (player.m_godMode)
		{
			return false;
		}
		if (Snapshots.TryGetValue(player.GetPlayerID(), out var value) && Time.time - value.LastPvpHitTime <= 30f)
		{
			return true;
		}
		if (IsInPvpCombat(player))
		{
			return true;
		}
		if (BountySystem.Instance.ResolveLastHit(player.GetPlayerID()) != null)
		{
			return true;
		}
		return false;
	}

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

	internal static void ForceSaveSnapshot(Player player)
	{
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)player == (Object)null || (Object)(object)player.m_skills == (Object)null)
		{
			return;
		}
		long playerID = player.GetPlayerID();
		if (Snapshots.TryGetValue(playerID, out var value))
		{
			value.LastPvpHitTime = Time.time;
			return;
		}
		SkillSnapshot skillSnapshot = new SkillSnapshot
		{
			LastPvpHitTime = Time.time
		};
		foreach (KeyValuePair<SkillType, Skill> skillDatum in player.m_skills.m_skillData)
		{
			if (skillDatum.Value != null)
			{
				skillSnapshot.Skills[skillDatum.Key] = new SkillData
				{
					Level = skillDatum.Value.m_level,
					Accumulator = skillDatum.Value.m_accumulator
				};
			}
		}
		SnapshotFortifySkills(skillSnapshot);
		Snapshots[playerID] = skillSnapshot;
		Logger.LogInfo((object)("[PvPOverhaul] PvP skill snapshot saved for " + player.GetPlayerName()));
	}

	internal static void RestoreSnapshot(Player player)
	{
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)player == (Object)null || (Object)(object)player.m_skills == (Object)null)
		{
			return;
		}
		long playerID = player.GetPlayerID();
		if (!Snapshots.TryGetValue(playerID, out var value))
		{
			return;
		}
		if (Time.time - value.LastPvpHitTime > 30f)
		{
			Snapshots.Remove(playerID);
			return;
		}
		foreach (KeyValuePair<SkillType, SkillData> skill2 in value.Skills)
		{
			Skill skill = player.m_skills.GetSkill(skill2.Key);
			if (skill != null)
			{
				skill.m_level = skill2.Value.Level;
				skill.m_accumulator = skill2.Value.Accumulator;
			}
		}
		RestoreFortifySkills(value);
		Logger.LogInfo((object)("[PvPOverhaul] PvP death skills restored for " + player.GetPlayerName()));
	}

	internal static void StartDelayedRestore(Player player)
	{
		if (!((Object)(object)PvPOverhaul.Instance == (Object)null) && !((Object)(object)player == (Object)null))
		{
			long playerID = player.GetPlayerID();
			if (Snapshots.ContainsKey(playerID) && !DelayedRestoreRunning.Contains(playerID))
			{
				DelayedRestoreRunning.Add(playerID);
				((MonoBehaviour)PvPOverhaul.Instance).StartCoroutine(DelayedRestore(player));
			}
		}
	}

	[IteratorStateMachine(typeof(<DelayedRestore>d__11))]
	private static IEnumerator DelayedRestore(Player player)
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <DelayedRestore>d__11(0)
		{
			player = player
		};
	}

	private static void SnapshotFortifySkills(SkillSnapshot snapshot)
	{
		try
		{
			Type type = AccessTools.TypeByName("FortifySkillsRedux.FortifySkillData");
			if (type == null)
			{
				return;
			}
			object obj = AccessTools.Field(type, "s_FortifySkills")?.GetValue(null);
			if (obj == null)
			{
				return;
			}
			foreach (object item in (IEnumerable)obj)
			{
				object obj2 = item.GetType().GetProperty("Key")?.GetValue(item, null);
				object obj3 = item.GetType().GetProperty("Value")?.GetValue(item, null);
				if (obj2 != null && obj3 != null)
				{
					FieldInfo fieldInfo = AccessTools.Field(obj3.GetType(), "FortifyLevel");
					FieldInfo fieldInfo2 = AccessTools.Field(obj3.GetType(), "FortifyAccumulator");
					if (!(fieldInfo == null) && !(fieldInfo2 == null))
					{
						snapshot.FortifySkills[obj2] = new FortifyData
						{
							Level = Convert.ToSingle(fieldInfo.GetValue(obj3)),
							Accumulator = Convert.ToSingle(fieldInfo2.GetValue(obj3))
						};
					}
				}
			}
		}
		catch (Exception ex)
		{
			Logger.LogWarning((object)("[PvPOverhaul] Fortify snapshot failed: " + ex.Message));
		}
	}

	private static void RestoreFortifySkills(SkillSnapshot snapshot)
	{
		try
		{
			Type type = AccessTools.TypeByName("FortifySkillsRedux.FortifySkillData");
			if (type == null)
			{
				return;
			}
			object obj = AccessTools.Field(type, "s_FortifySkills")?.GetValue(null);
			if (obj == null)
			{
				return;
			}
			foreach (object item in (IEnumerable)obj)
			{
				object obj2 = item.GetType().GetProperty("Key")?.GetValue(item, null);
				object obj3 = item.GetType().GetProperty("Value")?.GetValue(item, null);
				if (obj2 != null && obj3 != null && snapshot.FortifySkills.TryGetValue(obj2, out var value))
				{
					FieldInfo fieldInfo = AccessTools.Field(obj3.GetType(), "FortifyLevel");
					FieldInfo fieldInfo2 = AccessTools.Field(obj3.GetType(), "FortifyAccumulator");
					fieldInfo?.SetValue(obj3, value.Level);
					fieldInfo2?.SetValue(obj3, value.Accumulator);
				}
			}
		}
		catch (Exception ex)
		{
			Logger.LogWarning((object)("[PvPOverhaul] Fortify restore failed: " + ex.Message));
		}
	}

	internal static void PatchFortifySkillsRedux(Harmony harmony)
	{
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cc: Expected O, but got Unknown
		Type type = null;
		Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
		foreach (Assembly assembly in assemblies)
		{
			try
			{
				type = assembly.GetTypes().FirstOrDefault((Type t) => t.FullName == "FortifySkillsRedux.Patches.OnDeathPatches");
				if (type != null)
				{
					break;
				}
			}
			catch
			{
			}
		}
		if (type == null)
		{
			Logger.LogWarning((object)"[PvPOverhaul] FortifySkillsRedux OnDeathPatches type not found.");
			return;
		}
		MethodInfo methodInfo = AccessTools.DeclaredMethod(type, "OnDeathFinalizer", (Type[])null, (Type[])null);
		if (methodInfo == null)
		{
			Logger.LogWarning((object)"[PvPOverhaul] FortifySkillsRedux OnDeathFinalizer method not found.");
			return;
		}
		harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(PvpSkillLossPatch), "FortifyOnDeathFinalizerPrefix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		Logger.LogInfo((object)"[PvPOverhaul] FortifySkillsRedux death finalizer patched.");
	}

	private static bool FortifyOnDeathFinalizerPrefix(Player __instance)
	{
		if (!ShouldProtectSkills(__instance))
		{
			return true;
		}
		ForceFortifyLevelsToCurrentSkills(__instance);
		RestoreSnapshot(__instance);
		StartDelayedRestore(__instance);
		Logger.LogInfo((object)("[PvPOverhaul] FortifySkillsRedux death finalizer BLOCKED for PvP death: " + __instance.GetPlayerName()));
		return false;
	}

	private static void FortifyOnDeathFinalizerPostfix(Player __instance)
	{
		if (ShouldProtectSkills(__instance))
		{
			RestoreSnapshot(__instance);
			StartDelayedRestore(__instance);
		}
	}

	private static void ForceFortifyLevelsToCurrentSkills(Player player)
	{
		//IL_0148: Unknown result type (might be due to invalid IL or missing references)
		//IL_014d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0155: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)player == (Object)null || (Object)(object)player.m_skills == (Object)null)
		{
			return;
		}
		try
		{
			Type type = null;
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					type = assembly.GetTypes().FirstOrDefault((Type t) => t.FullName == "FortifySkillsRedux.FortifySkillData");
					if (type != null)
					{
						break;
					}
				}
				catch
				{
				}
			}
			if (type == null)
			{
				return;
			}
			object obj2 = AccessTools.Field(type, "s_FortifySkills")?.GetValue(null);
			if (obj2 == null)
			{
				return;
			}
			foreach (object item in (IEnumerable)obj2)
			{
				object obj3 = item.GetType().GetProperty("Key")?.GetValue(item, null);
				object obj4 = item.GetType().GetProperty("Value")?.GetValue(item, null);
				if (obj3 != null && obj4 != null)
				{
					SkillType val = (SkillType)obj3;
					Skill skill = player.m_skills.GetSkill(val);
					if (skill != null)
					{
						FieldInfo fieldInfo = AccessTools.Field(obj4.GetType(), "FortifyLevel");
						FieldInfo fieldInfo2 = AccessTools.Field(obj4.GetType(), "FortifyAccumulator");
						fieldInfo?.SetValue(obj4, skill.m_level);
						fieldInfo2?.SetValue(obj4, skill.m_accumulator);
					}
				}
			}
			Logger.LogInfo((object)("[PvPOverhaul] Fortify levels forced to current skill levels for " + player.GetPlayerName()));
		}
		catch (Exception ex)
		{
			Logger.LogWarning((object)("[PvPOverhaul] Failed to force Fortify levels: " + ex.Message));
		}
	}
}
[HarmonyPatch(typeof(Character), "Damage")]
internal static class PvpSkillSnapshotOnDamagePatch
{
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	private static void Prefix(Character __instance, HitData hit)
	{
		if (!PvPOverhaul.DisableSkillLossOnPvpDeath.Value)
		{
			return;
		}
		Player val = (Player)(object)((__instance is Player) ? __instance : null);
		if (val != null && hit != null)
		{
			Character attacker = hit.GetAttacker();
			Player val2 = (Player)(object)((attacker is Player) ? attacker : null);
			if (val2 != null && !((Object)(object)val2 == (Object)(object)val))
			{
				PvpSkillLossPatch.ForceSaveSnapshot(val);
			}
		}
	}
}
[HarmonyPatch(typeof(Skills), "OnDeath")]
internal static class PvpSkillsOnDeathPatch
{
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	private static bool Prefix(Skills __instance)
	{
		Player player = __instance.m_player;
		if (!PvpSkillLossPatch.ShouldProtectSkills(player))
		{
			return true;
		}
		Logger.LogInfo((object)("[PvPOverhaul] Blocked vanilla Skills.OnDeath for PvP death: " + player.GetPlayerName()));
		return false;
	}
}
[HarmonyPatch(typeof(Player), "OnDeath")]
internal static class PvpPlayerOnDeathSkillRestorePatch
{
	[HarmonyPrefix]
	[HarmonyPriority(800)]
	private static void Prefix(Player __instance)
	{
		if (PvpSkillLossPatch.ShouldProtectSkills(__instance))
		{
			PvpSkillLossPatch.Fo