Decompiled source of ValheimBattleRoyale v0.3.14

ValheimBattleRoyale.dll

Decompiled 7 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("ValheimBattleRoyale")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ValheimBattleRoyale")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("75C3A508-4CBA-494A-B957-1B0C9E976E0B")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace ValheimBattleRoyale;

[BepInPlugin("com.xtasy.ValheimBattleRoyale", "Valheim Battle Royale", "0.3.14")]
public sealed class ValheimBattleRoyalePlugin : BaseUnityPlugin
{
	public const string GUID = "com.xtasy.ValheimBattleRoyale";

	public const string NAME = "Valheim Battle Royale";

	public const string VERSION = "0.3.14";

	internal static ValheimBattleRoyalePlugin Instance;

	internal static ManualLogSource Log;

	internal ConfigEntry<KeyboardShortcut> StartHotkey;

	internal ConfigEntry<float> StartCountdown;

	internal ConfigEntry<float> InitialRadius;

	internal ConfigEntry<float> NextRadiusMultiplier;

	internal ConfigEntry<float> MinimumRadius;

	internal ConfigEntry<float> WaitBeforeShrink;

	internal ConfigEntry<float> ShrinkDuration;

	internal ConfigEntry<float> OutsideDamage;

	internal ConfigEntry<float> DamageInterval;

	internal ConfigEntry<bool> RequireNextInsideCurrent;

	internal ConfigEntry<bool> ShowHud;

	private Harmony _harmony;

	private FileSystemWatcher _watcher;

	private float _reloadAt = -1f;

	private float _nextDamageAt;

	private double _nextServerDamageAt;

	private Texture2D _whiteTexture;

	private bool _rpcRegistered;

	private GameObject _hudRoot;

	private TextMeshProUGUI _hudTitle;

	private TextMeshProUGUI _hudTimer;

	private TextMeshProUGUI _hudDanger;

	private static TMP_FontAsset _valheimNorseBold;

	private static bool _valheimNorseBoldSearched;

	private static string _selectedLanguage;

	private static float _nextLanguageProbeAt;

	private const string RpcRequest = "XBR_Request";

	private const string RpcState = "XBR_State";

	private const string RpcSnapshotRequest = "XBR_SnapshotRequest";

	private const string RpcZoneDamage = "XBR_ZoneDamage";

	private static StatusEffect _zoneFireVisual;

	private void Awake()
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Expected O, but got Unknown
		//IL_0032: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Expected O, but got Unknown
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		BindConfig();
		SetupConfigWatcher();
		_whiteTexture = new Texture2D(1, 1);
		_whiteTexture.SetPixel(0, 0, Color.white);
		_whiteTexture.Apply();
		_harmony = new Harmony("com.xtasy.ValheimBattleRoyale");
		_harmony.PatchAll();
		Log.LogInfo((object)"Valheim Battle Royale 0.3.14 loaded.");
	}

	private void OnDestroy()
	{
		if ((Object)(object)_hudRoot != (Object)null)
		{
			Object.Destroy((Object)(object)_hudRoot);
			_hudRoot = null;
		}
		if ((Object)(object)_whiteTexture != (Object)null)
		{
			Object.Destroy((Object)(object)_whiteTexture);
			_whiteTexture = null;
		}
		if (_watcher != null)
		{
			_watcher.Dispose();
			_watcher = null;
		}
		if (_harmony != null)
		{
			_harmony.UnpatchSelf();
		}
		Instance = null;
	}

	private void BindConfig()
	{
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		StartHotkey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "StartHotkey", new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>()), "Start/stop match request from an admin client.");
		StartCountdown = ((BaseUnityPlugin)this).Config.Bind<float>("Match", "StartCountdown", 10f, "Seconds between admin start and actual match start. Damage is disabled during this countdown.");
		InitialRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Zone", "InitialRadius", 3000f, "Initial safe-zone radius used by Shift+LeftClick / br zone when radius is omitted.");
		NextRadiusMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Zone", "NextRadiusMultiplier", 0.5f, "Next-zone radius as a multiplier of the current radius. 0.5 = 50%. Used when an explicit radius is not supplied.");
		MinimumRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Zone", "MinimumRadius", 25f, "Minimum automatically calculated next-zone radius.");
		WaitBeforeShrink = ((BaseUnityPlugin)this).Config.Bind<float>("Zone", "WaitBeforeShrink", 300f, "Seconds from assigning the next safe zone until shrinking starts.");
		ShrinkDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Zone", "ShrinkDuration", 180f, "Seconds used to move/shrink from current zone to next zone.");
		RequireNextInsideCurrent = ((BaseUnityPlugin)this).Config.Bind<bool>("Zone", "RequireNextZoneInsideCurrent", true, "Reject a next circle that is not fully inside the current circle.");
		OutsideDamage = ((BaseUnityPlugin)this).Config.Bind<float>("Damage", "OutsideDamage", 10f, "Direct health removed per tick outside the current safe zone. Bypasses armor, blocking and elemental resistances.");
		DamageInterval = ((BaseUnityPlugin)this).Config.Bind<float>("Damage", "DamageInterval", 2f, "Seconds between damage ticks.");
		ShowHud = ((BaseUnityPlugin)this).Config.Bind<bool>("UI", "ShowHud", true, "Show match/zone/timer information on screen.");
	}

	private void SetupConfigWatcher()
	{
		string configFilePath = ((BaseUnityPlugin)this).Config.ConfigFilePath;
		_watcher = new FileSystemWatcher(Path.GetDirectoryName(configFilePath) ?? Paths.ConfigPath, Path.GetFileName(configFilePath));
		_watcher.NotifyFilter = NotifyFilters.Size | NotifyFilters.LastWrite | NotifyFilters.CreationTime;
		_watcher.Changed += delegate
		{
			_reloadAt = Time.realtimeSinceStartup + 0.35f;
		};
		_watcher.Created += delegate
		{
			_reloadAt = Time.realtimeSinceStartup + 0.35f;
		};
		_watcher.Renamed += delegate
		{
			_reloadAt = Time.realtimeSinceStartup + 0.35f;
		};
		_watcher.EnableRaisingEvents = true;
	}

	private unsafe void Update()
	{
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: 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)
		UpdateTmpHud();
		if (_reloadAt > 0f && Time.realtimeSinceStartup >= _reloadAt)
		{
			_reloadAt = -1f;
			try
			{
				((BaseUnityPlugin)this).Config.Reload();
				Log.LogInfo((object)"Config reloaded on the fly.");
			}
			catch (Exception ex)
			{
				Log.LogError((object)("Config reload failed: " + ex));
			}
		}
		TryRegisterRpc();
		if ((Object)(object)ZNet.instance == (Object)null || ZRoutedRpc.instance == null)
		{
			return;
		}
		if ((Object)(object)Player.m_localPlayer != (Object)null && !IsTextInputActive())
		{
			KeyboardShortcut value = StartHotkey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				ManualLogSource log = Log;
				value = StartHotkey.Value;
				log.LogInfo((object)("Start hotkey pressed: " + ((object)(*(KeyboardShortcut*)(&value))/*cast due to .constrained prefix*/).ToString()));
				SendRequest("toggle");
			}
		}
		if (ZNet.instance.IsServer())
		{
			ApplyServerDamage();
		}
		else
		{
			ApplyLocalZoneVisual();
		}
	}

	private void TryRegisterRpc()
	{
		if (!_rpcRegistered && ZRoutedRpc.instance != null)
		{
			ZRoutedRpc.instance.Register<ZPackage>("XBR_Request", (Action<long, ZPackage>)RPC_Request);
			ZRoutedRpc.instance.Register<ZPackage>("XBR_State", (Action<long, ZPackage>)RPC_State);
			ZRoutedRpc.instance.Register("XBR_SnapshotRequest", (Action<long>)RPC_SnapshotRequest);
			ZRoutedRpc.instance.Register<float>("XBR_ZoneDamage", (Action<long, float>)RPC_ZoneDamage);
			_rpcRegistered = true;
			if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer())
			{
				ZRoutedRpc.instance.InvokeRoutedRPC("XBR_SnapshotRequest", Array.Empty<object>());
			}
		}
	}

	internal void ResetRpcRegistration()
	{
		_rpcRegistered = false;
	}

	private static bool IsTextInputActive()
	{
		if ((Object)(object)Console.instance != (Object)null && Console.IsVisible())
		{
			return true;
		}
		if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus())
		{
			return true;
		}
		return false;
	}

	internal void SendRequest(string action, Vector3 pos = default(Vector3), float radius = 0f)
	{
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Expected O, but got Unknown
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		if (ZRoutedRpc.instance == null)
		{
			Log.LogWarning((object)("Cannot send BR request '" + action + "': ZRoutedRpc is not ready."));
			return;
		}
		Log.LogInfo((object)$"Sending BR request: action={action}, pos=({pos.x:F0},{pos.z:F0}), radius={radius:F0}");
		ZPackage val = new ZPackage();
		val.Write(action);
		val.Write(pos);
		val.Write(radius);
		ZRoutedRpc.instance.InvokeRoutedRPC("XBR_Request", new object[1] { val });
	}

	private void RPC_Request(long sender, ZPackage pkg)
	{
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_01be: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b4: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
		{
			return;
		}
		if (!IsAuthorized(sender))
		{
			Log.LogWarning((object)("Rejected Battle Royale control request from non-admin peer " + sender));
			return;
		}
		string text = pkg.ReadString();
		Vector3 val = pkg.ReadVector3();
		float num = pkg.ReadSingle();
		Log.LogInfo((object)$"Server received BR request from {sender}: action={text}, pos=({val.x:F0},{val.z:F0}), radius={num:F0}");
		switch (text)
		{
		case "toggle":
			if (ZoneState.Running || ZoneState.Starting)
			{
				StopMatch();
			}
			else
			{
				StartMatch();
			}
			break;
		case "start":
			StartMatch();
			break;
		case "stop":
			StopMatch();
			break;
		case "zone":
			SetCurrentZone(val, num);
			break;
		case "next":
			SetNextZone(val, num);
			break;
		case "cancelnext":
			ZoneState.HasNext = false;
			ZoneState.Phase = ZonePhase.Idle;
			break;
		case "cancelzone":
			CancelAssignedZone();
			break;
		}
		BroadcastState();
	}

	private void StartMatch()
	{
		if (!ZoneState.HasCurrent)
		{
			Log.LogWarning((object)"Cannot start Battle Royale: current safe zone is not set.");
			return;
		}
		ZoneState.Running = false;
		ZoneState.Starting = true;
		ZoneState.MatchStartAt = ServerTime() + (double)Mathf.Max(0f, StartCountdown.Value);
		Log.LogInfo((object)$"Battle Royale start countdown: {Mathf.Max(0f, StartCountdown.Value):F0}s");
	}

	private void StopMatch()
	{
		ZoneState.Running = false;
		ZoneState.Starting = false;
		ZoneState.Phase = ZonePhase.Idle;
		Log.LogInfo((object)"Battle Royale stopped.");
	}

	private bool IsAuthorized(long sender)
	{
		if (sender == ZNet.GetUID())
		{
			return true;
		}
		ZNetPeer peer = ZNet.instance.GetPeer(sender);
		if (peer == null || peer.m_rpc == null || peer.m_rpc.GetSocket() == null)
		{
			return false;
		}
		string hostName = peer.m_rpc.GetSocket().GetHostName();
		if (!(AccessTools.Field(typeof(ZNet), "m_adminList")?.GetValue(ZNet.instance) is IEnumerable enumerable))
		{
			return false;
		}
		foreach (object item in enumerable)
		{
			if (item != null && string.Equals(item.ToString(), hostName, StringComparison.OrdinalIgnoreCase))
			{
				return true;
			}
		}
		return false;
	}

	private void CancelAssignedZone()
	{
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		if (ZoneState.Phase == ZonePhase.Shrinking)
		{
			Log.LogWarning((object)"Cannot cancel a zone while shrinking is in progress.");
		}
		else if (ZoneState.HasNext)
		{
			if (ZoneState.Phase == ZonePhase.Shrinking)
			{
				ZoneState.CurrentCenter = GetEffectiveCurrentCenter();
				ZoneState.CurrentRadius = GetEffectiveCurrentRadius();
			}
			ZoneState.HasNext = false;
			ZoneState.Phase = ZonePhase.Idle;
			Log.LogInfo((object)"Next zone cancelled.");
		}
		else if (ZoneState.HasCurrent)
		{
			ZoneState.HasCurrent = false;
			ZoneState.HasNext = false;
			ZoneState.Phase = ZonePhase.Idle;
			ZoneState.Running = false;
			ZoneState.Starting = false;
			Log.LogInfo((object)"Current/start zone cancelled.");
		}
	}

	private void SetCurrentZone(Vector3 pos, float radius)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		if (radius <= 0f)
		{
			radius = InitialRadius.Value;
		}
		ZoneState.CurrentCenter = Flat(pos);
		ZoneState.CurrentRadius = Mathf.Max(1f, radius);
		ZoneState.HasCurrent = true;
		ZoneState.HasNext = false;
		ZoneState.Phase = ZonePhase.Idle;
		Log.LogInfo((object)$"Current zone: X={pos.x:F0} Z={pos.z:F0} R={radius:F0}");
	}

	private void SetNextZone(Vector3 pos, float radius)
	{
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_013d: Unknown result type (might be due to invalid IL or missing references)
		//IL_014b: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0223: Unknown result type (might be due to invalid IL or missing references)
		//IL_022e: Unknown result type (might be due to invalid IL or missing references)
		if (!ZoneState.HasCurrent)
		{
			Log.LogWarning((object)"Set current zone first.");
			return;
		}
		if (ZoneState.Phase == ZonePhase.Shrinking)
		{
			Log.LogWarning((object)"Cannot assign or move the next zone while shrinking is in progress.");
			return;
		}
		bool hasNext = ZoneState.HasNext;
		if (radius <= 0f)
		{
			if (hasNext)
			{
				radius = ZoneState.NextRadius;
			}
			else
			{
				float num = Mathf.Clamp(NextRadiusMultiplier.Value, 0.01f, 1f);
				radius = Mathf.Max(Mathf.Max(1f, MinimumRadius.Value), ZoneState.CurrentRadius * num);
			}
		}
		else
		{
			radius = Mathf.Max(1f, radius);
		}
		pos = Flat(pos);
		if (RequireNextInsideCurrent.Value)
		{
			float num2 = DistanceXZ(ZoneState.CurrentCenter, pos);
			if (num2 + radius > ZoneState.CurrentRadius)
			{
				Log.LogWarning((object)$"Next zone rejected: distance {num2:F0} + radius {radius:F0} > current radius {ZoneState.CurrentRadius:F0}.");
				return;
			}
		}
		ZoneState.NextCenter = pos;
		ZoneState.NextRadius = radius;
		ZoneState.HasNext = true;
		if (!hasNext)
		{
			ZoneState.Phase = ZonePhase.Waiting;
			ZoneState.PhaseStartedAt = ServerTime();
			ZoneState.WaitSecondsAtAssignment = Mathf.Max(0f, WaitBeforeShrink.Value);
			Log.LogInfo((object)$"Next zone: X={pos.x:F0} Z={pos.z:F0} R={radius:F0}; wait={ZoneState.WaitSecondsAtAssignment:F0}s");
		}
		else if (ZoneState.Phase == ZonePhase.Waiting)
		{
			ZoneState.PhaseStartedAt = ServerTime();
			ZoneState.WaitSecondsAtAssignment = Mathf.Max(0f, WaitBeforeShrink.Value);
			Log.LogInfo((object)$"Next zone moved: X={pos.x:F0} Z={pos.z:F0} R={radius:F0}; wait timer restarted ({ZoneState.WaitSecondsAtAssignment:F0}s)");
		}
		else if (ZoneState.Phase == ZonePhase.Shrinking)
		{
			ZoneState.ShrinkFromCenter = GetEffectiveCurrentCenter();
			ZoneState.ShrinkFromRadius = GetEffectiveCurrentRadius();
			ZoneState.PhaseStartedAt = ServerTime();
			Log.LogInfo((object)$"Next zone moved during shrinking: X={pos.x:F0} Z={pos.z:F0} R={radius:F0}; shrink timer restarted");
		}
	}

	private void FixedUpdate()
	{
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_0100: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: Unknown result type (might be due to invalid IL or missing references)
		//IL_0127: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !ZoneState.HasCurrent)
		{
			return;
		}
		double num = ServerTime();
		if (ZoneState.Starting && num >= ZoneState.MatchStartAt)
		{
			ZoneState.Starting = false;
			ZoneState.Running = true;
			Log.LogInfo((object)"Battle Royale started.");
			BroadcastState();
		}
		if (!ZoneState.Running)
		{
			return;
		}
		if (ZoneState.Phase == ZonePhase.Waiting && ZoneState.HasNext)
		{
			if (num - ZoneState.PhaseStartedAt >= (double)Mathf.Max(0f, WaitBeforeShrink.Value))
			{
				ZoneState.Phase = ZonePhase.Shrinking;
				ZoneState.PhaseStartedAt = num;
				ZoneState.ShrinkFromCenter = ZoneState.CurrentCenter;
				ZoneState.ShrinkFromRadius = ZoneState.CurrentRadius;
				BroadcastState();
			}
		}
		else if (ZoneState.Phase == ZonePhase.Shrinking && ZoneState.HasNext)
		{
			float num2 = Mathf.Max(0.01f, ShrinkDuration.Value);
			float num3 = Mathf.Clamp01((float)((num - ZoneState.PhaseStartedAt) / (double)num2));
			ZoneState.CurrentCenter = Vector3.Lerp(ZoneState.ShrinkFromCenter, ZoneState.NextCenter, num3);
			ZoneState.CurrentRadius = Mathf.Lerp(ZoneState.ShrinkFromRadius, ZoneState.NextRadius, num3);
			if (num3 >= 1f)
			{
				ZoneState.CurrentCenter = ZoneState.NextCenter;
				ZoneState.CurrentRadius = ZoneState.NextRadius;
				ZoneState.HasNext = false;
				ZoneState.Phase = ZonePhase.Idle;
				BroadcastState();
			}
		}
	}

	private void ApplyServerDamage()
	{
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !ZoneState.Running || ZoneState.Starting || !ZoneState.HasCurrent)
		{
			return;
		}
		double num = ServerTime();
		if (num < _nextServerDamageAt)
		{
			return;
		}
		_nextServerDamageAt = num + Math.Max(0.1, DamageInterval.Value);
		float num2 = Mathf.Max(0f, OutsideDamage.Value);
		if (num2 <= 0f)
		{
			return;
		}
		Vector3 effectiveCurrentCenter = GetEffectiveCurrentCenter();
		float effectiveCurrentRadius = GetEffectiveCurrentRadius();
		List<Player> allPlayers = Player.GetAllPlayers();
		if (allPlayers == null)
		{
			return;
		}
		foreach (Player item in allPlayers)
		{
			if (!((Object)(object)item == (Object)null) && !((Character)item).IsDead() && !(DistanceXZ(((Component)item).transform.position, effectiveCurrentCenter) <= effectiveCurrentRadius))
			{
				SendZoneDamageToOwner(item, num2);
			}
		}
	}

	private static void SendZoneDamageToOwner(Player player, float damage)
	{
		if (!((Object)(object)player == (Object)null) && !(damage <= 0f) && ZRoutedRpc.instance != null)
		{
			long playerOwnerPeerId = GetPlayerOwnerPeerId(player);
			if (playerOwnerPeerId == 0L)
			{
				Log.LogWarning((object)"Cannot apply zone damage: player owner peer ID was not resolved.");
				return;
			}
			ZRoutedRpc.instance.InvokeRoutedRPC(playerOwnerPeerId, "XBR_ZoneDamage", new object[1] { damage });
		}
	}

	private static long GetPlayerOwnerPeerId(Player player)
	{
		try
		{
			object obj = AccessTools.Field(typeof(Character), "m_nview")?.GetValue(player);
			if (obj == null)
			{
				return 0L;
			}
			object obj2 = AccessTools.Method(obj.GetType(), "GetZDO", Type.EmptyTypes, (Type[])null)?.Invoke(obj, null);
			if (obj2 == null)
			{
				return 0L;
			}
			object obj3 = AccessTools.Method(obj2.GetType(), "GetOwner", Type.EmptyTypes, (Type[])null)?.Invoke(obj2, null);
			return (obj3 == null) ? 0 : Convert.ToInt64(obj3, CultureInfo.InvariantCulture);
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("Failed to resolve player owner for zone damage: " + ex.Message));
			return 0L;
		}
	}

	private void RPC_ZoneDamage(long sender, float damage)
	{
		if ((Object)(object)ZNet.instance == (Object)null || ZRoutedRpc.instance == null || (Object)(object)Player.m_localPlayer == (Object)null)
		{
			return;
		}
		if (!ZNet.instance.IsServer())
		{
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			if (serverPeer == null || sender != serverPeer.m_uid)
			{
				Log.LogWarning((object)("Rejected forged Battle Royale zone damage RPC from peer " + sender));
				return;
			}
		}
		ApplyAuthorizedZoneDamage(Player.m_localPlayer, Mathf.Max(0f, damage));
	}

	private static void ApplyAuthorizedZoneDamage(Player player, float damage)
	{
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Expected O, but got Unknown
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)player == (Object)null) && !(damage <= 0f) && !((Character)player).IsDead())
		{
			float health = ((Character)player).GetHealth();
			if (health > damage)
			{
				((Character)player).SetHealth(health - damage);
				return;
			}
			HitData val = new HitData();
			val.m_damage.m_damage = Mathf.Max(1f, health + damage);
			val.m_point = ((Component)player).transform.position;
			val.m_dir = Vector3.zero;
			val.m_dodgeable = false;
			val.m_blockable = false;
			((Character)player).Damage(val);
		}
	}

	private void ApplyLocalZoneVisual()
	{
		//IL_0053: 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)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		if (ZoneState.Running && !ZoneState.Starting && ZoneState.HasCurrent && !((Object)(object)Player.m_localPlayer == (Object)null) && !(Time.time < _nextDamageAt))
		{
			_nextDamageAt = Time.time + Mathf.Max(0.1f, DamageInterval.Value);
			Vector3 effectiveCurrentCenter = GetEffectiveCurrentCenter();
			float effectiveCurrentRadius = GetEffectiveCurrentRadius();
			if (!(DistanceXZ(((Component)Player.m_localPlayer).transform.position, effectiveCurrentCenter) <= effectiveCurrentRadius))
			{
				EnsureZoneFireVisual(Player.m_localPlayer);
			}
		}
	}

	private static void EnsureZoneFireVisual(Player player)
	{
		if ((Object)(object)player == (Object)null || ((Character)player).GetSEMan() == null || (Object)(object)ObjectDB.instance == (Object)null)
		{
			return;
		}
		try
		{
			if ((Object)(object)_zoneFireVisual == (Object)null)
			{
				StatusEffect statusEffect = ObjectDB.instance.GetStatusEffect(StringExtensionMethods.GetStableHashCode("Burning"));
				if ((Object)(object)statusEffect == (Object)null)
				{
					Log.LogWarning((object)"Vanilla Burning status effect was not found; zone fire visual is unavailable.");
					return;
				}
				_zoneFireVisual = ScriptableObject.CreateInstance<StatusEffect>();
				((Object)_zoneFireVisual).name = "BR_ZoneFireVisual";
				_zoneFireVisual.m_name = "";
				_zoneFireVisual.m_tooltip = "";
				_zoneFireVisual.m_ttl = Mathf.Max(0.5f, Instance.DamageInterval.Value + 0.35f);
				_zoneFireVisual.m_startEffects = statusEffect.m_startEffects;
				_zoneFireVisual.m_stopEffects = statusEffect.m_stopEffects;
			}
			_zoneFireVisual.m_ttl = Mathf.Max(0.5f, Instance.DamageInterval.Value + 0.35f);
			((Character)player).GetSEMan().AddStatusEffect(_zoneFireVisual, true, 0, 0f, (short)(-1));
		}
		catch (Exception ex)
		{
			Log.LogWarning((object)("Failed to apply cosmetic zone fire: " + ex.Message));
		}
	}

	private void RPC_SnapshotRequest(long sender)
	{
		if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
		{
			SendState(sender);
		}
	}

	private void BroadcastState()
	{
		SendState(0L);
	}

	private void SendState(long target)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Expected O, but got Unknown
		if (ZRoutedRpc.instance != null)
		{
			ZPackage val = new ZPackage();
			ZoneState.Write(val);
			ZRoutedRpc.instance.InvokeRoutedRPC(target, "XBR_State", new object[1] { val });
		}
	}

	private void RPC_State(long sender, ZPackage pkg)
	{
		if (!((Object)(object)ZNet.instance != (Object)null) || !ZNet.instance.IsServer() || sender == ZNet.GetUID())
		{
			ZoneState.Read(pkg);
		}
	}

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

	internal static float DistanceXZ(Vector3 a, Vector3 b)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		float num = a.x - b.x;
		float num2 = a.z - b.z;
		return Mathf.Sqrt(num * num + num2 * num2);
	}

	internal static Vector3 Flat(Vector3 v)
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		v.y = 0f;
		return v;
	}

	internal Vector3 GetEffectiveCurrentCenter()
	{
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		if (ZoneState.Phase != ZonePhase.Shrinking || !ZoneState.HasNext)
		{
			return ZoneState.CurrentCenter;
		}
		float num = Mathf.Max(0.01f, ShrinkDuration.Value);
		float num2 = Mathf.Clamp01((float)((ServerTime() - ZoneState.PhaseStartedAt) / (double)num));
		return Vector3.Lerp(ZoneState.ShrinkFromCenter, ZoneState.NextCenter, num2);
	}

	internal float GetEffectiveCurrentRadius()
	{
		if (ZoneState.Phase != ZonePhase.Shrinking || !ZoneState.HasNext)
		{
			return ZoneState.CurrentRadius;
		}
		float num = Mathf.Max(0.01f, ShrinkDuration.Value);
		float num2 = Mathf.Clamp01((float)((ServerTime() - ZoneState.PhaseStartedAt) / (double)num));
		return Mathf.Lerp(ZoneState.ShrinkFromRadius, ZoneState.NextRadius, num2);
	}

	private static bool IsRussianLanguage()
	{
		string selectedGameLanguage = GetSelectedGameLanguage();
		if (!string.IsNullOrEmpty(selectedGameLanguage))
		{
			if (!selectedGameLanguage.Equals("Russian", StringComparison.OrdinalIgnoreCase) && !selectedGameLanguage.Equals("Русский", StringComparison.OrdinalIgnoreCase))
			{
				return selectedGameLanguage.StartsWith("ru", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}
		return false;
	}

	private static string GetSelectedGameLanguage()
	{
		//IL_0222: Unknown result type (might be due to invalid IL or missing references)
		//IL_0227: Unknown result type (might be due to invalid IL or missing references)
		if (!string.IsNullOrEmpty(_selectedLanguage) && Time.realtimeSinceStartup < _nextLanguageProbeAt)
		{
			return _selectedLanguage;
		}
		_nextLanguageProbeAt = Time.realtimeSinceStartup + 2f;
		try
		{
			Type[] types = typeof(Player).Assembly.GetTypes();
			foreach (Type type in types)
			{
				MethodInfo method = type.GetMethod("GetSelectedLanguage", BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null);
				if (method == null || method.ReturnType != typeof(string))
				{
					continue;
				}
				if (method.IsStatic)
				{
					string text = method.Invoke(null, null) as string;
					if (!string.IsNullOrEmpty(text))
					{
						return _selectedLanguage = text;
					}
					continue;
				}
				object obj = null;
				FieldInfo[] fields = type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (type.IsAssignableFrom(fieldInfo.FieldType))
					{
						obj = fieldInfo.GetValue(null);
						if (obj != null)
						{
							break;
						}
					}
				}
				if (obj == null)
				{
					PropertyInfo[] properties = type.GetProperties(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					foreach (PropertyInfo propertyInfo in properties)
					{
						if (!type.IsAssignableFrom(propertyInfo.PropertyType) || propertyInfo.GetIndexParameters().Length != 0)
						{
							continue;
						}
						try
						{
							obj = propertyInfo.GetValue(null, null);
							if (obj != null)
							{
								break;
							}
						}
						catch
						{
						}
					}
				}
				if (obj == null && typeof(Object).IsAssignableFrom(type))
				{
					Object[] array = Resources.FindObjectsOfTypeAll(type);
					if (array != null && array.Length != 0)
					{
						obj = array[0];
					}
				}
				if (obj != null)
				{
					string text2 = method.Invoke(obj, null) as string;
					if (!string.IsNullOrEmpty(text2))
					{
						return _selectedLanguage = text2;
					}
				}
			}
		}
		catch (Exception ex)
		{
			ManualLogSource log = Log;
			if (log != null)
			{
				log.LogDebug((object)("Valheim language API lookup failed: " + ex.Message));
			}
		}
		string text3 = PlayerPrefs.GetString("language", "");
		if (string.IsNullOrEmpty(text3))
		{
			text3 = PlayerPrefs.GetString("Language", "");
		}
		if (!string.IsNullOrEmpty(text3))
		{
			return _selectedLanguage = text3;
		}
		return _selectedLanguage = ((object)Application.systemLanguage/*cast due to .constrained prefix*/).ToString();
	}

	private static string Localize(string english, string russian)
	{
		if (!IsRussianLanguage())
		{
			return english;
		}
		return russian;
	}

	private static TMP_FontAsset GetValheimNorseBold()
	{
		if ((Object)(object)_valheimNorseBold != (Object)null)
		{
			return _valheimNorseBold;
		}
		if (_valheimNorseBoldSearched)
		{
			return null;
		}
		_valheimNorseBoldSearched = true;
		try
		{
			TMP_FontAsset[] array = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
			foreach (TMP_FontAsset val in array)
			{
				if (!((Object)(object)val == (Object)null) && !string.IsNullOrEmpty(((Object)val).name) && ((Object)val).name.Equals("Valheim-Norsebold", StringComparison.OrdinalIgnoreCase))
				{
					_valheimNorseBold = val;
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogInfo((object)("TMP HUD font: " + ((Object)val).name));
					}
					return val;
				}
			}
			ManualLogSource log2 = Log;
			if (log2 != null)
			{
				log2.LogWarning((object)"TMP font asset Valheim-Norsebold was not found.");
			}
		}
		catch (Exception ex)
		{
			ManualLogSource log3 = Log;
			if (log3 != null)
			{
				log3.LogWarning((object)("Failed to locate TMP Valheim-Norsebold: " + ex.Message));
			}
		}
		return null;
	}

	private void EnsureTmpHud()
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Expected O, but got Unknown
		//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)_hudRoot != (Object)null))
		{
			GameObject val = new GameObject("XBR_TMP_HUD");
			Object.DontDestroyOnLoad((Object)(object)val);
			Canvas obj = val.AddComponent<Canvas>();
			obj.renderMode = (RenderMode)0;
			obj.sortingOrder = 200;
			val.AddComponent<CanvasScaler>();
			((Behaviour)val.AddComponent<GraphicRaycaster>()).enabled = false;
			_hudRoot = val;
			_hudTitle = CreateTmpText("Title", val.transform, 18f);
			_hudTimer = CreateTmpText("Timer", val.transform, 18f);
			_hudDanger = CreateTmpText("Danger", val.transform, 25f);
			((Graphic)_hudDanger).color = new Color(1f, 0.35f, 0.15f, 1f);
		}
	}

	private static TextMeshProUGUI CreateTmpText(string name, Transform parent, float size)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = new GameObject(name);
		val.transform.SetParent(parent, false);
		TextMeshProUGUI obj = val.AddComponent<TextMeshProUGUI>();
		((TMP_Text)obj).font = GetValheimNorseBold();
		((TMP_Text)obj).fontSize = size;
		((TMP_Text)obj).fontStyle = (FontStyles)1;
		((TMP_Text)obj).alignment = (TextAlignmentOptions)514;
		((Graphic)obj).color = Color.white;
		((Graphic)obj).raycastTarget = false;
		((TMP_Text)obj).textWrappingMode = (TextWrappingModes)0;
		return obj;
	}

	private static void SetScreenRect(RectTransform rt, Rect screenRect)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: 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_0050: Unknown result type (might be due to invalid IL or missing references)
		rt.anchorMin = Vector2.zero;
		rt.anchorMax = Vector2.zero;
		rt.pivot = Vector2.zero;
		rt.anchoredPosition = new Vector2(((Rect)(ref screenRect)).x, (float)Screen.height - ((Rect)(ref screenRect)).yMax);
		rt.sizeDelta = new Vector2(((Rect)(ref screenRect)).width, ((Rect)(ref screenRect)).height);
	}

	private void UpdateTmpHud()
	{
		//IL_01ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_021d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0260: Unknown result type (might be due to invalid IL or missing references)
		//IL_0266: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
		if (!ShowHud.Value || (Object)(object)ZNet.instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null)
		{
			if ((Object)(object)_hudRoot != (Object)null)
			{
				_hudRoot.SetActive(false);
			}
			return;
		}
		EnsureTmpHud();
		_hudRoot.SetActive(true);
		double num = ServerTime();
		string text = null;
		string text2 = null;
		if (ZoneState.Starting)
		{
			text = Localize("BATTLE ROYALE", "КОРОЛЕВСКАЯ БИТВА");
			text2 = Localize("MATCH STARTS IN ", "МАТЧ НАЧНЁТСЯ ЧЕРЕЗ ") + FormatSeconds((float)Math.Max(0.0, ZoneState.MatchStartAt - num));
		}
		else if (ZoneState.Running)
		{
			if (ZoneState.Phase == ZonePhase.Waiting && ZoneState.HasNext)
			{
				text = Localize("ZONE SHRINKS IN", "СУЖЕНИЕ ЗОНЫ ЧЕРЕЗ");
				text2 = FormatSeconds(Mathf.Max(0f, WaitBeforeShrink.Value - (float)(num - ZoneState.PhaseStartedAt)));
			}
			else if (ZoneState.Phase == ZonePhase.Shrinking && ZoneState.HasNext)
			{
				text = Localize("ZONE IS SHRINKING", "ЗОНА СУЖАЕТСЯ");
				text2 = FormatSeconds(Mathf.Max(0f, ShrinkDuration.Value - (float)(num - ZoneState.PhaseStartedAt)));
			}
			else
			{
				text = Localize("BATTLE ROYALE", "КОРОЛЕВСКАЯ БИТВА");
			}
		}
		bool flag = !string.IsNullOrEmpty(text);
		((Component)_hudTitle).gameObject.SetActive(flag);
		((Component)_hudTimer).gameObject.SetActive(flag && !string.IsNullOrEmpty(text2));
		if (flag)
		{
			if (!MinimapUpdatePatch.TryGetSmallMinimapScreenRect(Minimap.instance, out var screenRect))
			{
				((Rect)(ref screenRect))..ctor((float)Screen.width - 320f, 245f, 300f, 70f);
			}
			float num2 = ((Rect)(ref screenRect)).yMax + 8f;
			SetScreenRect(((TMP_Text)_hudTitle).rectTransform, new Rect(((Rect)(ref screenRect)).x, num2, ((Rect)(ref screenRect)).width, 28f));
			SetScreenRect(((TMP_Text)_hudTimer).rectTransform, new Rect(((Rect)(ref screenRect)).x, num2 + 27f, ((Rect)(ref screenRect)).width, 34f));
			((TMP_Text)_hudTitle).text = text;
			((TMP_Text)_hudTimer).text = text2 ?? "";
		}
		bool flag2 = ZoneState.Running && ZoneState.HasCurrent && DistanceXZ(((Component)Player.m_localPlayer).transform.position, GetEffectiveCurrentCenter()) > GetEffectiveCurrentRadius();
		((Component)_hudDanger).gameObject.SetActive(flag2);
		if (flag2)
		{
			SetScreenRect(((TMP_Text)_hudDanger).rectTransform, new Rect((float)Screen.width / 2f - 350f, (float)Screen.height * 0.7f, 700f, 45f));
			((TMP_Text)_hudDanger).text = Localize("YOU ARE OUTSIDE THE SAFE ZONE!", "ВЫ ВНЕ БЕЗОПАСНОЙ ЗОНЫ!");
		}
	}

	private static string FormatSeconds(float value)
	{
		int num = Mathf.CeilToInt(value);
		return (num / 60).ToString("00") + ":" + (num % 60).ToString("00");
	}

	private void OnGUI()
	{
		if ((Object)(object)Minimap.instance != (Object)null)
		{
			MinimapUpdatePatch.DrawZoneCircles(Minimap.instance);
		}
	}

	internal static bool HandleCommand(Terminal terminal, string line)
	{
		//IL_00d7: 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_0167: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)Instance == (Object)null || string.IsNullOrWhiteSpace(line))
		{
			return false;
		}
		string[] array = line.Trim().Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries);
		if (array.Length == 0 || !array[0].Equals("br", StringComparison.OrdinalIgnoreCase))
		{
			return false;
		}
		try
		{
			if (array.Length == 1 || array[1].Equals("help", StringComparison.OrdinalIgnoreCase))
			{
				terminal.AddString("br start | stop | toggle | status | zone <x> <z> [r] | next <x> <z> [r] | here [r] | nexthere [r] | cancelnext");
				return true;
			}
			string text = array[1].ToLowerInvariant();
			switch (text)
			{
			case "status":
				terminal.AddString(ZoneState.Describe());
				return true;
			case "start":
			case "stop":
			case "toggle":
			case "cancelnext":
				Instance.SendRequest(text);
				return true;
			case "here":
			case "nexthere":
			{
				if ((Object)(object)Player.m_localPlayer == (Object)null)
				{
					terminal.AddString("No local player.");
					return true;
				}
				float radius2 = ((array.Length >= 3) ? ParseFloat(array[2]) : 0f);
				Instance.SendRequest((text == "here") ? "zone" : "next", ((Component)Player.m_localPlayer).transform.position, radius2);
				return true;
			}
			case "zone":
			case "next":
			{
				if (array.Length < 4)
				{
					terminal.AddString("Usage: br " + text + " <x> <z> [radius]");
					return true;
				}
				float num = ParseFloat(array[2]);
				float num2 = ParseFloat(array[3]);
				float radius = ((array.Length >= 5) ? ParseFloat(array[4]) : 0f);
				Instance.SendRequest(text, new Vector3(num, 0f, num2), radius);
				return true;
			}
			default:
				terminal.AddString("Unknown BR command. Type: br help");
				break;
			}
		}
		catch (Exception ex)
		{
			terminal.AddString("BR error: " + ex.Message);
		}
		return true;
	}

	private static float ParseFloat(string s)
	{
		if (!float.TryParse(s.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
		{
			throw new FormatException("Invalid number: " + s);
		}
		return result;
	}
}
internal enum ZonePhase
{
	Idle,
	Waiting,
	Shrinking
}
internal static class ZoneState
{
	internal static bool Running;

	internal static bool Starting;

	internal static bool HasCurrent;

	internal static bool HasNext;

	internal static Vector3 CurrentCenter;

	internal static Vector3 NextCenter;

	internal static Vector3 ShrinkFromCenter;

	internal static float CurrentRadius;

	internal static float NextRadius;

	internal static float ShrinkFromRadius;

	internal static float WaitSecondsAtAssignment;

	internal static ZonePhase Phase;

	internal static double PhaseStartedAt;

	internal static double MatchStartAt;

	internal static void Write(ZPackage p)
	{
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		p.Write(Running);
		p.Write(Starting);
		p.Write(MatchStartAt);
		p.Write(HasCurrent);
		p.Write(HasNext);
		p.Write(CurrentCenter);
		p.Write(CurrentRadius);
		p.Write(NextCenter);
		p.Write(NextRadius);
		p.Write((int)Phase);
		p.Write(PhaseStartedAt);
		p.Write(ShrinkFromCenter);
		p.Write(ShrinkFromRadius);
		p.Write(WaitSecondsAtAssignment);
	}

	internal static void Read(ZPackage p)
	{
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		Running = p.ReadBool();
		Starting = p.ReadBool();
		MatchStartAt = p.ReadDouble();
		HasCurrent = p.ReadBool();
		HasNext = p.ReadBool();
		CurrentCenter = p.ReadVector3();
		CurrentRadius = p.ReadSingle();
		NextCenter = p.ReadVector3();
		NextRadius = p.ReadSingle();
		Phase = (ZonePhase)p.ReadInt();
		PhaseStartedAt = p.ReadDouble();
		ShrinkFromCenter = p.ReadVector3();
		ShrinkFromRadius = p.ReadSingle();
		WaitSecondsAtAssignment = p.ReadSingle();
	}

	internal static string Describe()
	{
		return string.Format("BR running={0}, starting={1}, phase={2}, current=({3:F0},{4:F0}) R={5:F0}, next={6}", Running, Starting, Phase, CurrentCenter.x, CurrentCenter.z, CurrentRadius, HasNext ? $"({NextCenter.x:F0},{NextCenter.z:F0}) R={NextRadius:F0}" : "none");
	}
}
[HarmonyPatch(typeof(Terminal), "TryRunCommand", new Type[]
{
	typeof(string),
	typeof(bool),
	typeof(bool)
})]
internal static class TerminalTryRunCommandPatch
{
	private static bool Prefix(Terminal __instance, string text)
	{
		return !ValheimBattleRoyalePlugin.HandleCommand(__instance, text);
	}
}
[HarmonyPatch(typeof(Game), "Start")]
internal static class GameStartPatch
{
	private static void Postfix()
	{
		if ((Object)(object)ValheimBattleRoyalePlugin.Instance != (Object)null)
		{
			ValheimBattleRoyalePlugin.Instance.ResetRpcRegistration();
		}
	}
}
[HarmonyPatch(typeof(Minimap), "Update")]
internal static class MinimapUpdatePatch
{
	private static readonly MethodInfo ScreenToWorldPoint = FindMethod("ScreenToWorldPoint", typeof(Vector3));

	private static bool _shiftWasDown;

	private static readonly FieldInfo MapImageLargeField = AccessTools.Field(typeof(Minimap), "m_mapImageLarge");

	private static readonly FieldInfo MapImageSmallField = AccessTools.Field(typeof(Minimap), "m_mapImageSmall");

	private static readonly FieldInfo PixelSizeField = AccessTools.Field(typeof(Minimap), "m_pixelSize");

	private static readonly FieldInfo TextureSizeField = AccessTools.Field(typeof(Minimap), "m_textureSize");

	private static Texture2D _lineTex;

	private static bool _overlayErrorLogged;

	private static MethodInfo FindMethod(string name, params Type[] parameterTypes)
	{
		MethodInfo[] methods = typeof(Minimap).GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		foreach (MethodInfo methodInfo in methods)
		{
			if (!string.Equals(methodInfo.Name, name, StringComparison.Ordinal))
			{
				continue;
			}
			ParameterInfo[] parameters = methodInfo.GetParameters();
			if (parameters.Length != parameterTypes.Length)
			{
				continue;
			}
			bool flag = true;
			for (int j = 0; j < parameters.Length; j++)
			{
				if (parameters[j].ParameterType != parameterTypes[j])
				{
					flag = false;
					break;
				}
			}
			if (flag)
			{
				return methodInfo;
			}
		}
		return null;
	}

	private static MethodInfo FindCompatibleOneArgMethod(string name, Type argumentType)
	{
		MethodInfo[] methods = typeof(Minimap).GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		foreach (MethodInfo methodInfo in methods)
		{
			if (!string.Equals(methodInfo.Name, name, StringComparison.Ordinal))
			{
				continue;
			}
			ParameterInfo[] parameters = methodInfo.GetParameters();
			if (parameters.Length == 1)
			{
				Type parameterType = parameters[0].ParameterType;
				if (argumentType == null || parameterType == argumentType || parameterType.IsAssignableFrom(argumentType))
				{
					return methodInfo;
				}
			}
		}
		return null;
	}

	private static void Postfix(Minimap __instance)
	{
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Invalid comparison between Unknown and I4
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_010b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0110: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_012f: 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_0157: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c4: 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_01a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
		ValheimBattleRoyalePlugin instance = ValheimBattleRoyalePlugin.Instance;
		if ((Object)(object)instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null || ScreenToWorldPoint == null || (int)__instance.m_mode != 2)
		{
			return;
		}
		bool flag = Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303);
		bool flag2 = Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305);
		bool mouseButtonDown = Input.GetMouseButtonDown(0);
		if (mouseButtonDown && flag2)
		{
			if (ZoneState.Phase == ZonePhase.Shrinking)
			{
				ValheimBattleRoyalePlugin.Log.LogInfo((object)"Map Ctrl+Click ignored: zone shrinking is already in progress.");
			}
			else
			{
				ValheimBattleRoyalePlugin.Log.LogInfo((object)(ZoneState.HasNext ? "Map Ctrl+Click: cancel next zone" : "Map Ctrl+Click: cancel current/start zone"));
				instance.SendRequest("cancelzone");
			}
		}
		else if (mouseButtonDown && flag)
		{
			if (ZoneState.Phase == ZonePhase.Shrinking)
			{
				ValheimBattleRoyalePlugin.Log.LogInfo((object)"Map Shift+Click ignored: zone shrinking is already in progress.");
				return;
			}
			try
			{
				Vector3 val = (Vector3)ScreenToWorldPoint.Invoke(__instance, new object[1] { Input.mousePosition });
				val.y = 0f;
				if (!ZoneState.HasCurrent)
				{
					ValheimBattleRoyalePlugin.Log.LogInfo((object)$"Map Shift+Click: initial zone at X={val.x:F0} Z={val.z:F0}");
					instance.SendRequest("zone", val, instance.InitialRadius.Value);
				}
				else
				{
					float num = (ZoneState.HasNext ? ZoneState.NextRadius : 0f);
					ValheimBattleRoyalePlugin.Log.LogInfo((object)(ZoneState.HasNext ? $"Map Shift+Click: move next zone to X={val.x:F0} Z={val.z:F0}, keep R={num:F0}" : $"Map Shift+Click: next zone at X={val.x:F0} Z={val.z:F0} (automatic radius)"));
					instance.SendRequest("next", val, num);
				}
			}
			catch (Exception ex)
			{
				ValheimBattleRoyalePlugin.Log.LogError((object)("Map click conversion failed: " + ex));
			}
		}
		_shiftWasDown = flag;
	}

	internal static void DrawZoneCircles(Minimap map)
	{
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
		if (!ZoneState.HasCurrent || (Object)(object)map == (Object)null)
		{
			return;
		}
		try
		{
			object activeMapImage = GetActiveMapImage(map);
			if (activeMapImage == null || !TryGetImageGeometry(activeMapImage, out var uvRect, out var screenRect))
			{
				return;
			}
			float numericField = GetNumericField(PixelSizeField, map, 1f);
			float numericField2 = GetNumericField(TextureSizeField, map, 2048f);
			float num = numericField * numericField2;
			if (!(num <= 0.001f))
			{
				Vector3 effectiveCurrentCenter = ValheimBattleRoyalePlugin.Instance.GetEffectiveCurrentCenter();
				float effectiveCurrentRadius = ValheimBattleRoyalePlugin.Instance.GetEffectiveCurrentRadius();
				DrawCircle(effectiveCurrentCenter, effectiveCurrentRadius, new Color(0.2f, 1f, 0.25f, 0.95f), uvRect, screenRect, num, 3f);
				if (ZoneState.HasNext)
				{
					DrawCircle(ZoneState.NextCenter, ZoneState.NextRadius, new Color(1f, 0.85f, 0.15f, 0.95f), uvRect, screenRect, num, 3f);
				}
				_overlayErrorLogged = false;
			}
		}
		catch (Exception ex)
		{
			if (!_overlayErrorLogged)
			{
				_overlayErrorLogged = true;
				ValheimBattleRoyalePlugin.Log.LogWarning((object)("Zone map overlay unavailable: " + ex));
			}
		}
	}

	private static object GetActiveMapImage(Minimap map)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Invalid comparison between Unknown and I4
		if ((int)map.m_mode == 2)
		{
			return MapImageLargeField?.GetValue(map);
		}
		return MapImageSmallField?.GetValue(map);
	}

	internal static bool TryGetSmallMinimapScreenRect(Minimap map, out Rect screenRect)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		screenRect = default(Rect);
		if ((Object)(object)map == (Object)null)
		{
			return false;
		}
		try
		{
			object obj = MapImageSmallField?.GetValue(map);
			if (obj == null)
			{
				return false;
			}
			Rect uvRect;
			return TryGetImageGeometry(obj, out uvRect, out screenRect);
		}
		catch
		{
			return false;
		}
	}

	private static float GetNumericField(FieldInfo field, object instance, float fallback)
	{
		if (field == null)
		{
			return fallback;
		}
		object value = field.GetValue(instance);
		if (value == null)
		{
			return fallback;
		}
		try
		{
			return Convert.ToSingle(value, CultureInfo.InvariantCulture);
		}
		catch
		{
			return fallback;
		}
	}

	private static bool TryGetImageGeometry(object image, out Rect uvRect, out Rect screenRect)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00db: Unknown result type (might be due to invalid IL or missing references)
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_010b: Unknown result type (might be due to invalid IL or missing references)
		//IL_014a: Unknown result type (might be due to invalid IL or missing references)
		//IL_014f: Unknown result type (might be due to invalid IL or missing references)
		uvRect = new Rect(0f, 0f, 1f, 1f);
		screenRect = default(Rect);
		PropertyInfo property = image.GetType().GetProperty("uvRect", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (property != null && property.PropertyType == typeof(Rect))
		{
			uvRect = (Rect)property.GetValue(image, null);
		}
		Component val = (Component)((image is Component) ? image : null);
		if ((Object)(object)val == (Object)null)
		{
			return false;
		}
		Transform transform = val.transform;
		RectTransform val2 = (RectTransform)(object)((transform is RectTransform) ? transform : null);
		if ((Object)(object)val2 == (Object)null)
		{
			return false;
		}
		Vector3[] array = (Vector3[])(object)new Vector3[4];
		val2.GetWorldCorners(array);
		float num = float.MaxValue;
		float num2 = float.MinValue;
		float num3 = float.MaxValue;
		float num4 = float.MinValue;
		for (int i = 0; i < array.Length; i++)
		{
			Vector3 val3 = Vector2.op_Implicit(RectTransformUtility.WorldToScreenPoint((Camera)null, array[i]));
			num = Mathf.Min(num, val3.x);
			num2 = Mathf.Max(num2, val3.x);
			num3 = Mathf.Min(num3, val3.y);
			num4 = Mathf.Max(num4, val3.y);
		}
		if (num2 <= num || num4 <= num3)
		{
			return false;
		}
		screenRect = new Rect(num, (float)Screen.height - num4, num2 - num, num4 - num3);
		return true;
	}

	private static Vector2 WorldToGui(Vector3 world, Rect uvRect, Rect screenRect, float worldSize)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		float num = 0.5f + world.x / worldSize;
		float num2 = 0.5f + world.z / worldSize;
		float num3 = (num - ((Rect)(ref uvRect)).xMin) / ((Rect)(ref uvRect)).width;
		float num4 = (num2 - ((Rect)(ref uvRect)).yMin) / ((Rect)(ref uvRect)).height;
		return new Vector2(((Rect)(ref screenRect)).xMin + num3 * ((Rect)(ref screenRect)).width, ((Rect)(ref screenRect)).yMax - num4 * ((Rect)(ref screenRect)).height);
	}

	private static void DrawCircle(Vector3 center, float radius, Color color, Rect uvRect, Rect screenRect, float worldSize, float width)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: 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_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		Vector2 a = WorldToGui(center + new Vector3(radius, 0f, 0f), uvRect, screenRect, worldSize);
		for (int i = 1; i <= 128; i++)
		{
			float num = (float)i * (float)Math.PI * 2f / 128f;
			Vector2 val = WorldToGui(center + new Vector3(Mathf.Cos(num) * radius, 0f, Mathf.Sin(num) * radius), uvRect, screenRect, worldSize);
			DrawClippedLine(a, val, color, width, screenRect);
			a = val;
		}
	}

	private static void DrawClippedLine(Vector2 a, Vector2 b, Color color, float width, Rect clip)
	{
		//IL_0000: 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_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		if ((!(a.x < ((Rect)(ref clip)).xMin) || !(b.x < ((Rect)(ref clip)).xMin)) && (!(a.x > ((Rect)(ref clip)).xMax) || !(b.x > ((Rect)(ref clip)).xMax)) && (!(a.y < ((Rect)(ref clip)).yMin) || !(b.y < ((Rect)(ref clip)).yMin)) && (!(a.y > ((Rect)(ref clip)).yMax) || !(b.y > ((Rect)(ref clip)).yMax)))
		{
			DrawLine(a, b, color, width);
		}
	}

	private static void DrawLine(Vector2 a, Vector2 b, Color color, float width)
	{
		//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)
		//IL_0036: 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)
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Expected O, but got Unknown
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)_lineTex == (Object)null)
		{
			_lineTex = new Texture2D(1, 1);
			_lineTex.SetPixel(0, 0, Color.white);
			_lineTex.Apply();
		}
		Vector2 val = b - a;
		if (!(((Vector2)(ref val)).sqrMagnitude < 0.01f))
		{
			Matrix4x4 matrix = GUI.matrix;
			Color color2 = GUI.color;
			GUI.color = color;
			GUIUtility.RotateAroundPivot(Mathf.Atan2(val.y, val.x) * 57.29578f, a);
			GUI.DrawTexture(new Rect(a.x, a.y - width * 0.5f, ((Vector2)(ref val)).magnitude, width), (Texture)(object)_lineTex);
			GUI.matrix = matrix;
			GUI.color = color2;
		}
	}
}