Decompiled source of Jafna v1.2.1

plugins/Jafna/Jafna.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using Ezomic.Core;
using Ezomic.Shared;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("Thijssen Software")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) 2026 Robbin Thijssen")]
[assembly: AssemblyDescription("Levelling with the hoe continues the flat ground it touches, and your Crafting decides how far it reaches.")]
[assembly: AssemblyFileVersion("1.2.1.0")]
[assembly: AssemblyInformationalVersion("1.2.1+d220c767f85da4014405597273c409f893542dff")]
[assembly: AssemblyProduct("Jafna")]
[assembly: AssemblyTitle("Jafna")]
[assembly: AssemblyVersion("1.2.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace Ezomic.Shared
{
	internal static class CraftingReach
	{
		internal static float Radius(float level, float min, float max, float fullLevel, float curve)
		{
			float num = Mathf.Max(1f, fullLevel);
			float num2 = Mathf.Max(0.01f, curve);
			float num3 = Mathf.Pow(Mathf.Clamp01(level / num), num2);
			return Mathf.Lerp(min, max, num3);
		}

		internal static float Level(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return 0f;
			}
			return ((Character)player).GetSkillFactor((SkillType)107) * 100f;
		}
	}
}
namespace Jafna
{
	internal static class DevConsole
	{
		[HarmonyPatch(typeof(Terminal), "InitTerminal")]
		internal static class Hook
		{
			[CompilerGenerated]
			private static class <>O
			{
				public static ConsoleEvent <0>__OnCommand;
			}

			private static void Postfix()
			{
				//IL_003d: Unknown result type (might be due to invalid IL or missing references)
				//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: Expected O, but got Unknown
				if (!_registered)
				{
					_registered = true;
					object obj = <>O.<0>__OnCommand;
					if (obj == null)
					{
						ConsoleEvent val = OnCommand;
						<>O.<0>__OnCommand = val;
						obj = (object)val;
					}
					new ConsoleCommand("jafna", "[stone on|off] - your own switch for paying stone to raise low ground, the way its key flips it. With no word it says what the switch is", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				}
			}
		}

		private static bool _registered;

		private static void OnCommand(ConsoleEventArgs args)
		{
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			Terminal context = args.Context;
			if ((Object)(object)context == (Object)null)
			{
				return;
			}
			string text = ((args.Length > 1) ? args[1].ToLowerInvariant() : "");
			if (text.Length > 0 && text != "stone")
			{
				context.AddString("jafna: unknown '" + args[1] + "'. Try jafna stone on|off.");
				return;
			}
			string text2 = ((args.Length > 2) ? args[2].ToLowerInvariant() : "");
			if (text2 == "on")
			{
				StoneSwitch.Set(on: true);
			}
			else if (text2 == "off")
			{
				StoneSwitch.Set(on: false);
			}
			else if (text2.Length > 0)
			{
				context.AddString("jafna stone: on or off, not '" + args[2] + "'");
				return;
			}
			context.AddString("jafna stone=" + (StoneSwitch.On ? "on" : "off") + " raise=" + (JafnaConfig.AutoRaise.Value ? "on" : "off") + "   (the key is " + ((object)JafnaConfig.StoneFillKey.Value/*cast due to .constrained prefix*/).ToString() + ", pressed with a levelling tool out)");
		}
	}
	internal static class Fill
	{
		internal sealed class Price
		{
			internal PieceTable Table;

			internal Piece Piece;

			internal float Scale;

			internal float Volume;

			internal string[] Items;

			internal float[] PerCubicMetre;

			internal GlobalKeys FreeKey;

			internal string Station;

			internal bool IsFree()
			{
				//IL_001c: Unknown result type (might be due to invalid IL or missing references)
				if (Items.Length != 0)
				{
					if ((Object)(object)ZoneSystem.instance != (Object)null)
					{
						return ZoneSystem.instance.GetGlobalKey(FreeKey);
					}
					return false;
				}
				return true;
			}
		}

		private struct Lift
		{
			public int Index;

			public float Metres;

			public float Smooth;

			public float Distance;

			public float Volume;
		}

		internal enum Terms
		{
			None,
			Free,
			Paid,
			Covered,
			Short,
			ShortCovered,
			Unaffordable,
			NoStation
		}

		internal const string BillRpc = "Jafna_FillBill";

		private const float MinVolume = 0.01f;

		private const float MinLift = 0.03f;

		private const float BillWindow = 10f;

		private const float Slack = 0.0001f;

		internal const float Everywhere = float.PositiveInfinity;

		private const float SameRing = 0.001f;

		private static FieldRef<TerrainComp, bool[]> _modifiedHeight;

		private static FieldRef<TerrainComp, float[]> _levelDelta;

		private static FieldRef<TerrainComp, float[]> _smoothDelta;

		private static bool _bound;

		private static bool _bindFailed;

		private static Price _price;

		private static PieceTable _noRaiseOn;

		private static readonly Dictionary<string, float> Credit = new Dictionary<string, float>();

		private static long _ledgerFor;

		private static ZNet _ledgerIn;

		private static readonly List<Lift> Lifts = new List<Lift>();

		private static readonly List<Lift> Planned = new List<Lift>();

		private static readonly List<Heightmap> Maps = new List<Heightmap>();

		private static readonly Comparison<Lift> Nearer = (Lift a, Lift b) => a.Distance.CompareTo(b.Distance);

		private static TerrainOp _plannedOp;

		private static float _plannedReach = -1f;

		private static Price _billedAt;

		private static float _askedAt = float.NegativeInfinity;

		private static int _askedFrame = -1;

		private static int[] _reserved = new int[0];

		private static int _frame = -1;

		private static readonly HashSet<Heightmap> Shaped = new HashSet<Heightmap>();

		private static readonly HashSet<Heightmap> Filled = new HashSet<Heightmap>();

		private static readonly List<Lift> Quoted = new List<Lift>();

		internal static bool Allowed
		{
			get
			{
				if (JafnaConfig.AutoRaise.Value)
				{
					return JafnaConfig.StoneFill.Value;
				}
				return false;
			}
		}

		private static bool Bind()
		{
			if (_bound)
			{
				return !_bindFailed;
			}
			_bound = true;
			try
			{
				_modifiedHeight = AccessTools.FieldRefAccess<TerrainComp, bool[]>("m_modifiedHeight");
				_levelDelta = AccessTools.FieldRefAccess<TerrainComp, float[]>("m_levelDelta");
				_smoothDelta = AccessTools.FieldRefAccess<TerrainComp, float[]>("m_smoothDelta");
			}
			catch (Exception ex)
			{
				_bindFailed = true;
				JafnaPlugin.Log.LogWarning((object)("Could not reach TerrainComp's height arrays, so raising ground with stone is off for this session and flattening keeps vanilla's one metre ceiling. " + ex.Message));
			}
			return !_bindFailed;
		}

		internal static bool Applies(Settings settings)
		{
			if (settings != null && settings.m_smooth && !settings.m_level)
			{
				return !settings.m_raise;
			}
			return false;
		}

		internal static Price Resolve(Player player, float scale)
		{
			//IL_0223: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				return null;
			}
			PieceTable buildTool = player.GetBuildTool();
			if ((Object)(object)buildTool == (Object)null)
			{
				return null;
			}
			if (_price != null && (Object)(object)_price.Table == (Object)(object)buildTool && (Object)(object)_price.Piece != (Object)null && _price.Scale == scale)
			{
				return _price;
			}
			if ((Object)(object)_noRaiseOn == (Object)(object)buildTool)
			{
				return null;
			}
			TerrainOp val2 = default(TerrainOp);
			Piece val3 = default(Piece);
			for (int i = 0; i < buildTool.m_pieces.Count; i++)
			{
				GameObject val = buildTool.m_pieces[i];
				if ((Object)(object)val == (Object)null || !val.TryGetComponent<TerrainOp>(ref val2) || val2.m_settings == null)
				{
					continue;
				}
				Settings settings = val2.m_settings;
				if (!settings.m_raise || settings.m_raiseDelta <= 0f || settings.m_raiseRadius <= 0f || !val.TryGetComponent<Piece>(ref val3))
				{
					continue;
				}
				float num = RaiseVolume(settings, scale);
				if (num <= 0.01f)
				{
					continue;
				}
				List<string> list = new List<string>();
				List<float> list2 = new List<float>();
				string text = "";
				Requirement[] resources = val3.m_resources;
				foreach (Requirement val4 in resources)
				{
					if (val4 != null && !((Object)(object)val4.m_resItem == (Object)null) && !val4.m_upgraderResource)
					{
						int amount = val4.GetAmount(0);
						if (amount > 0)
						{
							list.Add(val4.m_resItem.m_itemData.m_shared.m_name);
							list2.Add((float)amount / num);
							text = text + ((text.Length > 0) ? ", " : "") + ((Object)val4.m_resItem).name + " x" + amount;
						}
					}
				}
				_price = new Price
				{
					Table = buildTool,
					Piece = val3,
					Scale = scale,
					Volume = num,
					Items = list.ToArray(),
					PerCubicMetre = list2.ToArray(),
					FreeKey = val3.FreeBuildKey(),
					Station = (((Object)(object)val3.m_craftingStation != (Object)null) ? val3.m_craftingStation.m_name : null)
				};
				string text2 = "";
				for (int k = 0; k < _price.Items.Length; k++)
				{
					text2 = text2 + ((text2.Length > 0) ? ", " : "") + _price.PerCubicMetre[k].ToString("0.000") + " " + _price.Items[k];
				}
				JafnaPlugin.Log.LogInfo((object)("Raising ground is priced off '" + ((Object)val).name + "' on " + ((Object)buildTool).name + ": raise radius " + settings.m_raiseRadius.ToString("0.00") + ", delta " + settings.m_raiseDelta.ToString("0.00") + ", power " + settings.m_raisePower.ToString("0.00") + ", square " + settings.m_square + ", so one swing adds " + num.ToString("0.00") + " cubic metres to flat ground for " + ((text.Length > 0) ? text : "nothing") + ". That is " + ((text2.Length > 0) ? text2 : "free") + " per cubic metre, " + (string.IsNullOrEmpty(_price.Station) ? "with no station to stand near." : ("within build range of a " + _price.Station + "."))));
				return _price;
			}
			_noRaiseOn = buildTool;
			JafnaPlugin.Log.LogInfo((object)("'" + ((Object)buildTool).name + "' has no Raise ground entry to take a price from, so flattening with it keeps vanilla's one metre ceiling."));
			return null;
		}

		internal static float RaiseVolume(Settings s, float scale)
		{
			if (s == null || scale <= 0f)
			{
				return 0f;
			}
			float num = s.m_raiseRadius / scale;
			if (num <= 0f)
			{
				return 0f;
			}
			int num2 = Mathf.CeilToInt(num);
			float num3 = 0f;
			for (int i = -num2; i <= num2; i++)
			{
				for (int j = -num2; j <= num2; j++)
				{
					float num4 = 1f;
					if (!s.m_square)
					{
						float num5 = Mathf.Sqrt((float)(j * j + i * i));
						if (num5 > num)
						{
							continue;
						}
						if (s.m_raisePower > 0f)
						{
							num4 = 1f - num5 / num;
							if (s.m_raisePower != 1f)
							{
								num4 = Mathf.Pow(num4, s.m_raisePower);
							}
						}
					}
					num3 += s.m_raiseDelta * num4;
				}
			}
			return num3 * scale * scale;
		}

		internal static float ScaleAt(Vector3 point)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			Heightmap val = Heightmap.FindHeightmap(point);
			if (!((Object)(object)val == (Object)null))
			{
				return val.m_scale;
			}
			return 1f;
		}

		private static bool NearStation(Player player, Price price)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(price.Station))
			{
				return true;
			}
			if (player.NoCostCheat())
			{
				return true;
			}
			if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey((GlobalKeys)27))
			{
				return true;
			}
			return (Object)(object)CraftingStation.HaveBuildStationInRange(price.Station, ((Component)player).transform.position) != (Object)null;
		}

		private static string StationPhrase(Price price)
		{
			string text = ((Localization.instance != null) ? Localization.instance.Localize(price.Station) : price.Station);
			if (string.IsNullOrEmpty(text))
			{
				return "a crafting station";
			}
			return (("AEIOUaeiou".IndexOf(text[0]) >= 0) ? "an " : "a ") + text;
		}

		private static void Ledger(Player player)
		{
			long playerID = player.GetPlayerID();
			ZNet instance = ZNet.instance;
			if (playerID != _ledgerFor || !((Object)(object)instance == (Object)(object)_ledgerIn))
			{
				_ledgerFor = playerID;
				_ledgerIn = instance;
				Credit.Clear();
			}
		}

		private static float CreditOf(string item)
		{
			if (!Credit.TryGetValue(item, out var value))
			{
				return 0f;
			}
			return value;
		}

		private static int OwnCost(Piece own, string item)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)own == (Object)null || own.m_resources == null)
			{
				return 0;
			}
			if ((Object)(object)ZoneSystem.instance != (Object)null && ZoneSystem.instance.GetGlobalKey(own.FreeBuildKey()))
			{
				return 0;
			}
			int num = 0;
			Requirement[] resources = own.m_resources;
			foreach (Requirement val in resources)
			{
				if (val != null && !((Object)(object)val.m_resItem == (Object)null) && !val.m_upgraderResource && !(val.m_resItem.m_itemData.m_shared.m_name != item))
				{
					int amount = val.GetAmount(0);
					if (amount > 0)
					{
						num += amount;
					}
				}
			}
			return num;
		}

		private static float Affordable(Player player, Price price, Piece own, out string limiting)
		{
			limiting = null;
			if (price.IsFree() || player.NoCostCheat())
			{
				return float.PositiveInfinity;
			}
			Ledger(player);
			Inventory inventory = ((Humanoid)player).GetInventory();
			float num = float.PositiveInfinity;
			for (int i = 0; i < price.Items.Length; i++)
			{
				float num2 = CreditOf(price.Items[i]) + (float)((inventory != null) ? inventory.CountItems(price.Items[i], -1, true) : 0) - (float)OwnCost(own, price.Items[i]);
				float num3 = Mathf.Max(0f, num2) / price.PerCubicMetre[i];
				if (num3 < num)
				{
					num = num3;
					limiting = price.Items[i];
				}
			}
			return num;
		}

		private static float Measure(Heightmap hmap, float[] level, float[] smooth, Vector3 centre, float radius, float power, List<Lift> record, ref int beyond)
		{
			//IL_0033: 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_0049: 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_00aa: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)hmap == (Object)null || radius <= 0f)
			{
				return 0f;
			}
			float scale = hmap.m_scale;
			float num = radius / scale;
			if (num <= 0f)
			{
				return 0f;
			}
			int num2 = default(int);
			int num3 = default(int);
			hmap.WorldToVertex(centre, ref num2, ref num3);
			float num4 = centre.y - ((Component)hmap).transform.position.y;
			int num5 = Mathf.CeilToInt(num);
			int width = hmap.m_width;
			int num6 = width + 1;
			Vector2 val = default(Vector2);
			((Vector2)(ref val))..ctor((float)num2, (float)num3);
			float num7 = 1f;
			float num8 = 8f;
			float num9 = 0f;
			for (int i = num3 - num5; i <= num3 + num5; i++)
			{
				for (int j = num2 - num5; j <= num2 + num5; j++)
				{
					float num10 = Vector2.Distance(val, new Vector2((float)j, (float)i));
					if (num10 > num || j < 0 || i < 0 || j >= num6 || i >= num6)
					{
						continue;
					}
					float height = hmap.GetHeight(j, i);
					if (height >= num4)
					{
						continue;
					}
					int num11 = i * num6 + j;
					float num12 = ((smooth != null && num11 < smooth.Length) ? smooth[num11] : 0f);
					float num13 = ((level != null && num11 < level.Length) ? level[num11] : 0f);
					float num14 = height - Mathf.Clamp(num13 + num12, 0f - num8, num8);
					float num15 = Mathf.Min(num4, num14 + num8);
					float num16 = Mathf.Max(0f, num7 - num12);
					float num17 = num12 + Mathf.Clamp(num15 - height, 0f, num16);
					float num18 = num15 - num14 - num17 - num13;
					if (num4 - num14 > num8 + 0.03f)
					{
						if (num18 >= 0.03f)
						{
							beyond++;
						}
						continue;
					}
					if (num18 < 0.03f)
					{
						num18 = 0f;
					}
					float num19 = num10 / num;
					num19 = ((power != 3f) ? Mathf.Pow(num19, power) : (num19 * num19 * num19));
					float num20 = Mathf.Lerp(height, num4, 1f - num19) - height;
					float num21 = ((j < width && i < width) ? (num18 * scale * scale) : 0f);
					record?.Add(new Lift
					{
						Index = num11,
						Metres = num18,
						Smooth = num17 - num20,
						Distance = num10 * scale,
						Volume = num21
					});
					num9 += num21;
				}
			}
			return num9;
		}

		private static float Estimate(Settings settings, Vector3 point, float radius, bool fresh, List<Lift> record, out int beyond)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: 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_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: 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_010c: Unknown result type (might be due to invalid IL or missing references)
			beyond = 0;
			if (settings == null || !Bind())
			{
				return 0f;
			}
			Vector3 val = point + Vector3.up * settings.m_levelOffset;
			bool square = Reach.IsSquare(settings);
			Maps.Clear();
			Heightmap.FindHeightmap(point, radius, Maps);
			float num = 0f;
			for (int i = 0; i < Maps.Count; i++)
			{
				Heightmap val2 = Maps[i];
				if (!((Object)(object)val2 == (Object)null))
				{
					if (fresh)
					{
						Freshen(val2, filling: true);
					}
					TerrainComp val3 = TerrainComp.FindTerrainCompiler(((Component)val2).transform.position);
					Flat.Source source;
					float num2 = (Held.Active ? Held.Height : ((!((Object)(object)val3 != (Object)null)) ? val.y : Flat.Target(val3, val, radius, square, out source)));
					float[] level = (((Object)(object)val3 != (Object)null) ? _levelDelta.Invoke(val3) : null);
					float[] smooth = (((Object)(object)val3 != (Object)null) ? _smoothDelta.Invoke(val3) : null);
					num += Measure(val2, level, smooth, new Vector3(val.x, num2, val.z), radius, settings.m_smoothPower, record, ref beyond);
				}
			}
			return num;
		}

		internal static float Plan(TerrainOp op, Player player, float radius)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)op == (Object)null)
			{
				return -1f;
			}
			if ((Object)(object)op == (Object)(object)_plannedOp)
			{
				return _plannedReach;
			}
			_plannedOp = op;
			_plannedReach = -1f;
			if (!Allowed || (Object)(object)player == (Object)null)
			{
				return -1f;
			}
			Settings settings = op.m_settings;
			if (!Applies(settings))
			{
				return -1f;
			}
			Vector3 position = ((Component)op).transform.position;
			if (Character.InInterior(position))
			{
				return -1f;
			}
			Price price = Resolve(player, ScaleAt(position));
			if (price == null)
			{
				return -1f;
			}
			Planned.Clear();
			int beyond;
			float num = Estimate(settings, position, radius, fresh: true, Planned, out beyond);
			if (num <= 0.01f)
			{
				if (beyond > 0)
				{
					((Character)player).Message((MessageType)2, "Too far below to raise", 0, (Sprite)null, false);
				}
				return -1f;
			}
			if (!NearStation(player, price))
			{
				((Character)player).Message((MessageType)2, "Raising needs " + StationPhrase(price) + " nearby", 0, (Sprite)null, false);
				if (JafnaConfig.Verbose.Value)
				{
					JafnaPlugin.Log.LogInfo((object)("Fill wanted " + num.ToString("0.00") + " cubic metres, but no " + price.Station + " is in build range of where you stand, so the swing is the hoe's alone."));
				}
				return -1f;
			}
			Piece selectedPiece = player.GetSelectedPiece();
			string limiting;
			float num2 = Affordable(player, price, selectedPiece, out limiting);
			float num3 = ((num2 >= num) ? float.PositiveInfinity : Within(Planned, num2));
			if (!float.IsPositiveInfinity(num3))
			{
				((Character)player).Message((MessageType)2, "Not enough " + (limiting ?? "$item_stone") + " to raise all of it", 0, (Sprite)null, false);
			}
			else if (beyond > 0)
			{
				((Character)player).Message((MessageType)2, "Too far below to raise all of it", 0, (Sprite)null, false);
			}
			if (JafnaConfig.Verbose.Value)
			{
				JafnaPlugin.Log.LogInfo((object)("Fill wanted " + num.ToString("0.00") + " cubic metres, the pack covers " + (float.IsPositiveInfinity(num2) ? "all of it" : num2.ToString("0.00")) + ", so it raises " + Describe(num3) + ((beyond > 0) ? (", and leaves " + beyond + " points past the eight metre limit alone") : "") + "."));
			}
			if (num3 <= 0f)
			{
				return -1f;
			}
			if (_reserved.Length != price.Items.Length)
			{
				_reserved = new int[price.Items.Length];
			}
			for (int i = 0; i < price.Items.Length; i++)
			{
				_reserved[i] = OwnCost(selectedPiece, price.Items[i]);
			}
			_billedAt = price;
			_askedAt = Time.time;
			_askedFrame = Time.frameCount;
			_plannedReach = num3;
			return num3;
		}

		private static float Within(List<Lift> lifts, float affordable)
		{
			lifts.Sort(Nearer);
			float num = 0f;
			float num2 = -1f;
			int num3 = 0;
			while (num3 < lifts.Count)
			{
				float distance = lifts[num3].Distance;
				float num4 = 0f;
				int i;
				for (i = num3; i < lifts.Count && lifts[i].Distance - distance < 0.001f; i++)
				{
					num4 += lifts[i].Volume;
				}
				if (num + num4 > affordable)
				{
					if (!(num2 < 0f))
					{
						return (num2 + distance) * 0.5f;
					}
					return 0f;
				}
				num += num4;
				num2 = distance;
				num3 = i;
			}
			return float.PositiveInfinity;
		}

		private static string Describe(float reach)
		{
			if (float.IsPositiveInfinity(reach))
			{
				return "the whole swing";
			}
			if (reach <= 0f)
			{
				return "nothing";
			}
			return "every low point within " + reach.ToString("0.00") + "m of the middle";
		}

		internal static void Listen(TerrainComp comp)
		{
			ZNetView val = Reach.View(comp);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			try
			{
				val.Register<float>("Jafna_FillBill", (Action<long, float>)OnBill);
			}
			catch (ArgumentException)
			{
			}
		}

		private static void OnBill(long sender, float used)
		{
			if (used <= 0f)
			{
				return;
			}
			if (Time.time - _askedAt > 10f)
			{
				if (JafnaConfig.Verbose.Value)
				{
					JafnaPlugin.Log.LogInfo((object)("Ignored a bill for " + used.ToString("0.00") + " cubic metres that no swing of ours asked for."));
				}
			}
			else
			{
				Pay(used);
			}
		}

		private static void Pay(float used)
		{
			Player localPlayer = Player.m_localPlayer;
			Price billedAt = _billedAt;
			if ((Object)(object)localPlayer == (Object)null || billedAt == null || billedAt.IsFree())
			{
				return;
			}
			Ledger(localPlayer);
			Inventory inventory = ((Humanoid)localPlayer).GetInventory();
			bool flag = localPlayer.NoCostCheat();
			bool flag2 = Time.frameCount == _askedFrame && _reserved.Length == billedAt.Items.Length;
			for (int i = 0; i < billedAt.Items.Length; i++)
			{
				string text = billedAt.Items[i];
				float num = CreditOf(text) - used * billedAt.PerCubicMetre[i];
				int num2 = 0;
				if (num < 0f && inventory != null)
				{
					int num3 = Mathf.CeilToInt(0f - num - 0.0001f);
					int num4 = inventory.CountItems(text, -1, true) - (flag2 ? _reserved[i] : 0);
					num2 = Mathf.Min(num3, Mathf.Max(0, num4));
					if (num2 > 0)
					{
						inventory.RemoveItem(text, num2, -1, true);
						num += (float)num2;
					}
				}
				if (num < 0f && (flag || num > -0.0001f))
				{
					num = 0f;
				}
				Credit[text] = num;
				if (JafnaConfig.Verbose.Value)
				{
					JafnaPlugin.Log.LogInfo((object)("Paid " + num2 + " " + text + " for " + used.ToString("0.00") + " cubic metres of fill, " + ((num >= 0f) ? ("carrying " + num.ToString("0.00") + " toward the next") : ("still owing " + (0f - num).ToString("0.00"))) + "."));
				}
			}
		}

		internal static bool Wants(Settings modifier, float reach)
		{
			if (reach > 0f && JafnaConfig.AutoRaise.Value)
			{
				return Applies(modifier);
			}
			return false;
		}

		private static void Today()
		{
			if (_frame != Time.frameCount)
			{
				_frame = Time.frameCount;
				Shaped.Clear();
				Filled.Clear();
			}
		}

		internal static void Touched(TerrainComp comp, Settings modifier)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)comp == (Object)null) && modifier != null && (modifier.m_level || modifier.m_raise || modifier.m_smooth) && JafnaConfig.AutoRaise.Value)
			{
				Heightmap val = Heightmap.FindHeightmap(((Component)comp).transform.position);
				if (!((Object)(object)val == (Object)null))
				{
					Today();
					Shaped.Add(val);
				}
			}
		}

		internal static void Freshen(TerrainComp comp, bool filling)
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			Today();
			if ((Filled.Count != 0 || (filling && Shaped.Count != 0)) && !((Object)(object)comp == (Object)null))
			{
				Freshen(Heightmap.FindHeightmap(((Component)comp).transform.position), filling);
			}
		}

		private static void Freshen(Heightmap hmap, bool filling)
		{
			if ((Object)(object)hmap == (Object)null)
			{
				return;
			}
			Today();
			if (Filled.Contains(hmap) || (filling && Shaped.Contains(hmap)))
			{
				Shaped.Remove(hmap);
				Filled.Remove(hmap);
				if (hmap.m_doLateUpdate != 0)
				{
					hmap.Regenerate();
				}
			}
		}

		internal static void Apply(TerrainComp comp, Vector3 centre, Settings modifier, float reach, long sender)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			if (!Wants(modifier, reach) || (Object)(object)comp == (Object)null || !Bind() || Character.InInterior(centre))
			{
				return;
			}
			Heightmap val = Heightmap.FindHeightmap(((Component)comp).transform.position);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			bool[] array = _modifiedHeight.Invoke(comp);
			float[] array2 = _levelDelta.Invoke(comp);
			float[] array3 = _smoothDelta.Invoke(comp);
			if (array == null || array2 == null || array3 == null)
			{
				return;
			}
			Freshen(val, filling: true);
			Lifts.Clear();
			int beyond = 0;
			float num = Measure(val, array2, array3, centre, modifier.m_smoothRadius, modifier.m_smoothPower, Lifts, ref beyond);
			if (Lifts.Count == 0)
			{
				return;
			}
			float num2 = 8f;
			float num3 = 0f;
			int num4 = 0;
			for (int i = 0; i < Lifts.Count; i++)
			{
				if (!(Lifts[i].Distance >= reach))
				{
					int index = Lifts[i].Index;
					array2[index] = Mathf.Clamp(array2[index] + Lifts[i].Metres, 0f - num2, num2);
					array3[index] = Lifts[i].Smooth;
					array[index] = true;
					num3 += Lifts[i].Volume;
					num4++;
				}
			}
			if (num4 == 0)
			{
				return;
			}
			Today();
			Filled.Add(val);
			if (JafnaConfig.Verbose.Value)
			{
				JafnaPlugin.Log.LogInfo((object)("Filled " + num4 + " of " + Lifts.Count + " low points to " + centre.y.ToString("0.00") + "m, paying for " + num3.ToString("0.00") + " of " + num.ToString("0.00") + " cubic metres, asked for " + Describe(reach) + ", billing peer " + sender + "."));
			}
			if (!(num3 <= 0f))
			{
				ZNetView val2 = Reach.View(comp);
				if (!((Object)(object)val2 == (Object)null) && val2.IsValid())
				{
					val2.InvokeRPC(sender, "Jafna_FillBill", new object[1] { num3 });
				}
			}
		}

		internal static Terms Quote(Player player, Settings settings, Vector3 point, float radius, out string cost, out string all, out string carried, out string own, out bool far, out string station, out string items)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: 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)
			items = null;
			cost = null;
			all = null;
			carried = null;
			own = null;
			far = false;
			station = null;
			if (!Allowed || (Object)(object)player == (Object)null || !Applies(settings))
			{
				return Terms.None;
			}
			if (Character.InInterior(point))
			{
				return Terms.None;
			}
			Price price = Resolve(player, ScaleAt(point));
			if (price == null)
			{
				return Terms.None;
			}
			Quoted.Clear();
			int beyond;
			float num = Estimate(settings, point, radius, fresh: false, Quoted, out beyond);
			far = beyond > 0;
			if (num <= 0.01f)
			{
				return Terms.None;
			}
			if (!NearStation(player, price))
			{
				station = StationPhrase(price);
				return Terms.NoStation;
			}
			if (price.IsFree())
			{
				return Terms.Free;
			}
			Ledger(player);
			Inventory inventory = ((Humanoid)player).GetInventory();
			Piece selectedPiece = player.GetSelectedPiece();
			string limiting;
			float num2 = Affordable(player, price, selectedPiece, out limiting);
			float num3 = ((num2 >= num) ? float.PositiveInfinity : Within(Quoted, num2));
			bool flag = float.IsPositiveInfinity(num3);
			float volume = (flag ? num : Inside(Quoted, num3));
			bool flag2 = false;
			items = string.Join("/", price.Items);
			cost = "";
			all = "";
			carried = "";
			own = "";
			for (int i = 0; i < price.Items.Length; i++)
			{
				string text = price.Items[i];
				int num4 = WholeItems(volume, price, i);
				if (num4 > 0)
				{
					flag2 = true;
				}
				cost = cost + ((i > 0) ? "/" : "") + num4;
				all = all + ((i > 0) ? "/" : "") + WholeItems(num, price, i);
				carried = carried + ((i > 0) ? "/" : "") + ((inventory != null) ? inventory.CountItems(text, -1, true) : 0);
				int num5 = OwnCost(selectedPiece, text);
				if (num5 > 0)
				{
					own = own + ((own.Length > 0) ? ", " : "") + num5 + " " + text;
				}
			}
			if (flag)
			{
				if (!flag2)
				{
					return Terms.Covered;
				}
				return Terms.Paid;
			}
			if (num3 <= 0f)
			{
				return Terms.Unaffordable;
			}
			if (!flag2)
			{
				return Terms.ShortCovered;
			}
			return Terms.Short;
		}

		private static float Inside(List<Lift> lifts, float reach)
		{
			float num = 0f;
			for (int i = 0; i < lifts.Count; i++)
			{
				if (lifts[i].Distance < reach)
				{
					num += lifts[i].Volume;
				}
			}
			return num;
		}

		private static int WholeItems(float volume, Price price, int k)
		{
			return Mathf.Max(0, Mathf.CeilToInt(volume * price.PerCubicMetre[k] - CreditOf(price.Items[k]) - 0.0001f));
		}
	}
	internal static class Flat
	{
		internal enum Source
		{
			Crosshair,
			ContinuedFlat,
			Disagreed,
			TooLittle,
			Held
		}

		private static FieldRef<TerrainComp, bool[]> _modifiedHeight;

		private static FieldRef<TerrainComp, float[]> _levelDelta;

		private static FieldRef<TerrainComp, float[]> _smoothDelta;

		private static bool _bound;

		private static bool _bindFailed;

		private static readonly List<float> Heights = new List<float>();

		private static readonly List<float> Distances = new List<float>();

		private static readonly List<float> Chosen = new List<float>();

		internal static int LastFound;

		internal static int LastUsed;

		internal static float LastSpread;

		private static void Sort()
		{
			for (int i = 1; i < Distances.Count; i++)
			{
				float num = Distances[i];
				float value = Heights[i];
				int num2 = i - 1;
				while (num2 >= 0 && Distances[num2] > num)
				{
					Distances[num2 + 1] = Distances[num2];
					Heights[num2 + 1] = Heights[num2];
					num2--;
				}
				Distances[num2 + 1] = num;
				Heights[num2 + 1] = value;
			}
		}

		internal static void NoSearch()
		{
			LastFound = 0;
			LastUsed = 0;
			LastSpread = 0f;
		}

		private static bool Bind()
		{
			if (_bound)
			{
				return !_bindFailed;
			}
			_bound = true;
			try
			{
				_modifiedHeight = AccessTools.FieldRefAccess<TerrainComp, bool[]>("m_modifiedHeight");
				_levelDelta = AccessTools.FieldRefAccess<TerrainComp, float[]>("m_levelDelta");
				_smoothDelta = AccessTools.FieldRefAccess<TerrainComp, float[]>("m_smoothDelta");
			}
			catch (Exception ex)
			{
				_bindFailed = true;
				JafnaPlugin.Log.LogWarning((object)("Could not reach TerrainComp's height arrays, so continuing a flat is off for this session and levelling is vanilla. The reach and ward parts are unaffected. " + ex.Message));
			}
			return !_bindFailed;
		}

		internal static float Target(TerrainComp comp, Vector3 worldPos, float radius, bool square, out Source source)
		{
			//IL_0010: 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)
			//IL_0034: 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_007f: 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_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			source = Source.Crosshair;
			if (!JafnaConfig.ContinueFlat.Value)
			{
				return worldPos.y;
			}
			if ((Object)(object)comp == (Object)null || !Bind())
			{
				return worldPos.y;
			}
			Heightmap val = Heightmap.FindHeightmap(((Component)comp).transform.position);
			if ((Object)(object)val == (Object)null)
			{
				return worldPos.y;
			}
			bool[] array = _modifiedHeight.Invoke(comp);
			float[] array2 = _levelDelta.Invoke(comp);
			float[] array3 = _smoothDelta.Invoke(comp);
			if (array == null || array2 == null || array3 == null)
			{
				return worldPos.y;
			}
			int num = default(int);
			int num2 = default(int);
			val.WorldToVertex(worldPos, ref num, ref num2);
			float num3 = radius / val.m_scale;
			int num4 = Mathf.CeilToInt(num3);
			int num5 = val.m_width + 1;
			Vector2 val2 = default(Vector2);
			((Vector2)(ref val2))..ctor((float)num, (float)num2);
			Heights.Clear();
			Distances.Clear();
			for (int i = num2 - num4; i <= num2 + num4; i++)
			{
				for (int j = num - num4; j <= num + num4; j++)
				{
					if ((square || !(Vector2.Distance(val2, new Vector2((float)j, (float)i)) > num3)) && j >= 0 && i >= 0 && j < num5 && i < num5)
					{
						int num6 = i * num5 + j;
						if (num6 >= 0 && num6 < array.Length && array[num6])
						{
							Heights.Add(((Component)val).transform.position.y + val.GetHeight(j, i));
							Distances.Add((j - num) * (j - num) + (i - num2) * (i - num2));
						}
					}
				}
			}
			LastFound = Heights.Count;
			LastUsed = 0;
			LastSpread = 0f;
			int num7 = Mathf.Max(1, JafnaConfig.ContinueMinVertices.Value);
			if (Heights.Count < num7)
			{
				source = ((Heights.Count > 0) ? Source.TooLittle : Source.Crosshair);
				return worldPos.y;
			}
			Sort();
			int num8 = Mathf.Min(Heights.Count, Mathf.Max(num7, 8));
			float num9 = float.MaxValue;
			float num10 = float.MinValue;
			for (int k = 0; k < num8; k++)
			{
				float num11 = Heights[k];
				if (num11 < num9)
				{
					num9 = num11;
				}
				if (num11 > num10)
				{
					num10 = num11;
				}
			}
			LastUsed = num8;
			LastSpread = num10 - num9;
			if (LastSpread > Mathf.Max(0f, JafnaConfig.ContinueTolerance.Value))
			{
				source = Source.Disagreed;
				return worldPos.y;
			}
			source = Source.ContinuedFlat;
			Chosen.Clear();
			for (int l = 0; l < num8; l++)
			{
				Chosen.Add(Heights[l]);
			}
			Chosen.Sort();
			return Chosen[Chosen.Count / 2];
		}
	}
	internal static class Held
	{
		internal static bool Active;

		internal static float Height;

		internal static void Toggle(float height)
		{
			if (Active)
			{
				Active = false;
				if (JafnaConfig.Verbose.Value)
				{
					JafnaPlugin.Log.LogInfo((object)"Released the held height.");
				}
				return;
			}
			Active = true;
			Height = height;
			if (JafnaConfig.Verbose.Value)
			{
				JafnaPlugin.Log.LogInfo((object)("Holding " + height.ToString("0.00") + "m."));
			}
		}

		internal static void Release()
		{
			Active = false;
		}
	}
	internal static class JafnaConfig
	{
		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> ContinueFlat;

		internal static ConfigEntry<float> ContinueTolerance;

		internal static ConfigEntry<int> ContinueMinVertices;

		internal static ConfigEntry<KeyCode> HoldKey;

		internal static ConfigEntry<bool> AutoRaise;

		internal static ConfigEntry<bool> StoneFill;

		internal static ConfigEntry<KeyCode> StoneFillKey;

		internal static ConfigEntry<float> MinRadius;

		internal static ConfigEntry<float> MaxRadius;

		internal static ConfigEntry<float> FullLevel;

		internal static ConfigEntry<float> Curve;

		internal static ConfigEntry<bool> RespectWardFootprint;

		internal static ConfigEntry<bool> ShowReadout;

		internal static ConfigEntry<bool> Verbose;

		internal static void Bind(ConfigFile cfg)
		{
			Enabled = cfg.Bind<bool>("Jafna", "Enabled", true, "Off leaves the plugin loaded and changing nothing - vanilla levelling, vanilla reach, no readout. Not \"unloaded\": a plugin cannot unload itself, and a switch that pretends otherwise is a lie somebody will debug.");
			ContinueFlat = cfg.Bind<bool>("Levelling", "ContinueFlat", true, "The whole point of the mod. Vanilla levels every point under the tool to whatever height your crosshair happens to be resting on, so walking two steps moves the target and the overlap between two swings ends up at two heights. That is why a large yard comes out rippled however carefully you work. With this on, a swing that touches ground you have already levelled takes THAT height instead of your crosshair's, so flat spreads outward from wherever you started and keeps spreading across sessions. A swing touching nothing you levelled before behaves exactly like vanilla, which is how you start a new platform at a new height.");
			ContinueTolerance = cfg.Bind<float>("Levelling", "ContinueTolerance", 0.25f, "How far apart, in metres, the already-levelled ground under one swing may be before the mod gives up and lets vanilla decide. This is the setting that stops two platforms at different heights being averaged into a ramp through the middle: stand where a 2m terrace meets a 4m one, the readings disagree by 2m, and the swing falls back to your crosshair so you stay in control. Raising it past about half a metre turns that safety off and starts inventing heights that were never on screen.");
			ContinueMinVertices = cfg.Bind<int>("Levelling", "ContinueMinVertices", 3, "How many already-levelled grid points a swing must touch before it will adopt their height. Terrain is stored on a 1 metre grid, so this reads as \"at least three square metres of existing flat\". One is too few: a single stray point left over from an old op, or from the corner of something levelled last winter, would silently capture the swing and drag a new platform to a height you cannot see.");
			HoldKey = cfg.Bind<KeyCode>("Levelling", "HoldKey", (KeyCode)308, "Press this while a levelling tool is out to hold the height under your crosshair, and press it again to let go. While a height is held every swing flattens toward that one number, wherever you stand and wherever you aim - so you can walk a whole yard flat from one reading instead of taking what is under your feet at each step. It also settles the two cases the mod cannot decide on its own: a higher platform beside you capturing swings you meant lower, and a swing crossing a zone boundary where each zone would otherwise choose for itself. Nothing is held across a logout, on purpose - coming back to a swing that moves ground toward a number you set yesterday, with nothing on screen having changed, is a worse trap than setting it again. It counts when you let go, and only as a press of this key on its own: with any other key or mouse button pressed while it is down, or with the game window losing focus in between, letting go does nothing. That is what stops Alt+Tab from pinning a height on the way out of the game. NOTE: Left Alt is also vanilla's alt-placement key, which does have a meaning for terrain tools, so if the ghost starts behaving oddly while you use this, move it to a key of its own. Keybinds are never taken over by a server, so this one stays yours whatever the host runs.");
			AutoRaise = cfg.Bind<bool>("Levelling", "AutoRaise", true, "When a flattening swing needs the ground higher than the hoe's flattening can lift it, raise the rest and pay for it with stone from your pack. The hoe's Level ground eases each point toward its target and stops once it has moved that point one metre, and only raising ground frees that metre again, which is why a held height far above the ground used to stop short. With this on, the part the flattening cannot reach is raised anyway, at the price of the hoe's own Raise ground: whatever that entry costs, divided by the ground one Raise ground swing adds to flat ground. Both are read from the game, not written here. Like Raise ground itself it needs a workbench within build range of where you stand (the station that entry names, 20 metres for a workbench with no extensions): out of range a swing is the hoe's alone, takes no stone, and the panel and the swing say so. A world with NoWorkbench set, or the nocost command, lifts that, and NoBuildCost does not, the same as for Raise ground. On a slope Raise ground by hand adds less for the same stone, so there filling is cheaper. Alternating Raise ground and Level ground by hand can be cheaper than filling, because every Raise ground gives the next flattening swing its free metre back and a fill does not, or every swing would get a free metre and flattening would raise ground for nothing. A swing where every point is within the hoe's free metre of the height, less what earlier swings used of it, is one the hoe can finish on its own: it is left exactly as vanilla has it and costs nothing. A swing with a point further below than that needs stone, and lifts every point under it that is below the height straight onto it, in that one swing, leaving a step at the edge of the circle until the next swing beside it that needs stone. Each point comes up its free metre first and only the rest of the way is charged, so a paid swing costs what finishing that ground over several swings would have, only all at once. A point less than three centimetres past its free metre is neither raised past it nor charged for, so touching up finished ground costs nothing either. Each point comes all the way up or not at all, and a point that is not raised costs nothing. The game never lets ground sit more than eight metres above where the world made it, so ground further below the height than that is not raised at all, only eased by the hoe for free, and the panel and the swing say so. Stone comes out whole and the rest is kept toward your next swing until you log out. Short of stone, a swing lifts the middle of the circle first, as far out as your stone pays for, leaves the rest to the hoe, and says so. Stone the selected entry itself costs is left for it. In a world with NoBuildCost set it is free, and the nocost command lifts the limit but still takes the stone you carry, which is what vanilla's Raise ground does too. Lowering is unchanged: ground well above the target still stops after a metre, or up to two on ground raised before, because digging is the pickaxe's job. Off gives back the one metre ceiling. On a server the host decides this.");
			StoneFill = cfg.Bind<bool>("Levelling", "StoneFill", true, "Your own switch for the stone fill AutoRaise describes. On, a swing that needs stone takes it as AutoRaise says. Off, every swing of yours is the hoe's alone: its free metre, no stone taken, nothing lifted past it. It is flipped in game with StoneFillKey while a levelling tool is out, and the build panel says which it is. The host's AutoRaise stays the upper bound: off there means off for everybody and this switch cannot turn it back on, on there means each player may still turn it off for themselves. It is personal state and is never taken over by a server, so the host cannot switch it for you either. Kept across logouts, unlike a held height, because it is a preference and not a number you set for a reason you may forget, and the panel shows it every time the tool is out.");
			StoneFillKey = cfg.Bind<KeyCode>("Levelling", "StoneFillKey", (KeyCode)106, "Press this while a levelling tool is out to turn the stone fill on or off for yourself (StoneFill). It does nothing with the hoe put away, and nothing while chat, the console or a text field has the keyboard. None takes the key away and leaves the switch in the cfg. Keybinds are never taken over by a server.");
			MinRadius = cfg.Bind<float>("Reach", "MinRadius", 0f, "Levelling radius in metres at Crafting 0. Zero means the mod never makes the tool narrower or wider than vanilla at the bottom of the skill - the reach used is always the larger of this curve and the tool's own radius, so a new character gets exactly the hoe the game shipped. Raise it only if you want the reach handed over rather than earned.");
			MaxRadius = cfg.Bind<float>("Reach", "MaxRadius", 6f, "Levelling radius in metres once Crafting reaches FullLevel. The tool levels a square, so 6 is a 12x12 metre platform in one swing - a longhouse footprint and its doorstep. Every extra metre buys three things at once and only one of them is good: fewer swings, a wider strip of ground you can flatten inside somebody else's ward before the check stops you, and a bigger block of height data saved into the zone and pushed to everyone near it. Terrain does not heal, so this number is worth being stingy with.");
			FullLevel = cfg.Bind<float>("Reach", "FullLevel", 60f, "The Crafting level at which the radius reaches MaxRadius. Above it nothing changes. Deliberately not 100, which costs roughly 20,300 crafts against about 5,700 for 60 - a reward nobody arrives at is a reward that does not exist. Skaft uses the same number for the same reason and they are meant to stay in step.");
			Curve = cfg.Bind<float>("Reach", "Curve", 0.8f, "Exponent on the skill fraction: radius = min + (max-min) * (level/FullLevel)^Curve. 1.0 is a straight line, below 1 opens the reach earlier, above 1 hoards it for the top. Matches Skaft.");
			RespectWardFootprint = cfg.Bind<bool>("Wards", "RespectWardFootprint", true, "Refuse a level swing whose footprint does not clear a ward you have no access to. Vanilla checks the single point under your crosshair and nothing else, so even the stock hoe can already shave ground out from under a neighbour's wall while the ghost stays blue and the ward never flashes. That is a small hole at 2 metres and a wide one the moment Crafting makes the tool bigger, which is why this setting exists and why it defaults on. It is enforced where you swing, not where the world is saved: terrain is applied by whichever client owns that zone and there is no server-side validation anywhere in the path, so this stops the mod and an honest player and cannot stop a hostile one. That was already true of vanilla and this does not make it worse.");
			ShowReadout = cfg.Bind<bool>("Readout", "ShowReadout", true, "Put the numbers on screen while a levelling tool is out: the height under your crosshair, the height the swing will actually use, whether that came from the crosshair or from flat ground the swing is continuing, and what raising the low ground under it will take from your pack. Without it the mod is invisible and indistinguishable from the hoe behaving oddly, which is the complaint it was built to answer.");
			Verbose = cfg.Bind<bool>("Jafna", "Verbose", false, "Write what each swing decided to BepInEx/LogOutput.log. Off unless something looks wrong; it is a line per swing. It also logs the levelling tool's piece table skill once per session, which is the only way to read that number - it is asset data and no decompiler or prefab rip can reach it.");
		}
	}
	internal static class JafnaPatches
	{
		private static FieldRef<Player, GameObject> _placementGhost;

		private static FieldRef<Player, PlacementStatus> _placementStatus;

		private static FieldRef<Player, PieceTable> _buildPieces;

		private static bool _bound;

		private static bool _bindFailed;

		private static bool _skillReported;

		private static readonly Keys.Tap HoldTap = new Keys.Tap();

		private static float _heightAtPress;

		private static readonly HashSet<string> _describedPrefabs = new HashSet<string>();

		private static bool Bind()
		{
			if (_bound)
			{
				return !_bindFailed;
			}
			_bound = true;
			try
			{
				_placementGhost = AccessTools.FieldRefAccess<Player, GameObject>("m_placementGhost");
				_placementStatus = AccessTools.FieldRefAccess<Player, PlacementStatus>("m_placementStatus");
				_buildPieces = AccessTools.FieldRefAccess<Player, PieceTable>("m_buildPieces");
			}
			catch (Exception ex)
			{
				_bindFailed = true;
				JafnaPlugin.Log.LogWarning((object)("Could not reach the placement ghost fields, so the ward footprint check and the readout are off for this session. Levelling itself is unaffected. " + ex.Message));
			}
			return !_bindFailed;
		}

		private static Settings SelectedLevelOp(Player player)
		{
			if (!Bind())
			{
				return null;
			}
			PieceTable val = _buildPieces.Invoke(player);
			if ((Object)(object)val == (Object)null)
			{
				return null;
			}
			GameObject selectedPrefab = val.GetSelectedPrefab();
			if ((Object)(object)selectedPrefab == (Object)null)
			{
				return null;
			}
			ReportSelection(selectedPrefab);
			TerrainOp val2 = default(TerrainOp);
			if (!selectedPrefab.TryGetComponent<TerrainOp>(ref val2))
			{
				return null;
			}
			if (!Reach.IsFlatten(val2.m_settings))
			{
				return null;
			}
			return val2.m_settings;
		}

		private static void ReportSelection(GameObject prefab)
		{
			if (JafnaConfig.Verbose.Value && _describedPrefabs.Add(((Object)prefab).name))
			{
				string text = "Build menu selection '" + ((Object)prefab).name + "': ";
				TerrainOp val = default(TerrainOp);
				if (prefab.TryGetComponent<TerrainOp>(ref val))
				{
					Settings settings = val.m_settings;
					text += ((settings == null) ? "TerrainOp with no settings. " : ("TerrainOp level=" + settings.m_level + " radius=" + settings.m_levelRadius.ToString("0.00") + " square=" + settings.m_square + " offset=" + settings.m_levelOffset.ToString("0.00") + " raise=" + settings.m_raise + " smooth=" + settings.m_smooth + " smoothRadius=" + settings.m_smoothRadius.ToString("0.00") + " smoothPower=" + settings.m_smoothPower.ToString("0.00") + " paint=" + settings.m_paintCleared + " paintRadius=" + settings.m_paintRadius.ToString("0.00") + ". "));
				}
				else
				{
					text += "no TerrainOp. ";
				}
				Piece val2 = default(Piece);
				if (prefab.TryGetComponent<Piece>(ref val2))
				{
					text = text + "Piece groundPiece=" + val2.m_groundPiece + " allowAltGroundPlacement=" + val2.m_allowAltGroundPlacement + " clipGround=" + val2.m_clipGround + " costs " + Cost(val2) + ". ";
				}
				TerrainModifier val3 = default(TerrainModifier);
				text = ((!prefab.TryGetComponent<TerrainModifier>(ref val3)) ? (text + "no TerrainModifier.") : (text + "TerrainModifier level=" + val3.m_level + " radius=" + val3.m_levelRadius.ToString("0.00") + " square=" + val3.m_square + " offset=" + val3.m_levelOffset.ToString("0.00") + " useCompiler=" + val3.m_useTerrainCompiler + " smooth=" + val3.m_smooth + "."));
				JafnaPlugin.Log.LogInfo((object)text);
			}
		}

		private static string Cost(Piece piece)
		{
			if (piece.m_resources == null || piece.m_resources.Length == 0)
			{
				return "nothing";
			}
			string text = "";
			Requirement[] resources = piece.m_resources;
			foreach (Requirement val in resources)
			{
				if (val != null && !((Object)(object)val.m_resItem == (Object)null))
				{
					text = text + ((text.Length > 0) ? ", " : "") + ((Object)val.m_resItem).name + " x" + val.GetAmount(0) + (val.m_upgraderResource ? " (upgrade only)" : "");
				}
			}
			if (text.Length <= 0)
			{
				return "nothing";
			}
			return text;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(TerrainOp), "GetRadius")]
		private static void WidenSearch(TerrainOp __instance, ref float __result)
		{
			if (JafnaConfig.Enabled.Value && Reach.IsFlatten(__instance.m_settings) && Reach.IsSwing(__instance))
			{
				__result = Mathf.Max(__result, Reach.Earned(__instance.m_settings, Player.m_localPlayer));
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(TerrainComp), "ApplyOperation")]
		private static bool SendReach(TerrainComp __instance, TerrainOp modifier)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Expected O, but got Unknown
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			if (!JafnaConfig.Enabled.Value)
			{
				return true;
			}
			if ((Object)(object)modifier == (Object)null || !Reach.IsFlatten(modifier.m_settings))
			{
				return true;
			}
			if (!Reach.IsSwing(modifier))
			{
				return true;
			}
			Player localPlayer = Player.m_localPlayer;
			float radius = Reach.Earned(modifier.m_settings, localPlayer);
			float fill = Fill.Plan(modifier, localPlayer, radius);
			ZNetView val = Reach.View(__instance);
			if ((Object)(object)val == (Object)null || !val.IsValid())
			{
				return true;
			}
			ZPackage val2 = new ZPackage();
			val2.Write(((Component)modifier).transform.position);
			val2.Write(modifier.m_settings.m_rotation);
			if (modifier.m_settings.m_rotation)
			{
				val2.Write(((Component)modifier).transform.forward);
			}
			modifier.m_settings.Serialize(val2, ((Component)modifier).gameObject);
			Reach.Append(val2, radius, Held.Active, Held.Height, fill);
			val.InvokeRPC("RPC_ApplyOperation", new object[1] { val2 });
			return false;
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(TerrainComp), "RPC_ApplyOperation")]
		private static void ReceiveReach(long sender, ZPackage pkg)
		{
			float radius;
			bool held;
			float height;
			float fill;
			if (!JafnaConfig.Enabled.Value)
			{
				Reach.SetIncoming(-1f, held: false, 0f, -1f, 0L);
			}
			else if (Reach.Peek(pkg, out radius, out held, out height, out fill))
			{
				Reach.SetIncoming(radius, held, height, fill, sender);
			}
			else
			{
				Reach.SetIncoming(-1f, held: false, 0f, -1f, 0L);
			}
		}

		[HarmonyPrefix]
		[HarmonyPatch(typeof(TerrainComp), "DoOperation")]
		private static void LevelTo(TerrainComp __instance, ref Vector3 pos, ref Settings modifier)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: 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_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			if (!JafnaConfig.Enabled.Value)
			{
				Fill.Freshen(__instance, filling: false);
				return;
			}
			bool held;
			float height;
			float fill;
			long sender;
			float num = Reach.TakeIncoming(out held, out height, out fill, out sender);
			Fill.Freshen(__instance, Fill.Wants(modifier, fill));
			if (num <= 0f || !Reach.IsFlatten(modifier))
			{
				return;
			}
			if (num > Reach.VanillaRadius(modifier))
			{
				modifier = Reach.With(modifier, num);
			}
			float levelOffset = modifier.m_levelOffset;
			Vector3 val = pos + Vector3.up * levelOffset;
			Flat.Source source;
			if (held)
			{
				float num2 = height;
				source = Flat.Source.Held;
				pos.y = num2 - levelOffset;
				Flat.NoSearch();
			}
			else
			{
				float num2 = Flat.Target(__instance, val, Reach.VanillaRadius(modifier), Reach.IsSquare(modifier), out source);
				if (source == Flat.Source.ContinuedFlat)
				{
					pos.y = num2 - levelOffset;
				}
			}
			Fill.Apply(__instance, pos + Vector3.up * levelOffset, modifier, fill, sender);
			if (JafnaConfig.Verbose.Value)
			{
				JafnaPlugin.Log.LogInfo((object)("Level at " + pos.x.ToString("0.0") + "/" + pos.z.ToString("0.0") + ", radius " + Reach.VanillaRadius(modifier).ToString("0.0") + "m, crosshair " + val.y.ToString("0.00") + "m, used " + (val.y + (pos.y - (val.y - levelOffset))).ToString("0.00") + "m (" + source.ToString() + ", found " + Flat.LastFound + ", used " + Flat.LastUsed + ", spread " + Flat.LastSpread.ToString("0.000") + "m vs tolerance " + JafnaConfig.ContinueTolerance.Value.ToString("0.000") + "m)."));
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(TerrainComp), "DoOperation")]
		private static void NoteShaped(TerrainComp __instance, Settings modifier)
		{
			Fill.Touched(__instance, modifier);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "UpdatePlacementGhost")]
		private static void WardFootprint(Player __instance)
		{
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			if (!JafnaConfig.Enabled.Value || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || !Bind())
			{
				return;
			}
			Settings val = SelectedLevelOp(__instance);
			ReportSkill(__instance, val);
			if (val == null)
			{
				Readout.Clear();
				return;
			}
			if (!JafnaConfig.ContinueFlat.Value && Held.Active)
			{
				Held.Release();
			}
			GameObject val2 = _placementGhost.Invoke(__instance);
			if ((Object)(object)val2 == (Object)null)
			{
				Readout.Clear();
				return;
			}
			Vector3 position = val2.transform.position;
			switch (HoldTap.Read(JafnaConfig.HoldKey.Value))
			{
			case Keys.Edge.Down:
				_heightAtPress = position.y;
				break;
			case Keys.Edge.Tapped:
				Held.Toggle(_heightAtPress);
				break;
			}
			if (Keys.Pressed(JafnaConfig.StoneFillKey.Value))
			{
				StoneSwitch.Flip(__instance);
			}
			float radius = Reach.Earned(val, __instance);
			bool flag = Reach.FootprintClear(position, radius, Reach.IsSquare(val));
			if (!flag && (int)_placementStatus.Invoke(__instance) == 0)
			{
				_placementStatus.Invoke(__instance) = (PlacementStatus)4;
			}
			bool lands = (int)_placementStatus.Invoke(__instance) == 0 && val2.activeSelf;
			Readout.Update(__instance, val, position, radius, flag, lands);
		}

		private static void ReportSkill(Player player, Settings settings)
		{
			if (!_skillReported && settings != null && JafnaConfig.Verbose.Value)
			{
				_skillReported = true;
				PieceTable val = _buildPieces.Invoke(player);
				string text = (((Object)(object)val == (Object)null) ? "no piece table" : ((object)Unsafe.As<SkillType, SkillType>(ref val.m_skill)/*cast due to .constrained prefix*/).ToString());
				JafnaPlugin.Log.LogInfo((object)("Levelling tool's piece table skill: " + text + ". Crafting " + CraftingLevel(player).ToString("0") + " gives a reach of " + Reach.Earned(settings, player).ToString("0.0") + "m against the tool's own " + Reach.VanillaRadius(settings).ToString("0.0") + "m."));
			}
		}

		internal static float CraftingLevel(Player player)
		{
			if (!((Object)(object)player == (Object)null))
			{
				return ((Character)player).GetSkillFactor((SkillType)107) * 100f;
			}
			return 0f;
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(TerrainComp), "Awake")]
		private static void HearBill(TerrainComp __instance)
		{
			Fill.Listen(__instance);
		}
	}
	[BepInPlugin("ezomic.valheim.jafna", "Jafna", "1.2.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class JafnaPlugin : BaseUnityPlugin
	{
		public const string PluginGuid = "ezomic.valheim.jafna";

		public const string PluginName = "Jafna";

		public const string PluginVersion = "1.2.1";

		public const string PluginAuthor = "Robbin Thijssen";

		private const string CoreGuid = "ezomic.valheim.core";

		internal static ManualLogSource Log;

		internal static bool CorePresent;

		private Harmony _harmony;

		private void Awake()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			JafnaConfig.Bind(((BaseUnityPlugin)this).Config);
			TryRegisterWithCore();
			_harmony = new Harmony("ezomic.valheim.jafna");
			_harmony.PatchAll(typeof(JafnaPatches));
			_harmony.PatchAll(typeof(ReadoutProbe.Hook));
			_harmony.PatchAll(typeof(Readout));
			_harmony.PatchAll(typeof(DevConsole.Hook));
			Log.LogInfo((object)"Jafna 1.2.1 by Robbin Thijssen - ready.");
		}

		private void TryRegisterWithCore()
		{
			CorePresent = Chainloader.PluginInfos.ContainsKey("ezomic.valheim.core");
			if (!CorePresent)
			{
				Log.LogInfo((object)"Core not installed - running standalone, without the version gate.");
			}
			else
			{
				RegisterWithCore();
			}
		}

		[MethodImpl(MethodImplOptions.NoInlining)]
		private void RegisterWithCore()
		{
			Suite.Register("ezomic.valheim.jafna", "Jafna", "1.2.1", ((BaseUnityPlugin)this).Config, (Requirement)1, (Assembly)null);
			Suite.Sync((ConfigEntryBase)(object)JafnaConfig.Enabled);
			Suite.Sync((ConfigEntryBase)(object)JafnaConfig.MaxRadius);
			Suite.Sync((ConfigEntryBase)(object)JafnaConfig.AutoRaise);
			Suite.Sync((ConfigEntryBase)(object)JafnaConfig.RespectWardFootprint);
			Suite.Local((ConfigEntryBase[])(object)new ConfigEntryBase[2]
			{
				(ConfigEntryBase)JafnaConfig.StoneFill,
				(ConfigEntryBase)JafnaConfig.StoneFillKey
			});
		}

		private void OnDestroy()
		{
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
		}
	}
	internal static class Keys
	{
		internal enum Edge
		{
			None,
			Down,
			Tapped
		}

		internal sealed class Tap
		{
			private bool _held;

			private bool _spoiled;

			private int _seen = -1;

			internal Edge Read(KeyCode key)
			{
				//IL_0024: 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_0086: Unknown result type (might be due to invalid IL or missing references)
				//IL_0096: Unknown result type (might be due to invalid IL or missing references)
				//IL_0064: Unknown result type (might be due to invalid IL or missing references)
				int frameCount = Time.frameCount;
				if (frameCount == _seen)
				{
					return Edge.None;
				}
				bool flag = frameCount == _seen + 1;
				_seen = frameCount;
				if ((int)key == 0)
				{
					_held = false;
					return Edge.None;
				}
				if (_held && !flag)
				{
					_held = false;
				}
				if (!_held)
				{
					if (!ZInput.GetKeyDown(key, false))
					{
						return Edge.None;
					}
					_held = true;
					_spoiled = !Undisturbed() || OtherKeyWentDown(key, pressFrame: true);
					if (!_spoiled)
					{
						return Edge.Down;
					}
					return Edge.None;
				}
				if (!Application.isFocused || OtherKeyWentDown(key, pressFrame: false))
				{
					_spoiled = true;
				}
				if (ZInput.GetKey(key, false))
				{
					return Edge.None;
				}
				_held = false;
				if (_spoiled || !Undisturbed())
				{
					return Edge.None;
				}
				return Edge.Tapped;
			}
		}

		private static int _pressedFrame = -1;

		private static KeyCode[] _others;

		private static object[] _controls;

		private static bool _othersBound;

		internal static bool Pressed(KeyCode key)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			int frameCount = Time.frameCount;
			if (frameCount == _pressedFrame)
			{
				return false;
			}
			_pressedFrame = frameCount;
			if ((int)key == 0)
			{
				return false;
			}
			if (Undisturbed())
			{
				return ZInput.GetKeyDown(key, false);
			}
			return false;
		}

		private static bool Undisturbed()
		{
			if (!Application.isFocused)
			{
				return false;
			}
			if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus())
			{
				return false;
			}
			if (!Console.IsVisible())
			{
				return !TextInput.IsVisible();
			}
			return false;
		}

		private static bool BindOthers()
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Invalid comparison between Unknown and I4
			//IL_00d3: 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_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			if (_othersBound)
			{
				return _others != null;
			}
			_othersBound = true;
			try
			{
				FieldInfo fieldInfo = AccessTools.Field(typeof(ZInput), "s_keyCodeToKeyMap");
				IDictionary obj = ((fieldInfo == null) ? null : (fieldInfo.GetValue(null) as IDictionary)) ?? throw new MissingFieldException("ZInput", "s_keyCodeToKeyMap");
				List<KeyCode> list = new List<KeyCode>();
				List<object> list2 = new List<object>();
				foreach (DictionaryEntry item in obj)
				{
					if (item.Key is KeyCode val && (int)val != 0)
					{
						list.Add(val);
						list2.Add(item.Value);
					}
				}
				for (KeyCode val2 = (KeyCode)323; (int)val2 <= 327; val2 = (KeyCode)(val2 + 1))
				{
					list.Add(val2);
					list2.Add(val2);
				}
				_others = list.ToArray();
				_controls = list2.ToArray();
			}
			catch (Exception ex)
			{
				_others = null;
				_controls = null;
				JafnaPlugin.Log.LogWarning((object)("Could not read ZInput's key table, so the hold key cannot tell a tap from a chord this session and only the focus rule stops Alt+Tab from toggling it. " + ex.Message));
			}
			return _others != null;
		}

		private static bool OtherKeyWentDown(KeyCode key, bool pressFrame)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Invalid comparison between Unknown and I4
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Invalid comparison between I4 and Unknown
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Invalid comparison between I4 and Unknown
			if (!BindOthers())
			{
				return false;
			}
			bool flag = pressFrame && ((int)key == 307 || (int)key == 313);
			object obj = null;
			for (int i = 0; i < _others.Length; i++)
			{
				if ((int)_others[i] == (int)key)
				{
					obj = _controls[i];
					break;
				}
			}
			try
			{
				for (int j = 0; j < _others.Length; j++)
				{
					if ((int)_others[j] != (int)key && (obj == null || !obj.Equals(_controls[j])) && (!flag || (int)_others[j] != 306) && ZInput.GetKeyDown(_others[j], false))
					{
						return true;
					}
				}
			}
			catch (Exception ex)
			{
				_others = null;
				_controls = null;
				JafnaPlugin.Log.LogWarning((object)("Reading the other keys threw, so the hold key cannot tell a tap from a chord for the rest of this session. " + ex.Message));
			}
			return false;
		}
	}
	internal static class Reach
	{
		internal const int Magic = 1245791822;

		private static FieldRef<TerrainComp, ZNetView> _nview;

		private static bool _bound;

		private static bool _bindFailed;

		private static FieldInfo[] _settingsFields;

		private static float _incoming = -1f;

		private static bool _incomingHeld;

		private static float _incomingHeight;

		private static float _incomingFill = -1f;

		private static long _incomingSender;

		private static bool Bind()
		{
			if (_bound)
			{
				return !_bindFailed;
			}
			_bound = true;
			try
			{
				_nview = AccessTools.FieldRefAccess<TerrainComp, ZNetView>("m_nview");
			}
			catch (Exception ex)
			{
				_bindFailed = true;
				JafnaPlugin.Log.LogWarning((object)("Could not reach TerrainComp.m_nview, so no swing can carry Jafna's note to the zone that applies it, and levelling is vanilla this session, singleplayer included. " + ex.Message));
			}
			return !_bindFailed;
		}

		internal static ZNetView View(TerrainComp comp)
		{
			if (!Bind())
			{
				return null;
			}
			return _nview.Invoke(comp);
		}

		internal static bool IsFlatten(Settings settings)
		{
			if (settings != null)
			{
				if (!settings.m_smooth)
				{
					return settings.m_level;
				}
				return true;
			}
			return false;
		}

		internal static bool IsSwing(TerrainOp op)
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)op == (Object)null)
			{
				return false;
			}
			if (!((Character)localPlayer).InPlaceMode())
			{
				return false;
			}
			PieceTable buildTool = localPlayer.GetBuildTool();
			if ((Object)(object)buildTool == (Object)null)
			{
				return false;
			}
			GameObject selectedPrefab = buildTool.GetSelectedPrefab();
			if ((Object)(object)selectedPrefab == (Object)null)
			{
				return false;
			}
			return Utils.GetPrefabName(((Object)((Component)op).gameObject).name) == ((Object)selectedPrefab).name;
		}

		internal static float VanillaRadius(Settings settings)
		{
			if (settings == null)
			{
				return 0f;
			}
			if (!settings.m_smooth)
			{
				return settings.m_levelRadius;
			}
			return settings.m_smoothRadius;
		}

		internal static bool IsSquare(Settings settings)
		{
			if (settings != null && !settings.m_smooth)
			{
				return settings.m_square;
			}
			return false;
		}

		internal static float Earned(Settings settings, Player player)
		{
			float num = VanillaRadius(settings);
			if (!JafnaConfig.Enabled.Value || (Object)(object)player == (Object)null || !IsFlatten(settings))
			{
				return num;
			}
			float num2 = CraftingReach.Radius(CraftingReach.Level(player), JafnaConfig.MinRadius.Value, JafnaConfig.MaxRadius.Value, JafnaConfig.FullLevel.Value, JafnaConfig.Curve.Value);
			return Mathf.Max(num, num2);
		}

		internal static Settings With(Settings source, float radius)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Expected O, but got Unknown
			if (source == null)
			{
				return null;
			}
			if (_settingsFields == null)
			{
				_settingsFields = typeof(Settings).GetFields(BindingFlags.Instance | BindingFlags.Public);
			}
			Settings val = new Settings();
			for (int i = 0; i < _settingsFields.Length; i++)
			{
				_settingsFields[i].SetValue(val, _settingsFields[i].GetValue(source));
			}
			float num = VanillaRadius(source);
			if (source.m_smooth)
			{
				val.m_smoothRadius = radius;
			}
			else
			{
				val.m_levelRadius = radius;
			}
			if (source.m_paintCleared && num > 0.01f && source.m_paintRadius > 0f)
			{
				val.m_paintRadius = source.m_paintRadius * (radius / num);
			}
			return val;
		}

		internal static void Append(ZPackage pkg, float radius, bool held, float height, float fill)
		{
			pkg.Write(1245791822);
			pkg.Write(radius);
			pkg.Write(held);
			pkg.Write(height);
			pkg.Write(fill);
		}

		internal static bool Peek(ZPackage pkg, out float radius, out bool held, out float height, out float fill)
		{
			//IL_0026: 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)
			radius = -1f;
			held = false;
			height = 0f;
			fill = -1f;
			if (pkg == null)
			{
				return false;
			}
			int pos = pkg.GetPos();
			try
			{
				pkg.ReadVector3();
				if (pkg.ReadBool())
				{
					pkg.ReadVector3();
				}
				pkg.ReadInt();
				if (pkg.GetPos() + 13 > pkg.Size())
				{
					return false;
				}
				if (pkg.ReadInt() != 1245791822)
				{
					return false;
				}
				radius = pkg.ReadSingle();
				held = pkg.ReadBool();
				height = pkg.ReadSingle();
				if (pkg.GetPos() + 4 <= pkg.Size())
				{
					fill = pkg.ReadSingle();
				}
				return radius > 0f;
			}
			catch
			{
				return false;
			}
			finally
			{
				pkg.SetPos(pos);
			}
		}

		internal static void SetIncoming(float radius, bool held, float height, float fill, long sender)
		{
			_incoming = radius;
			_incomingHeld = held;
			_incomingHeight = height;
			_incomingFill = fill;
			_incomingSender = sender;
		}

		internal static float TakeIncoming(out bool held, out float height, out float fill, out long sender)
		{
			float incoming = _incoming;
			held = _incomingHeld;
			height = _incomingHeight;
			fill = _incomingFill;
			sender = _incomingSender;
			_incoming = -1f;
			_incomingHeld = false;
			_incomingHeight = 0f;
			_incomingFill = -1f;
			_incomingSender = 0L;
			return incoming;
		}

		internal static bool FootprintClear(Vector3 point, float radius, bool square)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (!JafnaConfig.RespectWardFootprint.Value)
			{
				return true;
			}
			return PrivateArea.CheckAccess(point, square ? (radius * 1.4142137f) : radius, false, false);
		}
	}
	internal static class Readout
	{
		private static Piece _described;

		private static string _original;

		private static string _written;

		private static string _lines;

		private static Piece _builtFor;

		private static bool _countWarned;

		internal const int Rows = 13;

		internal const int StoneAt = 10;

		internal const int BenchAt = 11;

		private const int MaxChars = 40;

		private const int HeldKeyChars = 14;

		private const string Dim = "#d9d0bb";

		private const string Red = "#ff6060";

		private static readonly List<Heightmap> Maps = new List<Heightmap>();

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

		private static string _namesLanguage;

		private static int _kx;

		private static int _ky;

		private static int _kz;

		private static int _kRadius;

		private static int _kTarget;

		private static int _kRaise;

		private static int _kLower;

		private static int _kCrafting;

		private static bool _kWard;

		private static bool _kFar;

		private static Flat.Source _kSource;

		private static Fill.Terms _kTerms;

		private static KeyCode _kKey;

		private static KeyCode _kSwitchKey;

		private static bool _kSwitchOn;

		private static bool _kHostRaise;

		private static string _kLanguage;

		private static string _kCost;

		private static string _kAll;

		private static string _kCarried;

		private static string _kOwn;

		private static string _kStation;

		private static string _kItems;

		private static string _panel;

		private const float PanelFont = 14f;

		private static float _labelHeight = -1f;

		private static Vector2 _labelAt;

		private static bool _labelLogged;

		internal static string PanelText => _panel;

		internal static string Lines => _lines;

		internal static void Update(Player player, Settings settings, Vector3 point, float radius, bool wardClear, bool lands)
		{
			//IL_002a: 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_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: 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_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_035d: Unknown result type (might be due to invalid IL or missing references)
			if (!JafnaConfig.ShowReadout.Value)
			{
				_lines = null;
				return;
			}
			_builtFor = (((Object)(object)player == (Object)null) ? null : player.GetSelectedPiece());
			TerrainComp comp = TerrainComp.FindTerrainCompiler(point);
			float levelOffset = settings.m_levelOffset;
			Vector3 val = point + Vector3.up * levelOffset;
			float target;
			Flat.Source source;
			if (Held.Active)
			{
				target = Held.Height;
				source = Flat.Source.Held;
			}
			else
			{
				target = Flat.Target(comp, val, radius, Reach.IsSquare(settings), out source);
			}
			Fill.Terms terms = Fill.Terms.None;
			string cost = null;
			string all = null;
			string carried = null;
			string own = null;
			string station = null;
			string items = null;
			bool far = false;
			if (wardClear && lands)
			{
				terms = Fill.Quote(player, settings, point, radius, out cost, out all, out carried, out own, out far, out station, out items);
			}
			Gap(point, radius, target, out var raise, out var lower);
			int crafting = Mathf.RoundToInt(JafnaPatches.CraftingLevel(player));
			string language = ((Localization.instance != null) ? Localization.instance.GetSelectedLanguage() : "");
			if (!Unchanged(point, radius, target, raise, lower, crafting, wardClear, far, source, terms, language, cost, all, carried, own, station, items) || _lines == null)
			{
				string text = crafting.ToString("0");
				List<string> list = new List<string>(13);
				list.Add(Slot(wardClear, far, terms, own, station));
				list.Add(Header("GROUND"));
				list.Add(Row("Raise up to: ", Metres(raise)));
				list.Add(Row("Lower up to: ", Metres(lower)));
				list.Add(Row("Flattens ", Num((radius * 2f).ToString("0.0") + "m"), " across (Crafting ", text, ")"));
				list.Add(Header("HEIGHT"));
				list.Add(Row("Crosshair: ", Num(val.y.ToString("0.00") + "m")));
				list.Add(Row("This swing: ", Num(target.ToString("0.00") + "m")));
				list.Add(Row("Taken from: ", Num(From(source))));
				list.Add(Header("COST"));
				list.Add(StoneRow(terms, cost, all, carried, ItemNames(items)));
				list.Add(WorkbenchRow(terms));
				list.Add(SwitchRow());
				List<string> list2 = list;
				if (list2.Count != 13 && !_countWarned)
				{
					_countWarned = true;
					JafnaPlugin.Log.LogError((object)("The build panel readout built " + list2.Count + " rows, not " + 13 + ". A branch added or dropped a row, which is the box changing size (LHM-46)."));
				}
				_lines = string.Join("\n", list2.ToArray());
				_panel = Panel(list2, raise, lower, val.y, target);
			}
		}

		private static string Panel(List<string> rows, float raise, float lower, float aim, float target)
		{
			return string.Join("\n", rows[0], Header("GROUND") + "<pos=50%>" + Header("HEIGHT"), Row("Raise: ", Metres(raise)) + "<pos=50%>" + Row("Aim: ", Num(aim.ToString("0.00") + "m")), Row("Lower: ", Metres(lower)) + "<pos=50%>" + Row("Swing: ", Num(target.ToString("0.00") + "m")), rows[4], rows[8], rows[10], rows[11] + "<pos=50%>" + FillCell());
		}

		private static string FillCell()
		{
			//IL_0032: 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_0040: Unknown result type (might be due to invalid IL or missing references)
			if (!JafnaConfig.AutoRaise.Value)
			{
				return Row("Fill: ", Num("host off"));
			}
			KeyCode value = JafnaConfig.StoneFillKey.Value;
			string text = (((int)value == 0) ? "" : (" (" + KeyName(value) + ")"));
			return Row("Fill: ", Num(StoneSwitch.On ? "on" : "off"), text);
		}

		private static bool Unchanged(Vector3 point, float radius, float target, float raise, float lower, int crafting, bool wardClear, bool far, Flat.Source source, Fill.Terms terms, string language, string cost, string all, string carried, string own, string station, string items)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: 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_01fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Unknown result type (might be due to invalid IL or missing references)
			//IL_021b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			int num = Mathf.RoundToInt(point.x * 100f);
			int num2 = Mathf.RoundToInt(point.y * 100f);
			int num3 = Mathf.RoundToInt(point.z * 100f);
			int num4 = Mathf.RoundToInt(radius * 100f);
			int num5 = Mathf.RoundToInt(target * 100f);
			int num6 = Mathf.RoundToInt(raise * 100f);
			int num7 = Mathf.RoundToInt(lower * 100f);
			if (num == _kx && num2 == _ky && num3 == _kz && num4 == _kRadius && num5 == _kTarget && num6 == _kRaise && num7 == _kLower && crafting == _kCrafting && wardClear == _kWard && far == _kFar && source == _kSource && terms == _kTerms && JafnaConfig.HoldKey.Value == _kKey && language == _kLanguage && StoneSwitch.On == _kSwitchOn && JafnaConfig.StoneFillKey.Value == _kSwitchKey && JafnaConfig.AutoRaise.Value == _kHostRaise && cost == _kCost && all == _kAll && carried == _kCarried && own == _kOwn && station == _kStation && items == _kItems)
			{
				return true;
			}
			_kx = num;
			_ky = num2;
			_kz = num3;
			_kRadius = num4;
			_kTarget = num5;
			_kRaise = num6;
			_kLower = num7;
			_kCrafting = crafting;
			_kWard = wardClear;
			_kFar = far;
			_kSource = source;
			_kTerms = terms;
			_kKey = JafnaConfig.HoldKey.Value;
			_kLanguage = language;
			_kSwitchOn = StoneSwitch.On;
			_kSwitchKey = JafnaConfig.StoneFillKey.Value;
			_kHostRaise = JafnaConfig.AutoRaise.Value;
			_kCost = cost;
			_kAll = all;
			_kCarried = carried;
			_kOwn = own;
			_kStation = station;
			_kItems = items;
			return false;
		}

		private static string ItemNames(string tokens)
		{
			if (string.IsNullOrEmpty(tokens))
			{
				return "";
			}
			Localization instance = Localization.instance;
			if (instance == null)
			{
				return tokens;
			}
			string selectedLanguage = instance.GetSelectedLanguage();
			if (selectedLanguage != _namesLanguage)
			{
				Names.Clear();
				_namesLanguage = selectedLanguage;
			}
			if (Names.TryGetValue(tokens, out var value))
			{
				return value;
			}
			string[] array = tokens.Split(new char[1] { '/' });
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = instance.Localize(array[i]);
			}
			value = string.Join("/", array);
			Names[tokens] = value;
			return value;
		}

		private static string SwitchRow()
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			if (!JafnaConfig.AutoRaise.Value)
			{
				return Row("Stone fill: ", Num("off"), ", this server has it off");
			}
			KeyCode value = JafnaConfig.StoneFillKey.Value;
			string text = (((int)value == 0) ? "" : (", " + KeyName(value) + " turns " + (StoneSwitch.On ? "off" : "on")));
			return Row("Stone fill: ", Num(StoneSwitch.On ? "on" : "off"), text);
		}

		private static string Slot(bool wardClear, bool far, Fill.Terms terms, string own, string station)
		{
			if (!wardClear)
			{
				return Warn("A ward you cannot use is in the swing");
			}
			if (far)
			{
				return Warn("Past 8m of the original ground");
			}
			switch (terms)
			{
			case Fill.Terms.NoStation:
				return Warn("Filling needs " + station + " nearby");
			case Fill.Terms.Short:
			case Fill.Terms.ShortCovered:
				return Warn(string.IsNullOrEmpty(own) ? "Short of stone, fills only the middle" : ("Short, the swing itself takes " + own));
			case Fill.Terms.Unaffordable:
				return Warn("Not enough stone to fill any of it");
			default:
				return Fit("<color=#d9d0bb>All clear</color>");
			}
		}

		private static string StoneRow(Fill.Terms terms, string cost, string all, string carried, string names)
		{
			switch (terms)
			{
			case Fill.Terms.Free:
				return Row("Stone: ", Num("free in this world"));
			case Fill.Terms.Covered:
				return Row("Stone: ", Num("already paid for"));
			case Fill.Terms.Paid:
				return Row("Stone: carry ", Num(carried), ", need ", Num(cost), " ", names);
			case Fill.Terms.Short:
			case Fill.Terms.ShortCovered:
			case Fill.Terms.Unaffordable:
				return Row("Stone: carry ", Num(carried), ", need ", Bad(all), " ", names);
			default:
				return Row("Stone: ", Num("-"));
			}
		}

		private static string WorkbenchRow(Fill.Terms terms)
		{
			return terms switch
			{
				Fill.Terms.None => Row("Workbench: ", Num("-")), 
				Fill.Terms.NoStation => Row("Workbench: ", Bad("none near")), 
				_ => Row("Workbench: ", Num("in range")), 
			};
		}

		private static string From(Flat.Source source)
		{
			return source switch
			{
				Flat.Source.Held => "HOLDING until " + HeldKeyName(), 
				Flat.Source.ContinuedFlat => "flat ground", 
				Flat.Source.Disagreed => "crosshair, heights disagree", 
				Flat.Source.TooLittle => "crosshair, too little flat", 
				_ => "your crosshair", 
			};
		}

		private static string HeldKeyName()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			return KeyName(JafnaConfig.HoldKey.Value);
		}

		private unsafe static string KeyName(KeyCode code)
		{
			string text = ((object)(*(KeyCode*)(&code))/*cast due to .constrained prefix*/).ToString();
			if (text.Length > 14)
			{
				return text.Substring(0, 14);
			}
			return text;
		}

		private static void Gap(Vector3 point, float radius, float target, out float raise, out float lower)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: 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)
			raise = 0f;
			lower = 0f;
			Maps.Clear();
			Heightmap.FindHeightmap(point, radius, Maps);
			int num2 = default(int);
			int num3 = default(int);
			Vector2 val2 = default(Vector2);
			for (int i = 0; i < Maps.Count; i++)
			{
				Heightmap val = Maps[i];
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				float num = radius / val.m_scale;
				if (num <= 0f)
				{
					continue;
				}
				val.WorldToVertex(point, ref num2, ref num3);
				int num4 = Mathf.CeilToInt(num);
				int num5 = val.m_width + 1;
				float y = ((Component)val).transform.position.y;
				((Vector2)(ref val2))..ctor((float)num2, (float)num3);
				for (int j = num3 - num4; j <= num3 + num4; j++)
				{
					for (int k = num2 - num4; k <= num2 + num4; k++)
					{
						if (k >= 0 && j >= 0 && k < num5 && j < num5 && !(Vector2.Distance(val2, new Vector2((float)k, (float)j)) > num))
						{
							float num6 = target - (val.GetHeight(k, j) + y);
							if (num6 > raise)
							{
								raise = num6;
							}
							if (0f - num6 > lower)
							{
								lower = 0f - num6;
							}
						}
					}
				}
			}
		}

		private static string Metres(float value)
		{
			return Num((value < 0.01f) ? "-" : (value.ToString("0.00") + "m"));
		}

		private static string Header(string name)
		{
			return "<color=#d9d0bb>" + name + "</color>";
		}

		private static string Warn(string text)
		{
			return Fit("<color=#ff6060>" + text + "</color>");
		}

		private static string Bad(string text)
		{
			return "<color=#ff6060>" + text + "</color>";
		}

		private static string Row(params string[] parts)
		{
			return Fit(string.Concat(parts));
		}

		private static string Fit(string line)
		{
			string text = ((Localization.instance != null && line.IndexOf('$') >= 0) ? Localization.instance.Localize(line) : line);
			string text2 = Plain(text);
			if (text2.Length > 40)
			{
				return text2.Substring(0, 38) + "..";
			}
			return text;
		}

		internal static string Plain(string line)
		{
			StringBuilder stringBuilder = new StringBuilder(line.Length);
			bool flag = false;
			foreach (char c in line)
			{
				if (c == '<')
				{
					flag = true;
				}
				else if (c == '>' && flag)
				{
					flag = false;
				}
				else if (!flag)
				{
					stringBuilder.Append(c);
				}
			}
			return stringBuilder.ToString();
		}

		internal static void Clear()
		{
			_lines = null;
			_panel = null;
		}

		private static string Num(string text)
		{
			return "<color=orange>" + text + "</color>";
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(Player), "UpdatePlacement", new Type[]
		{
			typeof(bool),
			typeof(float)
		})]
		private static void UpdatePlacement(Player __instance)
		{
			if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer)
			{
				return;
			}
			Piece selectedPiece = __instance.GetSelectedPiece();
			if ((Object)(object)selectedPiece != (Object)(object)_described)
			{
				Restore();
			}
			if (_lines != null && (Object)(object)selectedPiece != (Object)(object)_builtFor)
			{
				_lines = null;
				_panel = null;
				Restore();
				return;
			}
			if (!JafnaConfig.Enabled.Value || _lines == null || (Object)(object)selectedPiece == (Object)null)
			{
				Restore();
				return;
			}
			if ((Object)(object)_described == (Object)null)
			{
				_described = selectedPiece;
				_original = selectedPiece.m_description;
			}
			string panel = _panel;
			if (!(panel == _written))
			{
				selectedPiece.m_description = panel;
				_written = panel;
				LogLabelOnce();
				GrowLabel();
			}
		}

		private static void GrowLabel()
		{
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			Hud instance = Hud.instance;
			if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.m_pieceDescription == (Object)null))
			{
				RectTransform rectTransform = instance.m_pieceDescription.rectTransform;
				Rect rect;
				if (_labelHeight < 0f)
				{
					rect = rectTransform.rect;
					_labelHeight = ((Rect)(ref rect)).height;
					_labelAt = rectTransform.anchoredPosition;
				}
				instance.m_pieceDescription.enableAutoSizing = false;
				instance.m_pieceDescription.fontSize = 14f;
				instance.m_pieceDescription.lineSpacing = -12f;
				TMP_Text pieceDescription = instance.m_pieceDescription;
				string text = instance.m_pieceDescription.text;
				rect = rectTransform.rect;
				float y = pieceDescription.GetPreferredValues(text, ((Rect)(ref rect)).width, 0f).y;
				float num = Mathf.Max(_labelHeight, y);
				rectTransform.SetSizeWithCurrentAnchors((Axis)1, num);
				float num2 = num - _labelHeight;
				rectTransform.anchoredPosition = _labelAt + new Vector2(0f, (0f - num2) * (1f - rectTransform.pivot.y));
			}
		}

		private static void UngrowLabel()
		{
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			if (!(_labelHeight < 0f))
			{
				Hud instance = Hud.instance;
				if ((Object)(object)instance != (Object)null && (Object)(object)instance.m_pieceDescription != (Object)null)
				{
					instance.m_pieceDescription.enableAutoSizing = true;
					instance.m_pieceDescription.lineSpacing = 0f;
					instance.m_pieceDescription.rectTransform.SetSizeWithCurrentAnchors((Axis)1, _labelHeight);
					instance.m_pieceDescription.rectTransform.anchoredPosition = _labelAt;
				}
				_labelHeight = -1f;
			}
		}

		private static void LogLabelOnce()
		{
			//IL_00c1: 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_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			if (!_labelLogged)
			{
				Hud instance = Hud.instance;
				if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.m_pieceDescription == (Object)null))
				{
					TMP_Text pieceDescription = instance.m_pieceDescription;
					pieceDescription.ForceMeshUpdate(false, false);
					_labelLogged = true;
					ManualLogSource log = JafnaPlugin.Log;
					string[] obj = new string[12]
					{
						"Build panel label: auto=",
						pieceDescription.enableAutoSizing.ToString(),
						" size=",
						pieceDescription.fontSize.ToString("0.0"),
						" min=",
						pieceDescription.fontSizeMin.ToString(),
						" max=",
						pieceDescription.fontSizeMax.ToString(),
						" rect=",
						null,
						null,
						null
					};
					Rect rect = pieceDescription.rectTransform.rect;
					obj[9] = ((Rect)(ref rect)).width.ToString("0");
					obj[10] = "x";
					rect = pieceDescription.rectTransform.rect;
					obj[11] = ((Rect)(ref rect)).height.ToString("0");
					log.LogInfo((object)string.Concat(obj));
				}
			}
		}

		private static void Restore()
		{
			if (!((Object)(object)_described == (Object)null))
			{
				if ((Object)(object)_described != (Object)null)
				{
					_described.m_description = _original;
				}
				UngrowLabel();
				_described = null;
				_original = null;
				_written = null;
			}
		}
	}
	internal static class ReadoutProbe
	{
		[HarmonyPatch(typeof(Terminal), "InitTerminal")]
		internal static class Hook
		{
			[CompilerGenerated]
			private static class <>O
			{
				public static ConsoleEvent <0>__OnCommand;
			}

			private static void Postfix()
			{
				//IL_003d: Unknown result type (might be due to invalid IL or missing references)
				//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: Expected O, but got Unknown
				if (!_registered)
				{
					_registered = true;
					object obj = <>O.<0>__OnCommand;
					if (obj == null)
					{
						ConsoleEvent val = OnCommand;
						<>O.<0>__OnCommand = val;
						obj = (object)val;
					}
					new ConsoleCommand("jafnareadout", "- dev: how many lines the build panel's description drew, how tall it is and whether a line wrapped, for a scenario to hold equal from state to state", (ConsoleEvent)obj, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				}
			}
		}

		private static bool _registered;

		private static string RowKind(string[] rows, int at)
		{
			if (at >= rows.Length)
			{
				return "missing";
			}
			if (!Readout.Plain(rows[at]).EndsWith(" -"))
			{
				return "text";
			}
			return "dash";
		}

		private static void OnCommand(ConsoleEventArgs args)
		{
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: 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_00e7: Unknown result type (might be due to invalid IL or missing references)
			Terminal context = args.Context;
			if ((Object)(object)context == (Object)null)
			{
				return;
			}
			Hud instance = Hud.instance;
			TMP_Text val = (((Object)(object)instance == (Object)null) ? null : instance.m_pieceDescription);
			if ((Object)(object)val == (Object)null || !((Component)val).gameObject.activeInHierarchy)
			{
				context.AddString("jafnareadout: the build panel is not up, take the hoe out first");
				return;
			}
			val.ForceMeshUpdate(false, false);
			int num = val.text.Split(new char[1] { '\n' }).Length;
			int lineCount = val.textInfo.lineCount;
			float num2 = 0f;
			for (int i = 0; i < lineCount; i++)
			{
				if (val.textInfo.lineInfo[i].width > num2)
				{
					num2 = val.textInfo.lineInfo[i].width;
				}
			}
			Rect rect = val.rectTransform.rect;
			float num3 = ((Rect)(ref rect)).width - val.margin.x - val.margin.z;
			string lines = Readout.Lines;
			string[] array = ((lines == null) ? new string[0] : lines.Split(new char[1] { '\n' }));
			bool flag = false;
			for (int j = 0; j < array.Length; j++)
			{
				if (Readout.Plain(array[j]).EndsWith(".."))
				{
					flag = true;
				}
			}
			string panelText = Readout.PanelText;
			string text = ((panelText != null && val.text.EndsWith(panelText)) ? "yes" : "no");
			context.AddString("jafnareadout lines=" + lineCount + " written=" + num + " wrapped=" + ((lineCount == num) ? "no" : "yes") + " tall