Decompiled source of ValheimCultivatorCamera v1.2.0

BepInEx/plugins/FreeCultivatorCam/FreeCultivatorCam.dll

Decompiled 5 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.Rendering;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Free Cultivator Cam")]
[assembly: AssemblyFileVersion("0.1.18.0")]
[assembly: AssemblyVersion("0.1.18.0")]
namespace FreeCultivatorCam;

[BepInPlugin("valheim.freecultivatorcam", "Free Cultivator Cam", "0.1.18")]
[BepInProcess("valheim.exe")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[DefaultExecutionOrder(1000)]
public sealed class CultivatorCamera : BaseUnityPlugin
{
	private sealed class BackgroundBatch
	{
		internal Player Player;

		internal long PlayerId;

		internal CropProfile Crop;

		internal readonly List<Vector3> Positions = new List<Vector3>();

		internal Dictionary<string, CropProfile> Profiles;

		internal Vector3 Origin;

		internal float Reach;

		internal float NeighbourRadius;

		internal float Angle;

		internal float NextPlant;

		internal float NextRefresh;

		internal int Index;

		internal int Planted;

		internal int Skipped;
	}

	[HarmonyPatch(typeof(HotkeyBar), "Update")]
	private static class KeepToolbarInputInCamera
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			MethodInfo input = AccessTools.Method(typeof(ZInput), "GetButtonDown", new Type[1] { typeof(string) }, (Type[])null);
			MethodInfo replacement = AccessTools.Method(typeof(CultivatorCamera), "ToolbarButtonDown", (Type[])null, (Type[])null);
			int replaced = 0;
			foreach (CodeInstruction instruction in instructions)
			{
				if (CodeInstructionExtensions.Calls(instruction, input))
				{
					instruction.opcode = OpCodes.Call;
					instruction.operand = replacement;
					replaced++;
				}
				yield return instruction;
			}
			if (replaced != 3)
			{
				throw new InvalidOperationException("Unexpected toolbar input layout; camera hooks disabled to avoid D-pad conflicts.");
			}
		}
	}

	[HarmonyPatch(typeof(Hud), "UpdateBuild", new Type[]
	{
		typeof(Player),
		typeof(bool)
	})]
	private static class HideNativeBuildPanel
	{
		private static bool Prefix(Hud __instance)
		{
			if (!Object.op_Implicit((Object)(object)Instance) || !Instance.Active)
			{
				return true;
			}
			if (Object.op_Implicit((Object)(object)__instance.m_buildHud) && __instance.m_buildHud.activeSelf)
			{
				__instance.m_buildHud.SetActive(false);
			}
			if (Object.op_Implicit((Object)(object)__instance.m_buildUi) && ((Component)__instance.m_buildUi).gameObject.activeSelf)
			{
				Hud.CloseBuildUi();
			}
			return false;
		}
	}

	[HarmonyPatch(typeof(KeyHints), "UpdateHints")]
	private static class HideConflictingBuildHints
	{
		private static bool Prefix(KeyHints __instance)
		{
			if (!Object.op_Implicit((Object)(object)Instance) || !Instance.Active || MenuBlocksCamera())
			{
				return true;
			}
			if (Object.op_Implicit((Object)(object)__instance.m_buildHints) && __instance.m_buildHints.activeSelf)
			{
				__instance.m_buildHints.SetActive(false);
			}
			if (Object.op_Implicit((Object)(object)__instance.m_combatHints) && __instance.m_combatHints.activeSelf)
			{
				__instance.m_combatHints.SetActive(false);
			}
			return false;
		}
	}

	[HarmonyPatch(typeof(InventoryGui), "Show", new Type[]
	{
		typeof(Container),
		typeof(int)
	})]
	private static class KeepInventoryClosedInCultivatorCamera
	{
		private static bool Prefix()
		{
			CultivatorCamera instance = Instance;
			if (Object.op_Implicit((Object)(object)instance))
			{
				return !instance.Active;
			}
			return true;
		}
	}

	[HarmonyPatch(typeof(Player), "Update")]
	private static class BeforePlayerUpdate
	{
		private static void Prefix(Player __instance)
		{
			CultivatorCamera instance = Instance;
			if (Object.op_Implicit((Object)(object)instance) && instance.IsLocal(__instance))
			{
				instance.Tick();
			}
		}
	}

	[HarmonyPatch(typeof(GameCamera), "LateUpdate")]
	private static class DetachedCameraLateUpdate
	{
		private static void Postfix(GameCamera __instance)
		{
			CultivatorCamera instance = Instance;
			if (!Object.op_Implicit((Object)(object)instance) || !instance.Active || (Object)(object)__instance != (Object)(object)instance._gameCamera)
			{
				return;
			}
			try
			{
				instance.ApplyPose();
			}
			catch (Exception error)
			{
				instance.Fail(error);
			}
		}
	}

	[HarmonyPatch(typeof(GameCamera), "UpdateCamera")]
	private static class DetachedCamera
	{
		private static bool Prefix(GameCamera __instance)
		{
			CultivatorCamera instance = Instance;
			if (!Object.op_Implicit((Object)(object)instance) || !instance.Active || (Object)(object)__instance != (Object)(object)instance._gameCamera)
			{
				return true;
			}
			try
			{
				instance.ApplyPose();
				return false;
			}
			catch (Exception error)
			{
				instance.Fail(error);
				return true;
			}
		}
	}

	[HarmonyPatch(typeof(Player), "SetControls")]
	private static class KeepBodyStill
	{
		private static void Prefix(Player __instance, ref Vector3 movedir, ref bool attack, ref bool attackHold, ref bool secondaryAttack, ref bool secondaryAttackHold, ref bool block, ref bool blockHold, ref bool jump, ref bool crouch, ref bool run, ref bool autoRun, ref bool dodge)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			CultivatorCamera instance = Instance;
			if (Object.op_Implicit((Object)(object)instance) && instance.ControlsOwned(__instance))
			{
				movedir = Vector3.zero;
				attack = (attackHold = (secondaryAttack = (secondaryAttackHold = (block = (blockHold = false)))));
				jump = (crouch = (run = (autoRun = (dodge = false))));
				__instance.m_autoRun = false;
				if (instance._active)
				{
					((Character)__instance).StopMovement();
				}
			}
		}
	}

	[HarmonyPatch(typeof(Player), "SetMouseLook")]
	private static class KeepBodyLook
	{
		private static bool Prefix(Player __instance)
		{
			CultivatorCamera instance = Instance;
			if (Object.op_Implicit((Object)(object)instance))
			{
				return !instance.ControlsOwned(__instance);
			}
			return true;
		}
	}

	[HarmonyPatch(typeof(Player), "TakeInput")]
	private static class ProtectToggleAndDialogs
	{
		private static void Postfix(Player __instance, ref bool __result)
		{
			CultivatorCamera instance = Instance;
			if (Object.op_Implicit((Object)(object)instance) && instance.HooksEnabled && instance.IsLocal(__instance) && (instance._active || instance._chordHeld || Time.unscaledTime < instance._suppressUntil))
			{
				__result = false;
			}
		}
	}

	[HarmonyPatch(typeof(Player), "StartGuardianPower")]
	private static class ProtectGuardianPowerChord
	{
		private static bool Prefix(Player __instance)
		{
			CultivatorCamera instance = Instance;
			if (!Object.op_Implicit((Object)(object)instance) || !instance.HooksEnabled || !instance.IsLocal(__instance))
			{
				return true;
			}
			if (!instance._active)
			{
				return !instance._chordHeld;
			}
			return false;
		}
	}

	[HarmonyPatch(typeof(Pickable), "Drop")]
	private static class IdentifyHarvestDrops
	{
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			MethodInfo original = AccessTools.Method(typeof(ItemDrop), "OnCreateNew", new Type[2]
			{
				typeof(ItemDrop),
				typeof(bool)
			}, (Type[])null);
			MethodInfo replacement = AccessTools.Method(typeof(CultivatorCamera), "HarvestDropCreated", (Type[])null, (Type[])null);
			int count = 0;
			foreach (CodeInstruction instruction in instructions)
			{
				if (CodeInstructionExtensions.Calls(instruction, original))
				{
					CodeInstruction val = new CodeInstruction(OpCodes.Ldarg_0, (object)null);
					val.labels.AddRange(instruction.labels);
					instruction.labels.Clear();
					yield return val;
					instruction.opcode = OpCodes.Call;
					instruction.operand = replacement;
					count++;
				}
				yield return instruction;
			}
			if (count != 1)
			{
				throw new InvalidOperationException("Unexpected crop drop routine; refusing an unsafe inventory hook.");
			}
		}
	}

	[HarmonyPatch(typeof(Plant), "Grow")]
	private static class PreservePlanterOnGrowth
	{
		private static void Prefix(Plant __instance, out long __state)
		{
			__state = Planter((Component)(object)__instance);
		}

		private static void Postfix(GameObject __result, long __state)
		{
			if (Object.op_Implicit((Object)(object)__result) && __state != 0L)
			{
				ZNetView component = __result.GetComponent<ZNetView>();
				if (Object.op_Implicit((Object)(object)component) && component.IsValid() && component.IsOwner())
				{
					component.GetZDO().Set("FreeCultivatorCam.Planter", __state);
					AddOwnedTreeHover(__result.GetComponent<TreeBase>());
				}
			}
		}
	}

	[HarmonyPatch(typeof(Pickable), "Interact")]
	private static class BeginHarvestHold
	{
		private static void Prefix(Pickable __instance, Humanoid __0, bool __1)
		{
			if (!__1 && Object.op_Implicit((Object)(object)Instance) && Ready(__instance))
			{
				Instance.ArmHarvest((Component)(object)__instance, (Player)(object)((__0 is Player) ? __0 : null));
			}
		}
	}

	[HarmonyPatch(typeof(Pickable), "GetHoverText")]
	private static class OwnedCropHint
	{
		private static void Postfix(Pickable __instance, ref string __result)
		{
			if (Ready(__instance) && !string.IsNullOrEmpty(__result))
			{
				AddHarvestHint((Component)(object)__instance, ref __result);
			}
		}
	}

	[HarmonyPatch(typeof(Plant), "GetHoverText")]
	private static class OwnedPlantHint
	{
		private static void Postfix(Plant __instance, ref string __result)
		{
			AddHarvestHint((Component)(object)__instance, ref __result);
		}
	}

	[HarmonyPatch(typeof(Plant), "GetHoverText")]
	private static class PlantGrowthHover
	{
		private static readonly Func<Plant, float> GrowTime = (Func<Plant, float>)Delegate.CreateDelegate(typeof(Func<Plant, float>), AccessTools.Method(typeof(Plant), "GetGrowTime", (Type[])null, (Type[])null));

		private static readonly Func<Plant, double> Age = (Func<Plant, double>)Delegate.CreateDelegate(typeof(Func<Plant, double>), AccessTools.Method(typeof(Plant), "TimeSincePlanted", (Type[])null, (Type[])null));

		private static Plant cachedPlant;

		private static float refreshAt;

		private static string cachedLine;

		private static Status cachedStatus;

		private static void Postfix(Plant __instance, ZNetView ___m_nview, ref string __result)
		{
			//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_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			CultivatorCamera instance = Instance;
			if (Object.op_Implicit((Object)(object)instance) && instance.HooksEnabled && instance._enabled.Value && !instance.Active && Object.op_Implicit((Object)(object)Player.m_localPlayer) && Object.op_Implicit((Object)(object)EnvMan.instance) && Object.op_Implicit((Object)(object)ZNet.instance) && Object.op_Implicit((Object)(object)___m_nview) && ___m_nview.IsValid())
			{
				Status status = __instance.GetStatus();
				float unscaledTime = Time.unscaledTime;
				if ((Object)(object)cachedPlant != (Object)(object)__instance || status != cachedStatus || unscaledTime >= refreshAt)
				{
					cachedPlant = __instance;
					cachedStatus = status;
					refreshAt = unscaledTime + 0.25f;
					cachedLine = (((int)status != 0) ? "<color=#FFB870>Cannot grow — check the condition above</color>" : ("<color=#BDECB6>" + GrowthCountdown.Format((double)GrowTime(__instance) - Age(__instance), EnvMan.instance.m_dayLengthSec) + "</color>"));
				}
				__result = __result + "\n" + cachedLine;
			}
		}
	}

	private sealed class Spot
	{
		internal Vector3 Position;

		internal Vector3 Normal = Vector3.up;

		internal string Reason = "Checking";

		internal bool Checked;

		internal bool Planted;
	}

	[HarmonyPatch(typeof(GameCamera), "UpdateMouseCapture")]
	private static class PanelCursor
	{
		private static bool Prefix()
		{
			if (!Object.op_Implicit((Object)(object)Instance) || !Instance.Active || !Instance.panelOpen || MenuBlocksCamera())
			{
				return true;
			}
			ZCursor.LockState = (CursorLockMode)0;
			ZCursor.Show();
			return false;
		}
	}

	[HarmonyPatch(typeof(Player), "UpdatePlacement")]
	private static class SuppressNativePlacement
	{
		private static bool Prefix(Player __instance)
		{
			if (Object.op_Implicit((Object)(object)Instance) && Instance.Active)
			{
				return !Instance.IsLocal(__instance);
			}
			return true;
		}
	}

	private sealed class TreeHarvestScope
	{
		internal GameObject Parent;

		internal readonly Queue<Component> Parts = new Queue<Component>();

		internal readonly List<ItemDrop> Drops = new List<ItemDrop>();
	}

	[HarmonyPatch(typeof(HoverText), "GetHoverText")]
	private static class NativeTreeHarvestHint
	{
		private static void Postfix(HoverText __instance, ref string __result)
		{
			CultivatorCamera instance = Instance;
			Player localPlayer = Player.m_localPlayer;
			if (!Object.op_Implicit((Object)(object)instance) || !instance.HooksEnabled || !instance._enabled.Value || instance.Active || !Object.op_Implicit((Object)(object)localPlayer))
			{
				return;
			}
			TreeBase componentInParent = ((Component)__instance).GetComponentInParent<TreeBase>();
			if (Object.op_Implicit((Object)(object)componentInParent))
			{
				long num = Planter((Component)(object)componentInParent);
				if (num == localPlayer.GetPlayerID())
				{
					AddHarvestHint((Component)(object)componentInParent, ref __result);
				}
				else if (num == 0L)
				{
					__result += "\nMass harvest unavailable: no saved planter record";
				}
			}
		}
	}

	[HarmonyPatch(typeof(TreeBase), "Awake")]
	private static class RestoreOwnedTreeHover
	{
		private static void Postfix(TreeBase __instance)
		{
			AddOwnedTreeHover(__instance);
		}
	}

	public sealed class OwnedTreeHover : MonoBehaviour, Hoverable
	{
		public string GetHoverName()
		{
			return "Planted tree";
		}

		public float GetHoverOffset()
		{
			return 0f;
		}

		public string GetHoverText()
		{
			TreeBase component = ((Component)this).GetComponent<TreeBase>();
			string text = "";
			AddHarvestHint((Component)(object)component, ref text);
			if (text.Length != 0)
			{
				return "Planted tree" + text;
			}
			return "";
		}
	}

	[HarmonyPatch]
	private static class CaptureOwnedTreeProducts
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(TreeBase), "RPC_Damage", new Type[2]
			{
				typeof(long),
				typeof(HitData)
			}, (Type[])null);
			yield return AccessTools.Method(typeof(TreeBase), "SpawnLog", new Type[1] { typeof(Vector3) }, (Type[])null);
			yield return AccessTools.Method(typeof(TreeLog), "Destroy", new Type[2]
			{
				typeof(HitData),
				typeof(bool)
			}, (Type[])null);
			yield return AccessTools.Method(typeof(Destructible), "Destroy", new Type[1] { typeof(HitData) }, (Type[])null);
			yield return AccessTools.Method(typeof(DropOnDestroyed), "OnDestroyed", Type.EmptyTypes, (Type[])null);
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, MethodBase __originalMethod)
		{
			MethodInfo replacement = AccessTools.Method(typeof(CultivatorCamera), "CaptureTreeSpawn", (Type[])null, (Type[])null);
			int count = 0;
			foreach (CodeInstruction instruction in instructions)
			{
				MethodInfo methodInfo = instruction.operand as MethodInfo;
				if (methodInfo != null && methodInfo.DeclaringType == typeof(Object) && methodInfo.Name == "Instantiate" && methodInfo.IsGenericMethod && methodInfo.GetGenericArguments()[0] == typeof(GameObject))
				{
					ParameterInfo[] parameters = methodInfo.GetParameters();
					if (parameters.Length == 3 && parameters[1].ParameterType == typeof(Vector3) && parameters[2].ParameterType == typeof(Quaternion))
					{
						CodeInstruction val = new CodeInstruction(OpCodes.Ldarg_0, (object)null);
						val.labels.AddRange(instruction.labels);
						instruction.labels.Clear();
						val.blocks.AddRange(instruction.blocks);
						instruction.blocks.Clear();
						yield return val;
						instruction.opcode = OpCodes.Call;
						instruction.operand = replacement;
						count++;
					}
				}
				yield return instruction;
			}
			int num = ((!(__originalMethod.DeclaringType == typeof(TreeBase)) && !(__originalMethod.DeclaringType == typeof(TreeLog))) ? 1 : 2);
			if (count != num)
			{
				throw new InvalidOperationException("Tree harvest native spawn routine changed: " + __originalMethod.Name + ". Refusing unsafe capture.");
			}
		}
	}

	private BackgroundBatch backgroundBatch;

	private GUIStyle markerStyle;

	private GUIStyle planStyle;

	private string lastStripHelp;

	private readonly string[] stripColumns = new string[3];

	private bool cultivateMode = true;

	private bool cultivateArmed;

	private bool cultivateRequest;

	private float nextCultivate;

	private float nextBrushPreview;

	private ConfigEntry<float> cultivateRadius;

	private Vector3 cultivatePoint;

	private bool cultivateAim;

	private string cultivateStatus = "Aim at ground to cultivate";

	private LineRenderer cultivateRing;

	private Material cultivateMaterial;

	private readonly List<Heightmap> cultivateMaps = new List<Heightmap>();

	private readonly List<TerrainComp> cultivateCompilers = new List<TerrainComp>();

	private EffectList cultivateEffects;

	private EffectList cultivatePlaceEffects;

	private float nextCultivateSound;

	private static readonly FieldInfo CultivateInitialized = AccessTools.Field(typeof(TerrainComp), "m_initialized");

	private static readonly MethodInfo CultivateLoad = AccessTools.Method(typeof(TerrainComp), "CheckLoad", (Type[])null, (Type[])null);

	private static readonly MethodInfo CultivateApply = AccessTools.Method(typeof(TerrainComp), "DoOperation", new Type[3]
	{
		typeof(Vector3),
		typeof(Vector3),
		typeof(Settings)
	}, (Type[])null);

	public const string PluginId = "valheim.freecultivatorcam";

	public const string PluginVersion = "0.1.18";

	internal static CultivatorCamera Instance;

	private readonly CameraInputMode inputMode = new CameraInputMode();

	private float _nextCircletCheck;

	private ConfigEntry<bool> _enabled;

	private ConfigEntry<bool> _controllerToggle;

	private ConfigEntry<bool> _hidePlayer;

	private ConfigEntry<bool> _showIndicator;

	private ConfigEntry<bool> _exitOnDamage;

	private ConfigEntry<KeyboardShortcut> _toggleKey;

	private ConfigEntry<KeyboardShortcut> _resetKey;

	private ConfigEntry<KeyboardShortcut> _reportKey;

	private ConfigEntry<float> _speed;

	private ConfigEntry<float> _fastMultiplier;

	private ConfigEntry<float> _radius;

	private ConfigEntry<float> _reach;

	private ConfigEntry<float> _rangeMultiplier;

	private ConfigEntry<float> _startPitch;

	private ConfigEntry<float> _startFov;

	private Harmony _harmony;

	private bool _ready;

	private bool _active;

	private bool _failed;

	private bool _chordHeld;

	private Player _player;

	private GameCamera _gameCamera;

	private Camera _camera;

	private Camera _skyCamera;

	private Vector3 _position;

	private Vector3 _bodyOrigin;

	private Vector3 _savedCameraPosition;

	private Quaternion _savedCameraRotation;

	private Quaternion _rotation;

	private float _yaw;

	private float _pitch;

	private float _fov;

	private float _savedFov;

	private float _savedSkyFov;

	private float _savedNearClip;

	private float _savedReach;

	private float _appliedReach;

	private float _suppressUntil;

	private float _nextRendererScan;

	private float _activeNearClip;

	private int _tickFrame = -1;

	private string _lastExit = "Not activated yet";

	private readonly CameraToggleInput _toggleInput = new CameraToggleInput();

	private string _indicatorText;

	private const string HarvestSourceKey = "FreeCultivatorCam.HarvestSource";

	private static readonly Action<ItemDrop, bool> NativeDropCreated = (Action<ItemDrop, bool>)Delegate.CreateDelegate(typeof(Action<ItemDrop, bool>), AccessTools.Method(typeof(ItemDrop), "OnCreateNew", new Type[2]
	{
		typeof(ItemDrop),
		typeof(bool)
	}, (Type[])null));

	private static readonly Action<ItemDrop> RequestDropOwnership = (Action<ItemDrop>)Delegate.CreateDelegate(typeof(Action<ItemDrop>), AccessTools.Method(typeof(ItemDrop), "RequestOwn", (Type[])null, (Type[])null));

	private static readonly Action<ItemDrop> SaveDrop = (Action<ItemDrop>)Delegate.CreateDelegate(typeof(Action<ItemDrop>), AccessTools.Method(typeof(ItemDrop), "Save", (Type[])null, (Type[])null));

	private static readonly FieldInfo DropListField = AccessTools.Field(typeof(ItemDrop), "s_instances");

	private readonly Dictionary<ZDOID, float> harvestSources = new Dictionary<ZDOID, float>();

	private readonly HashSet<ZDOID> handledHarvestDrops = new HashSet<ZDOID>();

	private ZDOID firstHarvestSource;

	private int dropCursor;

	private float nextDropCheck;

	private float collectUntil;

	private readonly string[] controlHints = new string[8];

	private static readonly Func<Player, bool> HarvestTakeInput = (Func<Player, bool>)Delegate.CreateDelegate(typeof(Func<Player, bool>), AccessTools.Method(typeof(Player), "TakeInput", (Type[])null, (Type[])null));

	private const string PlanterKey = "FreeCultivatorCam.Planter";

	private const float HarvestRadius = 50f;

	private bool harvestArmed;

	private bool harvestLatched;

	private bool harvesting;

	private float harvestStarted;

	private float nextHarvest;

	private long harvestPlayer;

	private readonly HashSet<int> harvestTypes = new HashSet<int>();

	private readonly Queue<Pickable> harvestQueue = new Queue<Pickable>();

	private readonly HashSet<Pickable> harvestSeen = new HashSet<Pickable>();

	private static readonly FieldRef<Pickable, bool> Picked = AccessTools.FieldRefAccess<Pickable, bool>("m_picked");

	private static readonly FieldRef<Pickable, bool> PickedLocal = AccessTools.FieldRefAccess<Pickable, bool>("m_pickedLocal");

	private static readonly FieldRef<Pickable, int> PickEnabled = AccessTools.FieldRefAccess<Pickable, int>("m_enabled");

	private readonly List<CropProfile> crops = new List<CropProfile>();

	private readonly Dictionary<string, CropProfile> profiles = new Dictionary<string, CropProfile>();

	private readonly List<Spot> spots = new List<Spot>();

	private int cropIndex;

	private int countIndex;

	private int rows = 1;

	private int checkIndex;

	private int queueIndex;

	private int planted;

	private int skipped;

	private int seedCapacity;

	private float angle;

	private float nextCounts;

	private float nextCheck;

	private float nextPlant;

	private float neighbourRadius = 1f;

	private bool pinned;

	private bool queued;

	private bool armed;

	private bool hasAim;

	private bool patternDirty = true;

	private bool panelOpen;

	private bool requestPlant;

	private bool pinnedBeforeBatch;

	private bool initialValidationComplete;

	private bool plantingFault;

	private bool cursorOwned;

	private CursorLockMode savedCursorLock;

	private bool savedCursorVisible;

	private Vector3 centre;

	private GameObject savedGhost;

	private bool ghostActive;

	private Mesh rings;

	private Material ringMaterial;

	private Material ghostMaterial;

	private readonly MaterialPropertyBlock ghostProperties = new MaterialPropertyBlock();

	private GameObject ringObject;

	private MeshRenderer ringRenderer;

	private GameObject savedMarker;

	private bool markerActive;

	private static readonly FieldInfo PlacementMarker = AccessTools.Field(typeof(Player), "m_placementMarkerInstance");

	private Vector3[] ringVertices;

	private Color[] ringColors;

	private int[] ringIndices;

	private bool ringDirty;

	private GUIStyle titleStyle;

	private GUIStyle bodyStyle;

	private GUIStyle statusStyle;

	private GUIStyle buttonStyle;

	private readonly Plane[] previewPlanes = (Plane[])(object)new Plane[6];

	private string plantingStatus = "Choose a crop, then aim at cultivated ground";

	private static readonly FieldInfo Ghost = AccessTools.Field(typeof(Player), "m_placementGhost");

	private static object otherCamera;

	private static PropertyInfo otherActive;

	private bool harvestTrees;

	private int treesHarvested;

	private int treesSkipped;

	private Vector3 treeHarvestOrigin;

	private readonly Queue<TreeBase> treeHarvestQueue = new Queue<TreeBase>();

	private static readonly Action<TreeBase, long, HitData> DamageOwnedTree = (Action<TreeBase, long, HitData>)Delegate.CreateDelegate(typeof(Action<TreeBase, long, HitData>), AccessTools.Method(typeof(TreeBase), "RPC_Damage", new Type[2]
	{
		typeof(long),
		typeof(HitData)
	}, (Type[])null));

	private static readonly Action<TreeLog, HitData, bool> BreakOwnedLog = (Action<TreeLog, HitData, bool>)Delegate.CreateDelegate(typeof(Action<TreeLog, HitData, bool>), AccessTools.Method(typeof(TreeLog), "Destroy", new Type[2]
	{
		typeof(HitData),
		typeof(bool)
	}, (Type[])null));

	private static readonly Action<Destructible> BreakOwnedStump = (Action<Destructible>)Delegate.CreateDelegate(typeof(Action<Destructible>), AccessTools.Method(typeof(Destructible), "DestroyNow", Type.EmptyTypes, (Type[])null));

	private static TreeHarvestScope currentTreeHarvest;

	private bool UsingController
	{
		get
		{
			if (inputMode.Controller)
			{
				return Gamepad.current != null;
			}
			return false;
		}
	}

	internal bool HooksEnabled
	{
		get
		{
			if (_ready && !_failed)
			{
				return ((Behaviour)this).isActiveAndEnabled;
			}
			return false;
		}
	}

	internal bool Active
	{
		get
		{
			if (HooksEnabled)
			{
				return _active;
			}
			return false;
		}
	}

	private float EffectiveCameraRadius => CameraMath.CultivatorCameraRange(_radius.Value, 8f, _rangeMultiplier.Value);

	private float EffectiveBuildReach => CameraMath.CultivatorCameraRange(_reach.Value, 5f, _rangeMultiplier.Value);

	private CropProfile Crop
	{
		get
		{
			if (crops.Count != 0)
			{
				return crops[cropIndex];
			}
			return null;
		}
	}

	private static bool KeepBatchAfterCameraExit(string reason)
	{
		if (!(reason == "Toggled off") && !(reason == "Escape"))
		{
			return reason == "Cultivator unequipped";
		}
		return true;
	}

	private void DetachPlantingBatch()
	{
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
		if (!queued || Crop == null || !Object.op_Implicit((Object)(object)_player) || this.backgroundBatch != null)
		{
			return;
		}
		BackgroundBatch backgroundBatch = new BackgroundBatch
		{
			Player = _player,
			PlayerId = _player.GetPlayerID(),
			Crop = Crop,
			Profiles = new Dictionary<string, CropProfile>(profiles),
			Origin = _bodyOrigin,
			Reach = EffectiveBuildReach,
			NeighbourRadius = neighbourRadius,
			Angle = angle,
			Planted = planted,
			Skipped = skipped,
			NextPlant = nextPlant
		};
		for (int i = queueIndex; i < spots.Count; i++)
		{
			if (!spots[i].Planted)
			{
				backgroundBatch.Positions.Add(spots[i].Position);
			}
		}
		if (backgroundBatch.Positions.Count != 0)
		{
			this.backgroundBatch = backgroundBatch;
			queued = false;
			Notify("Planting continues after camera exit. Reopen the camera and press B / Backspace to cancel.");
		}
	}

	private void CancelBackgroundPlanting(string reason, bool message)
	{
		if (backgroundBatch != null)
		{
			int num = backgroundBatch.Planted;
			backgroundBatch = null;
			if (message)
			{
				Notify("Planting stopped: " + reason + ". Planted " + num + "; unused seeds kept.");
			}
		}
	}

	private void TickBackgroundPlanting()
	{
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: 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_0114: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Unknown result type (might be due to invalid IL or missing references)
		//IL_0124: Unknown result type (might be due to invalid IL or missing references)
		//IL_0144: Unknown result type (might be due to invalid IL or missing references)
		//IL_014c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0151: Unknown result type (might be due to invalid IL or missing references)
		//IL_0156: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c0: Unknown result type (might be due to invalid IL or missing references)
		BackgroundBatch backgroundBatch = this.backgroundBatch;
		if (backgroundBatch == null)
		{
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		if (HooksEnabled && _enabled.Value && Object.op_Implicit((Object)(object)localPlayer) && !((Object)(object)localPlayer != (Object)(object)backgroundBatch.Player) && localPlayer.GetPlayerID() == backgroundBatch.PlayerId && !((Character)localPlayer).IsDead() && !((Character)localPlayer).IsTeleporting() && !((Character)localPlayer).InCutscene())
		{
			Vector3 val = ((Component)localPlayer).transform.position - backgroundBatch.Origin;
			if (!(((Vector3)(ref val)).sqrMagnitude > backgroundBatch.Reach * backgroundBatch.Reach))
			{
				if (!Application.isFocused || MenuBlocksCamera() || Game.IsPaused() || Time.unscaledTime < backgroundBatch.NextPlant)
				{
					return;
				}
				backgroundBatch.NextPlant = Time.unscaledTime + 0.1f;
				try
				{
					if (Time.unscaledTime >= backgroundBatch.NextRefresh)
					{
						ChestResources.Refresh();
						backgroundBatch.NextRefresh = Time.unscaledTime + 0.5f;
					}
					for (int i = 0; i < 8; i++)
					{
						if (backgroundBatch.Index >= backgroundBatch.Positions.Count)
						{
							break;
						}
						Vector3 point = backgroundBatch.Positions[backgroundBatch.Index++];
						if (GrowingConditions.Check(backgroundBatch.Crop, ref point, backgroundBatch.Origin, backgroundBatch.Reach, backgroundBatch.NeighbourRadius, backgroundBatch.Profiles, out var _) == null)
						{
							val = point - ((Component)localPlayer).transform.position;
							if (!(((Vector3)(ref val)).sqrMagnitude > backgroundBatch.Reach * backgroundBatch.Reach))
							{
								if (ChestResources.Capacity(localPlayer, backgroundBatch.Crop.Piece) < 1)
								{
									CancelBackgroundPlanting("seeds exhausted", message: true);
									return;
								}
								ChestResources.Plant(localPlayer, backgroundBatch.Crop.Piece, point, Quaternion.Euler(0f, backgroundBatch.Angle, 0f));
								backgroundBatch.Planted++;
								Physics.SyncTransforms();
								break;
							}
						}
						backgroundBatch.Skipped++;
					}
					if (backgroundBatch.Index >= backgroundBatch.Positions.Count)
					{
						this.backgroundBatch = null;
						Notify("Planting finished: " + backgroundBatch.Planted + " planted, " + backgroundBatch.Skipped + " blocked.");
					}
					return;
				}
				catch (Exception ex)
				{
					CancelBackgroundPlanting("placement error; check the log", message: true);
					((BaseUnityPlugin)this).Logger.LogError((object)("Background planting cancelled without retrying the uncertain position: " + ex));
					return;
				}
			}
		}
		CancelBackgroundPlanting("player left the planting area or changed state", message: true);
	}

	private void PlantIcon(Rect area)
	{
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_003d: 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)
		if (Crop != null && Object.op_Implicit((Object)(object)Crop.Piece.m_icon))
		{
			Sprite icon = Crop.Piece.m_icon;
			Rect textureRect = icon.textureRect;
			Texture2D texture = icon.texture;
			GUI.DrawTextureWithTexCoords(area, (Texture)(object)texture, new Rect(((Rect)(ref textureRect)).x / (float)((Texture)texture).width, ((Rect)(ref textureRect)).y / (float)((Texture)texture).height, ((Rect)(ref textureRect)).width / (float)((Texture)texture).width, ((Rect)(ref textureRect)).height / (float)((Texture)texture).height));
		}
	}

	private static void HudBox(Rect rect)
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		GUI.color = new Color(0.025f, 0.045f, 0.055f, 0.94f);
		GUI.DrawTexture(rect, (Texture)(object)Texture2D.whiteTexture);
		GUI.color = Color.white;
	}

	private void EnsurePlanStyles()
	{
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0022: 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_0036: Expected O, but got Unknown
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Expected O, but got Unknown
		if (markerStyle == null)
		{
			markerStyle = new GUIStyle(bodyStyle)
			{
				alignment = (TextAnchor)4,
				fontStyle = (FontStyle)1,
				fontSize = 16,
				wordWrap = false
			};
			planStyle = new GUIStyle(bodyStyle)
			{
				fontSize = 17
			};
		}
	}

	private void DrawCompactPlantingHud()
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0105: Unknown result type (might be due to invalid IL or missing references)
		//IL_0114: Expected O, but got Unknown
		//IL_012d: Unknown result type (might be due to invalid IL or missing references)
		//IL_014c: Unknown result type (might be due to invalid IL or missing references)
		//IL_016a: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_023e: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_030c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0332: Unknown result type (might be due to invalid IL or missing references)
		//IL_0353: Unknown result type (might be due to invalid IL or missing references)
		//IL_0359: Unknown result type (might be due to invalid IL or missing references)
		EnsurePlanStyles();
		Matrix4x4 matrix = GUI.matrix;
		Color color = GUI.color;
		float num = Mathf.Clamp((float)Screen.height / 1080f, 0.6f, 2.5f);
		float sw = (float)Screen.width / num;
		float sh = (float)Screen.height / num;
		GUI.matrix = Matrix4x4.Scale(new Vector3(num, num, 1f));
		int num2 = 0;
		int num3 = 0;
		int num4 = 0;
		int num5 = 0;
		if (hasAim)
		{
			foreach (Spot spot in spots)
			{
				if (spot.Planted)
				{
					num5++;
				}
				else if (!spot.Checked)
				{
					num4++;
				}
				else if (spot.Reason != null)
				{
					num3++;
				}
				else
				{
					num2++;
				}
			}
		}
		int num6 = Math.Min(num2, seedCapacity);
		int num7 = Math.Max(0, num2 - seedCapacity);
		string text = PreviewMessage();
		float num8 = bodyStyle.CalcHeight(new GUIContent(text), 326f);
		HudBox(new Rect(20f, 150f, 350f, 190f + num8));
		PlantIcon(new Rect(32f, 160f, 36f, 36f));
		GUI.Label(new Rect(78f, 160f, 280f, 44f), (Crop == null) ? "Choose a seed" : Crop.Name, statusStyle);
		GUI.Label(new Rect(32f, 204f, 326f, 26f), rows + " rows × " + PlantingLayout.Counts[countIndex] + " plants · " + ((Crop == null) ? "" : (Crop.Spacing.ToString("0.00") + " m")), bodyStyle);
		GUI.Label(new Rect(32f, 234f, 326f, 26f), "Ready: " + num6 + " · Blocked: " + num3 + " · Planted: " + num5, planStyle);
		GUI.Label(new Rect(32f, 260f, 326f, 26f), "Seeds: " + seedCapacity + " · Missing: " + num7 + " · Checking: " + num4, planStyle);
		GUI.Label(new Rect(32f, 286f, 326f, 26f), "Green: ready · Red: blocked / missing seeds", planStyle);
		GUI.Label(new Rect(32f, 322f, 326f, num8), text, bodyStyle);
		DrawControlStrip(sw, sh, CurrentControlHints());
		GUI.matrix = matrix;
		GUI.color = color;
	}

	private void DrawControlStrip(float sw, float sh, string help)
	{
		//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d5: Expected O, but got Unknown
		//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_0167: Unknown result type (might be due to invalid IL or missing references)
		EnsurePlanStyles();
		if (lastStripHelp != help)
		{
			lastStripHelp = help;
			string[] array = help.Split('\n');
			int num = (array.Length + 2) / 3;
			for (int i = 0; i < 3; i++)
			{
				int num2 = i * num;
				int num3 = Math.Min(num, Math.Max(0, array.Length - num2));
				stripColumns[i] = ((num3 == 0) ? "" : string.Join("\n", array, num2, num3));
			}
		}
		float num4 = (panelOpen ? 484 : 350);
		float num5 = sw - num4 - 20f;
		float num6 = (num5 - 48f) / 3f;
		float num7 = 0f;
		for (int j = 0; j < 3; j++)
		{
			num7 = Mathf.Max(num7, planStyle.CalcHeight(new GUIContent(stripColumns[j]), num6));
		}
		float num8 = sh - num7 - 62f;
		HudBox(new Rect(num4, num8, num5, num7 + 42f));
		GUI.Label(new Rect(num4 + 12f, num8 + 7f, num5 - 24f, 23f), UsingController ? "CONTROLS · CONTROLLER" : "CONTROLS · MOUSE AND KEYBOARD", planStyle);
		for (int k = 0; k < 3; k++)
		{
			GUI.Label(new Rect(num4 + 12f + (float)k * (num6 + 12f), num8 + 31f, num6, num7), stripColumns[k], planStyle);
		}
	}

	private void FindCultivationEffects(PieceTable table)
	{
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Invalid comparison between Unknown and I4
		cultivateEffects = null;
		cultivatePlaceEffects = null;
		nextCultivateSound = 0f;
		foreach (GameObject piece in table.m_pieces)
		{
			if (!Object.op_Implicit((Object)(object)piece))
			{
				continue;
			}
			TerrainOp[] componentsInChildren = piece.GetComponentsInChildren<TerrainOp>(true);
			foreach (TerrainOp val in componentsInChildren)
			{
				if (val.m_settings != null && val.m_settings.m_paintCleared && (int)val.m_settings.m_paintType == 1)
				{
					Piece component = piece.GetComponent<Piece>();
					cultivateEffects = val.m_onPlacedEffect;
					cultivatePlaceEffects = (Object.op_Implicit((Object)(object)component) ? component.m_placeEffect : null);
					return;
				}
			}
		}
	}

	private void PlayCultivationEffect()
	{
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: 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_007e: 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)
		if (!(Time.unscaledTime < nextCultivateSound))
		{
			nextCultivateSound = Time.unscaledTime + 0.35f;
			if (cultivateEffects != null)
			{
				cultivateEffects.Create(cultivatePoint, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
			}
			if (cultivatePlaceEffects != null && cultivateEffects != cultivatePlaceEffects)
			{
				cultivatePlaceEffects.Create(cultivatePoint, Quaternion.identity, (Transform)null, 1f, -1, default(ZDOID));
			}
		}
	}

	private void ConfigureCultivation()
	{
		if (CultivateInitialized == null || CultivateLoad == null || CultivateApply == null)
		{
			throw new MissingMethodException("Native cultivation terrain API");
		}
		cultivateRadius = FloatSetting("Terrain", "BrushRadius", 3f, 1f, 6f, "Cultivation brush radius in metres; mouse wheel / D-pad up/down adjusts this in Cultivate mode.");
	}

	private void SetCultivateMode(bool value)
	{
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
		if (!queued && cultivateMode != value)
		{
			cultivateMode = value;
			cultivateArmed = false;
			armed = false;
			cultivateRequest = false;
			cultivateAim = false;
			pinned = false;
			hasAim = false;
			patternDirty = true;
			initialValidationComplete = false;
			spots.Clear();
			plantingStatus = "Choose a crop, then aim at suitable ground.";
			RaycastHit val = default(RaycastHit);
			if (!value && panelOpen && Physics.Raycast(((Component)_camera).transform.position, ((Component)_camera).transform.forward, ref val, EffectiveCameraRadius * 2f + 20f, LayerMask.GetMask(new string[1] { "terrain" }), (QueryTriggerInteraction)1))
			{
				centre = ((RaycastHit)(ref val)).point;
				hasAim = true;
			}
			nextCultivate = Time.unscaledTime + 0.25f;
			nextBrushPreview = 0f;
			if (Object.op_Implicit((Object)(object)cultivateRing))
			{
				((Renderer)cultivateRing).enabled = false;
			}
		}
	}

	private void StopCultivation()
	{
		if (Object.op_Implicit((Object)(object)cultivateRing))
		{
			Object.Destroy((Object)(object)((Component)cultivateRing).gameObject);
		}
		if (Object.op_Implicit((Object)(object)cultivateMaterial))
		{
			Object.Destroy((Object)(object)cultivateMaterial);
		}
		cultivateRing = null;
		cultivateMaterial = null;
		cultivateAim = false;
		cultivateRequest = false;
		cultivateMaps.Clear();
		cultivateCompilers.Clear();
		cultivateEffects = null;
		cultivatePlaceEffects = null;
	}

	private void TickCultivation(bool controller, Gamepad pad)
	{
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_013c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0141: Unknown result type (might be due to invalid IL or missing references)
		//IL_018c: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f6: Unknown result type (might be due to invalid IL or missing references)
		float num = ReadWheelStep();
		if (controller)
		{
			num += (pad.dpad.up.wasPressedThisFrame ? 1f : 0f) - (pad.dpad.down.wasPressedThisFrame ? 1f : 0f);
		}
		if (num != 0f)
		{
			cultivateRadius.Value = Mathf.Clamp(cultivateRadius.Value + num, 1f, 6f);
			nextBrushPreview = 0f;
		}
		bool flag = (controller ? pad.rightShoulder.isPressed : (!panelOpen && Mouse.current != null && Mouse.current.leftButton.isPressed));
		if (!flag)
		{
			cultivateArmed = true;
		}
		bool flag2 = cultivateRequest;
		cultivateRequest = false;
		if (!panelOpen || !cultivateAim)
		{
			RaycastHit val = default(RaycastHit);
			cultivateAim = Physics.Raycast(((Component)_camera).transform.position, ((Component)_camera).transform.forward, ref val, EffectiveCameraRadius * 2f + 20f, LayerMask.GetMask(new string[1] { "terrain" }), (QueryTriggerInteraction)1);
			if (cultivateAim)
			{
				cultivatePoint = ((RaycastHit)(ref val)).point;
			}
		}
		if (!cultivateAim)
		{
			cultivateStatus = "Aim at loaded ground";
			HidePreview();
			return;
		}
		float value = cultivateRadius.Value;
		if (Time.unscaledTime >= nextBrushPreview)
		{
			nextBrushPreview = Time.unscaledTime + 0.08f;
			string text = CultivationBlocked(cultivatePoint, value);
			if (text != null || Time.unscaledTime >= nextCultivate)
			{
				cultivateStatus = text ?? "Ready — cultivate the highlighted circle";
			}
			DrawCultivationRing(value, text == null);
		}
		if ((flag2 || (flag && cultivateArmed)) && !(Time.unscaledTime < nextCultivate))
		{
			nextCultivate = Time.unscaledTime + 0.3f;
			string text2 = CultivationBlocked(cultivatePoint, value);
			if (text2 != null)
			{
				cultivateStatus = text2;
			}
			else
			{
				ApplyCultivation(value);
			}
		}
	}

	private string CultivationBlocked(Vector3 point, float radius)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_001c: 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_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: 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_006b: 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_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: 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_00ae: Unknown result type (might be due to invalid IL or missing references)
		if (Vector3.Distance(_bodyOrigin, point) + radius > EffectiveBuildReach)
		{
			return "Outside camera range";
		}
		if (!PrivateArea.CheckAccess(point, radius, false, false))
		{
			return "Protected by a ward";
		}
		if (Location.IsInsideNoBuildLocation(point))
		{
			return "Protected location";
		}
		for (int i = 0; i < 16; i++)
		{
			float num = (float)i * (float)Math.PI / 8f;
			Vector3 val = point + new Vector3(Mathf.Cos(num), 0f, Mathf.Sin(num)) * radius;
			if (Location.IsInsideNoBuildLocation(val))
			{
				return "Protected location";
			}
			if (!Object.op_Implicit((Object)(object)Heightmap.FindHeightmap(val)))
			{
				return "Ground not loaded";
			}
		}
		if (!Object.op_Implicit((Object)(object)Heightmap.FindHeightmap(point)))
		{
			return "Ground not loaded";
		}
		if (Heightmap.HaveQueuedRebuild(point, radius))
		{
			return "Updating terrain";
		}
		return null;
	}

	private static Settings CultivationSettings(float radius)
	{
		//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)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0013: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: 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_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: 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_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		//IL_0069: Expected O, but got Unknown
		return new Settings
		{
			m_level = false,
			m_raise = false,
			m_smooth = false,
			m_square = false,
			m_halfOffset = false,
			m_paintCleared = true,
			m_paintType = (PaintType)1,
			m_paintRadius = radius,
			m_paintHeightCheck = false,
			m_paintStrength = 1f,
			m_paintExp = 0.1f,
			m_rotation = false,
			m_sides = false
		};
	}

	private void ApplyCultivation(float radius)
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_0193: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
		cultivateMaps.Clear();
		cultivateCompilers.Clear();
		Heightmap.FindHeightmap(cultivatePoint, radius, cultivateMaps);
		if (cultivateMaps.Count == 0 || cultivateMaps.Count > 4)
		{
			cultivateStatus = "Terrain not ready";
			return;
		}
		foreach (Heightmap cultivateMap in cultivateMaps)
		{
			if (!Object.op_Implicit((Object)(object)cultivateMap) || cultivateMap.HaveQueuedRebuild())
			{
				cultivateStatus = "Updating terrain";
				return;
			}
			TerrainComp andCreateTerrainCompiler = cultivateMap.GetAndCreateTerrainCompiler();
			if (!Object.op_Implicit((Object)(object)andCreateTerrainCompiler) || !(bool)CultivateInitialized.GetValue(andCreateTerrainCompiler))
			{
				cultivateStatus = "Terrain not ready";
				return;
			}
			ZNetView component = ((Component)andCreateTerrainCompiler).GetComponent<ZNetView>();
			if (!Object.op_Implicit((Object)(object)component) || !component.IsValid())
			{
				cultivateStatus = "Terrain not ready";
				return;
			}
			CultivateLoad.Invoke(andCreateTerrainCompiler, null);
			if (!component.IsOwner())
			{
				component.ClaimOwnership();
			}
			if (!component.IsOwner())
			{
				cultivateStatus = "Waiting for terrain ownership";
				return;
			}
			CultivateLoad.Invoke(andCreateTerrainCompiler, null);
			cultivateCompilers.Add(andCreateTerrainCompiler);
		}
		Settings val = CultivationSettings(radius);
		foreach (TerrainComp cultivateCompiler in cultivateCompilers)
		{
			if (!cultivateCompiler.IsOwner())
			{
				cultivateStatus = "Terrain ownership changed — try again";
				return;
			}
			CultivateApply.Invoke(cultivateCompiler, new object[3]
			{
				cultivatePoint,
				Vector3.zero,
				val
			});
		}
		cultivateStatus = "Ground cultivated — switch to Plant to sow seeds";
		PlayCultivationEffect();
	}

	private void DrawCultivationRing(float radius, bool allowed)
	{
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Expected O, but got Unknown
		//IL_0036: 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)
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_011c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0134: Unknown result type (might be due to invalid IL or missing references)
		//IL_013a: Unknown result type (might be due to invalid IL or missing references)
		//IL_013f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0144: Unknown result type (might be due to invalid IL or missing references)
		//IL_0146: Unknown result type (might be due to invalid IL or missing references)
		//IL_0171: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)cultivateRing))
		{
			Shader val = Shader.Find("Sprites/Default");
			if (!Object.op_Implicit((Object)(object)val))
			{
				return;
			}
			cultivateMaterial = new Material(val);
			cultivateRing = new GameObject("CultivatorCam_BrushPreview").AddComponent<LineRenderer>();
			((Renderer)cultivateRing).sharedMaterial = cultivateMaterial;
			cultivateRing.useWorldSpace = true;
			cultivateRing.loop = true;
			cultivateRing.positionCount = 48;
			LineRenderer obj = cultivateRing;
			float startWidth = (cultivateRing.endWidth = 0.06f);
			obj.startWidth = startWidth;
			((Renderer)cultivateRing).shadowCastingMode = (ShadowCastingMode)0;
			((Renderer)cultivateRing).receiveShadows = false;
		}
		LineRenderer obj2 = cultivateRing;
		Color startColor = (cultivateRing.endColor = (allowed ? new Color(0.35f, 1f, 0.6f, 0.9f) : new Color(1f, 0.25f, 0.2f, 0.9f)));
		obj2.startColor = startColor;
		float y = default(float);
		for (int i = 0; i < 48; i++)
		{
			float num2 = (float)i * (float)Math.PI * 2f / 48f;
			Vector3 val3 = cultivatePoint + new Vector3(Mathf.Cos(num2), 0f, Mathf.Sin(num2)) * radius;
			if (Heightmap.GetHeight(val3, ref y))
			{
				val3.y = y;
			}
			val3.y += 0.08f;
			cultivateRing.SetPosition(i, val3);
		}
		((Renderer)cultivateRing).enabled = true;
	}

	private void DrawModeButtons(float x, ref float y, float width)
	{
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: 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_009e: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		//IL_0189: Unknown result type (might be due to invalid IL or missing references)
		bool usingController = UsingController;
		GUI.Label(new Rect(x, y, width, 30f), cultivateMode ? "MODE: CULTIVATE GROUND" : "MODE: PLANT SEEDS", statusStyle);
		y += 32f;
		bool enabled = GUI.enabled;
		GUI.enabled = panelOpen && !queued;
		Color backgroundColor = GUI.backgroundColor;
		GUI.backgroundColor = (Color)(cultivateMode ? new Color(0.3f, 0.85f, 0.5f) : Color.white);
		if (GUI.Button(new Rect(x, y, (width - 10f) / 2f, 36f), "1. Cultivate ground", buttonStyle))
		{
			SetCultivateMode(value: true);
		}
		GUI.backgroundColor = (Color)(cultivateMode ? Color.white : new Color(0.3f, 0.85f, 0.5f));
		if (GUI.Button(new Rect(x + (width + 10f) / 2f, y, (width - 10f) / 2f, 36f), "2. Plant seeds", buttonStyle))
		{
			SetCultivateMode(value: false);
		}
		GUI.backgroundColor = backgroundColor;
		GUI.enabled = enabled;
		y += 42f;
		string text = (queued ? "Finish or stop this batch to switch mode" : ((usingController ? "Y: switch to " : "V: switch to ") + (cultivateMode ? "Plant seeds" : "Cultivate ground") + (usingController ? "" : "  ·  Tab: click controls")));
		GUI.Label(new Rect(x, y, width, 48f), text, bodyStyle);
		y += 54f;
	}

	private void DrawCultivationHud()
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: 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_00bd: 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_0130: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_0237: Unknown result type (might be due to invalid IL or missing references)
		//IL_027b: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_02c9: Unknown result type (might be due to invalid IL or missing references)
		bool usingController = UsingController;
		Matrix4x4 matrix = GUI.matrix;
		Color color = GUI.color;
		bool enabled = GUI.enabled;
		string help = CurrentControlHints();
		float num = Mathf.Clamp((float)Screen.height / 1080f, 0.6f, 2.5f);
		GUI.matrix = Matrix4x4.Scale(new Vector3(num, num, 1f));
		GUI.color = new Color(0.04f, 0.065f, 0.08f, 0.96f);
		GUI.DrawTexture(new Rect(20f, 150f, 468f, 481f), (Texture)(object)Texture2D.whiteTexture);
		GUI.color = Color.white;
		float num2 = 34f;
		float num3 = 160f;
		GUI.Label(new Rect(num2, num3, 440f, 34f), "FREE CULTIVATOR CAM", titleStyle);
		num3 += 42f;
		DrawModeButtons(num2, ref num3, 440f);
		GUI.Label(new Rect(num2, num3, 440f, 88f), usingController ? "1. Aim the circle at the ground.\n2. D-pad up / down: change brush size.\n3. Hold RB to cultivate the soil." : "1. Aim the circle at the ground.\n2. Mouse wheel: change brush size.\n3. Hold left click to cultivate the soil.", bodyStyle);
		num3 += 92f;
		GUI.Label(new Rect(num2, num3, 440f, 30f), "Brush radius: " + cultivateRadius.Value.ToString("0") + " m  ·  Diameter: " + (cultivateRadius.Value * 2f).ToString("0") + " m", bodyStyle);
		num3 += 36f;
		GUI.enabled = panelOpen;
		for (int i = 1; i <= 6; i++)
		{
			if (GUI.Button(new Rect(num2 + (float)((i - 1) * 74), num3, 68f, 38f), i + " m", buttonStyle))
			{
				cultivateRadius.Value = i;
				nextBrushPreview = 0f;
			}
		}
		num3 += 48f;
		GUI.enabled = enabled;
		GUI.Label(new Rect(num2, num3, 440f, 54f), cultivateStatus, bodyStyle);
		num3 += 60f;
		GUI.enabled = panelOpen && cultivateAim;
		if (GUI.Button(new Rect(num2, num3, 440f, 42f), "Cultivate highlighted area", buttonStyle))
		{
			cultivateRequest = true;
		}
		GUI.enabled = enabled;
		num3 += 51f;
		DrawControlStrip((float)Screen.width / num, (float)Screen.height / num, help);
		GUI.matrix = matrix;
		GUI.color = color;
	}

	private void UpdateCameraInputMode()
	{
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_0085: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
		Keyboard current = Keyboard.current;
		Mouse current2 = Mouse.current;
		Gamepad current3 = Gamepad.current;
		Vector2 val;
		int num;
		if (current == null || !((ButtonControl)current.anyKey).wasPressedThisFrame)
		{
			if (current2 != null)
			{
				val = ((InputControl<Vector2>)(object)((Pointer)current2).delta).ReadValue();
				num = ((((Vector2)(ref val)).sqrMagnitude > 1f || ((InputControl<Vector2>)(object)current2.scroll).ReadValue().y != 0f || current2.leftButton.wasPressedThisFrame || current2.rightButton.wasPressedThisFrame) ? 1 : 0);
			}
			else
			{
				num = 0;
			}
		}
		else
		{
			num = 1;
		}
		bool keyboardOrMouse = (byte)num != 0;
		int num2;
		if (current3 != null)
		{
			val = ((InputControl<Vector2>)(object)current3.leftStick).ReadValue();
			if (!(((Vector2)(ref val)).sqrMagnitude > 0.0625f))
			{
				val = ((InputControl<Vector2>)(object)current3.rightStick).ReadValue();
				if (!(((Vector2)(ref val)).sqrMagnitude > 0.0625f) && !current3.dpad.up.wasPressedThisFrame && !current3.dpad.down.wasPressedThisFrame && !current3.dpad.left.wasPressedThisFrame && !current3.dpad.right.wasPressedThisFrame && !current3.buttonSouth.wasPressedThisFrame && !current3.buttonNorth.wasPressedThisFrame && !current3.buttonWest.wasPressedThisFrame && !current3.buttonEast.wasPressedThisFrame && !current3.leftShoulder.wasPressedThisFrame && !current3.rightShoulder.wasPressedThisFrame && !current3.leftTrigger.wasPressedThisFrame && !current3.rightTrigger.wasPressedThisFrame && !current3.leftStickButton.wasPressedThisFrame)
				{
					num2 = (current3.rightStickButton.wasPressedThisFrame ? 1 : 0);
					goto IL_0195;
				}
			}
			num2 = 1;
		}
		else
		{
			num2 = 0;
		}
		goto IL_0195;
		IL_0195:
		bool gamepad = (byte)num2 != 0;
		inputMode.Update(keyboardOrMouse, gamepad);
	}

	private static bool ShortcutDown(KeyboardShortcut shortcut)
	{
		//IL_0002: 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)
		if (!ZInput.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey, true))
		{
			return false;
		}
		foreach (KeyCode modifier in ((KeyboardShortcut)(ref shortcut)).Modifiers)
		{
			if (!ZInput.GetKey(modifier, true))
			{
				return false;
			}
		}
		return true;
	}

	internal bool IsLocal(Player player)
	{
		if (Object.op_Implicit((Object)(object)player))
		{
			return (Object)(object)player == (Object)(object)Player.m_localPlayer;
		}
		return false;
	}

	internal bool ControlsOwned(Player player)
	{
		if (HooksEnabled && IsLocal(player))
		{
			if (!_active && !_chordHeld)
			{
				return Time.unscaledTime < _suppressUntil;
			}
			return true;
		}
		return false;
	}

	private void Awake()
	{
		//IL_004a: 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_00a8: Unknown result type (might be due to invalid IL or missing references)
		//IL_028c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0296: Expected O, but got Unknown
		if (Application.isBatchMode)
		{
			return;
		}
		Instance = this;
		_enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Enable the cultivator camera on this client.");
		_toggleKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "ToggleKey", new KeyboardShortcut((KeyCode)287, Array.Empty<KeyCode>()), "Enter/leave cultivator camera with a cultivator equipped.");
		_resetKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "ResetViewKey", new KeyboardShortcut((KeyCode)292, Array.Empty<KeyCode>()), "Reset the active camera to its initial angled view.");
		_reportKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "ReloadAndReportKey", new KeyboardShortcut((KeyCode)293, Array.Empty<KeyCode>()), "Write camera diagnostics, exit camera mode, and reload configuration. Separate from Larger HUD's F8.");
		_controllerToggle = ((BaseUnityPlugin)this).Config.Bind<bool>("Controls", "ControllerStickToggle", true, "Press both stick buttons together to enter/leave. A cultivator is needed to enter, but not to leave. Release both before toggling again.");
		_hidePlayer = ((BaseUnityPlugin)this).Config.Bind<bool>("View", "HideLocalPlayer", true, "Hide your character's renderers on your own screen while active. Your body remains in the world.");
		_showIndicator = ((BaseUnityPlugin)this).Config.Bind<bool>("View", "ShowIndicator", true, "Show the planting panel while the cultivator camera is active.");
		_exitOnDamage = ((BaseUnityPlugin)this).Config.Bind<bool>("Safety", "ExitOnDamage", true, "Exit immediately when the physical player takes damage. Set false only if you deliberately want environmental damage to be tolerated; death still exits.");
		_speed = FloatSetting("Camera", "MoveSpeed", 6f, 1f, 15f, "Camera movement in metres per second.");
		_fastMultiplier = FloatSetting("Camera", "FastMultiplier", 2f, 1f, 3f, "Hold left-stick click on controller, or Run on keyboard, for faster camera movement.");
		_radius = FloatSetting("Camera", "CameraRadius", 25f, 8f, 25f, "Base camera radius in metres, multiplied by FreeCameraRangeMultiplier. Free Cultivator Cam doubles the result: default 25 x 2 x 2 = 100 metres around the stationary body. Existing base settings are retained.");
		_reach = FloatSetting("Building", "BuildReach", 25f, 5f, 25f, "Planting reach from the stationary player, multiplied by FreeCameraRangeMultiplier and then doubled by Free Cultivator Cam (100m by default). Each crop's growth area must fit inside this distance.");
		_rangeMultiplier = FloatSetting("Camera", "FreeCameraRangeMultiplier", 2f, 1f, 2f, "Multiply both base camera radius and active build reach. The default multiplier is 2. Set 1 for a smaller working area. Does not affect normal player building reach.");
		_startPitch = FloatSetting("View", "StartingPitch", 50f, 20f, 75f, "Downward angle when entering/resetting the camera. Mouse/right stick can adjust it afterward.");
		_startFov = FloatSetting("View", "FieldOfView", 45f, 25f, 75f, "Starting perspective field of view. Page Up/Down zoom while active; mouse wheel changes plants per row.");
		try
		{
			StickInput.Initialize(typeof(ZInput), delegate(string message)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)message);
			});
			ValidateHooks();
			_harmony = new Harmony("valheim.freecultivatorcam");
			_harmony.PatchAll(typeof(CultivatorCamera).Assembly);
			InstallBuildCameraCompatibility();
			ConfigurePlanting();
			_ready = true;
			RefreshIndicatorText();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Free Cultivator Cam 0.1.18 loaded. F6 toggle; F11 reset; F12 report/reload.");
		}
		catch (Exception ex)
		{
			_ready = false;
			_failed = true;
			if (_harmony != null)
			{
				_harmony.UnpatchSelf();
			}
			((BaseUnityPlugin)this).Logger.LogError((object)("Free Cultivator Cam could not install its game hooks and remains inactive. " + ex));
		}
		WriteDiagnostics("Startup");
	}

	private ConfigEntry<float> FloatSetting(string section, string key, float value, float min, float max, string text)
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Expected O, but got Unknown
		return ((BaseUnityPlugin)this).Config.Bind<float>(section, key, value, new ConfigDescription(text, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), Array.Empty<object>()));
	}

	private void Update()
	{
		Tick();
		TickBackgroundPlanting();
		try
		{
			TickHarvest();
			TickHarvestCollection();
		}
		catch (Exception ex)
		{
			ClearHarvest();
			((BaseUnityPlugin)this).Logger.LogError((object)("Harvest cancelled: " + ex));
		}
	}

	internal void Tick()
	{
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a4: Unknown result type (might be due to invalid IL or missing references)
		if (!_ready || !((Behaviour)this).isActiveAndEnabled || _tickFrame == Time.frameCount)
		{
			return;
		}
		_tickFrame = Time.frameCount;
		try
		{
			if (Application.isFocused && ShortcutDown(_reportKey.Value))
			{
				WriteDiagnostics("F12 before reload");
				StopCamera("Configuration reload", message: false);
				ResetInput();
				((BaseUnityPlugin)this).Config.Reload();
				RefreshIndicatorText();
				_failed = false;
				Notify("Cultivator camera settings reloaded; diagnostic report saved.");
			}
			else
			{
				if (_failed)
				{
					return;
				}
				if (!_enabled.Value)
				{
					StopCamera("Disabled in configuration", message: false);
					ResetInput();
					return;
				}
				Player localPlayer = Player.m_localPlayer;
				if (!Object.op_Implicit((Object)(object)localPlayer) || ZInput.instance == null)
				{
					StopCamera("Player/input changed", message: false);
					ResetInput();
					return;
				}
				if (!Application.isFocused)
				{
					ResetInput();
					return;
				}
				bool flag = (ShortcutDown(_toggleKey.Value) || ZInput.GetKeyDown((KeyCode)287, true)) && (_active || HasBuildingTool(localPlayer));
				bool left = false;
				bool right = false;
				if (_controllerToggle.Value)
				{
					ReadControllerSticks(out left, out right);
				}
				if (!_active && !flag && !_toggleInput.HasPendingKeyboard && !left && !right)
				{
					_toggleInput.Step(Time.frameCount, keyDown: false, keyboardAllowed: false, left: false, right: false, controllerAllowed: false, Time.unscaledDeltaTime);
					_chordHeld = false;
					return;
				}
				bool flag2 = MenuBlocksCamera();
				bool flag3 = CameraMath.ControllerToggleEligible(_controllerToggle.Value, localPlayerPresent: true, flag2, _active, _active || HasBuildingTool(localPlayer));
				_chordHeld = flag3 && left && right;
				if (_toggleInput.Step(Time.frameCount, flag, _active || !flag2, left, right, flag3, Time.unscaledDeltaTime))
				{
					if (_active)
					{
						StopCamera("Toggled off", message: true);
					}
					else if (!flag2)
					{
						StartCamera(localPlayer);
					}
				}
				else
				{
					if (!_active)
					{
						return;
					}
					UpdateCameraInputMode();
					string text = InvalidActiveReason();
					if (text != null)
					{
						StopCamera(text, message: true);
						return;
					}
					KeepBodyAtAnchor();
					if (Menu.IsVisible() || ZInput.GetKeyDown((KeyCode)27, true) || ZInput.GetButtonDown("JoyMenu") || ZInput.GetButtonDown("JoyStart") || (Gamepad.current != null && Gamepad.current.startButton.wasPressedThisFrame))
					{
						StopCamera("Escape", message: true);
						return;
					}
					if (ShortcutDown(_resetKey.Value) && !flag2)
					{
						ResetView();
						_suppressUntil = Time.unscaledTime + 0.2f;
					}
					if (!flag2 && !panelOpen && !_chordHeld && Time.unscaledTime >= _suppressUntil)
					{
						MoveCamera();
					}
					ApplyPose();
					if (!flag2 && !_chordHeld && Time.unscaledTime >= _suppressUntil)
					{
						TickPlanting();
					}
					else
					{
						HidePreview();
					}
				}
			}
		}
		catch (Exception error)
		{
			Fail(error);
		}
	}

	private void LateUpdate()
	{
		if (!_active)
		{
			return;
		}
		try
		{
			CultivatorCameraRumble.Stop();
			string text = InvalidActiveReason();
			if (text != null)
			{
				StopCamera(text, message: true);
				return;
			}
			KeepBodyAtAnchor();
			ApplyPose();
			if (Time.unscaledTime >= _nextCircletCheck)
			{
				CircletCameraLight.Update(_player, _camera);
				_nextCircletCheck = Time.unscaledTime + 0.25f;
			}
			if (!_hidePlayer.Value)
			{
				RestoreRenderers();
			}
			else if (_hidePlayer.Value && Time.unscaledTime >= _nextRendererScan)
			{
				HideRenderers();
				_nextRendererScan = Time.unscaledTime + 0.25f;
			}
		}
		catch (Exception error)
		{
			Fail(error);
		}
	}

	private static bool HasBuildingTool(Player player)
	{
		return IsBuildTool(((Humanoid)player).RightItem);
	}

	private static bool IsBuildTool(ItemData item)
	{
		if (item != null && item.m_shared != null && (Object)(object)item.m_shared.m_buildPieces != (Object)null)
		{
			if (!Object.op_Implicit((Object)(object)item.m_dropPrefab) || !(((Object)item.m_dropPrefab).name == "Cultivator"))
			{
				return item.m_shared.m_name == "$item_cultivator";
			}
			return true;
		}
		return false;
	}

	private static void ReadControllerSticks(out bool left, out bool right)
	{
		left = ZInput.GetButton("JoyLStick");
		right = ZInput.GetButton("JoyRStick");
		try
		{
			Gamepad current = Gamepad.current;
			if (current != null)
			{
				left |= current.leftStickButton.isPressed;
				right |= current.rightStickButton.isPressed;
			}
		}
		catch (Exception)
		{
		}
	}

	internal static bool MenuBlocksCamera()
	{
		try
		{
			return (Object.op_Implicit((Object)(object)Chat.instance) && Chat.instance.HasFocus()) || Console.IsVisible() || TextInput.IsVisible() || InventoryGui.IsVisible() || Menu.IsVisible() || StoreGui.IsVisible() || Minimap.IsOpen() || (Object.op_Implicit((Object)(object)TextViewer.instance) && TextViewer.instance.IsVisible()) || UnifiedPopup.IsVisible() || Hud.IsPieceSelectionVisible() || Hud.InRadial() || PlayerCustomizaton.IsBarberGuiVisible() || IsGamePausedSafe();
		}
		catch (NullReferenceException)
		{
			return true;
		}
	}

	private static bool IsGamePausedSafe()
	{
		if ((Object)(object)ZNet.instance == (Object)null || (Object)(object)Player.m_localPlayer == (Object)null)
		{
			return false;
		}
		try
		{
			return Game.IsPaused();
		}
		catch (NullReferenceException)
		{
			return false;
		}
	}

	private void StartCamera(Player local)
	{
		//IL_00e8: 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_00fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0103: Unknown result type (might be due to invalid IL or missing references)
		//IL_0114: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)local) || !HasBuildingTool(local))
		{
			Notify("Equip your cultivator first.");
			return;
		}
		if (((Character)local).IsDead() || ((Character)local).InCutscene() || ((Character)local).IsTeleporting() || ((Character)local).IsAttached() || local.GetDoodadController() != null || !((Character)local).IsOnGround() || ((Character)local).IsSwimming())
		{
			Notify("Stand on solid ground to enter the cultivator camera.");
			return;
		}
		if (!Object.op_Implicit((Object)(object)GameCamera.instance) || GameCamera.InFreeFly())
		{
			Notify("Return to the normal game camera first.");
			return;
		}
		if (OtherBuildCameraActive())
		{
			Notify("Exit the hammer build camera first.");
			return;
		}
		_gameCamera = GameCamera.instance;
		_camera = ((Component)_gameCamera).GetComponent<Camera>();
		if (!Object.op_Implicit((Object)(object)_camera))
		{
			Notify("The game camera is not ready yet.");
			_gameCamera = null;
			return;
		}
		_skyCamera = _gameCamera.m_skyCamera;
		_player = local;
		_bodyOrigin = ((Component)local).transform.position;
		_savedCameraPosition = ((Component)_gameCamera).transform.position;
		_savedCameraRotation = ((Component)_gameCamera).transform.rotation;
		_savedFov = _camera.fieldOfView;
		_savedNearClip = _camera.nearClipPlane;
		_activeNearClip = Mathf.Min(_savedNearClip, 0.1f);
		_savedSkyFov = (Object.op_Implicit((Object)(object)_skyCamera) ? _skyCamera.fieldOfView : _savedFov);
		_savedReach = local.m_maxPlaceDistance;
		_appliedReach = EffectiveBuildReach;
		_active = true;
		inputMode.Reset(ZInput.IsGamepadActive());
		StartPlanting();
		local.m_autoRun = false;
		((Character)local).StopMovement();
		local.SetControls(Vector3.zero, false, false, false, false, false, false, false, false, false, false, false);
		local.m_maxPlaceDistance = _appliedReach;
		((Character)local).m_onDamaged = (Action<float, Character>)Delegate.Combine(((Character)local).m_onDamaged, new Action<float, Character>(OnPlayerDamaged));
		ResetView();
		if (_hidePlayer.Value)
		{
			HideRenderers();
		}
		_suppressUntil = Time.unscaledTime + 0.2f;
		_nextRendererScan = Time.unscaledTime + 0.25f;
		ApplyPose();
		Notify(ZInput.IsGamepadActive() ? "Cultivator camera ON. Y: switch Cultivate / Plant. RB: use. Both stick clicks: exit." : "Cultivator camera ON. V: switch Cultivate / Plant. Tab: clickable controls. F6: exit.");
		WriteDiagnostics("Activated");
	}

	private void KeepBodyAtAnchor()
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: 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_0029: 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)
		if (Object.op_Implicit((Object)(object)_player))
		{
			Vector3 val = ((Component)_player).transform.position - _bodyOrigin;
			if (((Vector3)(ref val)).sqrMagnitude > 0.0001f)
			{
				((Component)_player).transform.position = _bodyOrigin;
				Physics.SyncTransforms();
			}
			((Character)_player).StopMovement();
		}
	}

	private string InvalidActiveReason()
	{
		if (!Object.op_Implicit((Object)(object)_player) || (Object)(object)_player != (Object)(object)Player.m_localPlayer)
		{
			return "Player changed";
		}
		if (!Object.op_Implicit((Object)(object)_gameCamera) || (Object)(object)GameCamera.instance != (Object)(object)_gameCamera || !Object.op_Implicit((Object)(object)_camera))
		{
			return "Camera changed";
		}
		if (!HasBuildingTool(_player))
		{
			return "Cultivator unequipped";
		}
		if (((Character)_player).IsDead())
		{
			return "Player died";
		}
		if (((Character)_player).InCutscene() || ((Character)_player).IsTeleporting() || ((Character)_player).IsAttached() || _player.GetDoodadController() != null)
		{
			return "Player changed activity";
		}
		return null;
	}

	private void OnPlayerDamaged(float damage, Character attacker)
	{
		if (_active && _exitOnDamage.Value && damage > 0f)
		{
			StopCamera("You took damage", message: true);
		}
	}

	private void ResetView()
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: 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_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: 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)
		//IL_00c7: 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_00d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		//IL_00df: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e9: 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)
		Quaternion rotation = ((Character)_player).m_eye.rotation;
		_yaw = ((Quaternion)(ref rotation)).eulerAngles.y;
		_pitch = CameraMath.Clamp(_startPitch.Value, 50f, 20f, 75f);
		_fov = CameraMath.Clamp(_startFov.Value, 45f, 25f, 75f);
		_rotation = Quaternion.Euler(_pitch, _yaw, 0f);
		Vector3 val = ((Component)_player).transform.position + Quaternion.Euler(0f, _yaw, 0f) * Vector3.forward * 5f + Vector3.up;
		_position = val - _rotation * Vector3.forward * 12f;
		ClampCameraPosition();
	}

	private void MoveCamera()
	{
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: 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_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0161: Unknown result type (might be due to invalid IL or missing references)
		//IL_0166: Unknown result type (might be due to invalid IL or missing references)
		//IL_017b: Unknown result type (might be due to invalid IL or missing references)
		//IL_016e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0180: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_032a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0335: Unknown result type (might be due to invalid IL or missing references)
		//IL_033c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0342: Unknown result type (might be due to invalid IL or missing references)
		//IL_0347: Unknown result type (might be due to invalid IL or missing references)
		//IL_034c: Unknown result type (might be due to invalid IL or missing references)
		Gamepad current = Gamepad.current;
		bool usingController = UsingController;
		float num = CameraMath.Clamp(Time.unscaledDeltaTime, 0f, 0f, 0.05f);
		Vector2 val = Vector2.zero;
		if (usingController)
		{
			val = ((InputControl<Vector2>)(object)current.rightStick).ReadValue() * 110f * num * PlayerController.m_gamepadSens;
			if (PlayerController.m_invertCameraX)
			{
				val.x *= -1f;
			}
			if (PlayerController.m_invertCameraY)
			{
				val.y *= -1f;
			}
		}
		else if (Mouse.current != null)
		{
			val = ZInput.GetMouseDelta() * PlayerController.m_mouseSens;
			if (PlayerController.m_invertMouse)
			{
				val.y *= -1f;
			}
		}
		_yaw = Mathf.Repeat(_yaw + val.x, 360f);
		_pitch = Mathf.Clamp(_pitch - val.y, -80f, 85f);
		if (ZInput.GetKey((KeyCode)280, true))
		{
			_fov -= 25f * num;
		}
		if (ZInput.GetKey((KeyCode)281, true))
		{
			_fov += 25f * num;
		}
		_fov = Mathf.Clamp(_fov, 25f, 75f);
		_rotation = Quaternion.Euler(_pitch, _yaw, 0f);
		Vector2 val2 = (usingController ? ((InputControl<Vector2>)(object)current.leftStick).ReadValue() : Vector2.zero);
		float x = (usingController ? val2.x : ((ZInput.GetButton("Right") ? 1f : 0f) - (ZInput.GetButton("Left") ? 1f : 0f)));
		float z = (usingController ? val2.y : ((ZInput.GetButton("Forward") ? 1f : 0f) - (ZInput.GetButton("Backward") ? 1f : 0f)));
		float y = (usingController ? ((current.buttonSouth.isPressed ? 1f : 0f) - (current.rightStickButton.isPressed ? 1f : 0f)) : ((ZInput.GetButton("Jump") ? 1f : 0f) - (ZInput.GetButton("Crouch") ? 1f : 0f)));
		CameraMath.NormalizeInput(ref x, ref y, ref z);
		float num2 = CameraMath.Clamp(_speed.Value, 6f, 1f, 15f);
		if (usingController ? current.leftStickButton.isPressed : ZInput.GetButton("Run"))
		{
			num2 *= CameraMath.Clamp(_fastMultiplier.Value, 2f, 1f, 3f);
		}
		if (x != 0f || y != 0f || z != 0f)
		{
			MovementVector movementVector = CameraMath.ViewRelativeMovement(_yaw, _pitch, x, y, z);
			float x2 = movementVector.X;
			float y2 = movementVector.Y;
			float z2 = movementVector.Z;
			CameraMath.NormalizeInput(ref x2, ref y2, ref z2);
			_position += new Vector3(x2, y2, z2) * num2 * num;
			ClampCameraPosition();
		}
	}

	private void ClampCameraPosition()
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: 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_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: 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_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		Vector3 val = _bodyOrigin + Vector3.up;
		Vector3 val2 = _position - val;
		float x = val2.x;
		float y = val2.y;
		float z = val2.z;
		CameraMath.ClampToSphere(ref x, ref y, ref z, EffectiveCameraRadius);
		_position = val + new Vector3(x, y, z);
	}

	internal void ApplyPose()
	{
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_004b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		if (!_active || !Object.op_Implicit((Object)(object)_camera) || !Object.op_Implicit((Object)(object)_gameCamera))
		{
			return;
		}
		CultivatorCameraRumble.Stop();
		Transform transform = ((Component)_gameCamera).transform;
		Vector3 position = transform.position;
		if (((Vector3)(ref position)).Equals(_position))
		{
			Quaternion rotation = transform.rotation;
			if (((Quaternion)(ref rotation)).Equals(_rotation))
			{
				goto IL_0072;
			}
		}
		transform.SetPositionAndRotation(_position, _rotation);
		goto IL_0072;
		IL_0072:
		if (_camera.fieldOfView != _fov)
		{
			_camera.fieldOfView = _fov;
		}
		if (_camera.nearClipPlane != _activeNearClip)
		{
			_camera.nearClipPlane = _activeNearClip;
		}
		if (Object.op_Implicit((Object)(object)_skyCamera) && _skyCamera.fieldOfView != _fov)
		{
			_skyCamera.fieldOfView = _fov;
		}
	}

	private void HideRenderers()
	{
		LocalPlayerVisuals.Hide(_player, _camera);
	}

	private void StopCamera(string reason, bool message)
	{
		//IL_022c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0232: Unknown result type (might be due to invalid IL or missing references)
		//IL_0237: Unknown result type (might be due to invalid IL or missing references)
		//IL_023c: Unknown result type (might be due to invalid IL or missing references)
		//IL_027d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0283: Unknown result type (might be due to invalid IL or missing references)
		if (reason != "Focus lost" && !KeepBatchAfterCameraExit(reason))
		{
			CancelBackgroundPlanting(reason, message: false);
		}
		if (!_active)
		{
			_toggleInput.CancelPending();
			return;
		}
		_nextCircletCheck = 0f;
		if (KeepBatchAfterCameraExit(reason))
		{
			Cleanup("Pending planting", DetachPlantingBatch);
		}
		Cleanup("Planting preview", StopPlanting);
		if (_active)
		{
			Cleanup("Controller rumble", CultivatorCameraRumble.Stop);
		}
		Cleanup("Circlet light", CircletCameraLight.Stop);
		Cleanup("Mist", FeatherCapeMist.Clear);
		_toggleInput.CancelPending();
		if (!_active)
		{
			return;
		}
		_active = false;
		_lastExit = reason;
		try
		{
			if (Object.op_Implicit((Object)(object)_player))
			{
				Player player = _player;
				((Character)player).m_onDamaged = (Action<float, Character>)Delegate.Remove(((Character)player).m_onDamaged, new Action<float, Character>(OnPlayerDamaged));
				if (Mathf.Approximately(_player.m_maxPlaceDistance, _appliedReach))
				{
					_player.m_maxPlaceDistance = _savedReach;
				}
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Build reach cleanup: " + ex.Message));
		}
		Cleanup("Player visuals", RestoreRenderers);
		try
		{
			if (Object.op_Implicit((Object)(object)_camera))
			{
				_camera.fieldOfView = _savedFov;
				_camera.nearClipPlane = _savedNearClip;
			}
			if (Object.op_Implicit((Object)(object)_skyCamera))
			{
				_skyCamera.fieldOfView = _savedSkyFov;
			}
			if (Object.op_Implicit((Object)(object)_player) && (Object)(object)_player == (Object)(object)Player.m_localPlayer && !((Character)_player).IsTeleporting() && !((Character)_player).InCutscene() && !((Character)_player).IsAttached() && _player.GetDoodadController() == null)
			{
				Vector3 val = ((Component)_player).transform.position - _bodyOrigin;
				if (((Vector3)(ref val)).sqrMagnitude < 1f && Object.op_Implicit((Object)(object)_gameCamera) && (Object)(object)_gameCamera == (Object)(object)GameCamera.instance && !GameCamera.InFreeFly())
				{
					((Component)_gameCamera).transform.SetPositionAndRotation(_savedCameraPosition, _savedCameraRotation);
				}
			}
		}
		catch (Exception ex2)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Camera cleanup: " + ex2.Message));
		}
		_player = null;
		_gameCamera = null;
		_camera = null;
		_skyCamera = null;
		float suppressUntil;
		switch (reason)
		{
		default:
			suppressUntil = 0f;
			break;
		case "Toggled off":
		case "Escape":
		case "Configuration reload":
			suppressUntil = Time.unscaledTime + 0.2f;
			break;
		}
		_suppressUntil = suppressUntil;
		if (message)
		{
			Notify("Cultivator camera OFF: " + reason + "." + ((backgroundBatch != null) ? " Planting continues." : ""));
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)("Free Cultivator Cam stopped: " + reason));
	}

	private void RestoreRenderers()
	{
		LocalPlayerVisuals.Restore();
	}

	private void Cleanup(string label, Action action)
	{
		try
		{
			action();
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)(label + " cleanup: " + ex.Message));
		}
	}

	internal void Fail(Exception error)
	{
		StopCamera("Stopped after an error", message: false);
		_failed = true;
		ResetInput();
		((BaseUnityPlugin)this).Logger.LogError((object)("Free Cultivator Cam stopped and restored its local changes. " + error));
		WriteDiagnostics("Error: " + error.GetType().Name + ": " + error.Message);
	}

	private static void Notify(string text)
	{
		if (Object.op_Implicit((Object)(object)MessageHud.instance))
		{
			MessageHud.instance.ShowMessage((MessageType)1, text, 0, (Sprite)null, false, true);
		}
	}

	private void OnGUI()
	{
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0031: 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_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Invalid comparison between Unknown and I4
		if (!Active || MenuBlocksCamera() || (!panelOpen && (int)Event.current.type != 7))
		{
			return;
		}
		Matrix4x4 matrix = GUI.matrix;
		Color color = GUI.color;
		Color backgroundColor = GUI.backgroundColor;
		bool enabled = GUI.enabled;
		try
		{
			DrawPlantingHud();
		}
		finally
		{
			GUI.matrix = matrix;
			GUI.color = color;
			GUI.backgroundColor = backgroundColor;
			GUI.enabled = enabled;
		}
	}

	private void RefreshIndicatorText()
	{
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		ClearControlHints();
		_indicatorText = "CULTIVATOR CAM  |  " + ((object)_toggleKey.Value/*cast due to .constrained prefix*/).ToString() + (_controllerToggle.Value ? " / press both stick clicks to exit" : " to exit");
	}

	private void ResetInput()
	{
		_toggleInput.Reset(waitForRelease: true);
		_chordHeld = false;
		_suppressUntil = 0f;
	}

	private void OnApplicationFocus(bool focused)
	{
		if (!focused)
		{
			StopCamera("Focus lost", message: false);
			ResetInput();
		}
	}

	private void OnDisable()
	{
		ClearHarvest();
		StopCamera("Plugin disabled", message: false);
		ResetInput();
	}

	private void OnDestroy()
	{
		StopCamera("Plugin unloaded", message: false);
		ResetInput();
		if (_harmony != null)
		{
			_harmony.UnpatchSelf();
		}
		if ((Object)(object)Instance == (Object)(object)this)
		{
			Instance = null;
		}
	}

	private void WriteDiagnostics(string reason)
	{
		//IL_0226: Unknown result type (might be due to invalid IL or missing references)
		//IL_022c: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("Free Cultivator Cam 0.1.18 - client diagnostics");
			stringBuilder.AppendLine("Reason: " + reason);
			stringBuilder.AppendLine("No player names, server addresses, chat, item descriptions or world coordinates included.");
			stringBuilder.AppendLine("Unity=" + Application.unityVersion + "; screen=" + Screen.width + "x" + Screen.height);
			stringBuilder.AppendLine("Ready=" + _ready + "; failed=" + _failed + "; active=" + _active + "; last exit=" + _lastExit);
			stringBuilder.AppendLine("Effective camera radius=" + EffectiveCameraRadius + "; configured free-camera reach=" + EffectiveBuildReach + "; range multiplier=" + _rangeMultiplier.Value);
			stringBuilder.AppendLine("Game assembly=" + typeof(Player).Assembly.GetName().Version?.ToString() + "; Harmony=" + typeof(Harmony).Assembly.GetName().Version);
			if (Object.op_Implicit((Object)(object)_player))
			{
				stringBuilder.AppendLine("Building tool=" + HasBuildingTool(_player) + "; grounded=" + ((Character)_player).IsOnGround() + "; menu paused=" + MenuBlocksCamera());
				stringBuilder.AppendLine("Camera distance from body=" + Vector3.Distance(_position, _bodyOrigin).ToString("F2", CultureInfo.InvariantCulture));
				stringBuilder.AppendLine("Pitch=" + _pitch + "; FOV=" + _fov + "; temporary reach=" + _player.m_maxPlaceDistance + "; original reach=" + _savedReach);
				stringBuilder.AppendLine("Tracked local renderers=" + LocalPlayerVisuals.RendererCount);
			}
			stringBuilder.AppendLine("Planting uses native pieces and ordinary seeds, with optional Easy Craft & Build resource transactions. No terrain deformation is performed.");
			File.WriteAllText(Path.Combine(Paths.ConfigPath, "valheim.freecultivatorcam.diagnostics.txt"), stringBuilder.ToString());
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not write cultivator camera diagnostics: " + ex.Message));
		}
	}

	private static void ValidateHooks()
	{
		Require(typeof(Player), "Update", Type.EmptyTypes);
		Require(typeof(HotkeyBar), "Update", Type.EmptyTypes);
		Require(typeof(KeyHints), "UpdateHints", Type.EmptyTypes);
		Require(typeof(ParticleMist), "Update", Type.EmptyTypes);
		Require(typeof(InventoryGui), "Show", new Type[2]
		{
			typeof(Container),
			typeof(int)
		});
		Require(typeof(Player), "SetControls", new Type[12]
		{
			typeof(Vector3),
			typeof(bool),
			typeof(bool),
			typeof(bool),
			typeof(bool),
			typeof(bool),
			typeof(bool),
			typeof(bool),
			typeof(bool),
			typeof(bool),
			typeof(bool),
			typeof(bool)
		});
		Require(typeof(Player), "SetMouseLook", new Type[1] { typeof(Vector2) });
		Require(typeof(Player), "TakeInput", Type.EmptyTypes);
		Require(typeof(Player), "StartGuardianPower", Type.EmptyTypes);
		Require(typeof(GameCamera), "UpdateCamera", new Type[1] { typeof(float) });
		Require(typeof(GameCamera), "LateUpdate", Type.EmptyTypes);
		Require(typeof(Player), "UpdatePlacement", new Type[2]
		{
			typeof(bool),
			typeof(float)
		});
	}

	private static void Require(Type type, string name, Type[] arguments)
	{
		if (AccessTools.DeclaredMethod(type, name, arguments, (Type[])null) == null)
		{
			throw new MissingMethodException(type.FullName, name);
		}
	}

	private static bool ToolbarButtonDown(string name)
	{
		CultivatorCamera instance = Instance;
		if (Object.op_Implicit((Object)(object)instance) && instance.ControlsOwned(Player.m_localPlayer))
		{
			return false;
		}
		return ZInput.GetButtonDown(name);
	}

	private static void HarvestDropCreated(ItemDrop item, bool autoStack, Pickable crop)
	{
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		NativeDropCreated(item, autoStack);
		if (Object.op_Implicit((Object)(object)item) && Planter((Component)(object)crop) != 0L)
		{
			ZNetView component = ((Component)crop).GetComponent<ZNetView>();
			ZNetView component2 = ((Component)item).GetComponent<ZNetView>();
			if (Object.op_Implicit((Object)(object)component) && component.IsValid() && Object.op_Implicit((Object)(object)component2) && component2.IsValid() && component2.IsOwner())
			{
				component2.GetZDO().Set("FreeCultivatorCam.HarvestSource", component.GetZDO().m_uid);
			}
		}
	}

	private void RememberHarvestDropSource(Pickable crop)
	{
		//IL_001e: Unknown result type (might be due to invalid IL or missing references)
		ZNetView component = ((Component)crop).GetComponent<ZNetView>();
		if (Object.op_Implicit((Object)(object)component) && component.IsValid())
		{
			RememberHarvestDropSource(component.GetZDO().m_uid);
		}
	}

	private void RememberHarvestDropSource(ZDOID source)
	{
		//IL_0004: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: 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)
		if (!((ZDOID)(ref source)).Equals(default(ZDOID)))
		{
			collectUntil = Time.unscaledTime + 15f;
			harvestSources[source] = collectUntil;
		}
	}

	private void ClearHarvestCollection()
	{
		//IL_001c: Unknown result type (might be due to invalid IL or missing references)
		harvestSources.Clear();
		handledHarvestDrops.Clear();
		firstHarvestSource = default(ZDOID);
		dropCursor = 0;
	}

	private void TickHarvestCollection()
	{
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_0141: Unknown result type (might be due to invalid IL or missing references)
		//IL_0165: Unknown result type (might be due to invalid IL or missing references)
		//IL_0170: Unknown result type (might be due to invalid IL or missing references)
		//IL_0175: Unknown result type (might be due to invalid IL or missing references)
		//IL_017a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0191: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
		if (harvestSources.Count == 0)
		{
			return;
		}
		Player localPlayer = Player.m_localPlayer;
		if (!HooksEnabled || !_enabled.Value || Active || !Object.op_Implicit((Object)(object)localPlayer) || ((Character)localPlayer).IsDead() || localPlayer.GetPlayerID() != harvestPlayer || !Application.isFocused || MenuBlocksCamera() || Time.unscaledTime > collectUntil)
		{
			ClearHarvestCollection();
		}
		else
		{
			if (Time.unscaledTime < nextDropCheck)
			{
				return;
			}
			nextDropCheck = Time.unscaledTime + 0.05f;
			List<ItemDrop> list = (List<ItemDrop>)DropListField.GetValue(null);
			int num = Math.Min(32, list.Count);
			for (int i = 0; i < num; i++)
			{
				if (list.Count <= 0)
				{
					break;
				}
				if (dropCursor >= list.Count)
				{
					dropCursor = 0;
				}
				ItemDrop val = list[dropCursor++];
				if (!Object.op_Implicit((Object)(object)val))
				{
					continue;
				}
				ZNetView component = ((Component)v