Decompiled source of Troll Nuke v0.6.6

plugins/TrollNuke/TrollNuke.dll

Decompiled 4 days ago
using System;
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 System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using UnityEngine;
using UnityEngine.PostProcessing;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("TrollNuke")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.6.6.0")]
[assembly: AssemblyInformationalVersion("0.6.6")]
[assembly: AssemblyProduct("TrollNuke")]
[assembly: AssemblyTitle("TrollNuke")]
[assembly: AssemblyVersion("0.6.6.0")]
namespace WeaponsOfMassDestruction;

[Flags]
public enum BlastParts
{
	None = 0,
	Looks = 1,
	Sweep = 2,
	Crater = 4,
	All = 7
}
public struct BlastPlan
{
	public Vector3 Center;

	public float Radius;

	public float ShockSpeed;

	public double ImpactTime;

	public long Id;

	public long Shooter;

	public BlastParts Parts;
}
public static class Blast
{
	private readonly struct Identity
	{
		private readonly double m_start;

		private readonly long m_shooter;

		private readonly float m_x;

		private readonly float m_y;

		private readonly float m_z;

		public Identity(double start, long shooter, Vector3 target)
		{
			//IL_000f: 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_0027: Unknown result type (might be due to invalid IL or missing references)
			m_start = start;
			m_shooter = shooter;
			m_x = target.x;
			m_y = target.y;
			m_z = target.z;
		}

		public bool Same(Identity other)
		{
			if (m_start == other.m_start && m_shooter == other.m_shooter && m_x == other.m_x && m_y == other.m_y)
			{
				return m_z == other.m_z;
			}
			return false;
		}
	}

	private sealed class Entry
	{
		public BlastPlan Plan;

		public bool Console;

		public double LiftOff;

		public float Flown;

		public bool Dud;

		public double LastWarned = double.NegativeInfinity;

		public int Warnings;

		public bool Begun;

		public bool Closed;

		public float Late;

		public bool Own;

		public string OwnWhy = "";

		public string LooksNote = "not begun";

		public string PlayerNote = "not begun";

		public string WorldNote;

		public bool PlayerPending;

		public bool Far;

		public string FarNote = "";

		public bool AreaWaiting;

		public double AreaWaitUntil;

		public float AreaReadyAfter = -1f;

		public bool CraterWarm;

		public bool SweepWarm;

		public bool WarmFailed;

		public int WarmFrames;

		public bool WorldRan;

		public bool WorldActive;

		public double WorldStarted;

		public double WorldLimit;

		public CraterTicket Ticket;

		public bool CraterTried;

		public bool InnerCleared;

		public bool SweepSeenRunning;

		public bool SweepEnded;

		public float CraterWaited = -1f;

		public BlastSweep Sweep;

		public Action OnInnerCleared;

		public string CraterCheck = "";

		public string CraterDig = "";

		public string SweepNote = "";

		public string SweepGivenUp;

		public float InnerClearedAfter = -1f;

		public float SweepOverAfter = -1f;

		public string PlacesRemoved;

		public string RecordsNote;

		public bool RecordsBegun;

		public float FpsBefore;

		public float WorstFrame;

		public float Settled;

		public float AfterSeconds;

		public int AfterFrames;
	}

	private const double LateGrace = 3.0;

	private const double WorldGrace = 30.0;

	private const double SweepPatience = 30.0;

	private const double SweepStartGrace = 1.0;

	private const float SettleSeconds = 2f;

	private const float AfterWindow = 1f;

	internal const float MinArmingDistance = 10f;

	private const float MinWorldRadius = 10f;

	private const float MaxWorldRadius = 600f;

	private const double AreaPatience = 12.0;

	private const double WideAreaPatience = 30.0;

	private const float WideFrom = 150f;

	private const double WarmLead = 6.0;

	private const float KeepUnchanged = 3f;

	private const float KeepChanged = 120f;

	internal const float HeightReach = 1.5f;

	private const float BluntShare = 0.6f;

	private const float PushPerSpeed = 0.8f;

	private const float LiftForward = 0.6f;

	private const float LiftUp = 0.45f;

	private const float MaxLiftUp = 10f;

	private const float EdgeTolerance = 0.5f;

	private const float ZoneSize = 64f;

	private static readonly List<Entry> s_entries = new List<Entry>();

	private static readonly Dictionary<long, Identity> s_seen = new Dictionary<long, Identity>();

	private static readonly List<ProtectedDisc> s_discs = new List<ProtectedDisc>();

	private static ZNet s_world;

	private static BlastRunner s_runner;

	private static Entry s_worldEntry;

	private static string s_lastLine;

	private static float s_frameSeconds;

	private static bool s_loggedStepError;

	private static bool s_warnedIdClash;

	private static bool s_loggedPlayerError;

	internal const float ShooterRoom = 12f;

	public static bool SweepRunning
	{
		get
		{
			Entry entry = s_worldEntry;
			if (entry == null)
			{
				return false;
			}
			try
			{
				ZNet instance = ZNet.instance;
				if ((Object)(object)instance != (Object)null && instance == s_world && (Object)(object)s_runner != (Object)null && entry.WorldActive && !entry.Closed && instance.GetTimeSeconds() - entry.WorldStarted <= entry.WorldLimit + 5.0)
				{
					return true;
				}
			}
			catch (Exception)
			{
			}
			s_worldEntry = null;
			return false;
		}
	}

	internal static int Pending
	{
		get
		{
			if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance != s_world || (Object)(object)s_runner == (Object)null)
			{
				return 0;
			}
			int num = 0;
			for (int i = 0; i < s_entries.Count; i++)
			{
				if (!s_entries[i].Begun)
				{
					num++;
				}
			}
			return num;
		}
	}

	internal static string LastLine => s_lastLine;

	public static void Schedule(LaunchOrder order)
	{
		//IL_001f: 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_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0106: 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_011c: 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_023e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0244: Unknown result type (might be due to invalid IL or missing references)
		//IL_0249: Unknown result type (might be due to invalid IL or missing references)
		//IL_024e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0276: Unknown result type (might be due to invalid IL or missing references)
		//IL_027b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0352: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null)
			{
				Warn("[Blast] launch order not scheduled: no world is running.");
				return;
			}
			if (!FxLocal.IsFinite(order.Target) || !FxLocal.IsFinite(order.Pad) || !IsFinite(order.StartTime) || !FxLocal.IsFinite(order.PadHold) || !FxLocal.IsFinite(order.FlightTime) || !FxLocal.IsFinite(order.BlastRadius) || !(order.BlastRadius > 0f))
			{
				Warn(Text($"[Blast] launch order not scheduled: it holds a broken number (target {order.Target}, pad {order.Pad}, start {order.StartTime}, hold {order.PadHold}, flight {order.FlightTime}, blast {order.BlastRadius})."));
				return;
			}
			EnterWorld(instance);
			double num = RocketFlight.NetSeconds(instance);
			long id = BlastRules.OrderId(order.StartTime, order.Shooter, order.Target.x, order.Target.y, order.Target.z);
			if (!Claim(ref id, new Identity(order.StartTime, order.Shooter, order.Target)))
			{
				Warn(Text($"[Blast] the launch order with id {id:X} was scheduled a second time; the second one was dropped (one blast per order)."));
				return;
			}
			if (order.StartTime - num > 30.0)
			{
				Warn(Text($"[Blast] launch order {id:X} not scheduled: it starts {order.StartTime - num:F1} s from now (start {order.StartTime:F2}, clock {num:F2})."));
				return;
			}
			string why;
			bool flag = IsOwn(order.Shooter, instance, out why);
			double num2 = num - order.ImpactTime;
			if (num2 > (flag ? 30.0 : 3.0))
			{
				Info(Text($"[Blast] launch order {id:X} arrived {num2:F1} s after its blast: nothing is shown or done on this peer."));
				return;
			}
			Vector3 val = order.Target - order.Pad;
			val.y = 0f;
			float magnitude = ((Vector3)(ref val)).magnitude;
			Entry entry = new Entry
			{
				Plan = new BlastPlan
				{
					Center = order.Target,
					Radius = order.BlastRadius,
					ShockSpeed = ShockSpeedNow(),
					ImpactTime = order.ImpactTime,
					Id = id,
					Shooter = order.Shooter,
					Parts = BlastParts.All
				},
				LiftOff = order.StartTime + (double)order.PadHold,
				Flown = magnitude,
				Dud = !Arms(magnitude, order.BlastRadius),
				Own = flag,
				OwnWhy = why
			};
			s_entries.Add(entry);
			EnsureRunner();
			if (flag && !entry.Dud)
			{
				HoldArea(entry);
			}
			Info(Text($"[Blast] scheduled id={id:X} at {FxLocal.Vec(order.Target)} R={order.BlastRadius:0.#}: ") + ((num2 >= 0.0) ? Text($"its time passed {num2:F2} s ago, it runs at once") : Text($"in {0.0 - num2:F2} s")) + "; this peer " + (flag ? "changes the world" : "does not change the world") + " (" + why + ")" + ((flag && !entry.Dud) ? ("; far zones: " + entry.FarNote) : "") + (entry.Dud ? Text($"; the rocket stands {magnitude:F1} m from its target and needs {ArmingDistance(order.BlastRadius):F1} m to arm: it will be a dud") : ""));
		}
		catch (Exception ex)
		{
			Error("[Blast] a launch order could not be scheduled: " + ex);
		}
	}

	private static void HoldArea(Entry entry)
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			entry.Far = FarZones.Begin(entry.Plan.Id, entry.Plan.Center, LiveRadius(entry.Plan.Radius) + 16f, out entry.FarNote);
		}
		catch (Exception ex)
		{
			entry.Far = false;
			entry.FarNote = "the hold failed (" + ex.GetType().Name + ": " + ex.Message + ")";
			Warn("[Blast] the zones of blast " + entry.Plan.Id.ToString("X") + " could not be held: " + ex);
		}
	}

	internal static float LiveRadius(float radius)
	{
		float num = 0f;
		try
		{
			num = Crater.GroundReach(radius);
		}
		catch (Exception)
		{
			num = 0f;
		}
		return BlastRules.LiveRadius(radius, num);
	}

	public static void Now(Vector3 point, float radius, BlastParts parts)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		TryNow(point, radius, parts, out var _);
	}

	internal static bool TryNow(Vector3 point, float radius, BlastParts parts, out string note)
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: 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_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
		Entry entry = null;
		try
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null)
			{
				note = "no world is running";
				return false;
			}
			if (!FxLocal.IsFinite(point) || !FxLocal.IsFinite(radius) || !(radius > 0f))
			{
				note = "that is not a usable point or radius";
				return false;
			}
			parts &= BlastParts.All;
			if (parts == BlastParts.None)
			{
				note = "no part of the blast was asked for";
				return false;
			}
			EnterWorld(instance);
			double num = RocketFlight.NetSeconds(instance);
			long id = BlastRules.OrderId(num, 0L, point.x, point.y, point.z);
			if (!Claim(ref id, new Identity(num, 0L, point)))
			{
				note = "the same blast was asked for twice in the same instant";
				return false;
			}
			entry = new Entry
			{
				Plan = new BlastPlan
				{
					Center = point,
					Radius = radius,
					ShockSpeed = ShockSpeedNow(),
					ImpactTime = num,
					Id = id,
					Shooter = 0L,
					Parts = parts
				},
				Console = true,
				LiftOff = num,
				Flown = float.NaN,
				Own = true,
				OwnWhy = "typed into this console"
			};
			s_entries.Add(entry);
			EnsureRunner();
			if ((parts & (BlastParts.Sweep | BlastParts.Crater)) != BlastParts.None)
			{
				HoldArea(entry);
			}
			Begin(entry, num, instance);
			note = "looks " + entry.LooksNote + "; world " + WorldText(entry, withPlaces: false);
			return true;
		}
		catch (Exception ex)
		{
			Error("[Blast] a blast from the console failed: " + ex);
			if (entry != null)
			{
				Abandon(entry);
				s_entries.Remove(entry);
			}
			note = "it failed: " + ex.GetType().Name + ": " + ex.Message;
			return false;
		}
	}

	internal static float ArmingDistance(float blastRadius)
	{
		if (!(Value(Settings.MinSafeDistanceFactor, 1.5f) < 1f))
		{
			return Mathf.Max(blastRadius, 10f);
		}
		return 0f;
	}

	internal static bool Arms(float flown, float blastRadius)
	{
		return flown >= ArmingDistance(blastRadius);
	}

	internal static bool AreaLoaded(Vector3 center, float radius)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		int zones;
		int missing;
		return DiscLoaded(center, radius, out zones, out missing);
	}

	internal static string Describe(Vector3 point, float radius)
	{
		//IL_0010: 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_016c: 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_0199: Unknown result type (might be due to invalid IL or missing references)
		//IL_019a: Unknown result type (might be due to invalid IL or missing references)
		//IL_019f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			float num = ShockSpeedNow();
			BlastPlan plan = new BlastPlan
			{
				Center = point,
				Radius = radius,
				ShockSpeed = num,
				Parts = BlastParts.All
			};
			string say;
			string text = Refusal(plan, 0f, out say);
			if (text == null && !AreaReady(plan, out var why))
			{
				text = why + (FarZones.Available(out say) ? "; a launch would load it first" : "");
			}
			StringBuilder stringBuilder = new StringBuilder(256);
			float num2 = BlastRules.ClearRadius(radius, Crater.GroundReach(radius));
			stringBuilder.Append(Text($"blast: R={radius:0.#} m, fireball to {BlastRules.VaporiseRadius(radius):0.#} m, "));
			if (num2 > BlastRules.VaporiseRadius(radius))
			{
				stringBuilder.Append(Text($"everything goes whole out to {num2:0.#} m (the ground the crater moves), "));
			}
			stringBuilder.Append(Text($"shove to {BlastRules.PushRadius(radius):0.#} m, the wave at {num:0.#} m/s reaches the edge after {ShockFront.Arrival(radius, radius, num):0.00} s; "));
			stringBuilder.Append((text == null) ? "a blast here now would change the world" : ("a blast here now would be an explosion only: " + text));
			stringBuilder.Append("; protected places in reach: ").Append(ProtectedPlaces(plan));
			stringBuilder.Append("; places it would remove whole: ").Append(Protection.DoomedNames(point, radius));
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				Vector3 val = ((Component)localPlayer).transform.position - point;
				float num3 = Mathf.Abs(val.y);
				val.y = 0f;
				float magnitude = ((Vector3)(ref val)).magnitude;
				float num4 = BlastRules.PushRadius(radius);
				float distance = ((magnitude > num4 && magnitude <= num4 + 0.5f) ? num4 : magnitude);
				BlastBand blastBand = ((!(num3 > 1.5f * radius)) ? BlastRules.BandAt(radius, distance) : BlastBand.None);
				stringBuilder.Append(Text($"; you: {magnitude:0.0} m away, {BandName(blastBand)}"));
				if (blastBand != BlastBand.None)
				{
					PlayerDamageMode playerDamageMode = ((Settings.PlayerDamage != null) ? Settings.PlayerDamage.Value : PlayerDamageMode.Full);
					float num5 = ((blastBand == BlastBand.Push || playerDamageMode == PlayerDamageMode.None) ? 0f : BlastRules.CreatureDamage(radius, distance, Value(Settings.CreatureDamage, 0f)));
					stringBuilder.Append(Text($", the wave after {ShockFront.Arrival(distance, radius, num):0.00} s: ")).Append((num5 > 0f) ? Text($"a hit of {num5:0} before armour and a throw") : "a stagger and a shove, no damage").Append(" (PlayerDamage=")
						.Append(playerDamageMode)
						.Append(')');
				}
			}
			return stringBuilder.ToString();
		}
		catch (Exception ex)
		{
			return "blast: could not be described (" + ex.GetType().Name + ": " + ex.Message + ")";
		}
	}

	internal static string Status()
	{
		int num = 0;
		for (int i = 0; i < s_entries.Count; i++)
		{
			if (s_entries[i].Begun)
			{
				num++;
			}
		}
		return "blast: SweepRunning=" + SweepRunning + ", to come=" + Pending + ", being finished=" + (((Object)(object)ZNet.instance != (Object)null && ZNet.instance == s_world) ? num : 0);
	}

	private static bool IsOwn(long shooter, ZNet net, out string why)
	{
		if (net.IsDedicated())
		{
			why = "a dedicated server changes nothing itself";
			return false;
		}
		if (shooter == 0L)
		{
			bool flag = net.IsServer();
			why = (flag ? "launched from this console" : "launched from the host's console");
			return flag;
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer != (Object)null && localPlayer.GetPlayerID() == shooter)
		{
			why = "the shooter is this player";
			return true;
		}
		if (ZNet.IsSinglePlayer)
		{
			why = "single player";
			return true;
		}
		why = "the shooter is another player";
		return false;
	}

	private static bool Claim(ref long id, Identity blast)
	{
		if (s_seen.Count > 4096)
		{
			s_seen.Clear();
		}
		for (int i = 0; i < 256; i++)
		{
			if (!s_seen.TryGetValue(id, out var value))
			{
				s_seen.Add(id, blast);
				return true;
			}
			if (value.Same(blast))
			{
				return false;
			}
			if (!s_warnedIdClash)
			{
				s_warnedIdClash = true;
				Warn(Text($"[Blast] two different blasts were given the id {id:X}; the later one takes the next free number (said once)."));
			}
			id++;
		}
		return false;
	}

	private static void EnterWorld(ZNet net)
	{
		if (net != s_world)
		{
			if ((Object)(object)s_world != (Object)null || s_entries.Count > 0 || s_seen.Count > 0)
			{
				Close("another world was entered");
			}
			s_world = net;
		}
	}

	private static void EnsureRunner()
	{
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)s_runner != (Object)null))
		{
			s_runner = new GameObject("WMD_Blast").AddComponent<BlastRunner>();
		}
	}

	private static void Close(string why)
	{
		int num = 0;
		for (int i = 0; i < s_entries.Count; i++)
		{
			Entry entry = s_entries[i];
			entry.Closed = true;
			if (entry.Begun)
			{
				try
				{
					WriteLine(entry, (entry.PlayerPending || entry.WorldActive) ? why : null, withPlaces: false);
				}
				catch (Exception ex)
				{
					Warn("[Blast] the line of a blast could not be written: " + ex.Message);
				}
			}
			else
			{
				num++;
			}
			entry.Ticket = null;
			entry.Sweep = null;
		}
		if (num > 0)
		{
			Info("[Blast] " + why + ": " + num + " blast(s) whose time had not come were dropped. Nothing is left behind.");
		}
		s_entries.Clear();
		s_seen.Clear();
		s_worldEntry = null;
		s_world = null;
		s_frameSeconds = 0f;
		BlastRunner blastRunner = s_runner;
		s_runner = null;
		if ((Object)(object)blastRunner != (Object)null)
		{
			Object.Destroy((Object)(object)((Component)blastRunner).gameObject);
		}
	}

	internal static void RunnerGone(BlastRunner runner)
	{
		if (runner != s_runner)
		{
			return;
		}
		s_runner = null;
		try
		{
			Close("the world was closed");
		}
		catch (Exception ex)
		{
			s_entries.Clear();
			s_seen.Clear();
			s_worldEntry = null;
			s_world = null;
			Warn("[Blast] the schedule could not be closed in order: " + ex.Message);
		}
	}

	internal static void Tick(BlastRunner runner)
	{
		if (runner != s_runner)
		{
			Object.Destroy((Object)(object)((Component)runner).gameObject);
			return;
		}
		try
		{
			Step();
		}
		catch (Exception ex)
		{
			if (!s_loggedStepError)
			{
				s_loggedStepError = true;
				Error("[Blast] the schedule failed and was emptied (said once): " + ex);
			}
			try
			{
				Close("the schedule failed");
			}
			catch (Exception)
			{
				s_entries.Clear();
				s_worldEntry = null;
			}
		}
	}

	private static void Step()
	{
		ZNet instance = ZNet.instance;
		if ((Object)(object)instance == (Object)null || instance != s_world)
		{
			Close("the world was closed");
		}
		else
		{
			if (Time.timeScale <= 0f)
			{
				return;
			}
			float unscaledDeltaTime = Time.unscaledDeltaTime;
			if (unscaledDeltaTime > 0f && unscaledDeltaTime < 1f)
			{
				s_frameSeconds = ((s_frameSeconds > 0f) ? Mathf.Lerp(s_frameSeconds, unscaledDeltaTime, Mathf.Min(1f, 2f * unscaledDeltaTime)) : unscaledDeltaTime);
			}
			if (s_entries.Count == 0)
			{
				return;
			}
			double now = RocketFlight.NetSeconds(instance);
			for (int num = s_entries.Count - 1; num >= 0; num--)
			{
				Entry entry = s_entries[num];
				bool flag;
				try
				{
					flag = (entry.Begun ? Follow(entry, now, unscaledDeltaTime) : Wait(entry, now, instance));
				}
				catch (Exception ex)
				{
					Error(Text($"[Blast] blast {entry.Plan.Id:X} failed and was ended: ") + ex);
					Abandon(entry);
					entry.WorldNote = entry.WorldNote + " (ended by an error: " + ex.GetType().Name + ")";
					flag = true;
				}
				if (flag)
				{
					s_entries.RemoveAt(num);
					entry.Closed = true;
					if (entry.Far)
					{
						FarZones.Settle(entry.Plan.Id, entry.WorldRan ? Value(Settings.KeepBlastSite, 120f) : 3f);
					}
					if (entry.Begun)
					{
						try
						{
							WriteLine(entry, null, withPlaces: true);
						}
						catch (Exception ex2)
						{
							Warn("[Blast] the line of blast " + entry.Plan.Id.ToString("X") + " could not be written: " + ex2.Message);
						}
					}
				}
			}
		}
	}

	private static void Abandon(Entry entry)
	{
		entry.PlayerPending = false;
		entry.AreaWaiting = false;
		if (entry.Ticket != null && !entry.CraterTried)
		{
			entry.CraterDig = "crater never dug (the blast was ended first)";
		}
		EndWorld(entry);
	}

	private static bool Wait(Entry entry, double now, ZNet net)
	{
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		BlastPlan plan = entry.Plan;
		if (now >= plan.ImpactTime)
		{
			Begin(entry, now, net);
			return false;
		}
		if (entry.Own && !entry.Dud && !entry.WarmFailed && plan.ImpactTime - now <= 6.0)
		{
			WarmAhead(entry);
		}
		if (!entry.Dud && now >= entry.LiftOff && now - entry.LastWarned >= 1.0)
		{
			entry.LastWarned = now;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null && !((Character)localPlayer).IsDead() && InReach(((Component)localPlayer).transform.position, plan, plan.Radius))
			{
				entry.Warnings++;
				Tell("You are inside the blast radius");
			}
		}
		return false;
	}

	private static void WarmAhead(Entry entry)
	{
		//IL_003c: 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)
		try
		{
			BlastPlan plan = entry.Plan;
			bool flag = (plan.Parts & BlastParts.Crater) != BlastParts.None && !entry.CraterWarm;
			bool flag2 = (plan.Parts & BlastParts.Sweep) != BlastParts.None && !entry.SweepWarm;
			if ((flag || flag2) && FarZones.Ready(plan.Center, LiveRadius(plan.Radius), out var _, out var _, out var _))
			{
				float budgetMs = 0.5f * Mathf.Clamp(Value(Settings.BlastBudgetMs, 4f), 1f, 16f);
				entry.WarmFrames++;
				if (flag)
				{
					entry.CraterWarm = Crater.Warm(plan.Center, plan.Radius, budgetMs);
				}
				if (flag2)
				{
					plan.ShockSpeed = ShockSpeedNow();
					entry.SweepWarm = BlastSweep.Warm(plan, budgetMs);
				}
			}
		}
		catch (Exception ex)
		{
			entry.WarmFailed = true;
			Warn(Text($"[Blast] reading ahead for blast {entry.Plan.Id:X} failed and was given up (the blast itself is not affected): ") + ex);
		}
	}

	private static bool Follow(Entry entry, double now, float frame)
	{
		if (frame > entry.WorstFrame && frame < 10f)
		{
			entry.WorstFrame = frame;
		}
		if (entry.PlayerPending)
		{
			WatchPlayer(entry, now);
		}
		if (entry.WorldActive)
		{
			WatchWorld(entry, now);
		}
		if (entry.PlayerPending || entry.WorldActive)
		{
			return false;
		}
		entry.Settled += frame;
		if (entry.Settled > 1f)
		{
			entry.AfterSeconds += frame;
			entry.AfterFrames++;
		}
		return entry.Settled >= 2f;
	}

	private static void Begin(Entry entry, double now, ZNet net)
	{
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Invalid comparison between Unknown and I4
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		entry.Begun = true;
		BlastPlan plan = entry.Plan;
		plan.ShockSpeed = ShockSpeedNow();
		entry.Plan = plan;
		entry.Late = (float)Math.Max(0.0, now - plan.ImpactTime);
		entry.FpsBefore = ((s_frameSeconds > 0f) ? (1f / s_frameSeconds) : ((Time.smoothDeltaTime > 0f) ? (1f / Time.smoothDeltaTime) : 0f));
		if (!entry.Console)
		{
			entry.Own = IsOwn(plan.Shooter, net, out entry.OwnWhy);
		}
		bool flag = !net.IsDedicated() && (int)SystemInfo.graphicsDeviceType != 4;
		bool flag2 = (double)entry.Late <= 3.0;
		if (entry.Dud)
		{
			BeginDud(entry, flag && flag2);
			return;
		}
		if ((plan.Parts & BlastParts.Looks) == 0)
		{
			entry.LooksNote = "not asked for";
		}
		else if (!flag)
		{
			entry.LooksNote = "none (no display on this peer)";
		}
		else if (!flag2)
		{
			entry.LooksNote = Text($"skipped ({entry.Late:F1} s late)");
		}
		else
		{
			try
			{
				Impact.Detonate(plan.Center, plan.Radius);
				entry.LooksNote = "shown";
			}
			catch (Exception ex)
			{
				entry.LooksNote = "failed (" + ex.GetType().Name + ")";
				Error("[Blast] the explosion could not be shown: " + ex);
			}
		}
		if ((plan.Parts & BlastParts.Sweep) == 0)
		{
			entry.PlayerNote = "not asked for (it goes with the sweep)";
		}
		else if (net.IsDedicated())
		{
			entry.PlayerNote = "none on a dedicated server";
		}
		else if (!flag2)
		{
			entry.PlayerNote = Text($"skipped ({entry.Late:F1} s late)");
		}
		else
		{
			entry.PlayerPending = true;
			entry.PlayerNote = "the wave has not reached them";
		}
		if ((plan.Parts & (BlastParts.Sweep | BlastParts.Crater)) == 0)
		{
			entry.WorldNote = "not asked for";
		}
		else if (!entry.Own)
		{
			entry.WorldNote = "not this peer's to change (" + entry.OwnWhy + ")";
		}
		else
		{
			string say;
			string text = Refusal(plan, entry.Late, out say);
			string why;
			if (text != null)
			{
				entry.WorldNote = "REFUSED: " + text;
				if (say != null)
				{
					Tell(say);
				}
			}
			else if (AreaReady(plan, out why))
			{
				entry.AreaReadyAfter = 0f;
				StartWorld(entry, now);
			}
			else if (entry.Far)
			{
				entry.AreaWaiting = true;
				double num = ((entry.Plan.Radius > 150f) ? 30.0 : 12.0);
				entry.AreaWaitUntil = now + num;
				entry.WorldActive = true;
				entry.WorldStarted = now;
				entry.WorldLimit = num + 5.0;
				entry.WorldNote = "waiting for its area to load: " + why;
				s_worldEntry = entry;
			}
			else
			{
				entry.WorldNote = "REFUSED: " + why;
				Tell("Explosion only: the blast area is not fully loaded from where you are.");
			}
		}
		if (entry.PlayerPending)
		{
			WatchPlayer(entry, now);
		}
	}

	private static void BeginDud(Entry entry, bool show)
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		BlastPlan plan = entry.Plan;
		string text = "not shown";
		if (show)
		{
			try
			{
				RocketFlight.GroundBurst(plan.Center, RocketModel.PlannedLength(), out var dustShown, out var soundShown);
				text = "dust=" + dustShown + ", sound=" + soundShown;
			}
			catch (Exception ex)
			{
				text = "failed (" + ex.GetType().Name + ")";
			}
		}
		entry.LooksNote = Text($"DUD, the rocket stood {entry.Flown:F1} m from its target and needs {ArmingDistance(plan.Radius):F1} m to arm: a puff only ({text})");
		entry.PlayerNote = "untouched";
		entry.WorldNote = "untouched";
		if (entry.Own)
		{
			Tell(Text($"The rocket did not arm: it stood only {entry.Flown:0} m from its target."));
		}
	}

	private static string Refusal(BlastPlan plan, float late, out string say)
	{
		//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
		say = null;
		if ((double)late > 30.0)
		{
			return Text($"its time passed {late:F0} s ago");
		}
		if (!(plan.Radius >= 10f) || !(plan.Radius <= 600f))
		{
			say = Text($"A blast radius of {plan.Radius:0.#} m only shows the explosion (the world is changed from {10f:0} to {600f:0} m).");
			return Text($"the radius {plan.Radius:0.#} m is outside {10f:0} to {600f:0} m");
		}
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return "this peer has no player in the world (dead or not spawned)";
		}
		if ((Object)(object)ZNetScene.instance == (Object)null || (Object)(object)ZoneSystem.instance == (Object)null)
		{
			return "the world is not there (loading or closing)";
		}
		if (Character.InInterior(plan.Center))
		{
			say = "Explosion only: it went off inside a dungeon.";
			return "ground zero is inside a dungeon";
		}
		if (((Character)localPlayer).InInterior())
		{
			say = "Explosion only: you are inside a dungeon.";
			return "the player is inside a dungeon";
		}
		if (SweepRunning)
		{
			say = "Explosion only: the last blast is still settling.";
			return "another blast is still being applied";
		}
		return null;
	}

	private static bool AreaReady(BlastPlan plan, out string why)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		if (FarZones.Ready(plan.Center, LiveRadius(plan.Radius), out var zones, out var missing, out var objectsToCome))
		{
			why = null;
			return true;
		}
		why = Text(FormattableStringFactory.Create("the blast disc is not fully loaded ({0} of its {1} zones {2} missing", missing, zones, (missing == 1) ? "is" : "are")) + ((objectsToCome > 0) ? Text($", {objectsToCome} objects still to come)") : ")");
		return false;
	}

	private static void WatchArea(Entry entry, double now)
	{
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		BlastPlan plan = entry.Plan;
		float num = (float)(now - plan.ImpactTime);
		if (AreaReady(plan, out var why))
		{
			entry.AreaWaiting = false;
			entry.AreaReadyAfter = num;
			entry.WorldNote = null;
			StartWorld(entry, now);
		}
		else if (!(now < entry.AreaWaitUntil))
		{
			entry.AreaWaiting = false;
			if ((Object)(object)Player.m_localPlayer != (Object)null && FarZones.GroundReady(plan.Center, LiveRadius(plan.Radius)))
			{
				entry.AreaReadyAfter = num;
				entry.FarNote = entry.FarNote + Text($"; begun {num:F1} s after the flash without waiting longer for: ") + why;
				entry.WorldNote = null;
				StartWorld(entry, now);
			}
			else
			{
				entry.WorldNote = Text($"REFUSED: its area had not loaded {num:F1} s after the flash: ") + why;
				Tell("Explosion only: the blast area did not load in time.");
				EndWorld(entry);
			}
		}
	}

	private static bool DiscLoaded(Vector3 center, float radius, out int zones, out int missing)
	{
		//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)
		//IL_0033: 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)
		zones = 0;
		missing = 0;
		ZoneSystem instance = ZoneSystem.instance;
		ZNet instance2 = ZNet.instance;
		if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null)
		{
			return false;
		}
		return DiscWithin(instance, ZoneSystem.GetZone(instance2.GetReferencePosition()), instance2.GetSyncedSimulationDistance(), center, radius, mustBeBuilt: true, out zones, out missing);
	}

	internal static bool AreaHolds(Vector3 center, float radius, Vector3 shooter)
	{
		//IL_0000: 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)
		//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_0037: 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)
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: 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_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
		if (!DiscLoaded(center, radius, out var zones, out var missing))
		{
			return false;
		}
		ZoneSystem instance = ZoneSystem.instance;
		ZNet instance2 = ZNet.instance;
		if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null)
		{
			return false;
		}
		SimulationDistance syncedSimulationDistance = instance2.GetSyncedSimulationDistance();
		Vector3 val = center - shooter;
		val.y = 0f;
		if (((Vector3)(ref val)).magnitude + radius <= 64f * (float)((SimulationDistance)(ref syncedSimulationDistance)).NearSimulationDistance + 0.01f)
		{
			return true;
		}
		Vector2s zone = ZoneSystem.GetZone(shooter);
		for (int i = 0; i < 4; i++)
		{
			Vector2s zone2 = ZoneSystem.GetZone(shooter + new Vector3(((i & 1) == 0) ? (-12f) : 12f, 0f, ((i & 2) == 0) ? (-12f) : 12f));
			if (zone2 != zone && !DiscWithin(instance, zone2, syncedSimulationDistance, center, radius, mustBeBuilt: false, out missing, out zones))
			{
				return false;
			}
		}
		return true;
	}

	internal static float SureRange(float radius)
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0025: Unknown result type (might be due to invalid IL or missing references)
		ZNet instance = ZNet.instance;
		if (!((Object)(object)instance != (Object)null))
		{
			return 0f;
		}
		SimulationDistance syncedSimulationDistance = instance.GetSyncedSimulationDistance();
		return Mathf.Max(0f, 64f * (float)((SimulationDistance)(ref syncedSimulationDistance)).NearSimulationDistance - radius);
	}

	private static bool DiscWithin(ZoneSystem system, Vector2s home, SimulationDistance reach, Vector3 center, float radius, bool mustBeBuilt, out int zones, out int missing)
	{
		//IL_0008: 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_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_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: 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_00d6: Unknown result type (might be due to invalid IL or missing references)
		zones = 0;
		missing = 0;
		int num = ZoneIndex(center.x - radius);
		int num2 = ZoneIndex(center.x + radius);
		int num3 = ZoneIndex(center.z - radius);
		int num4 = ZoneIndex(center.z + radius);
		Vector2s val = default(Vector2s);
		for (int i = num3; i <= num4; i++)
		{
			for (int j = num; j <= num2; j++)
			{
				float num5 = (float)j * 64f;
				float num6 = (float)i * 64f;
				float num7 = Mathf.Max(0f, Mathf.Abs(center.x - num5) - 32f);
				float num8 = Mathf.Max(0f, Mathf.Abs(center.z - num6) - 32f);
				if (!(num7 * num7 + num8 * num8 > radius * radius))
				{
					zones++;
					((Vector2s)(ref val))..ctor(j, i);
					if (!InNearArea(system, home, reach, val) || (mustBeBuilt && !system.IsZoneLoaded(val)))
					{
						missing++;
					}
				}
			}
		}
		if (zones > 0)
		{
			return missing == 0;
		}
		return false;
	}

	internal static bool InNearArea(ZoneSystem system, Vector2s home, SimulationDistance reach, Vector2s zone)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: 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_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		int nearSimulationDistance = ((SimulationDistance)(ref reach)).NearSimulationDistance;
		if (Math.Abs(zone.x - home.x) > nearSimulationDistance || Math.Abs(zone.y - home.y) > nearSimulationDistance)
		{
			return false;
		}
		if (!((SimulationDistance)(ref reach)).IsClassic && !(zone == home))
		{
			return system.ZonesWithinRadius(home, zone, nearSimulationDistance, false);
		}
		return true;
	}

	private static int ZoneIndex(float coordinate)
	{
		return Mathf.FloorToInt((coordinate + 32f) / 64f);
	}

	private static string ProtectedPlaces(BlastPlan plan)
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			s_discs.Clear();
			Protection.ProtectedDiscs(plan.Center, plan.Radius, s_discs);
			if (s_discs.Count == 0)
			{
				return "none";
			}
			StringBuilder stringBuilder = new StringBuilder(64);
			stringBuilder.Append(s_discs.Count).Append(" (");
			for (int i = 0; i < s_discs.Count && i < 4; i++)
			{
				if (i > 0)
				{
					stringBuilder.Append(", ");
				}
				Vector3 val = s_discs[i].Center - plan.Center;
				val.y = 0f;
				stringBuilder.Append(string.IsNullOrEmpty(s_discs[i].Name) ? "a place" : s_discs[i].Name).Append(Text($" r={s_discs[i].Radius:0.#} m, {((Vector3)(ref val)).magnitude:0} m off"));
			}
			if (s_discs.Count > 4)
			{
				stringBuilder.Append(", ...");
			}
			stringBuilder.Append(')');
			s_discs.Clear();
			return stringBuilder.ToString();
		}
		catch (Exception ex)
		{
			s_discs.Clear();
			return "not known (" + ex.GetType().Name + ")";
		}
	}

	private static void StartWorld(Entry entry, double now)
	{
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		BlastPlan plan = entry.Plan;
		bool num = (plan.Parts & BlastParts.Crater) != 0;
		bool flag = (plan.Parts & BlastParts.Sweep) != 0;
		entry.WorldRan = true;
		entry.WorldActive = true;
		entry.WorldStarted = now;
		entry.WorldLimit = (double)ShockFront.Arrival(BlastRules.PushRadius(plan.Radius) + 8f, plan.Radius, plan.ShockSpeed) + 30.0;
		s_worldEntry = entry;
		if (!num)
		{
			entry.CraterCheck = "crater not asked for";
		}
		else
		{
			try
			{
				entry.Ticket = Crater.Check(plan.Center, plan.Radius, out var note);
				entry.CraterCheck = Labelled((entry.Ticket != null) ? "crater ready" : "no crater", note);
			}
			catch (Exception ex)
			{
				entry.Ticket = null;
				entry.CraterCheck = "no crater (its check failed: " + ex.GetType().Name + ": " + ex.Message + ")";
				Error("[Blast] the crater's check failed: " + ex);
			}
		}
		if (!flag)
		{
			entry.SweepNote = "not asked for";
		}
		else if (!(Value(Settings.ObjectDamage, 0f) > 0f) && !(Value(Settings.CreatureDamage, 0f) > 0f))
		{
			entry.SweepNote = "off (ObjectDamage and CreatureDamage are both 0)";
		}
		else
		{
			try
			{
				entry.OnInnerCleared = delegate
				{
					InnerCleared(entry);
				};
				entry.Sweep = BlastSweep.Run(plan, entry.OnInnerCleared);
				entry.SweepNote = (((Object)(object)entry.Sweep != (Object)null) ? "started" : "could not start (the sweep's own line says why)");
			}
			catch (Exception ex2)
			{
				entry.Sweep = null;
				entry.SweepNote = "could not start (" + ex2.GetType().Name + ": " + ex2.Message + ")";
				Error("[Blast] the sweep could not be started: " + ex2);
			}
		}
		if (flag && (Object)(object)entry.Sweep != (Object)null)
		{
			BeginRecords(entry);
		}
		if ((Object)(object)entry.Sweep == (Object)null)
		{
			if (entry.Ticket != null)
			{
				RemovePlaces(entry);
			}
			Dig(entry, null);
			if (!CraterBusy())
			{
				EndWorld(entry);
			}
		}
	}

	private static void BeginRecords(Entry entry)
	{
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		if (entry.RecordsNote != null)
		{
			return;
		}
		try
		{
			if (!RecordSweep.Applies(entry.Plan.Radius, out var why))
			{
				entry.RecordsNote = ((entry.Plan.Radius > 200f) ? ("not worked on (" + why + ")") : "");
				return;
			}
			entry.RecordsBegun = RecordSweep.Start(entry.Plan.Center, entry.Plan.Radius, entry.Plan.Id, out var note);
			entry.RecordsNote = Text($"beyond {LiveRadius(entry.Plan.Radius):0} m by the world's records, ") + (entry.RecordsBegun ? note : ("NOT begun (" + note + ")"));
		}
		catch (Exception ex)
		{
			entry.RecordsBegun = false;
			entry.RecordsNote = "NOT begun (" + ex.GetType().Name + ": " + ex.Message + ")";
			Error("[Blast] the work on the records could not be started: " + ex);
		}
	}

	private static bool RecordsBusy(Entry entry)
	{
		try
		{
			return entry.RecordsBegun && RecordSweep.Running;
		}
		catch (Exception)
		{
			return false;
		}
	}

	private static bool CraterBusy()
	{
		try
		{
			return Crater.Busy;
		}
		catch (Exception)
		{
			return false;
		}
	}

	private static void InnerCleared(Entry entry)
	{
		try
		{
			if (!entry.Closed && !entry.InnerCleared)
			{
				entry.InnerCleared = true;
				ZNet instance = ZNet.instance;
				if ((Object)(object)instance != (Object)null)
				{
					entry.InnerClearedAfter = (float)(RocketFlight.NetSeconds(instance) - entry.Plan.ImpactTime);
				}
				RemovePlaces(entry);
				Dig(entry, null);
			}
		}
		catch (Exception ex)
		{
			Error("[Blast] the crater could not be dug when the middle was cleared: " + ex);
		}
	}

	private static void RemovePlaces(Entry entry)
	{
		//IL_0023: Unknown result type (might be due to invalid IL or missing references)
		if (entry.PlacesRemoved != null || entry.Closed)
		{
			return;
		}
		entry.PlacesRemoved = "none";
		try
		{
			entry.PlacesRemoved = Protection.RemoveDoomed(entry.Plan.Center, entry.Plan.Radius) ?? "none";
		}
		catch (Exception ex)
		{
			entry.PlacesRemoved = "not known (" + ex.GetType().Name + ")";
			Error("[Blast] the places in the fireball could not be removed: " + ex);
		}
	}

	private static void Dig(Entry entry, string why)
	{
		CraterTicket ticket = entry.Ticket;
		if (ticket == null || entry.CraterTried || entry.Closed)
		{
			return;
		}
		entry.CraterTried = true;
		entry.Ticket = null;
		try
		{
			string note;
			bool flag = Crater.Dig(ticket, out note);
			entry.CraterDig = Labelled(flag ? "crater under way" : "crater NOT dug", note) + ((why != null) ? (" " + why) : "");
		}
		catch (Exception ex)
		{
			entry.CraterDig = "crater NOT dug (it failed: " + ex.GetType().Name + ": " + ex.Message + ")";
			Error("[Blast] the crater could not be dug: " + ex);
		}
	}

	private static void WatchWorld(Entry entry, double now)
	{
		if (entry.AreaWaiting)
		{
			WatchArea(entry, now);
			return;
		}
		bool running = BlastSweep.Running;
		if (running)
		{
			entry.SweepSeenRunning = true;
		}
		double num = now - entry.WorldStarted;
		bool flag = (Object)(object)entry.Sweep == (Object)null || (!running && (entry.SweepSeenRunning || num > 1.0));
		string sweepGivenUp = null;
		if (!flag && num > entry.WorldLimit)
		{
			flag = true;
			sweepGivenUp = Text($"it still said it was running {num:F0} s after it began and is no longer waited for");
			Warn(Text($"[Blast] the sweep of blast {entry.Plan.Id:X} still says it is running {num:F0} s after it began; the blast is taken as over."));
		}
		if (!flag)
		{
			return;
		}
		if (!entry.SweepEnded)
		{
			entry.SweepEnded = true;
			if (entry.Sweep != null)
			{
				entry.SweepOverAfter = (float)(now - entry.Plan.ImpactTime);
				entry.SweepGivenUp = sweepGivenUp;
			}
			if (entry.Ticket != null && !entry.CraterTried)
			{
				Dig(entry, "(the sweep ended without clearing the middle first)");
			}
		}
		if (num <= entry.WorldLimit && (CraterBusy() || RecordsBusy(entry)))
		{
			if (entry.CraterWaited < 0f)
			{
				entry.CraterWaited = 0f;
			}
			return;
		}
		if (entry.CraterWaited >= 0f)
		{
			entry.CraterWaited = (float)(now - entry.WorldStarted) - Math.Max(0f, entry.SweepOverAfter - (float)(entry.WorldStarted - entry.Plan.ImpactTime));
		}
		EndWorld(entry);
	}

	private static void EndWorld(Entry entry)
	{
		entry.WorldActive = false;
		entry.AreaWaiting = false;
		entry.Ticket = null;
		entry.Sweep = null;
		if (s_worldEntry == entry)
		{
			s_worldEntry = null;
		}
	}

	private static void WatchPlayer(Entry entry, double now)
	{
		//IL_0061: 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_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_0161: Unknown result type (might be due to invalid IL or missing references)
		BlastPlan plan = entry.Plan;
		float num = (float)(now - plan.ImpactTime);
		float num2 = BlastRules.PushRadius(plan.Radius);
		bool flag = num > ShockFront.Arrival(num2, plan.Radius, plan.ShockSpeed);
		Player localPlayer = Player.m_localPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			if (flag)
			{
				entry.PlayerPending = false;
				entry.PlayerNote = "none in the world on this peer";
			}
			return;
		}
		Vector3 val = ((Component)localPlayer).transform.position - plan.Center;
		float num3 = Mathf.Abs(val.y);
		val.y = 0f;
		float magnitude = ((Vector3)(ref val)).magnitude;
		if (num3 > 1.5f * plan.Radius || ((Character)localPlayer).InInterior() != Character.InInterior(plan.Center))
		{
			entry.PlayerPending = false;
			entry.PlayerNote = Text($"{magnitude:0.0} m away but {num3:0} m above or below ground zero: out of reach");
			return;
		}
		if (magnitude > num2 + 0.5f)
		{
			if (flag)
			{
				entry.PlayerPending = false;
				entry.PlayerNote = Text($"{magnitude:0.0} m away when the wave left the push radius ({num2:0.#} m): out of reach");
			}
			return;
		}
		float num4 = Mathf.Min(magnitude, num2);
		if (num < ShockFront.Arrival(num4, plan.Radius, plan.ShockSpeed))
		{
			return;
		}
		entry.PlayerPending = false;
		try
		{
			entry.PlayerNote = Strike(entry, localPlayer, val, num4, num);
		}
		catch (Exception ex)
		{
			entry.PlayerNote = Text($"{magnitude:0.0} m away: failed ({ex.GetType().Name}: {ex.Message})");
			if (!s_loggedPlayerError)
			{
				s_loggedPlayerError = true;
				Error("[Blast] the blast's effect on the player failed (said once): " + ex);
			}
		}
	}

	private static string Strike(Entry entry, Player player, Vector3 offset, float away, float sinceFlash)
	{
		//IL_0164: Unknown result type (might be due to invalid IL or missing references)
		//IL_0151: Unknown result type (might be due to invalid IL or missing references)
		//IL_0156: Unknown result type (might be due to invalid IL or missing references)
		//IL_015b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0169: Unknown result type (might be due to invalid IL or missing references)
		//IL_0179: Unknown result type (might be due to invalid IL or missing references)
		//IL_017e: Unknown result type (might be due to invalid IL or missing references)
		//IL_026a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0271: Expected O, but got Unknown
		//IL_0211: Unknown result type (might be due to invalid IL or missing references)
		//IL_0219: Unknown result type (might be due to invalid IL or missing references)
		//IL_02af: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_02bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0331: Unknown result type (might be due to invalid IL or missing references)
		//IL_0336: Unknown result type (might be due to invalid IL or missing references)
		//IL_0398: Unknown result type (might be due to invalid IL or missing references)
		//IL_03a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_03a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_03ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_03b3: Unknown result type (might be due to invalid IL or missing references)
		BlastPlan plan = entry.Plan;
		BlastBand blastBand = BlastRules.BandAt(plan.Radius, away);
		string text = Text($"{away:0.0} m away, {BandName(blastBand)}, the wave after {sinceFlash:0.00} s: ");
		if (blastBand == BlastBand.None)
		{
			return text + "out of reach";
		}
		if (((Character)player).IsDead())
		{
			return text + "already dead";
		}
		if (((Character)player).IsTeleporting())
		{
			return text + "teleporting, left alone";
		}
		if (((Character)player).InCutscene())
		{
			return text + "asleep or in a cutscene, left alone";
		}
		if (((Character)player).IsDebugFlying())
		{
			return text + "debug fly is on, left alone";
		}
		PlayerDamageMode playerDamageMode = ((Settings.PlayerDamage != null) ? Settings.PlayerDamage.Value : PlayerDamageMode.Full);
		string text2 = "PlayerDamage=" + playerDamageMode;
		if (!entry.Own && playerDamageMode != PlayerDamageMode.None)
		{
			HurtPlayersMode hurtPlayersMode = ((Settings.HurtOtherPlayers == null) ? HurtPlayersMode.PvpOnly : Settings.HurtOtherPlayers.Value);
			bool flag = hurtPlayersMode switch
			{
				HurtPlayersMode.PvpOnly => ((Character)player).IsPVPEnabled(), 
				HurtPlayersMode.Always => true, 
				_ => false, 
			};
			text2 = text2 + ", somebody else's blast with HurtOtherPlayers=" + hurtPlayersMode.ToString() + (flag ? "" : ": a shove only");
			if (!flag)
			{
				playerDamageMode = PlayerDamageMode.None;
			}
		}
		Vector3 val = ((((Vector3)(ref offset)).sqrMagnitude > 0.01f) ? ((Vector3)(ref offset)).normalized : (-Flat(((Component)player).transform.forward)));
		if (((Vector3)(ref val)).sqrMagnitude < 0.01f)
		{
			val = Vector3.forward;
		}
		float num = Mathf.Max(0f, BlastRules.ThrowSpeed(plan.Radius, away));
		bool flag2 = ((Character)player).IsAttached();
		float num2 = (flag2 ? 0f : (0.8f * ((Character)player).GetMass() * num));
		float num3 = ((blastBand == BlastBand.Push || playerDamageMode == PlayerDamageMode.None) ? 0f : BlastRules.CreatureDamage(plan.Radius, away, Value(Settings.CreatureDamage, 0f)));
		float health = ((Character)player).GetHealth();
		if (!(num3 > 0f))
		{
			if (flag2)
			{
				return text + "attached to something (sitting, steering, riding), not shoved; no damage (" + text2 + ")";
			}
			((Character)player).Stagger(val);
			((Character)player).ApplyPushback(val, num2);
			return text + Text($"staggered and shoved about {0.5f * num2 / Mathf.Max(0.01f, ((Character)player).GetMass()):0.0} m, no damage ({text2})");
		}
		bool flag3 = playerDamageMode == PlayerDamageMode.NeverKill;
		HitData val2 = new HitData();
		val2.m_damage.m_blunt = (flag3 ? num3 : (0.6f * num3));
		val2.m_damage.m_fire = (flag3 ? 0f : (0.39999998f * num3));
		val2.m_point = ((Character)player).GetCenterPoint();
		val2.m_dir = val;
		val2.m_pushForce = num2;
		val2.m_staggerMultiplier = (flag2 ? 1f : 100f);
		val2.m_hitType = (HitType)11;
		val2.m_dodgeable = false;
		val2.m_blockable = false;
		val2.m_ranged = true;
		val2.m_backstabBonus = 1f;
		val2.m_toolTier = 0;
		val2.m_itemWorldLevel = (byte)Mathf.Clamp(Game.m_worldLevel, 0, 255);
		val2.m_attacker = ZDOID.None;
		((Character)player).Damage(val2);
		string text3 = "";
		if (flag3)
		{
			if (!((Character)player).IsDead() && ((Character)player).GetHealth() < 1f)
			{
				((Character)player).SetHealth(1f);
			}
		}
		else if (!flag2 && num > 0f)
		{
			float num4 = Mathf.Min(0.45f * num, 10f);
			((Character)player).ForceJump(val * (0.6f * num) + Vector3.up * num4, false);
			text3 = Text($", thrown at {0.6f * num:0.0} m/s out and {num4:0.0} m/s up");
		}
		return text + Text(FormattableStringFactory.Create("hit for {0:0} before armour ({1}), health {2:0.#} -> {3:0.#}", num3, flag3 ? "blunt only" : "blunt and fire", health, ((Character)player).GetHealth())) + text3 + " (" + text2 + ((((Character)player).InGodMode() || ((Character)player).InGhostMode()) ? ", god or ghost mode keeps 1 health" : "") + ")";
	}

	private static void WriteLine(Entry entry, string closing, bool withPlaces)
	{
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		BlastPlan plan = entry.Plan;
		StringBuilder stringBuilder = new StringBuilder(640);
		stringBuilder.Append("[Blast] at ").Append(FxLocal.Vec(plan.Center)).Append(" R=")
			.Append(FxLocal.Num(plan.Radius))
			.Append(" c=")
			.Append(FxLocal.Num(plan.ShockSpeed, 0))
			.Append(" id=")
			.Append(plan.Id.ToString("X"))
			.Append(entry.Console ? " from the console" : " from a rocket")
			.Append(" (")
			.Append(PartsName(plan.Parts))
			.Append(')');
		if (entry.Late > 0.05f)
		{
			stringBuilder.Append(", begun ").Append(FxLocal.Num(entry.Late, 2)).Append(" s late");
		}
		stringBuilder.Append(": looks ").Append(entry.LooksNote);
		if (entry.Warnings > 0)
		{
			stringBuilder.Append("; warned the player ").Append(entry.Warnings).Append(" time(s) in flight that they stood inside the blast radius");
		}
		stringBuilder.Append("; player ").Append(entry.PlayerPending ? "not reached yet" : entry.PlayerNote);
		stringBuilder.Append("; world ").Append(WorldText(entry, withPlaces));
		stringBuilder.Append("; fps ").Append(FxLocal.Num(entry.FpsBefore, 0)).Append(" before, ")
			.Append((entry.WorstFrame > 0f) ? FxLocal.Num(1f / entry.WorstFrame, 0) : "?")
			.Append(" lowest, ")
			.Append((entry.AfterFrames > 0 && entry.AfterSeconds > 0f) ? FxLocal.Num((float)entry.AfterFrames / entry.AfterSeconds, 0) : "?")
			.Append(" after");
		if (closing != null)
		{
			stringBuilder.Append("; ended early: ").Append(closing);
		}
		Info(s_lastLine = stringBuilder.ToString());
	}

	private static string WorldText(Entry entry, bool withPlaces)
	{
		if (!entry.WorldRan)
		{
			return (entry.WorldNote ?? "not begun") + (entry.Far ? ("; far zones: " + entry.FarNote) : "");
		}
		StringBuilder stringBuilder = new StringBuilder(256);
		stringBuilder.Append("changed by this peer (").Append(entry.OwnWhy).Append("): ")
			.Append(entry.CraterCheck);
		if (entry.CraterDig.Length > 0)
		{
			stringBuilder.Append("; ").Append(entry.CraterDig);
		}
		else if (entry.Ticket != null)
		{
			stringBuilder.Append(entry.Closed ? "; never dug (the blast ended first)" : "; it is dug when the sweep has cleared the middle");
		}
		stringBuilder.Append("; sweep ").Append(entry.SweepNote);
		if (entry.InnerClearedAfter >= 0f)
		{
			stringBuilder.Append(", middle cleared after ").Append(FxLocal.Num(entry.InnerClearedAfter, 2)).Append(" s");
		}
		if (entry.SweepGivenUp != null)
		{
			stringBuilder.Append(", ").Append(entry.SweepGivenUp);
		}
		else if (entry.SweepOverAfter >= 0f)
		{
			stringBuilder.Append(", over after ").Append(FxLocal.Num(entry.SweepOverAfter, 2)).Append(" s");
		}
		else if (entry.WorldActive)
		{
			stringBuilder.Append(", still running");
		}
		if (entry.WarmFrames > 0)
		{
			stringBuilder.Append("; read ahead over ").Append(entry.WarmFrames).Append(" frame(s)")
				.Append(entry.WarmFailed ? " and then given up" : ((entry.CraterWarm && entry.SweepWarm) ? ", all of it" : ", not all of it"));
		}
		if (entry.Far)
		{
			stringBuilder.Append("; far zones: ").Append(entry.FarNote);
			if (entry.AreaReadyAfter > 0f)
			{
				stringBuilder.Append(", area ready ").Append(FxLocal.Num(entry.AreaReadyAfter, 2)).Append(" s after the flash");
			}
		}
		if (withPlaces)
		{
			stringBuilder.Append("; protected places in reach: ").Append(ProtectedPlaces(entry.Plan));
		}
		if (entry.PlacesRemoved != null && entry.PlacesRemoved != "none")
		{
			stringBuilder.Append("; places removed whole: ").Append(entry.PlacesRemoved);
		}
		if (!string.IsNullOrEmpty(entry.RecordsNote))
		{
			stringBuilder.Append("; ").Append(entry.RecordsNote);
		}
		if (entry.WorldNote != null)
		{
			stringBuilder.Append(entry.WorldNote);
		}
		return stringBuilder.ToString();
	}

	private static bool InReach(Vector3 point, BlastPlan plan, float radius)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: 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)
		Vector3 val = point - plan.Center;
		if (Mathf.Abs(val.y) > 1.5f * plan.Radius || Character.InInterior(point) != Character.InInterior(plan.Center))
		{
			return false;
		}
		val.y = 0f;
		return ((Vector3)(ref val)).sqrMagnitude < radius * radius;
	}

	private static Vector3 Flat(Vector3 direction)
	{
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		direction.y = 0f;
		float magnitude = ((Vector3)(ref direction)).magnitude;
		if (!(magnitude > 0.01f))
		{
			return Vector3.zero;
		}
		return direction / magnitude;
	}

	private static float ShockSpeedNow()
	{
		float num = Value(Settings.ShockSpeed, 30f);
		if (!(num >= 1f))
		{
			return 30f;
		}
		return num;
	}

	private static float Value(ConfigEntry<float> entry, float fallback)
	{
		if (entry == null)
		{
			return fallback;
		}
		float value = entry.Value;
		if (!FxLocal.IsFinite(value))
		{
			return fallback;
		}
		return value;
	}

	private static string BandName(BlastBand band)
	{
		return band switch
		{
			BlastBand.Vaporise => "inside the fireball", 
			BlastBand.Damage => "inside the blast radius", 
			BlastBand.Push => "in the push band", 
			_ => "outside the blast", 
		};
	}

	internal static string PartsName(BlastParts parts)
	{
		parts &= BlastParts.All;
		switch (parts)
		{
		case BlastParts.All:
			return "all parts";
		case BlastParts.None:
			return "no part";
		default:
		{
			string text = "";
			if ((parts & BlastParts.Looks) != BlastParts.None)
			{
				text = "looks";
			}
			if ((parts & BlastParts.Sweep) != BlastParts.None)
			{
				text = text + ((text.Length > 0) ? " + " : "") + "sweep";
			}
			if ((parts & BlastParts.Crater) != BlastParts.None)
			{
				text = text + ((text.Length > 0) ? " + " : "") + "crater";
			}
			return text + " only";
		}
		}
	}

	private static string Labelled(string state, string note)
	{
		if (string.IsNullOrEmpty(note))
		{
			return state;
		}
		if (!note.StartsWith(state, StringComparison.OrdinalIgnoreCase))
		{
			return state + " [" + note + "]";
		}
		return note;
	}

	private static void Tell(string text)
	{
		try
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null)
			{
				((Character)localPlayer).Message((MessageType)2, text, 0, (Sprite)null, false);
			}
		}
		catch (Exception ex)
		{
			Warn("[Blast] could not show the message \"" + text + "\": " + ex.Message);
		}
	}

	private static bool IsFinite(double value)
	{
		if (!double.IsNaN(value))
		{
			return !double.IsInfinity(value);
		}
		return false;
	}

	private static string Text(FormattableString text)
	{
		return FormattableString.Invariant(text);
	}

	private static void Info(string line)
	{
		ManualLogSource log = WeaponsOfMassDestructionPlugin.Log;
		if (log != null)
		{
			log.LogInfo((object)line);
		}
	}

	private static void Warn(string line)
	{
		ManualLogSource log = WeaponsOfMassDestructionPlugin.Log;
		if (log != null)
		{
			log.LogWarning((object)line);
		}
	}

	private static void Error(string line)
	{
		ManualLogSource log = WeaponsOfMassDestructionPlugin.Log;
		if (log != null)
		{
			log.LogError((object)line);
		}
	}
}
public sealed class BlastRunner : MonoBehaviour
{
	private void Update()
	{
		Blast.Tick(this);
	}

	private void OnDestroy()
	{
		Blast.RunnerGone(this);
	}
}
internal struct BlastTarget
{
	public ZNetView View;

	public ZDOID Uid;

	public PrefabInfo Info;

	public BlastBand Band;

	public float Distance;

	public float Arrival;

	public bool WouldFall;

	public bool Burns;
}
internal sealed class BlastScanResult
{
	public const int Kinds = 11;

	public const int Bands = 4;

	public const int Reasons = 9;

	public Vector3 Center;

	public float Radius;

	public float Speed;

	public BlastSettings Settings;

	public readonly List<BlastTarget> Targets = new List<BlastTarget>(1024);

	public readonly HashSet<ZDOID> Seen = new HashSet<ZDOID>();

	public readonly List<Ward> Wards = new List<Ward>();

	public readonly List<ProtectedDisc> Sacred = new List<ProtectedDisc>();

	public readonly int[,] Counts = new int[11, 4];

	public readonly int[] Spared = new int[9];

	public readonly Dictionary<string, int> NeverNames = new Dictionary<string, int>();

	public int Scanned;

	public int NoOwner;

	public int BeyondActiveArea;

	public int SparedBuiltBeyond;

	public double Milliseconds;

	public double AheadMs;

	public int AheadCalls;

	public int ReadLate;

	public bool FromTable;

	public int FallenTreeBudget;

	public int TreesThatWouldFall;

	public int TreesToBurn;

	public bool SparesBuilt => Spared[1] + Spared[2] + Spared[3] + Spared[4] + Spared[6] + SparedBuiltBeyond > 0;

	public int SparedTotal
	{
		get
		{
			int num = 0;
			for (int i = 1; i < 9; i++)
			{
				num += Spared[i];
			}
			return num;
		}
	}

	public int CountOf(BlastKind kind)
	{
		return Counts[(uint)kind, 3] + Counts[(uint)kind, 2] + Counts[(uint)kind, 1];
	}

	public int CountIn(BlastBand band)
	{
		int num = 0;
		for (int i = 0; i < 11; i++)
		{
			num += Counts[i, (int)band];
		}
		return num;
	}
}
internal sealed class BlastScanJob
{
	private const int Places = 0;

	private const int Listing = 1;

	private const int Reading = 2;

	private const int Resting = 3;

	private const int LookingAgain = 4;

	private const int ReadingNew = 5;

	private const int Ending = 6;

	private const int Finished = 7;

	private const int Chunk = 48;

	private const float AheadSeconds = 60f;

	private const float LookAgainEvery = 0.5f;

	private const int MostRestarts = 2;

	public readonly BlastScanResult Result;

	private static readonly List<ProtectedDisc> s_sacredNow = new List<ProtectedDisc>();

	private static readonly List<Ward> s_wardsNow = new List<Ward>();

	private readonly ZNetScene m_scene;

	private readonly List<ZNetView> m_views = new List<ZNetView>();

	private readonly Vector3 m_center;

	private readonly float m_radius;

	private readonly float m_reach;

	private readonly float m_reachSqr;

	private readonly float m_vertical;

	private readonly float m_born;

	private readonly int m_fallenTrees;

	private readonly bool m_wardsCount;

	private BlastSettings m_settings;

	private int m_phase;

	private int m_next;

	private int m_wardStamp;

	private int m_locationStamp;

	private int m_listedIn = -1;

	private int m_restarts;

	private bool m_closing;

	private bool m_late;

	private bool m_lookedAtClose;

	private float m_speed;

	private float m_restedAt;

	public bool Done => m_phase == 7;

	public bool Rested => m_phase == 3;

	public bool Stale
	{
		get
		{
			if (!(Time.realtimeSinceStartup - m_born > 60f))
			{
				return Time.realtimeSinceStartup < m_born;
			}
			return true;
		}
	}

	public string Progress
	{
		get
		{
			switch (m_phase)
			{
			case 0:
				return "it had not begun to read";
			case 1:
				return "it had collected the protected places";
			case 2:
			case 5:
				return "it had read " + m_next + " of " + m_views.Count + " objects";
			case 7:
				return "it was made";
			default:
				return "it had read everything there was when it was made ahead";
			}
		}
	}

	private BlastScanJob(ZNetScene scene, Vector3 center, float radius, BlastSettings settings, int fallenTrees)
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		m_scene = scene;
		m_center = center;
		m_radius = radius;
		m_settings = settings;
		m_fallenTrees = fallenTrees;
		m_born = Time.realtimeSinceStartup;
		m_reach = BlastRules.PushRadius(radius) + 8f;
		m_reachSqr = m_reach * m_reach;
		m_vertical = 1.5f * radius;
		m_wardsCount = Protection.WardsCount(settings.Structures);
		Result = new BlastScanResult
		{
			Center = center,
			Radius = radius,
			Settings = settings,
			FallenTreeBudget = fallenTrees
		};
	}

	public static BlastScanJob Begin(Vector3 center, float radius, BlastSettings settings, int fallenTrees, out string problem)
	{
		//IL_002d: 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_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		ZNetScene instance = ZNetScene.instance;
		if ((Object)(object)instance == (Object)null || (Object)(object)ZNet.instance == (Object)null || ZDOMan.instance == null)
		{
			problem = "no world is loaded";
			return null;
		}
		if (!IsNumber(center.x) || !IsNumber(center.y) || !IsNumber(center.z) || !(radius > 0f) || float.IsInfinity(radius))
		{
			problem = "the place or the radius is not a usable number";
			return null;
		}
		if (center.y > 3000f)
		{
			problem = "ground zero is inside a dungeon";
			return null;
		}
		problem = null;
		return new BlastScanJob(instance, center, radius, settings, fallenTrees);
	}

	public bool IsFor(Vector3 center, float radius, ref BlastSettings settings, int fallenTrees)
	{
		//IL_001b: 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_0047: Unknown result type (might be due to invalid IL or missing references)
		if (m_scene == ZNetScene.instance && m_center.x == center.x && m_center.y == center.y && m_center.z == center.z && m_radius == radius && m_fallenTrees == fallenTrees && m_settings.ObjectDamage == settings.ObjectDamage && m_settings.CreatureDamage == settings.CreatureDamage && m_settings.Structures == settings.Structures && m_settings.Drops == settings.Drops && m_settings.SpareTamed == settings.SpareTamed)
		{
			return m_settings.Clear == settings.Clear;
		}
		return false;
	}

	public void Close(float speed)
	{
		m_speed = speed;
		Result.Speed = speed;
		if (m_closing)
		{
			return;
		}
		m_closing = true;
		if (m_phase == 3)
		{
			if (m_listedIn == Time.frameCount)
			{
				m_phase = 6;
				return;
			}
			m_late = true;
			m_lookedAtClose = true;
			m_phase = 4;
		}
	}

	public bool Advance(double budgetMs)
	{
		if (m_phase == 7)
		{
			return true;
		}
		if (m_phase == 3 && !m_closing)
		{
			float realtimeSinceStartup = Time.realtimeSinceStartup;
			if (realtimeSinceStartup >= m_restedAt && realtimeSinceStartup - m_restedAt < 0.5f)
			{
				return true;
			}
			m_phase = 4;
		}
		long timestamp = Stopwatch.GetTimestamp();
		double num;
		do
		{
			Step();
			num = (double)(Stopwatch.GetTimestamp() - timestamp) * 1000.0 / (double)Stopwatch.Frequency;
		}
		while (m_phase != 7 && (m_phase != 3 || m_closing) && num < budgetMs);
		Result.Milliseconds += num;
		if (!m_closing)
		{
			Result.AheadMs += num;
			Result.AheadCalls++;
		}
		if (m_phase != 7)
		{
			if (m_phase == 3)
			{
				return !m_closing;
			}
			return false;
		}
		return true;
	}

	private void Step()
	{
		switch (m_phase)
		{
		case 0:
			CollectPlaces();
			m_phase = 1;
			break;
		case 1:
			BlastScan.ForgetPrefabsOfAnotherWorld(m_scene);
			Result.FromTable = BlastScan.Views(m_views);
			m_listedIn = Time.frameCount;
			m_next = 0;
			m_phase = 2;
			break;
		case 2:
		case 5:
		{
			int num = Math.Min(m_views.Count, m_next + 48);
			while (m_next < num)
			{
				ReadOne(m_views[m_next]);
				m_next++;
			}
			if (m_next >= m_views.Count)
			{
				m_views.Clear();
				m_next = 0;
				if (!m_closing)
				{
					m_phase = 3;
					m_restedAt = Time.realtimeSinceStartup;
				}
				else if (m_listedIn == Time.frameCount || m_lookedAtClose)
				{
					m_phase = 6;
				}
				else
				{
					m_late = true;
					m_lookedAtClose = true;
					m_phase = 4;
				}
			}
			break;
		}
		case 4:
			if ((!m_closing || m_restarts < 2) && !SamePlaces(!m_closing))
			{
				Restart();
				break;
			}
			BlastScan.Views(m_views);
			m_listedIn = Time.frameCount;
			m_next = 0;
			m_phase = 5;
			break;
		case 6:
			End();
			m_phase = 7;
			break;
		case 3:
			break;
		}
	}

	private void CollectPlaces()
	{
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		float num = BlastRules.PushRadius(m_radius);
		Protection.ProtectedDiscs(m_center, num + 8f, Result.Sacred);
		if (m_wardsCount)
		{
			Protection.CollectWards(Result.Wards);
		}
		m_wardStamp = GamePlaces.WardStamp();
		m_locationStamp = GamePlaces.LocationStamp();
	}

	private bool SamePlaces(bool byListsOnly)
	{
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		int num = GamePlaces.WardStamp();
		int num2 = GamePlaces.LocationStamp();
		bool flag = num != 0 && num2 != 0 && m_wardStamp != 0 && m_locationStamp != 0;
		if (flag && (!m_wardsCount || num == m_wardStamp) && num2 == m_locationStamp)
		{
			return true;
		}
		if (!flag && byListsOnly)
		{
			return true;
		}
		List<ProtectedDisc> list = s_sacredNow;
		List<Ward> list2 = s_wardsNow;
		list.Clear();
		list2.Clear();
		try
		{
			Protection.ProtectedDiscs(m_center, m_reach, list);
			if (m_wardsCount)
			{
				Protection.CollectWards(list2);
			}
			if (!SameSacred(list, Result.Sacred) || !SameWardsInReach(list2, Result.Wards))
			{
				return false;
			}
			m_wardStamp = num;
			m_locationStamp = num2;
			return true;
		}
		finally
		{
			list.Clear();
			list2.Clear();
		}
	}

	private static bool SameSacred(List<ProtectedDisc> now, List<ProtectedDisc> then)
	{
		//IL_0021: 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 (now.Count != then.Count)
		{
			return false;
		}
		for (int i = 0; i < now.Count; i++)
		{
			bool flag = false;
			for (int j = 0; j < then.Count; j++)
			{
				if (flag)
				{
					break;
				}
				flag = then[j].Center == now[i].Center && then[j].Radius == now[i].Radius;
			}
			if (!flag)
			{
				return false;
			}
		}
		return true;
	}

	private bool SameWardsInReach(List<Ward> now, List<Ward> then)
	{
		//IL_004f: 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)
		int num = 0;
		int num2 = 0;
		for (int i = 0; i < then.Count; i++)
		{
			if (InReach(then[i]))
			{
				num2++;
			}
		}
		for (int j = 0; j < now.Count; j++)
		{
			if (!InReach(now[j]))
			{
				continue;
			}
			num++;
			bool flag = false;
			for (int k = 0; k < then.Count; k++)
			{
				if (flag)
				{
					break;
				}
				flag = then[k].Position == now[j].Position && then[k].Radius == now[j].Radius;
			}
			if (!flag)
			{
				return false;
			}
		}
		return num == num2;
	}

	private bool InReach(Ward ward)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		return Protection.Flat(ward.Position, m_center) - ward.Radius <= m_reach;
	}

	private void Restart()
	{
		if (m_closing)
		{
			m_restarts++;
		}
		m_lookedAtClose = false;
		BlastScanResult result = Result;
		result.Targets.Clear();
		result.Seen.Clear();
		result.Wards.Clear();
		result.Sacred.Clear();
		Array.Clear(result.Counts, 0, result.Counts.Length);
		Array.Clear(result.Spared, 0, result.Spared.Length);
		result.NeverNames.Clear();
		result.Scanned = 0;
		result.NoOwner = 0;
		result.BeyondActiveArea = 0;
		result.SparedBuiltBeyond = 0;
		result.ReadLate = 0;
		m_views.Clear();
		m_late = false;
		m_phase = 0;
	}

	private void ReadOne(ZNetView view)
	{
		//IL_0027: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_004a: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: 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_005d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: 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_00e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e2: 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_00f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0114: 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_018c: Unknown result type (might be due to invalid IL or missing references)
		//IL_019e: Unknown result type (might be due to invalid IL or missing references)
		//IL_026c: Unknown result type (might be due to invalid IL or missing references)
		//IL_029c: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a1: Unknown result type (might be due to invalid IL or missing references)
		ZDO val = ((view != null) ? view.GetZDO() : null);
		if (val == null || !val.IsValid())
		{
			return;
		}
		BlastScanResult result = Result;
		if (!result.Seen.Add(val.m_uid))
		{
			return;
		}
		result.Scanned++;
		Vector3 center = m_center;
		Vector3 position = val.GetPosition();
		if (position.y > 3000f || Mathf.Abs(position.y - center.y) > m_vertical + 8f || Protection.FlatSqr(position, center) > m_reachSqr)
		{
			return;
		}
		int prefab = val.GetPrefab();
		if (prefab == 0)
		{
			return;
		}
		PrefabInfo prefabInfo = BlastScan.Info(prefab, view);
		if (prefabInfo.Kind == BlastKind.None || !Object.op_Implicit((Object)(object)view) || !view.IsValid())
		{
			return;
		}
		if (m_late)
		{
			result.ReadLate++;
		}
		Vector3 position2 = ((Component)view).transform.position;
		if (position2.y > 3000f || Mathf.Abs(position2.y - center.y) > m_vertical)
		{
			return;
		}
		float radius = m_radius;
		float distance = Protection.Flat(position2, center);
		BlastBand blastBand = BlastRules.BandAt(radius, distance, m_settings.Clear);
		bool flag = BlastScan.Concerns(prefabInfo.Kind, blastBand);
		bool flag2 = blastBand == BlastBand.Push && (prefabInfo.Kind == BlastKind.Piece || (prefabInfo.HasPiece && prefabInfo.Kind != BlastKind.Item));
		if (!flag && !flag2)
		{
			return;
		}
		bool inWard = m_wardsCount && Protection.InsideWard(result.Wards, position2);
		bool inSacred = Protection.InsideDisc(result.Sacred, position2);
		SpareReason spareReason = Protection.Judge(prefabInfo, view, inWard, inSacred, ref m_settings);
		if (!flag)
		{
			if (spareReason != SpareReason.None)
			{
				result.SparedBuiltBeyond++;
			}
			return;
		}
		if (spareReason != SpareReason.None)
		{
			if (spareReason == SpareReason.Never)
			{
				if (prefabInfo.Never == "player")
				{
					return;
				}
				string key = prefabInfo.Never ?? prefabInfo.Name;
				result.NeverNames.TryGetValue(key, out var value);
				result.NeverNames[key] = value + 1;
			}
			result.Spared[(uint)spareReason]++;
			return;
		}
		result.Counts[(uint)prefabInfo.Kind, (int)blastBand]++;
		if (!val.HasOwner())
		{
			result.NoOwner++;
		}
		if (m_scene.OutsideActiveArea(position2))
		{
			result.BeyondActiveArea++;
		}
		result.Targets.Add(new BlastTarget
		{
			View = view,
			Uid = val.m_uid,
			Info = prefabInfo,
			Band = blastBand,
			Distance = distance,
			WouldFall = (prefabInfo.Kind == BlastKind.Tree && blastBand == BlastBand.Damage && Recipes.TreeWouldFall(view, val, radius, distance, m_settings.ObjectDamage))
		});
	}

	private void End()
	{
		BlastScanResult result = Result;
		List<BlastTarget> targets = result.Targets;
		targets.Sort(BlastScan.NearestFirst);
		int num = 0;
		for (int i = 0; i < targets.Count; i++)
		{
			if (targets[i].WouldFall)
			{
				num++;
			}
		}
		int num2 = 0;
		for (int j = 0; j < targets.Count; j++)
		{
			BlastTarget value = targets[j];
			value.Arrival = ((value.Band == BlastBand.Vaporise) ? 0f : ShockFront.Arrival(value.Distance, m_radius, m_speed));
			if (value.WouldFall)
			{
				value.Burns = !BlastRules.TreeFalls(num2++, num, m_fallenTrees);
			}
			targets[j] = value;
		}
		result.TreesThatWouldFall = num;
		result.TreesToBurn = BlastRules.TreesBurnt(num, m_fallenTrees);
	}

	private static bool IsNumber(float value)
	{
		if (!float.IsNaN(value))
		{
			return !float.IsInfinity(value);
		}
		return false;
	}
}
public static class BlastScan
{
	internal const float InteriorHeight = 3000f;

	internal const float ReachMargin = 8f;

	internal const float VerticalFactor = 1.5f;

	private static readonly Dictionary<int, PrefabInfo> s_prefabs = new Dictionary<int, PrefabInfo>();

	private static ZNetScene s_prefabsOf;

	private static FieldInfo s_instancesField;

	private static bool s_bindTried;

	internal static readonly Comparison<BlastTarget> NearestFirst = (BlastTarget a, BlastTarget b) => a.Distance.CompareTo(b.Distance);

	public static string Describe(Vector3 center, float blastRadius)
	{
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (!(blastRadius > 0f) || float.IsInfinity(blastRadius))
			{
				return "Scan: the blast radius is not a usable number.";
			}
			BlastSettings settings = BlastSettings.Read(blastRadius);
			float speed = ((Settings.ShockSpeed != null) ? Settings.ShockSpeed.Value : 30f);
			string problem;
			BlastScanResult blastScanResult = Scan(center, blastRadius, speed, ref settings, out problem);
			if (blastScanResult == null)
			{
				return "Scan: " + problem + ".";
			}
			return Describe(blastScanResult);
		}
		catch (Exception ex)
		{
			WeaponsOfMassDestructionPlugin.Log.LogError((object)("[Blast] the scan failed: " + ex));
			return "Scan failed: " + ex.GetType().Name + ": " + ex.Message + " (the log has the details).";
		}
	}

	internal static BlastScanResult Scan(Vector3 center, float radius, float speed, ref BlastSettings settings, out string problem)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		BlastScanJob blastScanJob = BlastScanJob.Begin(center, radius, settings, FallenTreesNow(), out problem);
		if (blastScanJob == null)
		{
			return null;
		}
		blastScanJob.Close(speed);
		while (!blastScanJob.Advance(double.MaxValue))
		{
		}
		return blastScanJob.Result;
	}

	internal static int FallenTreesNow()
	{
		return BlastRules.FallenTreeBudget((Settings.FallenTrees != null) ? Settings.FallenTrees.Value : 0f);
	}

	internal static bool Snapshot(List<KeyValuePair<ZDO, ZNetView>> into)
	{
		into.Clear();
		ZNetScene instance = ZNetScene.instance;
		if ((Object)(object)instance == (Object)null)
		{
			return false;
		}
		Dictionary<ZDO, ZNetView> dictionary = Table(instance);
		if (dictionary != null)
		{
			foreach (KeyValuePair<ZDO, ZNetView> item in dictionary)
			{
				into.Add(item);
			}
			return true;
		}
		ZNetView[] array = Object.FindObjectsByType<ZNetView>((FindObjectsSortMode)0);
		foreach (ZNetView val in array)
		{
			ZDO val2 = (((Object)(object)val != (Object)null) ? val.GetZDO() : null);
			if (val2 != null)
			{
				into.Add(new KeyValuePair<ZDO, ZNetView>(val2, val));
			}
		}
		return false;
	}

	internal static bool Views(List<ZNetView> into)
	{
		into.Clear();
		ZNetScene instance = ZNetScene.instance;
		if ((Object)(object)instance == (Object)null)
		{
			return false;
		}
		Dictionary<ZDO, ZNetView> dictionary = Table(instance);
		if (dictionary != null)
		{
			foreach (KeyValuePair<ZDO, ZNetView> item in dictionary)
			{
				into.Add(item.Value);
			}
			return true;
		}
		into.AddRange(Object.FindObjectsByType<ZNetView>((FindObjectsSortMode)0));
		return false;
	}

	internal static PrefabInfo Info(int prefab, ZNetView view)
	{
		if (s_prefabs.TryGetValue(prefab, out var value))
		{
			return value;
		}
		if (!Object.op_Implicit((Object)(object)view))
		{
			return PrefabInfo.Unlisted;
		}
		value = Protection.Classify(((Component)view).gameObject);
		s_prefabs[prefab] = value;
		return value;
	}

	internal static void ForgetPrefabsOfAnotherWorld(ZNetScene scene)
	{
		if (scene != s_prefabsOf)
		{
			s_prefabsOf = scene;
			s_prefabs.Clear();
		}
	}

	internal static bool Concerns(BlastKind kind, BlastBand band)
	{
		switch (band)
		{
		case BlastBand.Vaporise:
			return true;
		case BlastBand.Damage:
			return kind != BlastKind.Pickable;
		case BlastBand.Push:
			if (kind != BlastKind.Creature && kind != BlastKind.Item && kind != BlastKind.Log)
			{
				return kind == BlastKind.Ragdoll;
			}
			return true;
		default:
			return false;
		}
	}

	private static Dictionary<ZDO, ZNetView> Table(ZNetScene scene)
	{
		if (!s_bindTried)
		{
			s_bindTried = true;
			string text = null;
			try
			{
				FieldInfo field = typeof(ZNetScene).GetField("m_instances", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field == null)
				{
					text = "the game's ZNetScene.m_instances was not found";
				}
				else if (!typeof(Dictionary<ZDO, ZNetView>).IsAssignableFrom(field.FieldType))
				{
					text = "the game's ZNetScene.m_instances is now a " + field.FieldType.Name;
				}
				else
				{
					s_instancesField = field;
				}
			}
			catch (Exception ex)
			{
				text = "the game's ZNetScene.m_instances could not be reached (" + ex.GetType().Name + ": " + ex.Message + ")";
			}
			if (text != null)
			{
				WeaponsOfMassDestructionPlugin.Log.LogWarning((object)("[Blast] " + text + ": the blast finds its targets by asking for every network view in the scene instead (slower, same result)."));
			}
		}
		if (s_instancesField == null)
		{
			return null;
		}
		try
		{
			return s_instancesField.GetValue(scene) as Dictionary<ZDO, ZNetView>;
		}
		catch (Exception)
		{
			return null;
		}
	}

	private static string Describe(BlastScanResult scan)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_08b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_08bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_097b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0981: Unknown result type (might be due to invalid IL or missing references)
		Recipes recipes = new Recipes(scan.Center, scan.Radius, scan.Settings);
		int[,,] tally = new int[4, 11, 14];
		int num = 0;
		int num2 = 0;
		for (int i = 0; i < scan.Targets.Count; i++)
		{
			BlastTarget target = scan.Targets[i];
			BlastDecision blastDecision;
			try
			{
				blastDecision = recipes.Decide(ref target);
			}
			catch (Exception)
			{
				blastDecision = new BlastDecision
				{
					Act = BlastAct.Nothing,
					Band = target.Band
				};
			}
			if (blastDecision.Act == BlastAct.InUse)
			{
				num++;
				continue;
			}
			if (blastDecision.Burnt)
			{
				num2++;
			}
			tally[(int)((blastDecision.Band == BlastBand.None) ? target.Band : blastDecision.Band), (uint)target.Info.Kind, (uint)blastDecision.Act]++;
		}
		CultureInfo invariantCulture = CultureInfo.InvariantCulture;
		StringBuilder stringBuilder = new StringBuilder(1200);
		float radius = scan.Radius;
		float num3 = BlastRules.VaporiseRadius(radius);
		stringBuilder.AppendFormat(invariantCulture, "Scan at ({0:0}, {1:0}, {2:0}), blast radius {3:0.#} m: fireball to {4:0.#} m{9}, damage to {3:0.#} m, push to {5:0.#} m. Read {6} loaded objects in {7:0.0} ms{8}.", scan.Center.x, scan.Center.y, scan.Center.z, radius, num3, BlastRules.PushRadius(radius), scan.Scanned, scan.Milliseconds, scan.FromTable ? "" : " (the slow way: the game's object table was not reachable)", (scan.Settings.Clear > num3) ? string.Format(invariantCulture, " (everything goes whole out to {0:0.#} m: the ground the crater moves)", scan.Settings.Clear) : "");
		stringBuilder.Append("\nFireball, removed: ");
		stringBuilder.Append(Count(Sum(BlastBand.Vaporise, BlastKind.Tree, new BlastAct[1] { BlastAct.Remove }), "tree")).Append(", ");
		stringBuilder.Append(Count(Sum(BlastBand.Vaporise, BlastKind.Log, new BlastAct[1] { BlastAct.Remove }), "log")).Append(", ");
		stringBuilder.Append(Count(Sum(BlastBand.Vaporise, BlastKind.Small, new BlastAct[2]
		{
			BlastAct.Remove,
			BlastAct.Spill
		}), "small thing")).Append(", ");
		stringBuilder.Append(Count(Sum(BlastBand.Vaporise, BlastKind.Rock, new BlastAct[1] { BlastAct.Remove }), "rock")).Append(", ");
		stringBuilder.Append(Count(Sum(BlastBand.Vaporise, BlastKind.Pickable, new BlastAct[1] { BlastAct.Remove }), "pickable")).Append(", ");
		stringBuilder.Append(Count(Sum(BlastBand.Vaporise, BlastKind.Piece, new BlastAct[1] { BlastAct.RemovePiece }), "building piece")).Append("; ");
		stringBuilder.Append(Count(Sum(BlastBand.Vaporise, BlastKind.Creature, new BlastAct[2]
		{
			BlastAct.Hit,
			BlastAct.Push
		}), "creature")).Append(" hit, ");
		stringBuilder.Append(Count(Loose(BlastBand.Vaporise), "loose thing")).Append(" thrown.");
		stringBuilder.Append("\nDamage band: ");
		stringBuilder.Append(Count(Sum(BlastBand.Damage, BlastKind.Tree, new BlastAct[4]
		{
			BlastAct.Fell,
			BlastAct.Remove,
			BlastAct.Weaken,
			BlastAct.Nothing
		}), "tree"));
		stringBuilder.AppendFormat(invariantCulture, " ({0} fall and leave their logs, {1} burn away, {2} weakened; FallenTrees {3}), ", Sum(BlastBand.Damage, BlastKind.Tree, new BlastAct[1] { BlastAct.Fell }), num2, Sum(BlastBand.Damage, BlastKind.Tree, new BlastAct[1] { BlastAct.Weaken }), scan.FallenTreeBudget);
		stringBuilder.Append(Count(Sum(BlastBand.Damage, BlastKind.Small, new BlastAct[4]
		{
			BlastAct.Break,
			BlastAct.Weaken,
			BlastAct.Nothing,
			BlastAct.LeaveRock
		}), "small thing"));
		stringBuilder.AppendFormat(invariantCulture, " ({0} break, {1} weakened), ", Sum(BlastBand.Damage, BlastKind.Small, new BlastAct[1] { BlastAct.Break }), Sum(BlastBand.Damage, BlastKind.Small, new BlastAct[1] { BlastAct.Weaken }));
		stringBuilder.Append(Count(Sum(BlastBand.Damage, BlastKind.Rock, new BlastAct[3]
		{
			BlastAct.Remove,
			BlastAct.LeaveRock,
			BlastAct.Nothing
		}), "rock"));
		stringBuilder.AppendFormat(invariantCulture, " ({0} burst, {1} left standing), ", Sum(BlastBand.Damage, BlastKind.Rock, new BlastAct[1] { BlastAct.Remove }), Sum(BlastBand.Damage, BlastKind.Rock, new BlastAct[2]
		{
			BlastAct.LeaveRock,
			BlastAct.Nothing
		}));
		stringBuilder.Append(Count(Sum(BlastBand.Damage, BlastKind.Piece, new BlastAct[3]
		{
			BlastAct.BreakPiece,
			BlastAct.WearPiece,
			BlastAct.Nothing
		}), "building piece"));
		stringBuilder.AppendFormat(invariantCulture, " ({0} break, {1} worn), ", Sum(BlastBand.Damage, BlastKind.Piece, new BlastAct[1] { BlastAct.BreakPiece }), Sum(BlastBand.Damage, BlastKind.Piece, new BlastAct[1] { BlastAct.WearPiece }));
		stringBuilder.Append(Count(Sum(BlastBand.Damage, BlastKind.Creature, new BlastAct[2]
		{
			BlastAct.Hit,
			BlastAct.Push
		}), "creature")).Append(" hit, ");
		stringBuilder.Append(Count(Loose(BlastBand.Damage), "loose thing")).Append(" thrown.");
		stringBuilder.Append("\nPush band: ");
		stringBuilder.Append(Count(Sum(BlastBand.Push, BlastKind.Creature, new BlastAct[2]
		{
			BlastAct.Push,
			BlastAct.Hit
		}), "creature")).Append(" shoved, ");
		stringBuilder.Append(Count(Loose(BlastBand.Push), "loose thing")).Append(" thrown.");
		stringBuilder.Append("\nSpared: ");
		stringBuilder.Append(Count(scan.Spared[1], "player-built piece")).Append(" and ");
		stringBuilder.Append(Count(scan.Spared[2], "world-built piece"));
		stringBuilder.Append(" by the StructureDamage setting (").Append(scan.Settings.Structures).Append("), ");
		stringBuilder.Append(Count(scan.Spared[3], "thing")).Append(" inside a ward, ");
		stringBuilder.Append(Count(scan.Spared[4], "thing")).Append(" at a protected place, ");
		stringBuilder.Append(Count(scan.Spared[5], "tamed animal")).Append(", ");
		stringBuilder.Append(Count(scan.Spared[6], "ward, portal or bed", "wards, portals and beds")).Append(", ");
		stringBuilder.Append(Count(num, "piece")).Append(" in use, ");
		stringBuilder.Append(scan.Spared[8].ToString(invariantCulture)).Append(" never touched");
		if (scan.NeverNames.Count > 0)
		{
			stringBuilder.Append(" (");
			bool flag = true;
			foreach (KeyValuePair<string, int> neverName in scan.NeverNames)
			{
				stringBuilder.Append(flag ? "" : ", ").Append(neverName.Value.ToString(invariantCulture)).Append(' ')
					.Append(neverName.Key);
				flag = false;
			}
			stringBuilder.Append(')');
		}
		stringBuilder.Append('.');
		stringBuilder.AppendFormat(invariantCulture, "\nOf the {0} targets {1} have no owner yet (the blast claims those) and {2} lie beyond the active area.", scan.Targets.Count, scan.NoOwner, scan.BeyondActiveArea);
		stringBuilder.Append("\nWards: ");
		if (!Protection.WardsCount(scan.Settings.Structures))
		{
			stringBuilder.Append("do not count (StructureDamage = Everything).");
		}
		else if (scan.Wards.Count == 0)
		{
			stringBuilder.Append("no active ward in the loaded world.");
		}
		else
		{
			float num4 = float.MaxValue;
			foreach (Ward ward in scan.Wards)
			{
				num4 = Mathf.Min(num4, Protection.Flat(ward.Position, scan.Center));
			}
			stringBuilder.AppendFormat(invariantCulture, "{0} active, the nearest {1:0} m from ground zero.", scan.Wards.Count, num4);
		}
		stringBuilder.Append(" Protected places: ");
		if (scan.Sacred.Count == 0)
		{
			stringBuilder.Append("none in reach.");
		}
		else
		{
			for (int j = 0; j < scan.Sacred.Count; j++)
			{
				ProtectedDisc protectedDisc = scan.Sacred[j];
				stringBuilder.AppendFormat(invariantCulture, "{0}{1} ({2:0} m away, {3:0} m wide)", (j == 0) ? "" : ", ", protectedDisc.Name, Protection.Flat(protectedDisc.Center, scan.Center), 2f * protectedDisc.Radius);
			}
			stringBuilder.Append('.');
		}
		stringBuilder.AppendFormat(invariantCulture, "\nSettings: ObjectDamage {0:0}, CreatureDamage {1:0}, StructureDamage {2}, SpareTamed {3}, Drops {4}, FallenTrees {5}. The scan changed nothing.", scan.Settings.ObjectDamage, scan.Settings.CreatureDamage, scan.Settings.Structures, scan.Settings.SpareTamed ? "on" : "off", scan.Settings.Drops, scan.FallenTreeBudget);
		return stringBuilder.ToString();
		int Loose(BlastBand band)
		{
			return Sum(band, BlastKind.Item, new BlastAct[1] { BlastAct.Throw }) + Sum(band, BlastKind.Ragdoll, new BlastAct[1] { BlastAct.Throw }) + Sum(band, BlastKind.Log, new BlastAct[1] { BlastAct.Throw });
		}
		int Sum(BlastBand band, BlastKind kind, params BlastAct[] acts)
		{
			int num5 = 0;
			foreach (BlastAct blastAct in acts)
			{
				num5 += tally[(int)band, (uint)kind, (uint)blastAct];
			}
			return num5;
		}
	}

	private static string Count(int number, string one, string many = null)
	{
		return number.ToString(CultureInfo.InvariantCulture) + " " + ((number == 1) ? one : (many ?? (one + "s")));
	}
}
public sealed class BlastSweep : MonoBehaviour
{
	private struct NewThing
	{
		public ZNetView View;

		public ZDOID Uid;

		public BlastKind Kind;
	}

	private const float NewThingMargin = 10f;

	private const float StoredSlack = 16f;

	private const int LogEffectEvery = 5;

	private const float GiveUpAfter = 180f;

	private const int NewestSlack = 16;

	private const int LogsATree = 4;

	private static readonly bool LogsKeepLandingDamage = true;

	private static BlastSweep s_live;

	private static BlastScanJob s_ahead;

	private static bool s_warmFailureLogged;

	private static readonly List<KeyValuePair<ZDO, ZNetView>> s_snapshot = new List<KeyValuePair<ZDO, ZNetView>>(4096);

	private BlastPlan m_plan;

	private Action m_innerCleared;

	private BlastScanJob m_job;

	private BlastScanResult m_scan;

	private Recipes m_recipes;

	private BlastSettings m_settings;

	private SweepProgress m_progress;

	private readonly Stopwatch m_watch = new Stopwatch();

	private readonly List<NewThing> m_new = new List<NewThing>();

	private double m_impact;

	private float m_pushRadius;

	private float m_seconds;

	private float m_awake;

	private bool m_over;

	private bool m_craterCalled;

	private bool m_scanAhead;

	private string m_leftBehind;

	private int m_vaporised;

	private int m_felled;

	private int m_burnt;

	private int m_broken;

	private int m_weakened;

	private int m_left;

	private int m_hit;

	private int m_pushed;

	private int m_thrown;

	private int m_inUse;

	private int m_untouched;

	private int m_gone;

	private int m_notOurs;

	private int m_errors;

	private int m_newLogs;

	private int m_newBodies;

	private int m_newItems;

	private int m_frames;

	private int m_scanFrames;

	private int m_satOut;

	private int m_framesSatOut;

	private double m_workMs;

	private double m_worstMs;

	private double m_scanMs;

	public static bool Running
	{
		get
		{
			if ((Object)(object)s_live != (Object)null)
			{
				return !s_live.m_over;
			}
			return false;
		}
	}

	public static string LastReport { get; private set; }

	public static bool Warm(BlastPlan plan, float budgetMs)
	{
		//IL_0071: 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)
		try
		{
			if (Running)
			{
				return true;
			}
			if ((Object)(object)ZNet.instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null || ZDOMan.instance == null)
			{
				return true;
			}
			BlastSettings settings = BlastSettings.Read(plan.Radius);
			int fallenTrees = BlastScan.FallenTreesNow();
			BlastScanJob blastScanJob = s_ahead;
			if (blastScanJob == null || !blastScanJob.IsFor(plan.Center, plan.Radius, ref settings, fallenTrees) || blastScanJob.Stale)
			{
				blastScanJob = (s_ahead = BlastScanJob.Begin(plan.Center, plan.Radius, settings, fallenTrees, out var _));
				if (blastScanJob == null)
				{
					return true;
				}
			}
			return blastScanJob.Advance(budgetMs);
		}
		catch (Exception ex)
		{
			s_ahead = null;
			if (!s_warmFailureLogged)
			{
				s_warmFailureLogged = true;
				WeaponsOfMassDestructionPlugin.Log.LogWarning((object)("[Blast] the scan ahead failed and is made at the landing instead (said once): " + ex));
			}
			return true;
		}
	}

	public static BlastSweep Run(BlastPlan plan, Action innerCleared)
	{
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0120: Expected O, but got Unknown
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = null;
		try
		{
			if (Running)
			{
				return Refuse("another sweep is still running");
			}
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)ZNetScene.instance == (Object)null || ZDOMan.instance == null || ZRoutedRpc.instance == null)
			{
				return Refuse("no world is loaded");
			}
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				return Refuse("there is no local player on this machine");
			}
			if ((Object)(object)Game.instance == (Object)null)
			{
				return Refuse("the game is not running");
			}
			BlastSettings settings = BlastSettings.Read(plan.Radius);
			int fallenTrees = BlastScan.FallenTreesNow();
			BlastScanJob blastScanJob = s_ahead;
			s_ahead = null;
			bool flag = blastScanJob != null && blastScanJob.IsFor(plan.Center, plan.Radius, ref settings, fallenTrees) && !blastScanJob.Stale;
			if (!flag)
			{
				blastScanJob = BlastScanJob.Begin(plan.Center, plan.Radius, settings, fallenTrees, out var problem);
				if (blastScanJob == null)
				{
					return Refuse(problem);
				}
			}
			blastScanJob.Close(plan.ShockSpeed);
			val = new GameObject("WMD_BlastSweep");
			BlastSweep blastSweep = val.AddComponent<BlastSweep>();
			blastSweep.m_plan = plan;
			blastSweep.m_innerCleared = innerCleared;
			blastSweep.m_job = blastScanJob;
			blastSweep.m_scanAhead = flag;
			blastSweep.m_settings = settings;
			blastSweep.m_pushRadius = BlastRules.PushRadius(plan.Radius);
			double num = NetSeconds(instance);
			blastSweep.m_impact = ((plan.ImpactTime <= num) ? plan.ImpactTime : num);
			s_live = blastSweep;
			return blastSweep;
		}
		catch (Exception ex)
		{
			WeaponsOfMassDestructionPlugin.Log.LogError((object)("[Blast] sweep not started: " + ex));
			if ((Object)(object)val != (Object)null)
			{
				Object.Destroy((Object)(object)val);
			}
			return null;
		}
	}

	private static BlastSweep Refuse(string why)
	{
		WeaponsOfMassDestructionPlugin.Log.LogWarning((object)("[Blast] sweep not started: " + why + "."));
		return null;
	}

	private void Update()
	{
		if (m_over)
		{
			return;
		}
		Action action = null;
		try
		{
			action = Step();
		}
		catch (Exception ex)
		{
			WeaponsOfMassDestructionPlugin.Log.LogError((object)("[Blast] the sweep failed and was stopped: " + ex));
			Finish("an error in its update");
			return;
		}
		if (action == null)
		{
			return;
		}
		m_craterCalled = true;
		try
		{
			action();
		}
		catch (Exception ex2)
		{
			WeaponsOfMassDestructionPlugin.Log.LogError((object)("[Blast] the call that follows the emptied fireball (the crater) failed: " + ex2));
		}
	}

	private void OnDestroy()
	{
		if (!m_over)
		{
			m_over