Decompiled source of Jafna v1.0.0

plugins/Jafna/Jafna.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using Ezomic.Core;
using Ezomic.Shared;
using HarmonyLib;
using Microsoft.CodeAnalysis;
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.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+4820a6d4865ec23461e744d98cfe94b8b1c097e1")]
[assembly: AssemblyProduct("Jafna")]
[assembly: AssemblyTitle("Jafna")]
[assembly: AssemblyVersion("1.0.0.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 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<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. 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.");
			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, and whether that came from the crosshair or from flat ground the swing is continuing. 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 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 + ". ";
				}
				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);
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(TerrainOp), "GetRadius")]
		private static void WidenSearch(TerrainOp __instance, ref float __result)
		{
			if (JafnaConfig.Enabled.Value && Reach.IsFlatten(__instance.m_settings))
			{
				__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_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: 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;
			}
			float num = Reach.Earned(modifier.m_settings, Player.m_localPlayer);
			bool active = Held.Active;
			if (num <= Reach.VanillaRadius(modifier.m_settings) && !active)
			{
				return true;
			}
			ZNetView val = Reach.View(__instance);
			if ((Object)(object)val == (Object)null)
			{
				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, num, active, Held.Height);
			val.InvokeRPC("RPC_ApplyOperation", new object[1] { val2 });
			return false;
		}

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

		[HarmonyPrefix]
		[HarmonyPatch(typeof(TerrainComp), "DoOperation")]
		private static void LevelTo(TerrainComp __instance, ref Vector3 pos, ref Settings modifier)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: 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_013d: Unknown result type (might be due to invalid IL or missing references)
			if (!JafnaConfig.Enabled.Value)
			{
				return;
			}
			bool held;
			float height;
			float num = Reach.TakeIncoming(out held, out height);
			if (!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;
				}
			}
			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(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_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: 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;
			if (Keys.Pressed(JafnaConfig.HoldKey.Value))
			{
				Held.Toggle(position.y);
			}
			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;
			}
			Readout.Update(__instance, val, position, radius, flag);
		}

		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;
		}
	}
	[BepInPlugin("ezomic.valheim.jafna", "Jafna", "1.0.0")]
	[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.0.0";

		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(Readout));
			Log.LogInfo((object)"Jafna 1.0.0 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.0.0", ((BaseUnityPlugin)this).Config, (Requirement)1, (Assembly)null);
			Suite.Sync((ConfigEntryBase)(object)JafnaConfig.Enabled);
			Suite.Sync((ConfigEntryBase)(object)JafnaConfig.MaxRadius);
			Suite.Sync((ConfigEntryBase)(object)JafnaConfig.RespectWardFootprint);
		}

		private void OnDestroy()
		{
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
		}
	}
	internal static class Keys
	{
		internal static bool Pressed(KeyCode key)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			if ((int)key == 0)
			{
				return false;
			}
			if (!ZInput.GetKeyDown(key, false))
			{
				return false;
			}
			if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus())
			{
				return false;
			}
			if (!Console.IsVisible())
			{
				return !TextInput.IsVisible();
			}
			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 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 the reach will not travel to other players this session and levelling stays vanilla width online. Singleplayer is unaffected. " + 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 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)
		{
			pkg.Write(1245791822);
			pkg.Write(radius);
			pkg.Write(held);
			pkg.Write(height);
		}

		internal static bool Peek(ZPackage pkg, out float radius, out bool held, out float height)
		{
			//IL_001e: 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)
			radius = -1f;
			held = false;
			height = 0f;
			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();
				return radius > 0f;
			}
			catch
			{
				return false;
			}
			finally
			{
				pkg.SetPos(pos);
			}
		}

		internal static void SetIncoming(float radius, bool held, float height)
		{
			_incoming = radius;
			_incomingHeld = held;
			_incomingHeight = height;
		}

		internal static float TakeIncoming(out bool held, out float height)
		{
			float incoming = _incoming;
			held = _incomingHeld;
			height = _incomingHeight;
			_incoming = -1f;
			_incomingHeld = false;
			_incomingHeight = 0f;
			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;

		internal static void Update(Player player, Settings settings, Vector3 point, float radius, bool wardClear)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: 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_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			if (!JafnaConfig.ShowReadout.Value)
			{
				_lines = null;
				return;
			}
			TerrainComp comp = TerrainComp.FindTerrainCompiler(point);
			float levelOffset = settings.m_levelOffset;
			Vector3 worldPos = point + Vector3.up * levelOffset;
			float num;
			Flat.Source source;
			if (Held.Active)
			{
				num = Held.Height;
				source = Flat.Source.Held;
			}
			else
			{
				num = Flat.Target(comp, worldPos, radius, Reach.IsSquare(settings), out source);
			}
			float num2 = Reach.VanillaRadius(settings);
			string text = source switch
			{
				Flat.Source.Held => "HOLDING height, press " + ((object)JafnaConfig.HoldKey.Value/*cast due to .constrained prefix*/).ToString() + " to release. Levelling to ", 
				Flat.Source.ContinuedFlat => "Continuing ground you already flattened, to ", 
				Flat.Source.Disagreed => "Two heights meet here, so using your crosshair at ", 
				Flat.Source.TooLittle => "Too little flat ground to follow, so using your crosshair at ", 
				_ => "Fresh ground, levelling to your crosshair at ", 
			};
			string text2 = JafnaPatches.CraftingLevel(player).ToString("0");
			string text3 = ((!(radius > num2 + 0.01f)) ? ("Flattens " + Num((radius * 2f).ToString("0.0") + "m") + " across, the hoe's own reach (Crafting " + text2 + " adds nothing yet)") : ("Flattens " + Num((radius * 2f).ToString("0.0") + "m") + " across, up from the hoe's own " + (num2 * 2f).ToString("0.0") + "m (Crafting " + text2 + ")"));
			text3 = text3 + "\n" + text + Num(num.ToString("0.00") + "m");
			if (!wardClear)
			{
				text3 += "\n<color=#ff6060>A ward you cannot use is inside this swing.</color>";
			}
			_lines = text3;
		}

		internal static void Clear()
		{
			_lines = 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 (!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 text = (string.IsNullOrEmpty(_original) ? _lines : (_original + "\n\n" + _lines));
			if (!(text == _written))
			{
				selectedPiece.m_description = text;
				_written = text;
			}
		}

		private static void Restore()
		{
			if (!((Object)(object)_described == (Object)null))
			{
				if ((Object)(object)_described != (Object)null)
				{
					_described.m_description = _original;
				}
				_described = null;
				_original = null;
				_written = null;
			}
		}
	}
}