Decompiled source of SelfieStick v0.3.2

CameraProof.dll

Decompiled 2 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.PostProcessing;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("CameraProof")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.3.2.0")]
[assembly: AssemblyInformationalVersion("0.3.2+cd4bbbbe83401105799f76907987befec8a1a8fc")]
[assembly: AssemblyProduct("CameraProof")]
[assembly: AssemblyTitle("CameraProof")]
[assembly: AssemblyVersion("0.3.2.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace CameraProof
{
	[BepInPlugin("dev.djc.camera-proof", "Camera Proof", "0.3.2")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private static class ClothPatches
		{
			private static Harmony _harmony;

			private static ManualLogSource _log;

			private static Type _magicaCloth;

			private static MethodInfo _disableAutoBuild;

			private static int _skipped;

			public static void Apply(ManualLogSource log, bool skipEquipment, bool disableAll)
			{
				//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c1: Expected O, but got Unknown
				//IL_00da: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e8: Expected O, but got Unknown
				//IL_0163: Unknown result type (might be due to invalid IL or missing references)
				//IL_0171: Expected O, but got Unknown
				_log = log;
				if (!skipEquipment && !disableAll)
				{
					log.LogInfo((object)"Cloth patches off by config.");
					return;
				}
				_magicaCloth = Type.GetType("MagicaCloth2.MagicaCloth, MagicaClothV2", throwOnError: false);
				Type type = Type.GetType("MagicaCloth2.ClothProcess, MagicaClothV2", throwOnError: false);
				MethodInfo methodInfo = AccessTools.Method(typeof(VisEquipment), "SetupCloth", new Type[1] { typeof(GameObject) }, (Type[])null);
				_disableAutoBuild = ((_magicaCloth == null) ? null : AccessTools.Method(_magicaCloth, "DisableAutoBuild", (Type[])null, (Type[])null));
				if (methodInfo == null || _magicaCloth == null || _disableAutoBuild == null)
				{
					log.LogInfo((object)"No VisEquipment.SetupCloth / MagicaCloth2 on this build; no cloth to skip.");
					return;
				}
				_harmony = new Harmony("dev.djc.camera-proof.cloth");
				if (skipEquipment)
				{
					_harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(ClothPatches), "SkipSetupCloth", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					log.LogInfo((object)"Cloth patch engaged: VisEquipment.SetupCloth prefixed; equipment cloth is never built.");
				}
				if (!disableAll)
				{
					return;
				}
				MethodInfo methodInfo2 = AccessTools.Method(_magicaCloth, "BuildAndRun", (Type[])null, (Type[])null);
				MethodInfo methodInfo3 = ((type == null) ? null : AccessTools.Method(type, "AutoBuild", (Type[])null, (Type[])null));
				MethodInfo[] array = new MethodInfo[2] { methodInfo2, methodInfo3 };
				foreach (MethodInfo methodInfo4 in array)
				{
					if (methodInfo4 != null)
					{
						_harmony.Patch((MethodBase)methodInfo4, new HarmonyMethod(typeof(ClothPatches), "NeverBuild", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					}
				}
				log.LogInfo((object)$"Cloth fallback engaged: BuildAndRun={methodInfo2 != null} AutoBuild={methodInfo3 != null}; neither ever builds.");
			}

			private static bool SkipSetupCloth(GameObject __0)
			{
				int num = 0;
				if ((Object)(object)__0 != (Object)null)
				{
					Component[] componentsInChildren = __0.GetComponentsInChildren(_magicaCloth, true);
					foreach (Component val in componentsInChildren)
					{
						try
						{
							_disableAutoBuild.Invoke(val, null);
						}
						catch (Exception ex)
						{
							_log.LogWarning((object)("DisableAutoBuild failed on '" + ((Object)val).name + "': " + ex.GetType().Name + ": " + ex.Message));
						}
						num++;
					}
				}
				if (_skipped++ < 8)
				{
					_log.LogInfo((object)string.Format("SetupCloth skipped for '{0}' ({1} MagicaCloth component(s)).", ((Object)(object)__0 == (Object)null) ? "(null)" : ((Object)__0).name, num));
				}
				return false;
			}

			private static bool NeverBuild()
			{
				return false;
			}
		}

		private sealed class Clip
		{
			public string Name;

			public string Label;

			public string Environment;

			public float TimeOfDay;

			public float DurationSeconds;

			public string Ease;

			public float FlashAt;

			public float FlashBearing;

			public Vector3 From;

			public Vector3 To;

			public float YawFrom;

			public float PitchFrom;

			public float YawTo;

			public float PitchTo;
		}

		private sealed class Shot
		{
			public int ClusterId;

			public string Name;

			public Vector3 Camera;

			public float Yaw;

			public float Pitch;

			public string Environment;

			public float TimeOfDay;

			public Vector3 Aim;

			public string Label;

			public string Mode;

			public bool Fires;

			public float? Flash;

			public Vector3? Lens;

			public float? VerticalFov;

			public int Width;

			public int Height;

			public float Roll;
		}

		private sealed class HeldFire
		{
			public Fireplace Fire;

			public ZDO Zdo;

			public bool InfiniteFuel;

			public bool DisableCoverCheck;

			public float Fuel;

			public int State;

			public bool FuelWritten;

			public bool StateWritten;
		}

		private sealed class HeldLod
		{
			public LightLod Lod;

			public float LightDistance;

			public float ShadowDistance;
		}

		private sealed class Waypoint
		{
			public int Rank;

			public float X;

			public float Z;

			public float? Y;

			public int Pieces;

			public float HeightM;
		}

		private sealed class Variant
		{
			public readonly string Slug;

			public readonly string Environment;

			public readonly float TimeOfDay;

			public Variant(string slug, string environment, float timeOfDay)
			{
				Slug = slug;
				Environment = environment;
				TimeOfDay = timeOfDay;
			}
		}

		public const string CaptureVersion = "0.3.2";

		private bool _stillJobRunning;

		private bool _strictCaptureIdentity;

		private string _captureWorldFile;

		private bool _hidePlayerForScreenshots = true;

		private int _waypointIndex = -1;

		private Vector3? _holdAt;

		private bool _quitWhenDone;

		private bool _rigArmed;

		private float _priorBoom = -1f;

		private const float FireSweepRadius = 80f;

		private const float FlashHoldSeconds = 1.2f;

		private ConfigEntry<KeyboardShortcut> _keyNext;

		private ConfigEntry<KeyboardShortcut> _keyPrev;

		private ConfigEntry<KeyboardShortcut> _keyCapture;

		private ConfigEntry<KeyboardShortcut> _keyStatus;

		private ConfigEntry<float> _settleSeconds;

		private ConfigEntry<string> _defaultVariantSet;

		private ConfigEntry<bool> _skipIntroCinematic;

		private ConfigEntry<bool> _skipEquipmentCloth;

		private ConfigEntry<bool> _disableAllCloth;

		private bool _lastWorldSettled;

		private readonly List<HeldFire> _heldFires = new List<HeldFire>();

		private readonly List<HeldLod> _heldLods = new List<HeldLod>();

		private int _lightLimitWas = int.MinValue;

		private int _shadowLimitWas = int.MinValue;

		private int _firesFound;

		private int _firesFoundAtSweep;

		private int _firesWereBurning;

		private int _firesWereWet;

		private int _firesLit;

		private int _firesInView;

		private int _firesUnowned;

		private int _lodsWidened;

		private bool _hudWasAlreadyHidden;

		private bool _cursorWasVisible;

		private string ConfigDir => Paths.ConfigPath;

		private string WaypointsPath => Path.Combine(ConfigDir, "waypoints.json");

		private string ProgressPath => Path.Combine(ConfigDir, "camera-proof-progress.json");

		private string OrbitRequestPath => Path.Combine(ConfigDir, "orbit-request.json");

		private string ClipPlanPath => Path.Combine(ConfigDir, "clipplan.tsv");

		private string ClipReceiptsPath => Path.Combine(ConfigDir, "clip-receipts.jsonl");

		private string ShotPlanPath => Path.Combine(ConfigDir, "shotplan.tsv");

		private string ShotReceiptsPath => Path.Combine(ConfigDir, "shotplan-receipts.jsonl");

		private string PosesPath => Path.Combine(ConfigDir, "camera-proof-poses.jsonl");

		private static string WorldFile(World world)
		{
			return typeof(World).GetField("m_worldName", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(world) as string;
		}

		private static float Finite(string text)
		{
			float num = P(text);
			if (float.IsNaN(num) || float.IsInfinity(num))
			{
				throw new FormatException("Non-finite camera value");
			}
			return num;
		}

		private static Shot ExtendShot(Shot shot, string[] fields)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			if (fields.Length > 16 && !string.IsNullOrWhiteSpace(fields[16]))
			{
				if (fields.Length < 22)
				{
					throw new FormatException("Exact lens requires lens xyz, vertical FOV, width and height");
				}
				shot.Lens = new Vector3(Finite(fields[16]), Finite(fields[17]), Finite(fields[18]));
			}
			if (fields.Length > 19 && !string.IsNullOrWhiteSpace(fields[19]))
			{
				shot.VerticalFov = Finite(fields[19]);
				if (shot.VerticalFov < 1f || shot.VerticalFov > 179f)
				{
					throw new FormatException("Vertical FOV must be 1..179");
				}
			}
			if (fields.Length > 20 && !string.IsNullOrWhiteSpace(fields[20]))
			{
				shot.Width = int.Parse(fields[20], CultureInfo.InvariantCulture);
				shot.Height = int.Parse(fields[21], CultureInfo.InvariantCulture);
				if (shot.Width < 64 || shot.Height < 64 || shot.Width > 3840 || shot.Height > 3840)
				{
					throw new FormatException("Capture dimensions must be 64..3840");
				}
			}
			if (fields.Length > 22 && !string.IsNullOrWhiteSpace(fields[22]))
			{
				shot.Roll = Finite(fields[22]);
			}
			if (shot.Lens.HasValue && (!shot.VerticalFov.HasValue || shot.Width == 0))
			{
				throw new FormatException("Exact lens requires projection settings");
			}
			if (shot.Mode == "exact" && !shot.Lens.HasValue)
			{
				throw new FormatException("Exact shot has no lens");
			}
			return shot;
		}

		private IEnumerator RunExactShot(Shot shot, string run, string directory, string plan)
		{
			string prefix = "{\"run\":" + JsonString(run) + ",\"plan\":" + JsonString(plan) + ",\"cluster_id\":" + shot.ClusterId + ",\"shot\":" + JsonString(shot.Name) + ",\"plugin_version\":" + JsonString("0.3.2") + ",\"exact\":true,";
			object localPlayer = GetLocalPlayer();
			if (localPlayer == null)
			{
				AppendReceipt(prefix + "\"skipped\":\"player_unavailable\"}");
				yield break;
			}
			Vector3 feet = shot.Lens.Value - Vector3.up * 1.7f;
			SetInvulnerable(localPlayer, on: true);
			PlacePlayer(localPlayer, feet);
			_holdAt = feet;
			TryAim(shot.Yaw, shot.Pitch);
			SetForcedEnvironment(shot.Environment);
			SetDebugTime(shot.TimeOfDay);
			yield return WaitForStablePlayer(feet, 15f);
			yield return WaitForWorld(shot.Aim, 25f);
			HoldFiresLit(shot.Aim, 80f, Mathf.Max(100f, Vector3.Distance(feet, shot.Aim) + 80f), shot.Fires);
			yield return (object)new WaitForSeconds(Mathf.Max(1f, _settleSeconds.Value));
			if (!_lastWorldSettled || PiecesNear(shot.Aim, 60f) <= 0)
			{
				AppendReceipt(prefix + "\"skipped\":\"world_never_loaded\"}");
				if (shot.Fires)
				{
					ReleaseHeldLight();
				}
				yield break;
			}
			if (Physics.CheckSphere(shot.Lens.Value, 0.08f, LayerMask.GetMask(new string[4] { "terrain", "static_solid", "Default", "piece" }), (QueryTriggerInteraction)1))
			{
				AppendReceipt(prefix + "\"skipped\":\"lens_obstructed\",\"requested_lens\":" + JsonVector(shot.Lens.Value) + "}");
				if (shot.Fires)
				{
					ReleaseHeldLight();
				}
				yield break;
			}
			if (shot.Flash.HasValue)
			{
				DriveFlash(shot.Aim, shot.Lens.Value, shot.Flash.Value, 1.2f);
				yield return (object)new WaitForSeconds(0.2f);
			}
			yield return (object)new WaitForEndOfFrame();
			string text = $"{shot.ClusterId:0000}_{shot.Name}.png";
			try
			{
				string text2 = CaptureProjection(shot, Path.Combine(directory, text), exact: true);
				AppendReceipt(prefix + "\"file\":" + JsonString(text) + ",\"lens\":" + JsonVector(shot.Lens.Value) + ",\"aim\":" + JsonVector(shot.Aim) + ",\"yaw\":" + JsonNumber(shot.Yaw) + ",\"pitch\":" + JsonNumber(shot.Pitch) + ",\"fov\":" + JsonNumber(shot.VerticalFov.Value) + ",\"occluded\":" + JsonBool(IsOccluded(shot.Lens.Value, shot.Aim)) + "," + text2 + "}");
			}
			catch (Exception ex)
			{
				AppendReceipt(prefix + "\"skipped\":" + JsonString("exact_capture_failed: " + ex.Message) + "}");
				((BaseUnityPlugin)this).Logger.LogError((object)ex);
			}
			finally
			{
				if (shot.Fires)
				{
					ReleaseHeldLight();
				}
			}
		}

		private string CaptureProjection(Shot shot, string path, bool exact)
		{
			//IL_0316: Unknown result type (might be due to invalid IL or missing references)
			//IL_0317: 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_033d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0042: 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_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0143: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0161: Expected O, but got Unknown
			//IL_00e7: 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_019a: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_022a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0243: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c6: Expected O, but got Unknown
			//IL_02d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_03da: Unknown result type (might be due to invalid IL or missing references)
			//IL_03df: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f8: Unknown result type (might be due to invalid IL or missing references)
			Camera main = Camera.main;
			if ((Object)(object)main == (Object)null)
			{
				throw new InvalidOperationException("game_camera_unavailable");
			}
			Vector3 position = ((Component)main).transform.position;
			Quaternion rotation = ((Component)main).transform.rotation;
			float fieldOfView = main.fieldOfView;
			float aspect = main.aspect;
			Matrix4x4 projectionMatrix = main.projectionMatrix;
			Rect rect = main.rect;
			bool orthographic = main.orthographic;
			RenderTexture targetTexture = main.targetTexture;
			RenderTexture active = RenderTexture.active;
			PostProcessingBehaviour component = ((Component)main).GetComponent<PostProcessingBehaviour>();
			MotionBlurModel val = component?.profile?.motionBlur;
			bool flag = val != null && ((PostProcessingModel)val).enabled;
			int num = ((shot.Width > 0) ? shot.Width : Screen.width);
			int num2 = ((shot.Height > 0) ? shot.Height : Screen.height);
			RenderTexture val2 = null;
			Texture2D val3 = null;
			string text = null;
			byte[] array = null;
			try
			{
				if (exact)
				{
					((Component)main).transform.SetPositionAndRotation(shot.Lens.Value, Quaternion.Euler(shot.Pitch, shot.Yaw, shot.Roll));
				}
				main.orthographic = false;
				main.fieldOfView = shot.VerticalFov ?? fieldOfView;
				main.aspect = (float)num / (float)num2;
				main.rect = new Rect(0f, 0f, 1f, 1f);
				main.ResetProjectionMatrix();
				val2 = new RenderTexture(num, num2, 24, (RenderTextureFormat)0);
				val2.Create();
				main.targetTexture = val2;
				if (val != null)
				{
					((PostProcessingModel)val).enabled = false;
				}
				if ((Object)(object)component != (Object)null)
				{
					component.ResetTemporalEffects();
				}
				main.Render();
				Vector3 eulerAngles = ((Component)main).transform.eulerAngles;
				text = "\"observed\":{\"lens\":[" + JsonNumber(((Component)main).transform.position.x) + "," + JsonNumber(((Component)main).transform.position.y) + "," + JsonNumber(((Component)main).transform.position.z) + "],\"yaw\":" + JsonNumber(eulerAngles.y) + ",\"pitch\":" + JsonNumber(eulerAngles.x) + ",\"roll\":" + JsonNumber(eulerAngles.z) + ",\"verticalFov\":" + JsonNumber(main.fieldOfView) + ",\"width\":" + ((Texture)val2).width + ",\"height\":" + ((Texture)val2).height + "}";
				RenderTexture.active = val2;
				val3 = new Texture2D(num, num2, (TextureFormat)3, false);
				val3.ReadPixels(new Rect(0f, 0f, (float)num, (float)num2), 0, 0);
				val3.Apply();
				array = ImageConversion.EncodeToPNG(val3);
				File.WriteAllBytes(path, array);
			}
			finally
			{
				main.targetTexture = targetTexture;
				RenderTexture.active = active;
				((Component)main).transform.SetPositionAndRotation(position, rotation);
				main.orthographic = orthographic;
				main.fieldOfView = fieldOfView;
				main.aspect = aspect;
				main.rect = rect;
				main.projectionMatrix = projectionMatrix;
				if (val != null)
				{
					((PostProcessingModel)val).enabled = flag;
				}
				if ((Object)(object)component != (Object)null)
				{
					component.ResetTemporalEffects();
				}
				if ((Object)(object)val3 != (Object)null)
				{
					Object.Destroy((Object)(object)val3);
				}
				if ((Object)(object)val2 != (Object)null)
				{
					val2.Release();
					Object.Destroy((Object)(object)val2);
				}
			}
			bool value = (Object)(object)main.targetTexture == (Object)(object)targetTexture && ((Component)main).transform.position == position && Quaternion.Angle(((Component)main).transform.rotation, rotation) < 0.001f && main.fieldOfView == fieldOfView && main.aspect == aspect && main.rect == rect && main.orthographic == orthographic && main.projectionMatrix == projectionMatrix && (val == null || ((PostProcessingModel)val).enabled == flag);
			using SHA256 sHA = SHA256.Create();
			return text + ",\"temporal_history_reset\":" + JsonBool((Object)(object)component != (Object)null) + ",\"camera_restored\":" + JsonBool(value) + ",\"image_sha256\":" + JsonString(BitConverter.ToString(sHA.ComputeHash(array)).Replace("-", "").ToLowerInvariant());
		}

		private void Awake()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Unknown result type (might be due to invalid IL or missing references)
			_keyNext = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "next", new KeyboardShortcut((KeyCode)275, Array.Empty<KeyCode>()), "Teleport to the next waypoint in waypoints.json.");
			_keyPrev = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "previous", new KeyboardShortcut((KeyCode)276, Array.Empty<KeyCode>()), "Teleport to the previous waypoint.");
			_keyCapture = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "capture", new KeyboardShortcut((KeyCode)273, Array.Empty<KeyCode>()), "Capture the current framing in four moods. Avoid F7 — reserved for control bindings.");
			_keyStatus = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Hotkeys", "status", new KeyboardShortcut((KeyCode)274, Array.Empty<KeyCode>()), "Write the status proof file.");
			_settleSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Capture", "settleSeconds", 6f, "Seconds to let the sky and fog finish crossfading before each shot. Atmospherics lerp in, so too short gives a frame mid-transition.");
			_defaultVariantSet = ((BaseUnityPlugin)this).Config.Bind<string>("Capture", "defaultVariantSet", "basic", "Which set the capture hotkey shoots: basic (24 frames, all weather), weather (12), or quick (4).");
			_skipIntroCinematic = ((BaseUnityPlugin)this).Config.Bind<bool>("Capture", "skipIntroCinematic", true, "Valheim 1.0 plays $cinematics_intro on every cold start. Its only gate is Game.m_hasStartedOnce, a process-static bool that nothing persists, so it is pre-set here, before the start scene loads. No-op on builds without the field.");
			if (_skipIntroCinematic.Value)
			{
				SkipStartupCinematic();
			}
			_skipEquipmentCloth = ((BaseUnityPlugin)this).Config.Bind<bool>("Capture", "skipEquipmentCloth", true, "Valheim 1.0 builds MagicaCloth2 physics for every cape an armour item attaches (VisEquipment.SetupCloth -> MagicaCloth.BuildAndRun). On the Linux Mono player that build dies at character spawn in MagicaCloth2.ColliderManager.AddColliderInternal (mono_class_from_mono_type_internal assertion; 3 of 3 on build 25185596). The player is hidden in every capture, so a Harmony prefix disables each cloth's auto-build and skips SetupCloth. No-op on builds without SetupCloth or MagicaClothV2.");
			_disableAllCloth = ((BaseUnityPlugin)this).Config.Bind<bool>("Capture", "disableAllCloth", false, "Fallback if the crash moves: also prefix MagicaCloth.BuildAndRun and ClothProcess.AutoBuild so no cloth at all (sails, banners, prefab-authored) is ever built. Off unless needed.");
			try
			{
				ClothPatches.Apply(((BaseUnityPlugin)this).Logger, _skipEquipmentCloth.Value, _disableAllCloth.Value);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Cloth patches unavailable on this game build: " + ex.GetType().Name + ": " + ex.Message));
			}
			LoadProgress();
			try
			{
				RegisterConsoleCommands();
			}
			catch (Exception ex2)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Console commands unavailable on this game build: " + ex2.GetType().Name + ": " + ex2.Message));
			}
			if (TryReadOrbitRequest(out var world, out var character, out var quitWhenDone))
			{
				((MonoBehaviour)this).StartCoroutine(AutoBoot(world, character, quitWhenDone));
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)($"Camera Proof loaded. {_keyNext.Value} next, {_keyPrev.Value} previous, " + $"{_keyCapture.Value} capture, {_keyStatus.Value} status. " + $"Resuming at waypoint {_waypointIndex + 1}."));
		}

		private void LoadProgress()
		{
			try
			{
				if (File.Exists(ProgressPath))
				{
					Match match = Regex.Match(File.ReadAllText(ProgressPath), "\"index\"\\s*:\\s*(-?\\d+)");
					if (match.Success)
					{
						_waypointIndex = int.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
					}
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not read waypoint progress: " + ex.Message));
			}
		}

		private void SaveProgress()
		{
			try
			{
				WriteJson("camera-proof-progress.json", "{\n  \"index\": " + _waypointIndex + ",\n  \"shown\": " + (_waypointIndex + 1) + "\n}\n");
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not save waypoint progress: " + ex.Message));
			}
		}

		private static bool TypingSomewhere()
		{
			try
			{
				if (Console.IsVisible())
				{
					return true;
				}
				if (TextInput.IsVisible())
				{
					return true;
				}
				if ((Object)(object)Chat.instance != (Object)null && Chat.instance.HasFocus())
				{
					return true;
				}
			}
			catch (Exception)
			{
			}
			return false;
		}

		private void Update()
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: 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_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			if (_holdAt.HasValue)
			{
				object localPlayer = GetLocalPlayer();
				if (localPlayer != null)
				{
					PlacePlayer(localPlayer, _holdAt.Value);
				}
			}
			if (!TypingSomewhere())
			{
				KeyboardShortcut value = _keyStatus.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					WriteStatusProof("hotkey-status");
				}
				value = _keyNext.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					StepWaypoint(1);
				}
				value = _keyPrev.Value;
				if (((KeyboardShortcut)(ref value)).IsDown())
				{
					StepWaypoint(-1);
				}
				value = _keyCapture.Value;
				if (((KeyboardShortcut)(ref value)).IsDown() && !_stillJobRunning)
				{
					((MonoBehaviour)this).StartCoroutine(CaptureHere(_defaultVariantSet.Value));
				}
			}
		}

		private void StepWaypoint(int delta)
		{
			List<Waypoint> list = LoadWaypoints();
			if (list.Count == 0)
			{
				Announce("No waypoints in BepInEx/config/waypoints.json");
				return;
			}
			_waypointIndex += delta;
			if (_waypointIndex >= list.Count)
			{
				_waypointIndex = 0;
			}
			if (_waypointIndex < 0)
			{
				_waypointIndex = list.Count - 1;
			}
			Waypoint waypoint = list[_waypointIndex];
			string arg = ((waypoint.Pieces > 0) ? $"{waypoint.Pieces:n0} pieces, {waypoint.HeightM:0}m tall" : "no detail");
			Announce($"[{_waypointIndex + 1}/{list.Count}] {arg}");
			SaveProgress();
			((MonoBehaviour)this).StartCoroutine(TeleportProof(waypoint));
		}

		private void Announce(string message)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)message);
			try
			{
				Console instance = Console.instance;
				if (instance != null)
				{
					instance.Print(message);
				}
			}
			catch (Exception)
			{
			}
		}

		private IEnumerator TeleportProof(Waypoint explicitWaypoint)
		{
			Waypoint waypoint = explicitWaypoint ?? LoadFirstWaypoint();
			if (waypoint == null)
			{
				WriteJson("camera-proof-move.json", "{\n  \"ok\": false,\n  \"error\": \"No waypoint found in BepInEx/config/waypoints.json\"\n}\n");
				yield break;
			}
			object player = GetLocalPlayer();
			if (player == null)
			{
				WriteJson("camera-proof-move.json", "{\n  \"ok\": false,\n  \"error\": \"Player.m_localPlayer not present\"\n}\n");
				yield break;
			}
			Vector3 target = new Vector3(waypoint.X, waypoint.Y ?? 80f, waypoint.Z);
			TryResolveGroundHeight(waypoint.X, waypoint.Z, out var ground);
			if (!waypoint.Y.HasValue && ground.HasValue)
			{
				target.y = ground.Value + 55f;
			}
			Vector3 before = ((Component)player).transform.position;
			bool moved = TryTeleport(player, target);
			yield return (object)new WaitForSeconds(4f);
			Vector3 position = ((Component)player).transform.position;
			if (Vector3.Distance(position, target) > 10f)
			{
				((Component)player).transform.position = target;
				Rigidbody component = ((Component)player).GetComponent<Rigidbody>();
				if ((Object)(object)component != (Object)null)
				{
					component.position = target;
					component.velocity = Vector3.zero;
				}
				yield return (object)new WaitForSeconds(1f);
				position = ((Component)player).transform.position;
				moved = Vector3.Distance(position, target) <= 10f;
			}
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("{");
			stringBuilder.AppendLine("  \"ok\": " + JsonBool(moved) + ",");
			stringBuilder.AppendLine($"  \"rank\": {waypoint.Rank},");
			stringBuilder.AppendLine("  \"player_present\": true,");
			stringBuilder.AppendLine("  \"ground_height\": " + JsonNumberOrNull(ground) + ",");
			stringBuilder.AppendLine("  \"target\": " + JsonVector(target) + ",");
			stringBuilder.AppendLine("  \"before\": " + JsonVector(before) + ",");
			stringBuilder.AppendLine("  \"after\": " + JsonVector(position));
			stringBuilder.AppendLine("}");
			WriteJson("camera-proof-move.json", stringBuilder.ToString());
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Camera Proof movement file written.");
		}

		private void RegisterConsoleCommands()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_0020: 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_004b: Expected O, but got Unknown
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected O, but got Unknown
			//IL_0092: 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_00bd: Expected O, but got Unknown
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e3: Expected O, but got Unknown
			//IL_00de: 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_0109: Expected O, but got Unknown
			//IL_0104: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_012f: Expected O, but got Unknown
			//IL_012a: 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_0155: Expected O, but got Unknown
			//IL_0150: 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)
			//IL_017b: Expected O, but got Unknown
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Expected O, but got Unknown
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c7: Expected O, but got Unknown
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ed: Expected O, but got Unknown
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Expected O, but got Unknown
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_0239: Expected O, but got Unknown
			//IL_0234: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: Unknown result type (might be due to invalid IL or missing references)
			//IL_025f: Expected O, but got Unknown
			//IL_025a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0271: Unknown result type (might be due to invalid IL or missing references)
			//IL_0285: Expected O, but got Unknown
			//IL_0280: Unknown result type (might be due to invalid IL or missing references)
			//IL_0297: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ab: Expected O, but got Unknown
			//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d1: Expected O, but got Unknown
			//IL_02cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f7: Expected O, but got Unknown
			//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0309: Unknown result type (might be due to invalid IL or missing references)
			//IL_031d: Expected O, but got Unknown
			//IL_0318: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				new ConsoleCommand("camproof_status", "write camera status proof", new ConsoleEvent(ConsoleStatus), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				new ConsoleCommand("camproof_move", "teleport to first waypoint, or to x y z when provided", new ConsoleEvent(ConsoleMove), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				new ConsoleCommand("camproof_tp", "teleport to x y z and write movement proof", new ConsoleEvent(ConsoleTeleport), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				new ConsoleCommand("camproof_stills", "capture stills for waypoints: camproof_stills [limit] [settleSeconds]", new ConsoleEvent(ConsoleStills), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				new ConsoleCommand("camproof_env", "force environment: camproof_env Clear|Misty|Rain|ThunderStorm|noforce", new ConsoleEvent(ConsoleEnv), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				new ConsoleCommand("camproof_time", "force time of day 0..1, or off: camproof_time 0.5", new ConsoleEvent(ConsoleTime), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				new ConsoleCommand("camproof_lights", "dump every light prefab and all 39 environments to camera-proof-lights.json", new ConsoleEvent(ConsoleLights), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				new ConsoleCommand("camproof_envs", "write available environment names to camera-proof-envs.json", new ConsoleEvent(ConsoleEnvList), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				new ConsoleCommand("camproof_sky", "dump sun/moon direction per time of day: camproof_sky [0.8,0.9,...]", new ConsoleEvent(ConsoleSky), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				new ConsoleCommand("camproof_hideplayer", "hide/show local player: camproof_hideplayer on|off", new ConsoleEvent(ConsoleHidePlayer), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				new ConsoleCommand("camproof_variantstills", "capture env/time variants: camproof_variantstills [limit] [settle] [variantSet]", new ConsoleEvent(ConsoleVariantStills), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				new ConsoleCommand("camproof_capture", "capture variants at current position: camproof_capture [variantSet]", new ConsoleEvent(ConsoleCaptureHere), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				new ConsoleCommand("capture", "capture variants at current position: capture [variantSet]", new ConsoleEvent(ConsoleCaptureHere), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				new ConsoleCommand("next", "teleport to the next waypoint in the shot list", (ConsoleEvent)delegate
				{
					StepWaypoint(1);
				}, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				new ConsoleCommand("prev", "teleport to the previous waypoint in the shot list", (ConsoleEvent)delegate
				{
					StepWaypoint(-1);
				}, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				new ConsoleCommand("shot", "jump to shot list entry N: shot 12", new ConsoleEvent(ConsoleShot), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				new ConsoleCommand("aim", "point the camera: aim <yaw> <pitch>  (pitch + = down)", new ConsoleEvent(ConsoleAim), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				new ConsoleCommand("aimtest", "prove aiming works and holds: aimtest", new ConsoleEvent(ConsoleAimTest), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				new ConsoleCommand("runplan", "shoot every line of shotplan.tsv: runplan [startIndex]", new ConsoleEvent(ConsoleRunPlan), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				new ConsoleCommand("pose", "record the current camera as a shot row: pose [name] [aimDistance]", new ConsoleEvent(ConsolePose), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
				new ConsoleCommand("runclips", "fly every clip in clipplan.tsv now (demo a path before recording)", new ConsoleEvent(ConsoleRunClips), false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not register console commands: " + ex.Message));
			}
		}

		private void ConsoleStatus(ConsoleEventArgs args)
		{
			WriteStatusProof("console-command");
		}

		private void ConsoleMove(ConsoleEventArgs args)
		{
			if (TryWaypointFromArgs(args, out var waypoint))
			{
				((MonoBehaviour)this).StartCoroutine(TeleportProof(waypoint));
			}
			else
			{
				((MonoBehaviour)this).StartCoroutine(TeleportProof(null));
			}
		}

		private void ConsoleTeleport(ConsoleEventArgs args)
		{
			if (!TryWaypointFromArgs(args, out var waypoint))
			{
				WriteJson("camera-proof-move.json", "{\n  \"ok\": false,\n  \"error\": \"Usage: camproof_tp x y z\"\n}\n");
			}
			else
			{
				((MonoBehaviour)this).StartCoroutine(TeleportProof(waypoint));
			}
		}

		private void ConsoleStills(ConsoleEventArgs args)
		{
			if (_stillJobRunning)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Still capture job is already running.");
				return;
			}
			int limit = args.TryParameterInt(1, 0);
			float num = args.TryParameterFloat(2, 8f);
			if (num < 1f)
			{
				num = 1f;
			}
			((MonoBehaviour)this).StartCoroutine(CaptureStills(limit, num));
		}

		private void ConsoleVariantStills(ConsoleEventArgs args)
		{
			if (_stillJobRunning)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Still capture job is already running.");
				return;
			}
			int limit = args.TryParameterInt(1, 0);
			float num = args.TryParameterFloat(2, 18f);
			string variantSet = ((args.Length >= 4) ? args[3] : "basic");
			if (num < 1f)
			{
				num = 1f;
			}
			((MonoBehaviour)this).StartCoroutine(CaptureVariantStills(limit, num, variantSet));
		}

		private void ConsoleShot(ConsoleEventArgs args)
		{
			int num = default(int);
			if (!args.TryParameterInt(1, ref num))
			{
				Announce("Usage: shot <number from the shot list>");
				return;
			}
			_waypointIndex = num - 2;
			StepWaypoint(1);
		}

		private bool TryReadOrbitRequest(out string world, out string character, out bool quitWhenDone)
		{
			world = null;
			character = null;
			quitWhenDone = true;
			try
			{
				if (!File.Exists(OrbitRequestPath))
				{
					return false;
				}
				string text = File.ReadAllText(OrbitRequestPath);
				_strictCaptureIdentity = Regex.IsMatch(text, "\"strict_identity\"\\s*:\\s*true");
				_captureWorldFile = Match(text, "world_file");
				world = Match(text, "world");
				character = Match(text, "character");
				Match match = Regex.Match(text, "\"quit_when_done\"\\s*:\\s*(true|false)");
				if (match.Success)
				{
					quitWhenDone = match.Groups[1].Value == "true";
				}
				return !string.IsNullOrEmpty(world);
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)$"Could not read orbit request: {arg}");
				return false;
			}
		}

		private bool ReadLightDumpRequested()
		{
			try
			{
				if (!File.Exists(OrbitRequestPath))
				{
					return false;
				}
				Match match = Regex.Match(File.ReadAllText(OrbitRequestPath), "\"light_dump\"\\s*:\\s*(true|false)");
				return match.Success && match.Groups[1].Value == "true";
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Could not read light_dump flag: " + ex.Message));
				return false;
			}
		}

		private bool ReadFeedRequest(out string feedDir, out float idleSeconds)
		{
			feedDir = null;
			idleSeconds = 600f;
			try
			{
				if (!File.Exists(OrbitRequestPath))
				{
					return false;
				}
				string text = File.ReadAllText(OrbitRequestPath);
				feedDir = Match(text, "feed_dir");
				Match match = Regex.Match(text, "\"feed_idle_seconds\"\\s*:\\s*([0-9.]+)");
				if (match.Success)
				{
					idleSeconds = float.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture);
				}
				return !string.IsNullOrEmpty(feedDir);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Could not read feed request: " + ex.Message));
				return false;
			}
		}

		private List<float> ReadSkyTimes()
		{
			List<float> list = new List<float>();
			try
			{
				if (!File.Exists(OrbitRequestPath))
				{
					return list;
				}
				Match match = Regex.Match(File.ReadAllText(OrbitRequestPath), "\"sky_times\"\\s*:\\s*\\[([^\\]]*)\\]");
				if (!match.Success)
				{
					return list;
				}
				string[] array = match.Groups[1].Value.Split(new char[1] { ',' });
				for (int i = 0; i < array.Length; i++)
				{
					if (float.TryParse(array[i].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
					{
						list.Add(Mathf.Clamp01(result));
					}
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Could not read sky_times: " + ex.Message));
			}
			return list;
		}

		private static string Match(string text, string key)
		{
			Match match = Regex.Match(text, "\"" + key + "\"\\s*:\\s*\"([^\"]*)\"");
			if (!match.Success)
			{
				return null;
			}
			return match.Groups[1].Value;
		}

		private void SkipStartupCinematic()
		{
			FieldInfo field = typeof(Game).GetField("m_hasStartedOnce", BindingFlags.Static | BindingFlags.Public);
			if (field == null)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"No Game.m_hasStartedOnce on this build; no startup cinematic to skip.");
				return;
			}
			field.SetValue(null, true);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Startup cinematic disabled: Game.m_hasStartedOnce pre-set before FejdStartup.Start.");
		}

		private void SkipQueuedStoryIntro()
		{
			Game instance = Game.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			MethodInfo method = typeof(Game).GetMethod("InIntro", BindingFlags.Instance | BindingFlags.Public);
			MethodInfo method2 = typeof(Game).GetMethod("SkipIntro", BindingFlags.Instance | BindingFlags.Public);
			if (!(method == null) && !(method2 == null))
			{
				object[] parameters = ((method.GetParameters().Length != 1) ? null : new object[1] { true });
				if ((bool)method.Invoke(instance, parameters))
				{
					method2.Invoke(instance, null);
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Story intro skipped: the character profile had never spawned (m_firstSpawn).");
				}
			}
		}

		private static string ProfileSource(object profile)
		{
			if (profile == null)
			{
				return "?";
			}
			try
			{
				return profile.GetType().GetField("m_fileSource", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(profile)?.ToString() ?? "?";
			}
			catch
			{
				return "?";
			}
		}

		private IEnumerator AutoBoot(string worldName, string characterName, bool quitWhenDone)
		{
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Orbit auto-boot: world='" + worldName + "' character='" + (characterName ?? "(first)") + "'"));
			BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
			float deadline = Time.realtimeSinceStartup + 180f;
			while ((Object)(object)FejdStartup.instance == (Object)null && Time.realtimeSinceStartup < deadline)
			{
				yield return (object)new WaitForSeconds(0.5f);
			}
			FejdStartup fejd = FejdStartup.instance;
			if ((Object)(object)fejd == (Object)null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Orbit auto-boot: no FejdStartup.");
				yield break;
			}
			while (!PlayFabManager.IsLoggedIn && Time.realtimeSinceStartup < deadline)
			{
				yield return (object)new WaitForSeconds(0.5f);
			}
			Type typeFromHandle = typeof(FejdStartup);
			MethodInfo? method = typeFromHandle.GetMethod("ShowCharacterSelection", flags);
			MethodInfo updateList = typeFromHandle.GetMethod("UpdateCharacterList", flags);
			MethodInfo setSelected = typeFromHandle.GetMethod("SetSelectedProfile", flags);
			MethodInfo onStart = typeFromHandle.GetMethod("OnCharacterStart", flags);
			MethodInfo loadScene = typeFromHandle.GetMethod("LoadMainScene", flags);
			FieldInfo profilesField = typeFromHandle.GetField("m_profiles", flags);
			FieldInfo profileIndex = typeFromHandle.GetField("m_profileIndex", flags);
			method?.Invoke(fejd, null);
			yield return (object)new WaitForSeconds(1f);
			IList list = null;
			while (Time.realtimeSinceStartup < deadline)
			{
				updateList?.Invoke(fejd, null);
				yield return (object)new WaitForSeconds(0.5f);
				list = profilesField?.GetValue(fejd) as IList;
				if (list != null && list.Count > 0)
				{
					break;
				}
			}
			if (list == null || list.Count == 0)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Orbit auto-boot: no character profiles available.");
				yield break;
			}
			int num = -1;
			if (!string.IsNullOrEmpty(characterName))
			{
				for (int i = 0; i < list.Count; i++)
				{
					object? obj = list[i];
					PlayerProfile val = (PlayerProfile)((obj is PlayerProfile) ? obj : null);
					string text = ((val != null) ? val.GetName() : null);
					string text2 = ((val != null) ? val.GetFilename() : null);
					bool flag = !string.IsNullOrEmpty(text) && text.Equals(characterName, StringComparison.OrdinalIgnoreCase);
					bool flag2 = !string.IsNullOrEmpty(text2) && text2.Equals(characterName, StringComparison.OrdinalIgnoreCase);
					if (!(_strictCaptureIdentity ? (!flag2) : (!flag && !flag2)) && (num < 0 || (ProfileSource(val) == "Local" && ProfileSource(list[num]) != "Local")))
					{
						num = i;
					}
				}
				if (_strictCaptureIdentity && (num < 0 || ProfileSource(list[num]) != "Local"))
				{
					((BaseUnityPlugin)this).Logger.LogError((object)"Exact capture refused: disposable local character is unavailable.");
					if (quitWhenDone)
					{
						Application.Quit();
					}
					yield break;
				}
				if (num < 0)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Orbit auto-boot: no character named '" + characterName + "'; taking the first profile."));
				}
			}
			if (num < 0)
			{
				num = 0;
			}
			object? obj2 = list[num];
			PlayerProfile chosen = (PlayerProfile)((obj2 is PlayerProfile) ? obj2 : null);
			if (_strictCaptureIdentity && (chosen == null || ProfileSource(chosen) != "Local" || !string.Equals(chosen.GetFilename(), characterName, StringComparison.Ordinal)))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Exact capture refused: character identity mismatch.");
				if (quitWhenDone)
				{
					Application.Quit();
				}
				yield break;
			}
			ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
			object[] array = new object[4];
			array[0] = ((chosen != null) ? chosen.GetName() : null);
			array[1] = num;
			array[2] = ProfileSource(chosen);
			array[3] = ((chosen != null) ? chosen.GetFilename() : null);
			logger.LogInfo((object)string.Format("Orbit auto-boot: character '{0}' (index {1}, source {2}, file {3}).", array));
			if (ProfileSource(chosen) != "Local")
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Orbit auto-boot: the chosen profile is not the local file; its logout point comes from a previous session.");
			}
			profileIndex?.SetValue(fejd, num);
			if (chosen != null)
			{
				setSelected?.Invoke(fejd, new object[1] { chosen.GetFilename() });
			}
			updateList?.Invoke(fejd, null);
			yield return (object)new WaitForSeconds(0.5f);
			onStart?.Invoke(fejd, null);
			yield return (object)new WaitForSeconds(0.5f);
			World val2 = null;
			SaveSystem.ForceRefreshCache();
			foreach (World world in SaveSystem.GetWorldList())
			{
				if (world != null && string.Equals(world.m_name, worldName, StringComparison.OrdinalIgnoreCase) && (!_strictCaptureIdentity || (!(ProfileSource(world) != "Local") && string.Equals(WorldFile(world), _captureWorldFile, StringComparison.Ordinal))))
				{
					val2 = world;
					break;
				}
			}
			if (val2 == null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Orbit auto-boot: world '" + worldName + "' not found; refusing to create one."));
				if (_strictCaptureIdentity && quitWhenDone)
				{
					Application.Quit();
				}
				yield break;
			}
			if (_strictCaptureIdentity)
			{
				File.WriteAllText(Path.Combine(ConfigDir, "capture-identity.json"), "{\"schema\":\"selfiestick-local-identity/v1\",\"characterFile\":" + JsonString(chosen.GetFilename()) + ",\"characterSource\":" + JsonString(ProfileSource(chosen)) + ",\"worldFile\":" + JsonString(WorldFile(val2)) + ",\"worldSource\":" + JsonString(ProfileSource(val2)) + "}");
			}
			ZNet.SetServer(true, false, false, string.Empty, string.Empty, val2);
			loadScene?.Invoke(fejd, null);
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Orbit auto-boot: loading '" + val2.m_name + "' single-player."));
			float worldDeadline = Time.realtimeSinceStartup + 600f;
			while (GetLocalPlayer() == null && Time.realtimeSinceStartup < worldDeadline)
			{
				yield return (object)new WaitForSeconds(1f);
			}
			if (GetLocalPlayer() == null)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Orbit auto-boot: player never spawned.");
				yield break;
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Orbit auto-boot: player is in-world; settling before capture.");
			SkipQueuedStoryIntro();
			yield return (object)new WaitForSeconds(12f);
			_quitWhenDone = quitWhenDone;
			List<float> list2 = ReadSkyTimes();
			if (ReadLightDumpRequested())
			{
				yield return LightDumpRoutine();
			}
			else if (list2.Count > 0)
			{
				yield return SkyDumpRoutine(list2);
			}
			else if (ReadClipsRequested())
			{
				yield return RunClipPlan();
			}
			else
			{
				string feedDir;
				float feedIdle;
				bool feed = ReadFeedRequest(out feedDir, out feedIdle);
				yield return RunShotPlan(0, !feed);
				if (feed)
				{
					yield return FeedLoop(feedDir, feedIdle);
				}
			}
			if (quitWhenDone)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Orbit auto-boot: plan complete, quitting.");
				yield return (object)new WaitForSeconds(2f);
				Application.Quit();
			}
		}

		private bool ReadClipsRequested()
		{
			try
			{
				if (!File.Exists(OrbitRequestPath))
				{
					return false;
				}
				Match match = Regex.Match(File.ReadAllText(OrbitRequestPath), "\"clips\"\\s*:\\s*(true|false)");
				return match.Success && match.Groups[1].Value == "true";
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Could not read clips flag: " + ex.Message));
				return false;
			}
		}

		private List<Clip> LoadClipPlan()
		{
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			List<Clip> list = new List<Clip>();
			if (!File.Exists(ClipPlanPath))
			{
				return list;
			}
			string[] array = File.ReadAllLines(ClipPlanPath);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length == 0 || text.StartsWith("#"))
				{
					continue;
				}
				string[] array2 = text.Split(new char[1] { '\t' });
				if (array2.Length >= 18)
				{
					try
					{
						list.Add(new Clip
						{
							Name = array2[0],
							Label = array2[1],
							Environment = array2[2],
							TimeOfDay = P(array2[3]),
							DurationSeconds = Mathf.Clamp(P(array2[4]), 0.5f, 120f),
							Ease = (string.IsNullOrEmpty(array2[5]) ? "smooth" : array2[5]),
							FlashAt = (string.IsNullOrEmpty(array2[6]) ? (-1f) : P(array2[6])),
							FlashBearing = (string.IsNullOrEmpty(array2[7]) ? 0f : P(array2[7])),
							From = new Vector3(P(array2[8]), P(array2[9]), P(array2[10])),
							YawFrom = P(array2[11]),
							PitchFrom = P(array2[12]),
							To = new Vector3(P(array2[13]), P(array2[14]), P(array2[15])),
							YawTo = P(array2[16]),
							PitchTo = P(array2[17])
						});
					}
					catch (Exception ex)
					{
						((BaseUnityPlugin)this).Logger.LogWarning((object)("Bad clip row skipped: " + ex.Message));
					}
				}
			}
			return list;
		}

		private static Vector3 ForwardPoint(Vector3 lens, float yawDeg, float pitchDeg, float dist)
		{
			//IL_0017: 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_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)
			float num = yawDeg * ((float)Math.PI / 180f);
			float num2 = pitchDeg * ((float)Math.PI / 180f);
			float num3 = Mathf.Cos(num2);
			return lens + new Vector3(Mathf.Sin(num) * num3, 0f - Mathf.Sin(num2), Mathf.Cos(num) * num3) * dist;
		}

		private IEnumerator RunClipPlan()
		{
			List<Clip> clips = LoadClipPlan();
			if (clips.Count == 0)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("clips requested but no rows in " + ClipPlanPath));
				yield break;
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"clip plan: {clips.Count} clip(s)");
			float priorBoom = SetCameraBoom(0f);
			SetCaptureChromeHidden(hide: true);
			if (_hidePlayerForScreenshots)
			{
				SetLocalPlayerVisible(visible: false);
			}
			for (int i = 0; i < clips.Count; i++)
			{
				Clip c = clips[i];
				object player = GetLocalPlayer();
				if (player == null)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)$"[clip {i + 1}/{clips.Count}] no local player; waiting");
					float waited = 0f;
					while (player == null && waited < 90f)
					{
						yield return (object)new WaitForSeconds(2f);
						waited += 2f;
						player = GetLocalPlayer();
					}
					if (player == null)
					{
						((BaseUnityPlugin)this).Logger.LogError((object)"clip plan stopped: player never returned");
						break;
					}
					if (_hidePlayerForScreenshots)
					{
						SetLocalPlayerVisible(visible: false);
					}
				}
				SetInvulnerable(player, on: true);
				PlacePlayer(player, c.From);
				_holdAt = c.From;
				TryAim(c.YawFrom, c.PitchFrom);
				SetForcedEnvironment(c.Environment);
				SetDebugTime(c.TimeOfDay);
				yield return WaitForStablePlayer(c.From, 15f);
				yield return WaitForWorld(ForwardPoint(c.From, c.YawFrom, c.PitchFrom, 25f), 25f);
				yield return (object)new WaitForSeconds(Mathf.Max(1f, _settleSeconds.Value));
				DateTime startedUtc = DateTime.UtcNow;
				((BaseUnityPlugin)this).Logger.LogInfo((object)($"[clip {i + 1}/{clips.Count}] {c.Name} ROLLING " + $"{startedUtc:yyyy-MM-ddTHH:mm:ss.fffZ} dur={c.DurationSeconds:0.00}s"));
				float elapsed = 0f;
				bool flashed = c.FlashAt < 0f;
				int frames = 0;
				for (; elapsed < c.DurationSeconds; elapsed += Time.deltaTime)
				{
					float num = Mathf.Clamp01(elapsed / c.DurationSeconds);
					float num2 = ((c.Ease == "linear") ? num : (num * num * (3f - 2f * num)));
					Vector3 val = Vector3.Lerp(c.From, c.To, num2);
					float yawDeg = Mathf.LerpAngle(c.YawFrom, c.YawTo, num2);
					float pitchDeg = Mathf.Lerp(c.PitchFrom, c.PitchTo, num2);
					_holdAt = val;
					PlacePlayer(player, val);
					TryAim(yawDeg, pitchDeg);
					if (!flashed && elapsed >= c.FlashAt)
					{
						string arg = DriveFlash(ForwardPoint(val, yawDeg, pitchDeg, 40f), val, c.FlashBearing, 0.35f);
						((BaseUnityPlugin)this).Logger.LogInfo((object)$"clip {c.Name}: flash at {elapsed:F2}s -> {arg}");
						flashed = true;
					}
					frames++;
					yield return null;
				}
				DateTime utcNow = DateTime.UtcNow;
				float num3 = (float)(utcNow - startedUtc).TotalSeconds;
				((BaseUnityPlugin)this).Logger.LogInfo((object)($"[clip {i + 1}/{clips.Count}] {c.Name} CUT " + $"{utcNow:yyyy-MM-ddTHH:mm:ss.fffZ} " + $"{frames} frames in {num3:0.00}s ({(float)frames / Mathf.Max(num3, 0.01f):0.0} fps)"));
				AppendClipReceipt(c, startedUtc, utcNow, frames, num3);
			}
			SetForcedEnvironment("");
			SetDebugTime(null);
			SetCaptureChromeHidden(hide: false);
			SetLocalPlayerVisible(!_hidePlayerForScreenshots);
			if (priorBoom >= 0f)
			{
				SetCameraBoom(priorBoom);
			}
			ReleaseRigOrHoldForExit();
			Announce($"clip plan finished: {clips.Count} clip(s)");
		}

		private void ReleaseRigOrHoldForExit()
		{
			if (_quitWhenDone)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"holding the rig pinned and immortal through shutdown (quit_when_done): a released rig falls and the death is saved into the next run");
				return;
			}
			_holdAt = null;
			SetInvulnerable(GetLocalPlayer(), on: false);
		}

		private void AppendClipReceipt(Clip c, DateTime started, DateTime ended, int frames, float wall)
		{
			try
			{
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.Append("{");
				stringBuilder.Append("\"clip\":\"" + c.Name + "\",");
				stringBuilder.Append("\"label\":\"" + c.Label.Replace("\"", "'") + "\",");
				stringBuilder.Append("\"environment\":\"" + c.Environment + "\",");
				stringBuilder.Append("\"time_of_day\":" + c.TimeOfDay.ToString("0.###", CultureInfo.InvariantCulture) + ",");
				stringBuilder.Append($"\"started_utc\":\"{started:yyyy-MM-ddTHH:mm:ss.fffZ}\",");
				stringBuilder.Append($"\"ended_utc\":\"{ended:yyyy-MM-ddTHH:mm:ss.fffZ}\",");
				stringBuilder.Append("\"planned_s\":" + c.DurationSeconds.ToString("0.###", CultureInfo.InvariantCulture) + ",");
				stringBuilder.Append("\"wall_s\":" + wall.ToString("0.###", CultureInfo.InvariantCulture) + ",");
				stringBuilder.Append($"\"frames\":{frames},");
				stringBuilder.Append("\"fps\":" + ((float)frames / Mathf.Max(wall, 0.01f)).ToString("0.##", CultureInfo.InvariantCulture) + ",");
				stringBuilder.Append("\"flash_at\":" + c.FlashAt.ToString("0.###", CultureInfo.InvariantCulture) + ",");
				stringBuilder.Append("\"flash_bearing_deg\":" + c.FlashBearing.ToString("0.#", CultureInfo.InvariantCulture) + ",");
				stringBuilder.Append("\"from\":[" + c.From.x.ToString("0.#", CultureInfo.InvariantCulture) + "," + c.From.y.ToString("0.##", CultureInfo.InvariantCulture) + "," + c.From.z.ToString("0.#", CultureInfo.InvariantCulture) + "],");
				stringBuilder.Append("\"to\":[" + c.To.x.ToString("0.#", CultureInfo.InvariantCulture) + "," + c.To.y.ToString("0.##", CultureInfo.InvariantCulture) + "," + c.To.z.ToString("0.#", CultureInfo.InvariantCulture) + "],");
				stringBuilder.Append("\"yaw\":[" + c.YawFrom.ToString("0.##", CultureInfo.InvariantCulture) + "," + c.YawTo.ToString("0.##", CultureInfo.InvariantCulture) + "],");
				stringBuilder.Append("\"pitch\":[" + c.PitchFrom.ToString("0.##", CultureInfo.InvariantCulture) + "," + c.PitchTo.ToString("0.##", CultureInfo.InvariantCulture) + "]");
				stringBuilder.Append("}");
				File.AppendAllText(ClipReceiptsPath, stringBuilder.ToString() + Environment.NewLine);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not append clip receipt: " + ex.Message));
			}
		}

		private static bool TsvFlag(string value)
		{
			if (string.IsNullOrEmpty(value))
			{
				return false;
			}
			string text = value.Trim();
			if (!(text == "1") && !text.Equals("true", StringComparison.OrdinalIgnoreCase))
			{
				return text.Equals("yes", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		private static float? TsvFloat(string value)
		{
			if (string.IsNullOrEmpty(value))
			{
				return null;
			}
			string text = value.Trim();
			if (text.Length == 0 || text == "-" || text.Equals("off", StringComparison.OrdinalIgnoreCase))
			{
				return null;
			}
			if (!float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return null;
			}
			return result;
		}

		private static bool IsInterior(Shot s)
		{
			return string.Equals(s.Mode, "interior", StringComparison.OrdinalIgnoreCase);
		}

		private static FieldInfo FindField(Type type, string name)
		{
			Type type2 = type;
			while (type2 != null)
			{
				FieldInfo field = type2.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					return field;
				}
				type2 = type2.BaseType;
			}
			return null;
		}

		private void SetInvulnerable(object player, bool on)
		{
			if (player == null)
			{
				return;
			}
			try
			{
				Type type = player.GetType();
				string text = null;
				MethodInfo method = type.GetMethod("SetGodMode", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (method != null)
				{
					method.Invoke(player, new object[1] { on });
					text = "SetGodMode()";
				}
				else
				{
					FieldInfo fieldInfo = FindField(type, "m_godMode");
					if (fieldInfo != null)
					{
						fieldInfo.SetValue(player, on);
						text = "m_godMode field";
					}
				}
				FieldInfo fieldInfo2 = FindField(type, "m_ghostMode");
				if (fieldInfo2 != null)
				{
					fieldInfo2.SetValue(player, on);
				}
				MethodInfo method2 = type.GetMethod("InDebugFlyMode", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				MethodInfo method3 = type.GetMethod("ToggleDebugFly", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				object arg = "<no fly API>";
				if (method2 != null && method3 != null)
				{
					if ((bool)method2.Invoke(player, null) != on)
					{
						method3.Invoke(player, null);
					}
					arg = method2.Invoke(player, null);
				}
				FieldInfo fieldInfo3 = FindField(type, "m_godMode");
				object arg2 = ((fieldInfo3 != null) ? fieldInfo3.GetValue(player) : "<no m_godMode field>");
				object arg3 = ((fieldInfo2 != null) ? fieldInfo2.GetValue(player) : "<no m_ghostMode field>");
				if (text == null && fieldInfo3 == null)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("god mode NOT applied: neither SetGodMode nor m_godMode exists on " + type.FullName + " -- the rig is MORTAL"));
				}
				else
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)(string.Format("invulnerable={0} via {1}; ", on, text ?? "<none>") + $"god now {arg2}, ghost now {arg3}, fly now {arg}"));
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not set god/ghost mode: " + ex.Message));
			}
		}

		private List<Shot> LoadShotPlan()
		{
			return LoadShotPlanFrom(ShotPlanPath);
		}

		private List<Shot> LoadShotPlanFrom(string path)
		{
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			List<Shot> list = new List<Shot>();
			if (!File.Exists(path))
			{
				return list;
			}
			string[] array = File.ReadAllLines(path);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length == 0 || text.StartsWith("#"))
				{
					continue;
				}
				string[] array2 = text.Split(new char[1] { '\t' });
				if (array2.Length >= 12)
				{
					try
					{
						list.Add(ExtendShot(new Shot
						{
							ClusterId = int.Parse(array2[0], CultureInfo.InvariantCulture),
							Name = array2[1],
							Camera = new Vector3(P(array2[2]), P(array2[3]), P(array2[4])),
							Yaw = P(array2[5]),
							Pitch = P(array2[6]),
							Environment = array2[7],
							TimeOfDay = P(array2[8]),
							Aim = new Vector3(P(array2[9]), P(array2[10]), P(array2[11])),
							Label = ((array2.Length > 12) ? array2[12] : ""),
							Mode = ((array2.Length > 13) ? array2[13].Trim() : ""),
							Fires = (array2.Length > 14 && TsvFlag(array2[14])),
							Flash = ((array2.Length > 15) ? TsvFloat(array2[15]) : ((float?)null))
						}, array2));
					}
					catch (Exception ex)
					{
						((BaseUnityPlugin)this).Logger.LogWarning((object)("shotplan line skipped: " + ex.Message));
					}
				}
			}
			return list;
		}

		private static float P(string s)
		{
			return float.Parse(s, CultureInfo.InvariantCulture);
		}

		private float SetCameraBoom(float distance)
		{
			try
			{
				GameCamera instance = GameCamera.instance;
				if ((Object)(object)instance == (Object)null)
				{
					return -1f;
				}
				BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
				FieldInfo field = typeof(GameCamera).GetField("m_distance", bindingAttr);
				if (field == null)
				{
					return -1f;
				}
				float result = (float)field.GetValue(instance);
				field.SetValue(instance, distance);
				FieldInfo field2 = typeof(GameCamera).GetField("m_minDistance", bindingAttr);
				if (field2 != null && distance <= 0f)
				{
					field2.SetValue(instance, 0f);
				}
				return result;
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not set camera boom: " + ex.Message));
				return -1f;
			}
		}

		private static float CameraFov()
		{
			Camera main = Camera.main;
			if (!((Object)(object)main == (Object)null))
			{
				return main.fieldOfView;
			}
			return -1f;
		}

		private static bool IsOccluded(Vector3 from, Vector3 to)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: 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)
			try
			{
				Vector3 val = to - from;
				float magnitude = ((Vector3)(ref val)).magnitude;
				if (magnitude < 1f)
				{
					return false;
				}
				RaycastHit val2 = default(RaycastHit);
				return Physics.Raycast(from, ((Vector3)(ref val)).normalized, ref val2, magnitude * 0.85f, LayerMask.GetMask(new string[3] { "terrain", "static_solid", "Default" }));
			}
			catch (Exception)
			{
				return false;
			}
		}

		private static int PiecesNear(Vector3 point, float radius)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Collider[] array = Physics.OverlapSphere(point, radius, LayerMask.GetMask(new string[2] { "piece", "static_solid" }));
				return (array != null) ? array.Length : 0;
			}
			catch (Exception)
			{
				return -1;
			}
		}

		private IEnumerator WaitForWorld(Vector3 aim, float maxSeconds)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			_lastWorldSettled = true;
			float start = Time.time;
			int last = -1;
			float stableFor = 0f;
			while (Time.time - start < maxSeconds)
			{
				int num = PiecesNear(aim, 60f);
				if (num > 0 && num == last)
				{
					stableFor += 0.5f;
					if (stableFor >= 1.5f)
					{
						_lastWorldSettled = true;
						yield break;
					}
				}
				else
				{
					stableFor = 0f;
				}
				last = num;
				yield return (object)new WaitForSeconds(0.5f);
			}
			((BaseUnityPlugin)this).Logger.LogWarning((object)$"World did not settle within {maxSeconds:0}s near {JsonVector(aim)}");
			_lastWorldSettled = PiecesNear(aim, 60f) > 0;
		}

		private static void LookAngles(Vector3 from, Vector3 at, out float yaw, out float pitch)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: 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)
			Vector3 val = at - from;
			float magnitude = ((Vector3)(ref val)).magnitude;
			if (magnitude < 0.001f)
			{
				yaw = 0f;
				pitch = 0f;
			}
			else
			{
				yaw = Mathf.Repeat(Mathf.Atan2(val.x, val.z) * 57.29578f, 360f);
				pitch = (0f - Mathf.Asin(Mathf.Clamp(val.y / magnitude, -1f, 1f))) * 57.29578f;
			}
		}

		private Vector3 FindClearView(Vector3 desired, Vector3 aim, out string how)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: 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_0031: 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_0040: 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_004d: 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_0079: 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_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: 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_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: 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_01b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_012a: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: 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_0178: Unknown result type (might be due to invalid IL or missing references)
			how = "planned";
			if (!IsOccluded(desired, aim))
			{
				return desired;
			}
			float[] array = new float[5] { 8f, 16f, 26f, 40f, 60f };
			Vector3 val = default(Vector3);
			foreach (float num in array)
			{
				((Vector3)(ref val))..ctor(desired.x, desired.y + num, desired.z);
				if (!IsOccluded(val, aim))
				{
					how = $"lifted+{num:0}m";
					return val;
				}
			}
			Vector3 val2 = desired - aim;
			Vector2 val3 = new Vector2(val2.x, val2.z);
			float magnitude = ((Vector2)(ref val3)).magnitude;
			float num2 = Mathf.Atan2(val2.x, val2.z);
			array = new float[4] { 60f, 40f, 26f, 16f };
			Vector3 val4 = default(Vector3);
			foreach (float num3 in array)
			{
				for (float num4 = 15f; num4 <= 90f; num4 += 15f)
				{
					float[] array2 = new float[2] { 1f, -1f };
					foreach (float num5 in array2)
					{
						float num6 = num2 + (float)Math.PI / 180f * num4 * num5;
						((Vector3)(ref val4))..ctor(aim.x + Mathf.Sin(num6) * magnitude, desired.y + num3, aim.z + Mathf.Cos(num6) * magnitude);
						if (!IsOccluded(val4, aim))
						{
							how = string.Format("swung{0}{1:0}deg+{2:0}m", (num5 > 0f) ? "+" : "-", num4, num3);
							return val4;
						}
					}
				}
			}
			how = "still_blocked";
			return desired;
		}

		private void ConsolePose(ConsoleEventArgs args)
		{
			//IL_03f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0407: Unknown result type (might be due to invalid IL or missing references)
			//IL_0416: 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_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_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)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: 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_0291: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_02c7: Unknown result type (might be due to invalid IL or missing references)
			object localPlayer = GetLocalPlayer();
			Camera main = Camera.main;
			if (localPlayer == null || (Object)(object)main == (Object)null)
			{
				Announce("no local player or camera");
				return;
			}
			string text = ((args.Length > 1) ? args[1] : ("pose" + DateTime.Now.ToString("HHmmss", CultureInfo.InvariantCulture)));
			float num2 = default(float);
			float num = (args.TryParameterFloat(2, ref num2) ? Mathf.Clamp(num2, 2f, 400f) : 40f);
			Vector3 position = ((Component)localPlayer).transform.position;
			Vector3 position2 = ((Component)main).transform.position;
			Vector3 forward = ((Component)main).transform.forward;
			Vector3 val = position2 + forward * num;
			LookAngles(position, val, out var yaw, out var pitch);
			CultureInfo invariantCulture = CultureInfo.InvariantCulture;
			string text2 = string.Join("\t", "0", text, position.x.ToString("0.0", invariantCulture), position.y.ToString("0.0", invariantCulture), position.z.ToString("0.0", invariantCulture), yaw.ToString("0.00", invariantCulture), pitch.ToString("0.00", invariantCulture), ((Object)(object)EnvMan.instance != (Object)null && !string.IsNullOrEmpty(EnvMan.instance.GetCurrentEnvironment()?.m_name)) ? EnvMan.instance.GetCurrentEnvironment().m_name : "Clear", (((Object)(object)EnvMan.instance != (Object)null) ? EnvMan.instance.GetDayFraction() : 0.64f).ToString("0.00", invariantCulture), val.x.ToString("0.0", invariantCulture), val.y.ToString("0.0", invariantCulture), val.z.ToString("0.0", invariantCulture), "Pose " + text, "", "0", "");
			string text3 = new StringBuilder().Append("{").Append("\"name\":" + JsonString(text) + ",").Append("\"at\":" + JsonString(DateTime.Now.ToString("o", invariantCulture)) + ",")
				.Append("\"feet\":" + JsonVector(position) + ",")
				.Append("\"lens\":" + JsonVector(position2) + ",")
				.Append("\"aim\":" + JsonVector(val) + ",")
				.Append("\"aim_distance_m\":" + num.ToString("0.0", invariantCulture) + ",")
				.Append("\"yaw\":" + yaw.ToString("0.00", invariantCulture) + ",")
				.Append("\"pitch\":" + pitch.ToString("0.00", invariantCulture) + ",")
				.Append("\"fov\":" + main.fieldOfView.ToString("0.0", invariantCulture) + ",")
				.Append("\"tsv\":" + JsonString(text2))
				.Append("}")
				.ToString();
			try
			{
				File.AppendAllText(PosesPath, text3 + "\n", Encoding.UTF8);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not append pose: " + ex.Message));
			}
			Announce($"pose {text}: yaw {yaw:0.#} pitch {pitch:0.#} lens {position2.x:0.0},{position2.y:0.0},{position2.z:0.0} -> {Path.GetFileName(PosesPath)}");
			((BaseUnityPlugin)this).Logger.LogInfo((object)("pose row: " + text2));
		}

		private void ConsoleRunClips(ConsoleEventArgs args)
		{
			if (_stillJobRunning)
			{
				Announce("a capture job is already running");
			}
			else if (!File.Exists(ClipPlanPath))
			{
				Announce("no clip plan at " + ClipPlanPath);
			}
			else
			{
				((MonoBehaviour)this).StartCoroutine(RunClipPlan());
			}
		}

		private void ConsoleRunPlan(ConsoleEventArgs args)
		{
			if (_stillJobRunning)
			{
				Announce("a capture job is already running");
				return;
			}
			int startIndex = args.TryParameterInt(1, 0);
			((MonoBehaviour)this).StartCoroutine(RunShotPlan(startIndex));
		}

		private IEnumerator RunShotPlan(int startIndex, bool finalize = true)
		{
			List<Shot> list = LoadShotPlan();
			if (list.Count == 0)
			{
				Announce("no shots in " + ShotPlanPath);
			}
			else
			{
				yield return RunShotRows(list.GetRange(startIndex, list.Count - startIndex), "shotplan.tsv", finalize);
			}
		}

		private IEnumerator RunShotRows(List<Shot> plan, string planName, bool finalize)
		{
			_stillJobRunning = true;
			string outRoot = Path.Combine(ConfigDir, "orbit-captures");
			string runId = DateTime.Now.ToString("yyyyMMdd-HHmmss", CultureInfo.InvariantCulture);
			string outDir = Path.Combine(outRoot, runId);
			while (Directory.Exists(outDir))
			{
				yield return (object)new WaitForSeconds(1f);
				runId = DateTime.Now.ToString("yyyyMMdd-HHmmss", CultureInfo.InvariantCulture);
				outDir = Path.Combine(outRoot, runId);
			}
			Directory.CreateDirectory(outDir);
			Announce($"orbit capture: {plan.Count} shot(s) -> {runId} ({planName})");
			WriteEnvironmentNames();
			float settle = Mathf.Max(1f, _settleSeconds.Value);
			ArmRig();
			int shotIndex = 0;
			for (int i = 0; i < plan.Count; i++)
			{
				Shot s = plan[i];
				if (s.Lens.HasValue)
				{
					yield return RunExactShot(s, runId, outDir, planName);
					shotIndex++;
					continue;
				}
				object player = GetLocalPlayer();
				if (player == null)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)$"[{i + 1}/{plan.Count}] no local player; waiting for respawn");
					float waited = 0f;
					while (player == null && waited < 90f)
					{
						yield return (object)new WaitForSeconds(2f);
						waited += 2f;
						player = GetLocalPlayer();
					}
					if (player == null)
					{
						((BaseUnityPlugin)this).Logger.LogError((object)($"Orbit capture stopped at {i + 1}/{plan.Count}: " + "player did not return within 90s."));
						break;
					}
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"  player back after {waited:0}s; continuing");
					if (_hidePlayerForScreenshots)
					{
						SetLocalPlayerVisible(visible: false);
					}
				}
				SetInvulnerable(player, on: true);
				Vector3 target = s.Camera;
				float num = (IsInterior(s) ? 0.2f : 2f);
				if (TryResolveGroundHeight(target.x, target.z, out var height) && height.HasValue && target.y < height.Value + num)
				{
					target.y = height.Value + num;
				}
				float yaw = s.Yaw;
				float pitch = s.Pitch;
				PlacePlayer(player, target);
				_holdAt = target;
				TryAim(yaw, pitch);
				SetForcedEnvironment(s.Environment);
				SetDebugTime(s.TimeOfDay);
				yield return WaitForStablePlayer(target, 15f);
				yield return WaitForWorld(s.Aim, 25f);
				string clearance = "planned";
				Vector3 val = target;
				if (!IsInterior(s))
				{
					val = FindClearView(target, s.Aim, out clearance);
				}
				if (clearance != "planned")
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)("  view was blocked; " + clearance));
					target = val;
					LookAngles(target, s.Aim, out yaw, out pitch);
					PlacePlayer(player, target);
					_holdAt = target;
					yield return WaitForStablePlayer(target, 10f);
				}
				TryAim(yaw, pitch);
				HoldFiresLit(s.Aim, 80f, Vector3.Distance(target, s.Aim) + 40f, s.Fires);
				yield return (object)new WaitForSeconds(settle);
				RecountFires(s.Aim, 80f);
				int pieces = PiecesNear(s.Aim, 60f);
				if (pieces <= 0)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)($"[{i + 1}/{plan.Count}] {s.Label} {s.Name}: world never " + "arrived (0 pieces) -- shot skipped"));
					AppendReceipt("{\"run\":" + JsonString(runId) + ",\"plan\":" + JsonString(planName) + ",\"index\":" + i + ",\"cluster_id\":" + s.ClusterId + ",\"shot\":" + JsonString(s.Name) + ",\"mode\":" + JsonString(s.Mode) + ",\"skipped\":\"world_never_loaded\"}");
					continue;
				}
				bool occluded = IsOccluded(target, s.Aim);
				string fileName = $"{s.ClusterId:0000}_{s.Name}.png";
				string path = Path.Combine(outDir, fileName);
				string flash = "off";
				if (s.Flash.HasValue)
				{
					flash = DriveFlash(s.Aim, target, s.Flash.Value, 1.2f);
					yield return (object)new WaitForSeconds(0.2f);
				}
				string measuredProjection = null;
				if (s.VerticalFov.HasValue || s.Width > 0)
				{
					measuredProjection = CaptureProjection(s, path, exact: false);
				}
				else
				{
					ScreenCapture.CaptureScreenshot(path);
				}
				yield return (object)new WaitForSeconds(1f);
				if (s.Fires)
				{
					ReleaseHeldLight();
				}
				Camera main = Camera.main;
				Vector3 val2 = (((Object)(object)main == (Object)null) ? target : ((Component)main).transform.position);
				AppendReceipt(new StringBuilder().Append("{").Append("\"run\":" + JsonString(runId) + ",").Append("\"plan\":" + JsonString(planName) + ",")
					.Append($"\"index\":{i},")
					.Append($"\"cluster_id\":{s.ClusterId},")
					.Append("\"shot\":" + JsonString(s.Name) + ",")
					.Append("\"mode\":" + JsonString(s.Mode) + ",")
					.Append("\"label\":" + JsonString(s.Label) + ",")
					.Append("\"file\":" + JsonString(fileName) + ",")
					.Append("\"planned\":" + JsonVector(s.Camera) + ",")
					.Append("\"placed\":" + JsonVector(target) + ",")
					.Append("\"lens\":" + JsonVector(val2) + ",")
					.Append("\"lens_offset_m\":" + JsonNumber(Vector3.Distance(val2, target)) + ",")
					.Append("\"aim\":" + JsonVector(s.Aim) + ",")
					.Append("\"yaw\":" + JsonNumber(yaw) + ",\"pitch\":" + JsonNumber(pitch) + ",")
					.Append("\"clearance\":" + JsonString(clearance) + ",")
					.Append("\"fov\":" + JsonNumber(s.VerticalFov ?? CameraFov()) + ",")
					.Append((measuredProjection == null) ? "" : (measuredProjection + ","))
					.Append("\"environment\":" + JsonString(s.Environment) + ",")
					.Append("\"time_of_day\":" + JsonNumber(s.TimeOfDay) + ",")
					.Append("\"fires\":" + JsonBool(s.Fires) + ",")
					.Append($"\"fires_found\":{_firesFound},")
					.Append($"\"fires_found_at_sweep\":{_firesFoundAtSweep},")
					.Append($"\"fires_in_view\":{_firesInView},")
					.Append($"\"fires_burning\":{_firesWereBurning},")
					.Append($"\"fires_wet\":{_firesWereWet},")
					.Append($"\"fires_lit\":{_firesLit},")
					.Append($"\"fires_unowned\":{_firesUnowned},")
					.Append($"\"light_lods\":{_lodsWidened},")
					.Append("\"flash\":" + JsonString(flash) + ",")
					.Append("\"flash_bearing_deg\":" + JsonNumberOrNull(s.Flash) + ",")
					.Append("\"flash_hold_s\":" + (s.Flash.HasValue ? JsonNumber(1.2f) : "null") + ",")
					.Append($"\"pieces_near_aim\":{pieces},")
					.Append("\"occluded\":" + JsonBool(occluded) + ",")
					.Append("\"at\":" + JsonString(DateTime.Now.ToString("o", CultureInfo.InvariantCulture)))
					.Append("}")
					.ToString());
				shotIndex++;
				((BaseUnityPlugin)this).Logger.LogInfo((object)($"[{i + 1}/{plan.Count}] {s.Label} {s.Name} " + $"pieces={pieces} occluded={occluded}"));
			}
			ReleaseHeldLight();
			if (finalize)
			{
				FinalizeRig();
				_stillJobRunning = false;
			}
			int num2 = shotIndex;
			Announce((num2 >= plan.Count) ? $"orbit capture finished: {num2}/{plan.Count} -> {outDir}" : $"orbit capture ENDED EARLY: {num2}/{plan.Count} -> {outDir}");
		}

		private void ArmRig()
		{
			if (!_rigArmed)
			{
				_rigArmed = true;
				_priorBoom = SetCameraBoom(0f);
				SetCaptureChromeHidden(hide: true);
				if (_hidePlayerForScreenshots)
				{
					SetLocalPlayerVisible(visible: false);
				}
			}
		}

		private void FinalizeRig()
		{
			SetForcedEnvironment("");
			SetDebugTime(null);
			SetCaptureChromeHidden(hide: false);
			SetLocalPlayerVisible(!_hidePlayerForScreenshots);
			if (_priorBoom >= 0f)
			{
				SetCameraBoom(_priorBoom);
			}
			_priorBoom = -1f;
			ReleaseRigOrHoldForExit();
			_rigArmed = false;
		}

		private IEnumerator FeedLoop(string feedDir, float idleSeconds)
		{
			string dir = Path.Combine(ConfigDir, feedDir);
			Directory.CreateDirectory(dir);
			string stopPath = Path.Combine(dir, "STOP");
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Feed: watching {dir} (idle {idleSeconds:0}s)");
			_stillJobRunning = true;
			HashSet<string> fed = new HashSet<string>(StringComparer.Ordinal);
			float lastActivity = Time.realtimeSinceStartup;
			while (true)
			{
				if (File.Exists(stopPath))
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)"Feed: STOP");
					break;
				}
				if (Time.realtimeSinceStartup - lastActivity > idleSeconds)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)$"Feed: idle timeout after {idleSeconds:0}s");
					break;
				}
				string next = null;
				try
				{
					string[] files = Directory.GetFiles(dir, "*.tsv");
					Array.Sort(files, (IComparer<string>?)StringComparer.Ordinal);
					string[] array = files;
					foreach (string text in array)
					{
						if (text.EndsWith(".tsv", StringComparison.OrdinalIgnoreCase) && !fed.Contains(text))
						{
							next = text;
							break;
						}
					}
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Feed: scan failed: " + ex.Message));
				}
				if (next == null)
				{
					yield return (object)new WaitForSeconds(1f);
					continue;
				}
				fed.Add(next);
				string name = Path.GetFileName(next);
				List<Shot> list = LoadShotPlanFrom(next);
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Feed: plan {name} {list.Count} rows");
				if (list.Count > 0)
				{
					yield return RunShotRows(list, name, finalize: false);
				}
				try
				{
					string text2 = next + ".done";
					if (File.Exists(text2))
					{
						File.Delete(text2);
					}
					File.Move(next, text2);
				}
				catch (Exception ex2)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Feed: could not mark " + name + " done: " + ex2.Message));
				}
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Feed: plan " + name + " done"));
				lastActivity = Time.realtimeSinceStartup;
			}
			FinalizeRig();
			_stillJobRunning = false;
		}

		private void AppendReceipt(string json)
		{
			try
			{
				File.AppendAllText(ShotReceiptsPath, json + "\n", Encoding.UTF8);
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not append receipt: " + ex.Message));
			}
		}

		private void ConsoleAim(ConsoleEventArgs args)
		{
			float num = default(float);
			float num2 = default(float);
			if (!args.TryParameterFloat(1, ref num) || !args.TryParameterFloat(2, ref num2))
			{
				Announce("Usage: aim <yaw 0-360> <pitch, + is down>");
			}
			else
			{
				Announce(TryAim(num, num2) ? $"aimed yaw {num:0.#} pitch {num2:0.#}" : "aim failed");
			}
		}

		private void ConsoleAimTest(ConsoleEventArgs args)
		{
			((MonoBehaviour)this).StartCoroutine(AimTest());
		}

		private IEnumerator AimTest()
		{
			StringBuilder results = new StringBuilder();
			results.AppendLine("{");
			results.AppendLine("  \"cases\": [");
			float[][] array = new float[4][]
			{
				new float[2] { 0f, 40f },
				new float[2] { 90f, 40f },
				new float[2] { 180f, 20f },
				new float[2] { 270f, 55f }
			};
			List<string> lines = new List<string>();
			float[][] array2 = array;
			foreach (float[] array3 in array2)
			{
				float yaw = array3[0];
				float pitch = array3[1];
				Vector3 want = Quaternion.Euler(pitch, yaw, 0f) * Vector3.forward;
				if (!TryAim(yaw, pitch))
				{
					lines.Add("    { \"yaw\": " + JsonNumber(yaw) + ", \"error\": \"aim call failed\" }");
					continue;
				}
				yield return null;
				float? immediate = AngleFrom(want);
				yield return (object)new WaitForSeconds(1f);
				float? num = AngleFrom(want);
				lines.Add("    { \"yaw\": " + JsonNumber(yaw) + ", \"pitch\": " + JsonNumber(pitch) + ", \"err_immediate_deg\": " + JsonNumberOrNull(immediate) + ", \"err_after_1s_deg\": " + JsonNumberOrNull(num) + " }");
				((BaseUnityPlugin)this).Logger.LogInfo((object)($"aimtest yaw={yaw} pitch={pitch} " + $"immediate={immediate:0.##} after1s={num:0.##}"));
			}
			results.AppendLine(string.Join(",\n", lines));
			results.AppendLine("  ]");
			results.AppendLine("}");
			WriteJson("camera-proof-aimtest.json", results.ToString());
			Announce("aimtest written to camera-proof-aimtest.json");
		}

		private void ConsoleSky(ConsoleEventArgs args)
		{
			if (_stillJobRunning)
			{
				Announce("busy: a capture job is already running");
				return;
			}
			List<float> list = new List<float>();
			for (int i = 1; i < args.Length; i++)
			{
				string[] array = (args[i] ?? "").Split(new char[1] { ',' });
				for (int j = 0; j < array.Length; j++)
				{
					if (float.TryParse(array[j], NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
					{
						list.Add(Mathf.Clamp01(result));
					}
				}
			}
			if (list.Count == 0)
			{
				for (int k = 30; k <= 50; k++)
				{
					list.Add((float)k / 50f);
				}
				for (int l = 0; l <= 20; l++)
				{
					list.Add((float)l / 50f);
				}
			}
			((MonoBehaviour)this).StartCoroutine(SkyDumpRoutine(list));
		}

		private IEnumerator SkyDumpRoutine(List<float> times)
		{
			_stillJobRunning = true;
			Announce($"sky dump: {times.Count} time(s)");
			List<string> samples = new List<string>();
			foreach (float t in times)
			{
				SetDebugTime(t);
				yield return (object)new WaitForSeconds(1f);
				string settling = ReadSky();
				yield return (object)new WaitForSeconds(0.5f);
				string text = ReadSky();
				samples.Add("    { \"time_of_day\": " + JsonNumber(t) + ", \"settling\": " + settling + ", \"settled\": " + text + " }");
			}
			SetDebugTime(null);
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("{");
			stringBuilder.AppendLine("  \"written_at\": " + JsonString(DateTime.Now.ToString("o", CultureInfo.InvariantCulture)) + ",");
			stringBuilder.AppendLine("  \"env_fields\": " + EnvManSkyFields() + ",");
			stringBuilder.AppendLine("  \"sky_renderers\": " + SkyRenderers() + ",");
			stringBuilder.AppendLine("  \"samples\": [");
			stringBuilder.AppendLine(string.Join(",\n", samples.ToArray()));
			stringBuilder.AppendLine("  ]");
			stringBuilder.AppendLine("}");
			WriteJson("camera-proof-sky.json", stringBuilder.ToString());
			_stillJobRunning = false;
			Announce("sky dump written to camera-proof-sky.json");
		}

		private string ReadSky()
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: 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_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			EnvMan instance = EnvMan.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return "null";
			}
			object? obj = FindField(typeof(EnvMan), "m_dirLight")?.GetValue(instance);
			Light val = (Light)((obj is Light) ? obj : null);
			if ((Object)(object)val == (Object)null)
			{
				return "null";
			}
			Vector3 forward = ((Component)val).transform.forward;
			Vector3 val2 = -forward;
			float num = Mathf.Max(((Vector3)(ref val2)).magnitude, 1E-06f);
			float value = Mathf.Repeat(Mathf.Atan2(val2.x, val2.z) * 57.29578f, 360f);
			float value2 = Mathf.Asin(Mathf.Clamp(val2.y / num, -1f, 1f)) * 57.29578f;
			Color color = val.color;
			return "{ \"forward\": " + JsonVector(forward) + ", \"body_azimuth_deg\": " + JsonNumber(value) + ", \"body_altitude_deg\": " + JsonNumber(value2) + ", \"intensity\": " + JsonNumber(val.intensity) + ", \"enabled\": " + JsonBool(((Behaviour)val).enabled) + ", \"color\": { \"r\": " + JsonNumber(color.r) + ", \"g\": " + JsonNumber(color.g) + ", \"b\": " + JsonNumber(color.b) + " } }";
		}

		private string EnvManSkyFields()
		{
			EnvMan env = EnvMan.instance;
			if ((Object)(object)env == (Object)null)
			{
				return "[]";
			}
			Regex regex = new Regex("moon|phase|sun", RegexOptions.IgnoreCase);
			List<string> list = new List<string>();
			BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
			Type type = typeof(EnvMan);
			while (type != null && type != typeof(MonoBehaviour))
			{
				FieldInfo[] fields = type.GetFields(bindingAttr);
				foreach (FieldInfo fieldInfo in fields)
				{
					if (regex.IsMatch(fieldInfo.Name))
					{
						FieldInfo field = fieldInfo;
						list.Add("    { \"kind\": \"field\", \"name\": " + JsonString(field.Name) + ", \"type\": " + JsonString(field.FieldType.Name) + ", \"value\": " + JsonString(SafeValue(() => field.GetValue(env))) + " }");
					}
				}
				MethodInfo[] methods = type.GetMethods(bindingAttr);
				foreach (MethodInfo methodInfo in methods)
				{
					if (regex.IsMatch(methodInfo.Name) && methodInfo.GetParameters().Length == 0 && (methodInfo.Name.StartsWith("Get") || methodInfo.Name.StartsWith("Is")))
					{
						MethodInfo method = methodInfo;
						list.Add("    { \"kind\": \"method\", \"name\": " + JsonString(method.Name) + ", \"type\": " + JsonString(method.ReturnType.Name) + ", \"value\": " + JsonString(SafeValue(() => method.Invoke(env, null))) + " }");
					}
				}
				type = type.BaseType;
			}
			if (list.Count != 0)
			{
				return "[\n" + string.Join(",\n", list.ToArray()) + "\n  ]";
			}
			return "[]";
		}

		private string SkyRenderers()
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: 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_0117: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = new List<string>();
			try
			{
				GameCamera instance = GameCamera.instance;
				Camera val = (Camera)(((Object)(object)instance == (Object)null) ? null : /*isinst with value type is only supported in some contexts*/);
				if ((Object)(object)val != (Object)null)
				{
					Vector3 position = ((Component)val).transform.position;
					Renderer[] componentsInChildren = ((Component)((Component)val).transform.root).GetComponentsInChildren<Renderer>(true);
					foreach (Renderer val2 in componentsInChildren)
					{
						Bounds bounds = val2.bounds;
						list.Add("    { \"path\": " + JsonString(PathOf(((Component)val2).transform)) + ", \"enabled\": " + JsonBool(val2.enabled) + ", \"lossy_scale\": " + JsonVector(((Component)val2).transform.lossyScale) + ", \"extents\": " + JsonVector(((Bounds)(ref bounds)).extents) + ", \"center\": " + JsonVector(((Bounds)(ref bounds)).center) + ", \"distance_from_sky_cam\": " + JsonNumber(Vector3.Distance(position, ((Bounds)(ref bounds)).center)) + " }");
					}
				}
			}
			catch (Exception ex)
			{
				list.Add("    { \"error\": " + JsonString(ex.Message) + " }");
			}
			if (list.Count != 0)
			{
				return "[\n" + string.Join(",\n", list.ToArray()) + "\n  ]";
			}
			return "[]";
		}

		private static string PathOf(Transform t)
		{
			StringBuilder stringBuilder = new StringBuilder(((Object)t).name);
			Transform parent = t.parent;
			while ((Object)(object)parent != (Object)null)
			{
				stringBuilder.Insert(0, ((Object)parent).name + "/");
				parent = parent.parent;
			}
			return stringBuilder.ToString();
		}

		private static string SafeValue(Func<object> get)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				object obj = get();
				if (obj == null)
				{
					return "null";
				}
				if (obj is Vector3 val)
				{
					return "(" + JsonNumber(val.x) + ", " + JsonNumber(val.y) + ", " + JsonNumber(val.z) + ")";
				}
				if (obj is float num)
				{
					return num.ToString("0.#