Decompiled source of TerraHoe v2.0.0

TerraHoe.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: InternalsVisibleTo("TerraHoe.WireTest")]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("TerraHoe")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("TerraHoe")]
[assembly: AssemblyTitle("TerraHoe")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace TerraHoe;

[BepInPlugin("terrahoe", "TerraHoe", "2.0.0")]
[BepInProcess("valheim.exe")]
public class TerraHoePlugin : BaseUnityPlugin
{
	[HarmonyPatch(typeof(TerrainComp), "LevelTerrain")]
	private static class LevelTerrainPatch
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return RewriteClamp(instructions, "TerraformDelta");
		}
	}

	[HarmonyPatch(typeof(TerrainComp), "RaiseTerrain")]
	private static class RaiseTerrainPatch
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return RewriteClamp(instructions, "TerraformDelta");
		}
	}

	[HarmonyPatch(typeof(TerrainComp), "ApplyToHeightmap")]
	private static class ApplyToHeightmapPatch
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return RewriteClamp(instructions, "TerraformHeight");
		}
	}

	private static class RadiusWire
	{
		internal const int Magic = 1413829170;

		internal const int Version = 2;

		internal const int PayloadBytes = 33;

		internal const int MaxScanBytes = 96;

		internal const float MaxSaneRadius = 500f;

		internal const float MaxSaneOffset = 200f;

		private const int FlagLevel = 1;

		private const int FlagRaise = 2;

		private const int FlagSmooth = 4;

		private const int FlagPaint = 8;

		private const int FlagSquare = 16;

		private static readonly MethodInfo _clone = typeof(object).GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic);

		private static bool _failureLogged;

		internal static void LogFailure(Exception ex)
		{
			if (!_failureLogged)
			{
				_failureLogged = true;
				if (_log != null)
				{
					_log.LogWarning((object)("RadiusWire failed, terrain falls back to the vanilla radius sync: " + ex));
				}
			}
		}

		internal static void Write(Settings settings, ZPackage pkg)
		{
			if (settings != null && pkg != null && !(_clone == null))
			{
				pkg.Write(1413829170);
				pkg.Write(2);
				pkg.Write(FlagsOf(settings));
				pkg.Write(settings.m_levelRadius);
				pkg.Write(settings.m_raiseRadius);
				pkg.Write(settings.m_smoothRadius);
				pkg.Write(settings.m_paintRadius);
				pkg.Write(settings.m_square);
				pkg.Write(settings.m_levelOffset);
				if (NetworkLogEnabled())
				{
					DebugLog($"wire sent: level={settings.m_level}/{settings.m_levelRadius:0.###}, " + $"raise={settings.m_raise}/{settings.m_raiseRadius:0.###}, " + $"smooth={settings.m_smooth}/{settings.m_smoothRadius:0.###}, " + $"paint={settings.m_paintCleared}/{settings.m_paintRadius:0.###}, square={settings.m_square}");
				}
			}
		}

		internal static Settings Read(ZPackage pkg, Settings fallback)
		{
			if (pkg == null || fallback == null || _clone == null)
			{
				return fallback;
			}
			int pos = pkg.GetPos();
			int num = pkg.Size();
			try
			{
				int num2 = Mathf.Min(pos + 96, num - 33);
				for (int i = pos; i <= num2; i++)
				{
					pkg.SetPos(i);
					if (pkg.ReadInt() == 1413829170)
					{
						int num3 = pkg.ReadInt();
						if (num3 != 2)
						{
							DebugLog($"wire payload version {num3} ignored, expected {2}");
							break;
						}
						int num4 = pkg.ReadInt();
						float num5 = pkg.ReadSingle();
						float num6 = pkg.ReadSingle();
						float num7 = pkg.ReadSingle();
						float num8 = pkg.ReadSingle();
						bool flag = pkg.ReadBool();
						float num9 = pkg.ReadSingle();
						if (!SaneRadius(num5) || !SaneRadius(num6) || !SaneRadius(num7) || !SaneRadius(num8) || !SaneOffset(num9))
						{
							DebugWarning("wire payload rejected by validation, vanilla settings kept");
							break;
						}
						if (NetworkLogEnabled())
						{
							DebugLog($"wire received at +{i - pos}: level={num5:0.###}, " + $"raise={num6:0.###}, smooth={num7:0.###}, " + $"paint={num8:0.###}, square={flag}, flags={num4}");
						}
						return WithSettings(fallback, num4, num5, num6, num7, num8, flag, num9);
					}
				}
			}
			catch (Exception ex)
			{
				LogFailure(ex);
			}
			pkg.SetPos(pos);
			return fallback;
		}

		private static int FlagsOf(Settings s)
		{
			int num = 0;
			if (s.m_level)
			{
				num |= 1;
			}
			if (s.m_raise)
			{
				num |= 2;
			}
			if (s.m_smooth)
			{
				num |= 4;
			}
			if (s.m_paintCleared)
			{
				num |= 8;
			}
			if (s.m_square)
			{
				num |= 0x10;
			}
			return num;
		}

		private static Settings WithSettings(Settings src, int flags, float levelRadius, float raiseRadius, float smoothRadius, float paintRadius, bool square, float levelOffset)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			Settings val = (Settings)_clone.Invoke(src, null);
			if (val == null)
			{
				return src;
			}
			val.m_level = (flags & 1) != 0;
			val.m_raise = (flags & 2) != 0;
			val.m_smooth = (flags & 4) != 0;
			val.m_paintCleared = (flags & 8) != 0;
			val.m_square = square;
			val.m_levelRadius = levelRadius;
			val.m_raiseRadius = raiseRadius;
			val.m_smoothRadius = smoothRadius;
			val.m_paintRadius = paintRadius;
			val.m_levelOffset = levelOffset;
			return val;
		}

		private static bool SaneRadius(float radius)
		{
			if (!float.IsNaN(radius) && !float.IsInfinity(radius) && radius >= 0f)
			{
				return radius <= 500f;
			}
			return false;
		}

		private static bool SaneOffset(float offset)
		{
			if (!float.IsNaN(offset) && !float.IsInfinity(offset) && offset >= -200f)
			{
				return offset <= 200f;
			}
			return false;
		}
	}

	[HarmonyPatch(typeof(Settings), "Serialize")]
	private static class SettingsSerializePatch
	{
		private static void Postfix(Settings __instance, ZPackage pkg)
		{
			if (!IsActive())
			{
				return;
			}
			try
			{
				RadiusWire.Write(__instance, pkg);
			}
			catch (Exception ex)
			{
				RadiusWire.LogFailure(ex);
			}
		}
	}

	[HarmonyPatch(typeof(Settings), "Deserialize")]
	private static class SettingsDeserializePatch
	{
		private static void Postfix(ZPackage pkg, ref Settings __result)
		{
			if (!IsActive())
			{
				return;
			}
			try
			{
				__result = RadiusWire.Read(pkg, __result);
			}
			catch (Exception ex)
			{
				RadiusWire.LogFailure(ex);
			}
		}
	}

	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	private static class ZNetSceneAwakePatch
	{
		[HarmonyPriority(0)]
		private static void Postfix(ZNetScene __instance)
		{
			if (!_suppressHoeDust.Value || (Object)(object)__instance == (Object)null || __instance.m_prefabs == null)
			{
				return;
			}
			int num = 0;
			for (int i = 0; i < __instance.m_prefabs.Count; i++)
			{
				GameObject val = __instance.m_prefabs[i];
				if (!((Object)(object)val == (Object)null))
				{
					Piece component = val.GetComponent<Piece>();
					if ((Object)(object)component != (Object)null)
					{
						num += StripHoeDust(component);
					}
				}
			}
			if (num > 0 && !_dustStripLogged)
			{
				_dustStripLogged = true;
				_log.LogInfo((object)$"Dust suppression: removed {num} dust vfx entr(ies) from the hoe build pieces.");
			}
		}
	}

	[HarmonyPatch(typeof(Player), "PlacePiece")]
	private static class PlayerPlacePiecePatch
	{
		private static void Prefix(ref Piece piece)
		{
			StripHoeDust(piece);
		}
	}

	public const string PluginGuid = "terrahoe";

	public const string PluginName = "TerraHoe";

	public const string PluginVersion = "2.0.0";

	internal const float VanillaDeltaLimit = 8f;

	internal const float VanillaHeightRange = 16f;

	internal const float Epsilon = 0.001f;

	private static ConfigEntry<bool> _enabled;

	private static ConfigEntry<KeyboardShortcut> _scrollModifier;

	private static ConfigEntry<float> _scrollStep;

	private static ConfigEntry<float> _minRadius;

	private static ConfigEntry<float> _maxRadius;

	private static ConfigEntry<bool> _showMessage;

	private static ConfigEntry<bool> _fullTerraformingEnabled;

	private static ConfigEntry<KeyboardShortcut> _fullTerraformingToggle;

	private static ConfigEntry<float> _minTerraformHeight;

	private static ConfigEntry<float> _maxTerraformHeight;

	private static ConfigEntry<bool> _suppressHoeDust;

	private static ConfigEntry<bool> _debugLogging;

	private static ConfigEntry<bool> _debugInput;

	private static ConfigEntry<bool> _debugSelection;

	private static ConfigEntry<bool> _debugNetwork;

	private static ConfigEntry<KeyboardShortcut> _debugToggle;

	private static ManualLogSource _log;

	private static FieldInfo _buildPieces;

	private static FieldInfo _placementGhost;

	private static TerrainOp _activeOperation;

	private static float _baseLevel;

	private static float _baseRaise;

	private static float _baseSmooth;

	private static float _basePaint;

	private static float _baseRadius;

	private static bool _baseLevelEnabled;

	private static bool _baseRaiseEnabled;

	private static bool _baseSmoothEnabled;

	private static bool _basePaintEnabled;

	private static float _currentRadius;

	private static float _lastMessageRadius = -1f;

	private static bool _lastModifierState;

	private static string _lastSelectionName;

	private static bool _fullTerraformingActive;

	private static bool _lastToggleState;

	private static bool _lastAppliedFullMode;

	private static bool _narrowClampWarned;

	private static bool _dustStripLogged;

	private void Awake()
	{
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_0235: Unknown result type (might be due to invalid IL or missing references)
		//IL_0290: Unknown result type (might be due to invalid IL or missing references)
		//IL_0305: Unknown result type (might be due to invalid IL or missing references)
		//IL_030a: Unknown result type (might be due to invalid IL or missing references)
		//IL_030d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0377: Unknown result type (might be due to invalid IL or missing references)
		//IL_037c: Unknown result type (might be due to invalid IL or missing references)
		//IL_037f: Unknown result type (might be due to invalid IL or missing references)
		_log = ((BaseUnityPlugin)this).Logger;
		_enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable TerraHoe radius control.");
		_fullTerraformingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "FullTerraformingEnabled", true, "Allow terrain edits far beyond the vanilla +/-8 m limit. This is part of the terrain data contract: use the same value (and the same FullTerraforming.MinHeight/MaxHeight) on the server and on every client, otherwise the stored terrain cannot match everywhere.");
		_fullTerraformingToggle = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("General", "FullTerraformingToggle", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Local toggle: place the flatten (level) operation only, instead of the selected hoe mode.");
		_scrollModifier = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Radius", "ScrollModifier", new KeyboardShortcut((KeyCode)306, Array.Empty<KeyCode>()), "Modifier held while scrolling the mouse wheel.");
		_scrollStep = ((BaseUnityPlugin)this).Config.Bind<float>("Radius", "ScrollStepMeters", 0.5f, "Radius change per mouse-wheel notch.");
		_minRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Radius", "MinRadius", 0.5f, "Minimum radius in metres.");
		_maxRadius = ((BaseUnityPlugin)this).Config.Bind<float>("Radius", "MaxRadius", 100f, "Maximum radius in metres.");
		_showMessage = ((BaseUnityPlugin)this).Config.Bind<bool>("Radius", "ShowRadiusMessage", true, "Show the current radius in the top-left corner.");
		_minTerraformHeight = ((BaseUnityPlugin)this).Config.Bind<float>("FullTerraforming", "MinHeight", -200f, "Lowest per-vertex terrain delta in metres (negative = dig down). Values above -8 are ignored so the vanilla limit is never tightened.");
		_maxTerraformHeight = ((BaseUnityPlugin)this).Config.Bind<float>("FullTerraforming", "MaxHeight", 200f, "Highest per-vertex terrain delta in metres. Values below 8 are ignored so the vanilla limit is never tightened.");
		_suppressHoeDust = ((BaseUnityPlugin)this).Config.Bind<bool>("Effects", "SuppressHoeDust", false, "Strip the dust vfx from the vanilla hoe pieces (road/raise/path/paved). Leave this OFF when NoHoeDust or MyDirtyHoe is installed: NoHoeDust does exactly the same work, and MyDirtyHoe replaces those effect lists with its own.");
		_debugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "EnableDebugLogging", true, "Write TerraHoe diagnostic events to the BepInEx log.");
		_debugInput = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogInput", true, "When debug logging is enabled, log modifier and wheel events.");
		_debugSelection = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogSelection", true, "When debug logging is enabled, log selected TerrainOp changes.");
		_debugNetwork = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "LogNetwork", false, "Log the terrain settings that TerraHoe sends to and receives from the heightmap owner.");
		_debugToggle = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Debug", "DebugToggle", new KeyboardShortcut((KeyCode)290, Array.Empty<KeyCode>()), "Toggle diagnostic logging while playing.");
		_buildPieces = AccessTools.Field(typeof(Player), "m_buildPieces");
		_placementGhost = AccessTools.Field(typeof(Player), "m_placementGhost");
		_currentRadius = 0f;
		_fullTerraformingActive = false;
		new Harmony("terrahoe").PatchAll(typeof(TerraHoePlugin).Assembly);
		_log.LogInfo((object)"TerraHoe v2.0.0 loaded. RadiusWire network sync active.");
		ManualLogSource log = _log;
		object[] obj = new object[4] { _minRadius.Value, _maxRadius.Value, _scrollStep.Value, null };
		KeyboardShortcut value = _scrollModifier.Value;
		obj[3] = ((KeyboardShortcut)(ref value)).MainKey;
		log.LogInfo((object)string.Format("Radius: {0:0.###}-{1:0.###} m, step {2:0.###} m, modifier {3}", obj));
		_log.LogInfo((object)$"Terrain clamps: delta {ClampLow():0.#}..{ClampHigh():0.#} m (FullTerraformingEnabled={_fullTerraformingEnabled.Value})");
		_log.LogInfo((object)"Terrain clamps are a multiplayer contract: identical config on the server and on every client.");
		ManualLogSource log2 = _log;
		value = _fullTerraformingToggle.Value;
		log2.LogInfo((object)$"Flatten-only toggle: {((KeyboardShortcut)(ref value)).MainKey}, active={_fullTerraformingActive}");
		_log.LogInfo((object)$"Dust suppression: {_suppressHoeDust.Value}");
		_log.LogInfo((object)$"Debug config: enabled={_debugLogging.Value}, input={_debugInput.Value}, selection={_debugSelection.Value}, network={_debugNetwork.Value}");
		DebugLog($"Reflection: m_buildPieces={_buildPieces != null}, m_placementGhost={_placementGhost != null}");
		LogModConflicts();
	}

	private static void LogModConflicts()
	{
		bool flag = false;
		bool flag2 = false;
		Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
		for (int i = 0; i < assemblies.Length; i++)
		{
			string name = assemblies[i].GetName().Name;
			if (name == "MyDirtyHoe")
			{
				flag = true;
			}
			else if (name == "NoHoeDust")
			{
				flag2 = true;
			}
		}
		if (flag)
		{
			_log.LogWarning((object)"MyDirtyHoe is loaded and patches the same terrain methods as TerraHoe (TerrainOp.Settings.Serialize/Deserialize, TerrainComp.ApplyOperation, TerrainOp.Awake). Terrain operations can be applied twice and the radius payloads compete. Run only one of the two mods.");
		}
		if (flag2)
		{
			string text = (_suppressHoeDust.Value ? "SuppressHoeDust is enabled as well, so both mods strip the same dust vfx. Drop one." : "Dust stays with NoHoeDust, TerraHoe does not touch any effect list.");
			_log.LogInfo((object)("NoHoeDust is loaded: " + text));
		}
		else if (!_suppressHoeDust.Value)
		{
			_log.LogInfo((object)"No dust-removal mod detected and SuppressHoeDust is off: vanilla hoe dust stays active.");
		}
	}

	private static bool IsActive()
	{
		if (_enabled != null)
		{
			return _enabled.Value;
		}
		return false;
	}

	private static void DebugLog(string message)
	{
		if (_debugLogging != null && _debugLogging.Value)
		{
			_log.LogInfo((object)("[Debug] " + message));
		}
	}

	private static void DebugWarning(string message)
	{
		if (_debugLogging != null && _debugLogging.Value)
		{
			_log.LogWarning((object)("[Debug] " + message));
		}
	}

	internal static bool NetworkLogEnabled()
	{
		if (_debugNetwork != null)
		{
			return _debugNetwork.Value;
		}
		return false;
	}

	internal static bool WideTerraforming()
	{
		if (_fullTerraformingEnabled != null)
		{
			return _fullTerraformingEnabled.Value;
		}
		return false;
	}

	internal static float ClampLow()
	{
		if (!WideTerraforming() || _minTerraformHeight == null)
		{
			return -8f;
		}
		return Mathf.Min(-8f, _minTerraformHeight.Value);
	}

	internal static float ClampHigh()
	{
		if (!WideTerraforming() || _maxTerraformHeight == null)
		{
			return 8f;
		}
		return Mathf.Max(8f, _maxTerraformHeight.Value);
	}

	private static void WarnNarrowClamp(float delta)
	{
		if (!_narrowClampWarned && _log != null)
		{
			_narrowClampWarned = true;
			_log.LogWarning((object)($"A terrain edit of {delta:0.#} m arrived while FullTerraformingEnabled is off, so the " + $"vanilla +/-{8f:0.#} m clamp trims it on this machine. Enable " + "General.FullTerraformingEnabled and use the same FullTerraforming.MinHeight/MaxHeight on the server and on every client, otherwise the terrain cannot match."));
		}
	}

	private void Update()
	{
		//IL_0022: 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_011b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0120: 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_00f9: 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_0102: Unknown result type (might be due to invalid IL or missing references)
		//IL_019f: Unknown result type (might be due to invalid IL or missing references)
		if (!IsActive())
		{
			return;
		}
		try
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				return;
			}
			KeyboardShortcut value = _fullTerraformingToggle.Value;
			bool num = ((KeyboardShortcut)(ref value)).IsDown();
			if (num && !_lastToggleState)
			{
				if (_fullTerraformingEnabled.Value)
				{
					_fullTerraformingActive = !_fullTerraformingActive;
					string text = (_fullTerraformingActive ? "ON" : "OFF");
					((Character)localPlayer).Message((MessageType)1, "TerraHoe full terraforming: " + text, 0, (Sprite)null, false);
					_log.LogInfo((object)("Full terraforming toggled " + text + "."));
				}
				else
				{
					_log.LogInfo((object)"Full terraforming toggle ignored: disabled by global config.");
				}
			}
			_lastToggleState = num;
			if (_debugLogging.Value)
			{
				value = _debugToggle.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					_debugLogging.Value = false;
					((Character)localPlayer).Message((MessageType)1, "TerraHoe debug logging: OFF", 0, (Sprite)null, false);
					ManualLogSource log = _log;
					value = _debugToggle.Value;
					log.LogInfo((object)$"Debug logging toggled OFF by {((KeyboardShortcut)(ref value)).MainKey}.");
				}
			}
			value = _scrollModifier.Value;
			bool flag = ((KeyboardShortcut)(ref value)).IsPressed();
			if (flag != _lastModifierState)
			{
				_lastModifierState = flag;
				if (_debugLogging.Value && _debugInput.Value)
				{
					_log.LogInfo((object)("[Debug] modifier " + (flag ? "PRESSED" : "RELEASED")));
				}
			}
			TerrainOp selectedTerrainOp = GetSelectedTerrainOp(localPlayer);
			TrackSelection(selectedTerrainOp);
			if ((Object)(object)selectedTerrainOp == (Object)null)
			{
				RestoreActiveOperation();
				return;
			}
			EnsureActiveOperation(selectedTerrainOp);
			ApplyFullTerraformingMode(selectedTerrainOp);
			float y = Input.mouseScrollDelta.y;
			if (!(Mathf.Abs(y) < 0.001f))
			{
				if (_debugLogging.Value && _debugInput.Value)
				{
					_log.LogInfo((object)$"[Debug] wheel={y:0.###}, modifierHeld={flag}, radiusBefore={_currentRadius:0.###}");
				}
				if (flag)
				{
					ChangeRadius(y * _scrollStep.Value);
				}
			}
		}
		catch (Exception ex)
		{
			DebugWarning("Update failed: " + ex);
		}
	}

	private static TerrainOp GetSelectedTerrainOp(Player player)
	{
		if (_buildPieces == null)
		{
			return null;
		}
		object? value = _buildPieces.GetValue(player);
		PieceTable val = (PieceTable)((value is PieceTable) ? value : null);
		Piece val2 = (((Object)(object)val != (Object)null) ? val.GetSelectedPiece() : null);
		if ((Object)(object)val2 == (Object)null)
		{
			return null;
		}
		return ((Component)val2).gameObject.GetComponent<TerrainOp>();
	}

	private static void TrackSelection(TerrainOp operation)
	{
		string text = (((Object)(object)operation == (Object)null) ? "<none>" : ((Object)((Component)operation).gameObject).name);
		if (!(text == _lastSelectionName))
		{
			_lastSelectionName = text;
			if (_debugLogging != null && _debugLogging.Value && _debugSelection.Value)
			{
				_log.LogInfo((object)("[Debug] selected TerrainOp=" + text));
			}
		}
	}

	private static void EnsureActiveOperation(TerrainOp operation)
	{
		if (!((Object)(object)_activeOperation == (Object)(object)operation))
		{
			RestoreActiveOperation();
			_activeOperation = operation;
			Settings settings = operation.m_settings;
			if (settings == null)
			{
				DebugWarning("Selected TerrainOp has null settings: " + ((Object)((Component)operation).gameObject).name);
				_activeOperation = null;
				return;
			}
			_baseLevel = settings.m_levelRadius;
			_baseRaise = settings.m_raiseRadius;
			_baseSmooth = settings.m_smoothRadius;
			_basePaint = settings.m_paintRadius;
			_baseLevelEnabled = settings.m_level;
			_baseRaiseEnabled = settings.m_raise;
			_baseSmoothEnabled = settings.m_smooth;
			_basePaintEnabled = settings.m_paintCleared;
			_baseRadius = PickRadius(settings);
			_currentRadius = _baseRadius;
			ScalePreview(1f);
			DebugLog($"Captured settings: baseRadius={_baseRadius:0.###}, level={_baseLevel:0.###}, raise={_baseRaise:0.###}, smooth={_baseSmooth:0.###}, paint={_basePaint:0.###}");
		}
	}

	private static void ApplyFullTerraformingMode(TerrainOp operation)
	{
		Settings settings = operation.m_settings;
		if (settings == null)
		{
			return;
		}
		if (_fullTerraformingActive && _fullTerraformingEnabled.Value)
		{
			settings.m_level = true;
			settings.m_raise = false;
			settings.m_smooth = false;
			settings.m_paintCleared = false;
			if (!_lastAppliedFullMode)
			{
				DebugLog("Full mode applied to " + ((Object)((Component)operation).gameObject).name + ": LevelTerrain only");
			}
			_lastAppliedFullMode = true;
		}
		else
		{
			settings.m_level = _baseLevelEnabled;
			settings.m_raise = _baseRaiseEnabled;
			settings.m_smooth = _baseSmoothEnabled;
			settings.m_paintCleared = _basePaintEnabled;
			_lastAppliedFullMode = false;
		}
	}

	private static void ChangeRadius(float delta)
	{
		if ((Object)(object)_activeOperation == (Object)null || _baseRadius <= 0.001f)
		{
			return;
		}
		float num = Mathf.Clamp(_currentRadius + delta, _minRadius.Value, _maxRadius.Value);
		if (!(Mathf.Abs(num - _currentRadius) < 0.001f))
		{
			_currentRadius = num;
			Settings settings = _activeOperation.m_settings;
			float num2 = _currentRadius / _baseRadius;
			settings.m_levelRadius = _baseLevel * num2;
			settings.m_raiseRadius = _baseRaise * num2;
			settings.m_smoothRadius = _baseSmooth * num2;
			settings.m_paintRadius = _basePaint * num2;
			ScalePreview(num2);
			if (_showMessage.Value && (Object)(object)Player.m_localPlayer != (Object)null && Mathf.Abs(_currentRadius - _lastMessageRadius) >= 0.01f)
			{
				_lastMessageRadius = _currentRadius;
				((Character)Player.m_localPlayer).Message((MessageType)1, $"TerraHoe radius: {_currentRadius:0.##} m", 0, (Sprite)null, false);
			}
			DebugLog($"radius changed: {_currentRadius:0.###} m, factor={num2:0.###}");
		}
	}

	private static void ScalePreview(float factor)
	{
		//IL_007b: Unknown result type (might be due to invalid IL or missing references)
		GameObject val = (GameObject)((_placementGhost != null && (Object)(object)Player.m_localPlayer != (Object)null) ? /*isinst with value type is only supported in some contexts*/: null);
		if (!((Object)(object)val == (Object)null))
		{
			Transform val2 = val.transform.Find("_GhostOnly");
			if ((Object)(object)val2 == (Object)null)
			{
				val2 = val.transform.Find("GhostOnly");
			}
			if ((Object)(object)val2 == (Object)null)
			{
				val2 = val.transform;
			}
			val2.localScale = new Vector3(factor, factor, factor);
		}
	}

	private static float PickRadius(Settings s)
	{
		float num = 0f;
		if (s.m_level && s.m_levelRadius > num)
		{
			num = s.m_levelRadius;
		}
		if (s.m_raise && s.m_raiseRadius > num)
		{
			num = s.m_raiseRadius;
		}
		if (s.m_smooth && s.m_smoothRadius > num)
		{
			num = s.m_smoothRadius;
		}
		if (s.m_paintCleared && s.m_paintRadius > num)
		{
			num = s.m_paintRadius;
		}
		return num;
	}

	private static void RestoreActiveOperation()
	{
		if (!((Object)(object)_activeOperation == (Object)null))
		{
			Settings settings = _activeOperation.m_settings;
			if (settings != null)
			{
				settings.m_levelRadius = _baseLevel;
				settings.m_raiseRadius = _baseRaise;
				settings.m_smoothRadius = _baseSmooth;
				settings.m_paintRadius = _basePaint;
				settings.m_level = _baseLevelEnabled;
				settings.m_raise = _baseRaiseEnabled;
				settings.m_smooth = _baseSmoothEnabled;
				settings.m_paintCleared = _basePaintEnabled;
			}
			ScalePreview(1f);
			_activeOperation = null;
			_currentRadius = 0f;
			_lastMessageRadius = -1f;
		}
	}

	internal static float TerraformDelta(float value, float min, float max)
	{
		if (!WideTerraforming() && TerrainClamp.IsVanillaDeltaBounds(min, max) && Mathf.Abs(value) > 8.001f)
		{
			WarnNarrowClamp(value);
		}
		return TerrainClamp.Delta(value, min, max, WideTerraforming(), ClampLow(), ClampHigh());
	}

	internal static float TerraformHeight(float value, float min, float max)
	{
		float num = (min + max) * 0.5f;
		if (!WideTerraforming() && TerrainClamp.IsVanillaHeightBounds(min, max) && Mathf.Abs(value - num) > 8.001f)
		{
			WarnNarrowClamp(value - num);
		}
		return TerrainClamp.Height(value, min, max, WideTerraforming(), ClampLow(), ClampHigh());
	}

	private static bool IsFloatClamp(MethodInfo method)
	{
		if (method == null || method.Name != "Clamp" || method.DeclaringType != typeof(Mathf))
		{
			return false;
		}
		if (method.ReturnType != typeof(float))
		{
			return false;
		}
		ParameterInfo[] parameters = method.GetParameters();
		if (parameters.Length == 3 && parameters[0].ParameterType == typeof(float) && parameters[1].ParameterType == typeof(float))
		{
			return parameters[2].ParameterType == typeof(float);
		}
		return false;
	}

	private static IEnumerable<CodeInstruction> RewriteClamp(IEnumerable<CodeInstruction> instructions, string replacement)
	{
		MethodInfo target = AccessTools.Method(typeof(TerraHoePlugin), replacement, (Type[])null, (Type[])null);
		if (target == null)
		{
			_log.LogWarning((object)("TerraHoe could not resolve " + replacement + "; the terrain clamp stays vanilla."));
			foreach (CodeInstruction instruction in instructions)
			{
				yield return instruction;
			}
			yield break;
		}
		foreach (CodeInstruction instruction2 in instructions)
		{
			if (instruction2.opcode == OpCodes.Call && instruction2.operand is MethodInfo method && IsFloatClamp(method))
			{
				yield return new CodeInstruction(OpCodes.Call, (object)target);
			}
			else
			{
				yield return instruction2;
			}
		}
	}

	private static int StripHoeDust(Piece piece)
	{
		if (!_suppressHoeDust.Value || (Object)(object)piece == (Object)null)
		{
			return 0;
		}
		string text = (((Object)(object)((Component)piece).gameObject != (Object)null) ? ((Object)((Component)piece).gameObject).name : string.Empty);
		if (text.IndexOf("road", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("raise", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("path_", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("paved", StringComparison.OrdinalIgnoreCase) < 0)
		{
			return 0;
		}
		EffectList placeEffect = piece.m_placeEffect;
		if (placeEffect == null || placeEffect.m_effectPrefabs == null || placeEffect.m_effectPrefabs.Length == 0)
		{
			return 0;
		}
		List<EffectData> list = new List<EffectData>(placeEffect.m_effectPrefabs.Length);
		int num = 0;
		for (int i = 0; i < placeEffect.m_effectPrefabs.Length; i++)
		{
			EffectData val = placeEffect.m_effectPrefabs[i];
			GameObject val2 = val?.m_prefab;
			if ((Object)(object)val2 != (Object)null && ((Object)val2).name.IndexOf("vfx", StringComparison.OrdinalIgnoreCase) >= 0)
			{
				num++;
			}
			else
			{
				list.Add(val);
			}
		}
		if (num == 0)
		{
			return 0;
		}
		placeEffect.m_effectPrefabs = list.ToArray();
		DebugLog($"Stripped {num} dust vfx from {text}");
		return num;
	}
}
internal static class TerrainClamp
{
	internal static bool IsVanillaDeltaBounds(float min, float max)
	{
		if (Mathf.Abs(min + 8f) <= 0.001f)
		{
			return Mathf.Abs(max - 8f) <= 0.001f;
		}
		return false;
	}

	internal static bool IsVanillaHeightBounds(float min, float max)
	{
		return Mathf.Abs(max - min - 16f) <= 0.001f;
	}

	internal static float Delta(float value, float min, float max, bool wide, float low, float high)
	{
		if (!wide || !IsVanillaDeltaBounds(min, max))
		{
			return Mathf.Clamp(value, min, max);
		}
		return Mathf.Clamp(value, low, high);
	}

	internal static float Height(float value, float min, float max, bool wide, float low, float high)
	{
		if (!wide || !IsVanillaHeightBounds(min, max))
		{
			return Mathf.Clamp(value, min, max);
		}
		float num = (min + max) * 0.5f;
		return Mathf.Clamp(value, num + low, num + high);
	}
}