Decompiled source of KillerCam v1.0.0

KillerCam.dll

Decompiled 2 months ago
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using BepInEx.Unity.IL2CPP.UnityEngine;
using HarmonyLib;
using Il2CppSystem.Collections.Generic;
using SOD.Common;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("KillerCam")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+fd18034234b1bc5b3744e48ee190066c38503ea8")]
[assembly: AssemblyProduct("KillerCam")]
[assembly: AssemblyTitle("KillerCam")]
[assembly: AssemblyVersion("1.0.0.0")]
[HarmonyPatch(typeof(SessionData))]
[HarmonyPatch("PauseGame")]
public class PauseGameManager
{
	public static void Prefix(ref bool showPauseText, ref bool delayOverride, ref bool openDesktopMode)
	{
		GameStateVars.isPaused = true;
	}
}
[HarmonyPatch(typeof(SessionData))]
[HarmonyPatch("ResumeGame")]
public class ResumeGameManager
{
	public static void Prefix()
	{
		GameStateVars.isPaused = false;
	}
}
public class GameStateVars
{
	public static bool isPaused;
}
[HarmonyPatch(typeof(MurderController))]
[HarmonyPatch("SpawnItem")]
public class SpawnItemManager
{
	public static void Prefix(ref Murder murder, ref InteractablePreset spawnItem, ref LeadSpawnWhere spawnWhere, ref LeadCitizen spawnBelongsTo, ref LeadCitizen spawnWriter, ref LeadCitizen spawnReceiver, ref int security, ref OwnedPlacementRule ownedRule, ref int priority, ref JobTag itemTag)
	{
	}
}
namespace KillerCam;

[HarmonyPatch(typeof(Player), "Update")]
public class CamPatch
{
	public enum SpectateTarget
	{
		None,
		Murderer,
		Victim
	}

	public static bool isSpectatingMurderer = false;

	public static bool isSpectatingVictim = false;

	public static Camera murdererCamera = null;

	private static GameObject murdererCameraObject = null;

	public static Camera victimCamera = null;

	private static GameObject victimCameraObject = null;

	private static Camera playerCamera = null;

	private static NewNode originalPlayerNode = null;

	private static bool isAudioNodeModified = false;

	private static SpectateTarget currentSpectateTarget = SpectateTarget.None;

	public static Human targetHuman = null;

	public static MurdererInfoProvider murdererInfoProvider;

	public static MurderController murderController;

	public static SpeechController speechController;

	private static KeyCode toggleKey = (KeyCode)289;

	private static KeyCode il2cppToggleKeyMurderer = (KeyCode)289;

	private static KeyCode il2cppToggleKeyVictim = (KeyCode)290;

	private static NewRoom lastMurdererRoom = null;

	private static float cullingUpdateCooldown = 0f;

	private static bool wasSpectatingLastFrame = false;

	private static KeyCode il2cppUpKey = (KeyCode)273;

	private static KeyCode il2cppDownKey = (KeyCode)274;

	private static KeyCode il2cppLeftKey = (KeyCode)276;

	private static KeyCode il2cppRightKey = (KeyCode)275;

	private static bool wasKeyPressed = false;

	private static float rotationX = 0f;

	private static float rotationY = 0f;

	private static float rotationSpeed = 5f;

	private static bool isTransitioning = false;

	private static Vector3 transitionStartPosition;

	private static Quaternion transitionStartRotation;

	private static Vector3 transitionTargetPosition;

	private static Quaternion transitionTargetRotation;

	private static float transitionProgress = 0f;

	private static float transitionDuration = 0.5f;

	private static SpectateTarget transitionTargetType = SpectateTarget.None;

	private static Camera activeCamera = null;

	private static Camera sourceCamera = null;

	private static Camera targetCamera = null;

	private static bool wasUpPressed = false;

	private static bool wasDownPressed = false;

	private static bool wasLeftPressed = false;

	private static bool wasRightPressed = false;

	private static float cameraRotationX = 0f;

	private static float cameraYOffset = 0f;

	private static float cameraRotationSpeed = 3.5f;

	private static float defaultCameraDistance = 1.5f;

	private static float minCameraDistance = 0.5f;

	private static float rotationThresholdDegrees = 2f;

	private static float rotationSmoothFactor = 5f;

	private static Quaternion targetCameraRotation;

	private static Quaternion lastSignificantTargetRotation;

	private static float prevCameraRotationX = 0f;

	private static float prevCameraYOffset = 0f;

	private static void TransitionCamera(Camera source, Camera target, SpectateTarget targetType)
	{
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_01e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ed: Expected O, but got Unknown
		//IL_00e0: 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_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0109: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0113: Unknown result type (might be due to invalid IL or missing references)
		//IL_0115: Unknown result type (might be due to invalid IL or missing references)
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0124: Unknown result type (might be due to invalid IL or missing references)
		//IL_0129: Unknown result type (might be due to invalid IL or missing references)
		//IL_012e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0133: Unknown result type (might be due to invalid IL or missing references)
		//IL_0135: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)source == (Object)null || (Object)(object)target == (Object)null)
		{
			KillerCam.Logger.LogError((object)"Cannot transition with null cameras");
			return;
		}
		isTransitioning = true;
		transitionStartPosition = ((Component)source).transform.position;
		transitionStartRotation = ((Component)source).transform.rotation;
		if (targetType != 0)
		{
			if (targetType == SpectateTarget.Murderer && (Object)(object)murderController != (Object)null)
			{
				Human currentMurderer = murderController.currentMurderer;
				targetHuman = ((currentMurderer != null) ? ((Component)currentMurderer).GetComponent<Human>() : null);
			}
			else if (targetType == SpectateTarget.Victim && (Object)(object)murderController != (Object)null)
			{
				Human currentVictim = murderController.currentVictim;
				targetHuman = ((currentVictim != null) ? ((Component)currentVictim).GetComponent<Human>() : null);
			}
			if ((Object)(object)targetHuman != (Object)null)
			{
				Vector3 position = ((Component)targetHuman).transform.position;
				Quaternion rotation = ((Component)targetHuman).transform.rotation;
				transitionTargetPosition = position + new Vector3(0f, 1.7f, 0f) - rotation * Vector3.forward * 1.5f;
				transitionTargetRotation = rotation;
			}
			else
			{
				SwitchToPlayerCamera();
			}
		}
		if ((Object)(object)playerCamera != (Object)null)
		{
			((Behaviour)playerCamera).enabled = false;
		}
		if ((Object)(object)murdererCamera != (Object)null)
		{
			((Behaviour)murdererCamera).enabled = false;
		}
		if ((Object)(object)victimCamera != (Object)null)
		{
			((Behaviour)victimCamera).enabled = false;
		}
		if ((Object)(object)target != (Object)null)
		{
			((Behaviour)target).enabled = true;
		}
		activeCamera = target;
		sourceCamera = source;
		targetCamera = target;
		transitionProgress = 0f;
		isTransitioning = true;
		transitionTargetType = targetType;
		ManualLogSource logger = KillerCam.Logger;
		bool flag = default(bool);
		BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(35, 2, ref flag);
		if (flag)
		{
			((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Started camera transition to ");
			((BepInExLogInterpolatedStringHandler)val).AppendFormatted<SpectateTarget>(targetType);
			((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" from ");
			((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(((Object)(object)source == (Object)(object)playerCamera) ? "player" : (((Object)(object)source == (Object)(object)murdererCamera) ? "murderer" : "victim"));
		}
		logger.LogInfo(val);
	}

	[HarmonyPrefix]
	public static void Prefix(Player __instance)
	{
		//IL_06b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_06b8: Expected O, but got Unknown
		//IL_0901: Unknown result type (might be due to invalid IL or missing references)
		//IL_0908: Expected O, but got Unknown
		//IL_002a: 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_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_0206: 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_017e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0183: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ee: Unknown result type (might be due to invalid IL or missing references)
		//IL_02f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_02fa: Unknown result type (might be due to invalid IL or missing references)
		//IL_02fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0301: Unknown result type (might be due to invalid IL or missing references)
		//IL_0308: Unknown result type (might be due to invalid IL or missing references)
		//IL_030d: Unknown result type (might be due to invalid IL or missing references)
		//IL_032b: 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_0645: Unknown result type (might be due to invalid IL or missing references)
		//IL_064c: Expected O, but got Unknown
		if (!SpectatorUI.isCreated)
		{
			SpectatorUI.CreateSpectatorText();
		}
		if (isSpectatingMurderer || isSpectatingVictim)
		{
		}
		bool keyInt = Input.GetKeyInt(il2cppToggleKeyMurderer);
		bool keyInt2 = Input.GetKeyInt(il2cppToggleKeyVictim);
		if (keyInt && !wasKeyPressed)
		{
			murderController = MurderController.Instance;
			KillerCam.Logger.LogInfo((object)("F8 Pressed, MurderController: " + (((Object)(object)murderController != (Object)null) ? "Not null" : "Null")));
			if ((Object)(object)murderController != (Object)null && (Object)(object)murderController.currentMurderer != (Object)null)
			{
				Vector3 position = ((Component)murderController.currentMurderer).transform.position;
				KillerCam.Logger.LogInfo((object)("Murderer position: " + ((object)(Vector3)(ref position)).ToString()));
			}
			ToggleSpectateCamera(SpectateTarget.Murderer);
		}
		if (keyInt2 && !wasKeyPressed)
		{
			murderController = MurderController.Instance;
			KillerCam.Logger.LogInfo((object)("F9 Pressed, MurderController: " + (((Object)(object)murderController != (Object)null) ? "Not null" : "Null")));
			if ((Object)(object)murderController != (Object)null && (Object)(object)murderController.currentVictim != (Object)null)
			{
				Vector3 position2 = ((Component)murderController.currentVictim).transform.position;
				KillerCam.Logger.LogInfo((object)("Victim position: " + ((object)(Vector3)(ref position2)).ToString()));
			}
			ToggleSpectateCamera(SpectateTarget.Victim);
		}
		wasKeyPressed = keyInt || keyInt2;
		if ((isSpectatingMurderer || isSpectatingVictim) && ((Object)(object)murdererCamera != (Object)null || (Object)(object)victimCamera != (Object)null))
		{
			bool keyInt3 = Input.GetKeyInt(il2cppUpKey);
			bool keyInt4 = Input.GetKeyInt(il2cppDownKey);
			bool keyInt5 = Input.GetKeyInt(il2cppLeftKey);
			bool keyInt6 = Input.GetKeyInt(il2cppRightKey);
			if (keyInt3)
			{
				cameraRotationX -= cameraRotationSpeed;
			}
			if (keyInt4)
			{
				cameraRotationX += cameraRotationSpeed;
			}
			if (keyInt5)
			{
				cameraYOffset -= cameraRotationSpeed;
			}
			if (keyInt6)
			{
				cameraYOffset += cameraRotationSpeed;
			}
			cameraRotationX = Mathf.Clamp(cameraRotationX, -80f, 80f);
			if (isTransitioning)
			{
				transitionProgress += Time.deltaTime / transitionDuration;
				transitionProgress = Mathf.Clamp01(transitionProgress);
				float num = Mathf.SmoothStep(0f, 1f, transitionProgress);
				Vector3 position3 = Vector3.Lerp(transitionStartPosition, transitionTargetPosition, num);
				Quaternion rotation = Quaternion.Slerp(transitionStartRotation, transitionTargetRotation, num);
				if ((Object)(object)activeCamera != (Object)null)
				{
					((Component)activeCamera).transform.position = position3;
					((Component)activeCamera).transform.rotation = rotation;
				}
				if (transitionProgress >= 1f)
				{
					isTransitioning = false;
					transitionProgress = 0f;
					if (transitionTargetType == SpectateTarget.Murderer)
					{
						if ((Object)(object)murdererCamera != (Object)null)
						{
							((Behaviour)murdererCamera).enabled = true;
						}
						if ((Object)(object)victimCamera != (Object)null)
						{
							((Behaviour)victimCamera).enabled = false;
						}
						if ((Object)(object)playerCamera != (Object)null)
						{
							((Behaviour)playerCamera).enabled = false;
						}
						isSpectatingMurderer = true;
						isSpectatingVictim = false;
						currentSpectateTarget = SpectateTarget.Murderer;
						KillerCam.Logger.LogInfo((object)"Completed transition to murderer camera");
					}
					else if (transitionTargetType == SpectateTarget.Victim)
					{
						if ((Object)(object)victimCamera != (Object)null)
						{
							((Behaviour)victimCamera).enabled = true;
						}
						if ((Object)(object)murdererCamera != (Object)null)
						{
							((Behaviour)murdererCamera).enabled = false;
						}
						if ((Object)(object)playerCamera != (Object)null)
						{
							((Behaviour)playerCamera).enabled = false;
						}
						isSpectatingVictim = true;
						isSpectatingMurderer = false;
						currentSpectateTarget = SpectateTarget.Victim;
						KillerCam.Logger.LogInfo((object)"Completed transition to victim camera");
					}
				}
			}
			else if (isSpectatingMurderer)
			{
				UpdateMurdererCamera();
			}
			else if (isSpectatingVictim)
			{
				UpdateVictimCamera();
			}
		}
		else if (wasSpectatingLastFrame && !isSpectatingMurderer)
		{
			wasSpectatingLastFrame = false;
		}
		if (isSpectatingMurderer || isSpectatingVictim)
		{
			wasSpectatingLastFrame = true;
		}
		bool flag = default(bool);
		if (isSpectatingMurderer || isSpectatingVictim)
		{
			if (isSpectatingMurderer)
			{
				UpdateMurdererCamera();
			}
			else
			{
				UpdateVictimCamera();
			}
			Human val = null;
			NewRoom val2 = null;
			string text = "";
			if (isSpectatingMurderer && (Object)(object)murderController != (Object)null && (Object)(object)murderController.currentMurderer != (Object)null)
			{
				val = ((Component)murderController.currentMurderer).GetComponent<Human>();
				text = "murderer";
			}
			else if (isSpectatingVictim && (Object)(object)murderController != (Object)null && (Object)(object)murderController.currentVictim != (Object)null)
			{
				val = ((Component)murderController.currentVictim).GetComponent<Human>();
				text = "victim";
			}
			if ((Object)(object)val != (Object)null)
			{
				val2 = ((Actor)val).currentRoom;
			}
			if ((Object)(object)val2 != (Object)null)
			{
				try
				{
					cullingUpdateCooldown -= Time.deltaTime;
					if ((Object)(object)val2 != (Object)(object)lastMurdererRoom || cullingUpdateCooldown <= 0f)
					{
						SpectatorRoomTracker.UpdateTargetRoom(val2);
						ManualLogSource logger = KillerCam.Logger;
						BepInExInfoLogInterpolatedStringHandler val3 = new BepInExInfoLogInterpolatedStringHandler(33, 2, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("Full culling update for ");
							((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(text);
							((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("'s room: ");
							((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(val2.name);
						}
						logger.LogInfo(val3);
						lastMurdererRoom = val2;
						cullingUpdateCooldown = 3f;
					}
				}
				catch (Exception ex)
				{
					ManualLogSource logger2 = KillerCam.Logger;
					BepInExErrorLogInterpolatedStringHandler val4 = new BepInExErrorLogInterpolatedStringHandler(36, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("Error updating culling for ");
						((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(text);
						((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("'s room: ");
						((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(ex.Message);
					}
					logger2.LogError(val4);
				}
			}
		}
		Human val5;
		string text2;
		if (isSpectatingMurderer || isSpectatingVictim)
		{
			val5 = null;
			text2 = "";
			if (isSpectatingMurderer)
			{
				MurderController obj = murderController;
				if ((Object)(object)((obj != null) ? obj.currentMurderer : null) != (Object)null)
				{
					val5 = ((Component)murderController.currentMurderer).GetComponent<Human>();
					text2 = "Murderer";
					goto IL_07a5;
				}
			}
			if (isSpectatingVictim)
			{
				MurderController obj2 = murderController;
				if ((Object)(object)((obj2 != null) ? obj2.currentVictim : null) != (Object)null)
				{
					val5 = ((Component)murderController.currentVictim).GetComponent<Human>();
					text2 = "Victim";
				}
			}
			goto IL_07a5;
		}
		SpectatorUI.HideText();
		return;
		IL_07a5:
		if ((Object)(object)val5 != (Object)null)
		{
			string text3 = val5.GetCitizenName() ?? "Unknown";
			string value = "Idle";
			try
			{
				if ((Object)(object)((Actor)val5).ai != (Object)null)
				{
					NewAIAction currentAction = ((Actor)val5).ai.currentAction;
					object obj3;
					if (currentAction == null)
					{
						obj3 = null;
					}
					else
					{
						AIActionPreset preset = currentAction.preset;
						obj3 = ((preset != null) ? ((Object)preset).name : null);
					}
					if (obj3 != null && !string.IsNullOrEmpty(((Object)((Actor)val5).ai.currentAction.preset).name))
					{
						value = ((Object)((Actor)val5).ai.currentAction.preset).name;
					}
					else
					{
						NewAIGoal currentGoal = ((Actor)val5).ai.currentGoal;
						object obj4;
						if (currentGoal == null)
						{
							obj4 = null;
						}
						else
						{
							AIGoalPreset preset2 = currentGoal.preset;
							obj4 = ((preset2 != null) ? ((Object)preset2).name : null);
						}
						if (obj4 != null && !string.IsNullOrEmpty(((Object)((Actor)val5).ai.currentGoal.preset).name))
						{
							value = ((Object)((Actor)val5).ai.currentGoal.preset).name;
						}
						else if (((Actor)val5).interactingWith != null)
						{
							value = "Interacting";
						}
						else if (((Actor)val5).inConversation)
						{
							value = "In Conversation";
						}
					}
				}
			}
			catch (Exception ex2)
			{
				ManualLogSource logger3 = KillerCam.Logger;
				BepInExErrorLogInterpolatedStringHandler val4 = new BepInExErrorLogInterpolatedStringHandler(30, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("Error getting AI status for ");
					((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(text3);
					((BepInExLogInterpolatedStringHandler)val4).AppendLiteral(": ");
					((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(ex2.Message);
				}
				logger3.LogError(val4);
				value = "Error Reading Status";
			}
			if (KillerCam.hideTargetName.Value)
			{
				text3 = "Hidden";
			}
			string message = $"Spectating {text2}: {text3} - {value}";
			SpectatorUI.UpdateText(message);
			SpectatorUI.ShowText();
		}
		else
		{
			SpectatorUI.UpdateText("Spectating " + text2 + ": Target Lost");
			Lib.GameMessage.ShowPlayerSpeech("No current " + text2 + " available", 2f, true);
			SwitchToPlayerCamera();
		}
	}

	private static void ToggleSpectateCamera(SpectateTarget target)
	{
		if ((target == SpectateTarget.Murderer && isSpectatingMurderer) || (target == SpectateTarget.Victim && isSpectatingVictim))
		{
			SwitchToPlayerCamera();
			isSpectatingMurderer = false;
			isSpectatingVictim = false;
			currentSpectateTarget = SpectateTarget.None;
			return;
		}
		Player.Instance.EnablePlayerMovement(false, true);
		if (isSpectatingMurderer || isSpectatingVictim)
		{
			SwitchToTargetCamera(target);
			switch (target)
			{
			case SpectateTarget.Murderer:
				isSpectatingMurderer = true;
				isSpectatingVictim = false;
				break;
			case SpectateTarget.Victim:
				isSpectatingVictim = true;
				isSpectatingMurderer = false;
				break;
			}
		}
		else
		{
			SwitchToTargetCamera(target);
			switch (target)
			{
			case SpectateTarget.Murderer:
				isSpectatingMurderer = true;
				isSpectatingVictim = false;
				break;
			case SpectateTarget.Victim:
				isSpectatingVictim = true;
				isSpectatingMurderer = false;
				break;
			}
		}
		currentSpectateTarget = target;
	}

	private static void TeleportPlayerToTarget(SpectateTarget target)
	{
		//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a8: Expected O, but got Unknown
		try
		{
			Human val = null;
			if (target == SpectateTarget.Murderer)
			{
				MurderController obj = murderController;
				if ((Object)(object)((obj != null) ? obj.currentMurderer : null) != (Object)null)
				{
					val = ((Component)murderController.currentMurderer).GetComponent<Human>();
					goto IL_006e;
				}
			}
			if (target == SpectateTarget.Victim)
			{
				MurderController obj2 = murderController;
				if ((Object)(object)((obj2 != null) ? obj2.currentVictim : null) != (Object)null)
				{
					val = ((Component)murderController.currentVictim).GetComponent<Human>();
				}
			}
			goto IL_006e;
			IL_006e:
			if (!((Object)(object)val != (Object)null))
			{
				KillerCam.Logger.LogWarning((object)"Could not find target human to teleport player to.");
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger = KillerCam.Logger;
			bool flag = default(bool);
			BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(36, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Error teleporting player to target: ");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.ToString());
			}
			logger.LogError(val2);
		}
	}

	private static void SwitchToVictimCamera()
	{
		try
		{
			if ((Object)(object)playerCamera == (Object)null)
			{
				playerCamera = FindActiveCamera();
				KillerCam.Logger.LogInfo((object)("Found active camera: " + ((Object)(object)playerCamera != (Object)null) + (((Object)(object)playerCamera != (Object)null) ? (", Name: " + ((Object)playerCamera).name) : "")));
			}
			if ((Object)(object)murdererCamera == (Object)null)
			{
				CreateMurdererCamera();
				KillerCam.Logger.LogInfo((object)("Created murderer camera: " + ((Object)(object)murdererCamera != (Object)null)));
			}
			if ((Object)(object)playerCamera != (Object)null)
			{
				((Behaviour)playerCamera).enabled = false;
				KillerCam.Logger.LogInfo((object)"Disabled player camera");
			}
			if ((Object)(object)murderController.currentVictim == (Object)null)
			{
				SwitchToPlayerCamera();
				Lib.GameMessage.ShowPlayerSpeech("No current victim available", 2f, true);
			}
			else
			{
				if (!((Object)(object)murdererCamera != (Object)null))
				{
					return;
				}
				((Behaviour)murdererCamera).enabled = true;
				UpdateMurdererCamera();
				if ((Object)(object)murderController != (Object)null && (Object)(object)murderController.currentMurderer != (Object)null)
				{
					Human component = ((Component)murderController.currentMurderer).GetComponent<Human>();
					NewRoom val = ((component != null) ? ((Actor)component).currentRoom : null);
					if ((Object)(object)val != (Object)null)
					{
						SpectatorRoomTracker.StartSpectating(val);
						KillerCam.Logger.LogInfo((object)("Activated SpectatorRoomTracker for room: " + val.name));
					}
				}
				KillerCam.Logger.LogInfo((object)("Switched to murderer camera. Press " + ((object)(KeyCode)(ref toggleKey)).ToString() + " to switch back."));
			}
		}
		catch (Exception ex)
		{
			if ((Object)(object)playerCamera != (Object)null)
			{
				((Behaviour)playerCamera).enabled = true;
			}
			if ((Object)(object)murdererCamera != (Object)null)
			{
				((Behaviour)murdererCamera).enabled = false;
			}
			KillerCam.Logger.LogError((object)("Error switching to murderer camera: " + ex.Message));
		}
	}

	public static void SwitchToPlayerCamera()
	{
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f2: Expected O, but got Unknown
		//IL_009d: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: Expected O, but got Unknown
		//IL_0241: Unknown result type (might be due to invalid IL or missing references)
		//IL_0248: Expected O, but got Unknown
		KillerCam.Logger.LogInfo((object)"Switching back to player camera.");
		Player.Instance.EnablePlayerMovement(true, true);
		if ((Object)(object)playerCamera == (Object)null)
		{
			playerCamera = FindActiveCamera();
			if ((Object)(object)playerCamera == (Object)null)
			{
				KillerCam.Logger.LogError((object)"Could not find player camera!");
				return;
			}
		}
		bool flag = default(bool);
		try
		{
			if (isAudioNodeModified && originalPlayerNode != null && (Object)(object)Player.Instance != (Object)null)
			{
				((Actor)Player.Instance).currentNode = originalPlayerNode;
				ManualLogSource logger = KillerCam.Logger;
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(33, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Restored player's original node: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<NewNode>(originalPlayerNode);
				}
				logger.LogInfo(val);
				isAudioNodeModified = false;
				originalPlayerNode = null;
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger2 = KillerCam.Logger;
			BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(29, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Error restoring player node: ");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
			}
			logger2.LogError(val2);
		}
		if ((Object)(object)murdererCamera != (Object)null)
		{
			((Behaviour)murdererCamera).enabled = false;
		}
		if ((Object)(object)victimCamera != (Object)null)
		{
			((Behaviour)victimCamera).enabled = false;
		}
		if ((Object)(object)playerCamera != (Object)null)
		{
			((Behaviour)playerCamera).enabled = true;
			activeCamera = playerCamera;
			KillerCam.Logger.LogInfo((object)"Enabled player camera directly.");
			isSpectatingMurderer = false;
			isSpectatingVictim = false;
			currentSpectateTarget = SpectateTarget.None;
			lastMurdererRoom = null;
			wasSpectatingLastFrame = false;
			SpectatorUI.HideText();
			rotationX = 0f;
			rotationY = 0f;
			SpectatorRoomTracker.StopSpectating();
			if ((Object)(object)Player.Instance != (Object)null && (Object)(object)((Actor)Player.Instance).currentRoom != (Object)null && (Object)(object)GeometryCullingController.Instance != (Object)null)
			{
				GeometryCullingController.Instance.UpdateCullingForRoom(((Actor)Player.Instance).currentRoom, true, false, (AirDuctGroup)null, true);
				ManualLogSource logger3 = KillerCam.Logger;
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(39, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Forced culling update for player room: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(((Actor)Player.Instance).currentRoom.name);
				}
				logger3.LogInfo(val);
			}
			else
			{
				KillerCam.Logger.LogWarning((object)"Could not force player culling update (Player, Room, or CullingController missing).");
			}
			InterfaceControls instance = InterfaceControls.Instance;
			if ((Object)(object)instance != (Object)null && !((Component)instance.hudCanvas).gameObject.activeSelf)
			{
				((Component)instance.hudCanvas).gameObject.SetActive(true);
				KillerCam.Logger.LogInfo((object)"Interface controls enabled");
			}
		}
		else
		{
			KillerCam.Logger.LogError((object)"Failed to switch to player camera - Player camera is null.");
		}
	}

	private static void SwitchToTargetCamera(SpectateTarget targetType)
	{
		//IL_0279: Unknown result type (might be due to invalid IL or missing references)
		//IL_0280: Expected O, but got Unknown
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0082: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: 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_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_040d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0414: Expected O, but got Unknown
		//IL_06e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_06e9: Expected O, but got Unknown
		//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c6: Expected O, but got Unknown
		//IL_021e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0225: Expected O, but got Unknown
		try
		{
			Transform val = null;
			if (targetType == SpectateTarget.Murderer)
			{
				MurderController obj = murderController;
				if ((Object)(object)((obj != null) ? obj.currentMurderer : null) != (Object)null)
				{
					val = ((Component)murderController.currentMurderer).transform;
					goto IL_006e;
				}
			}
			if (targetType == SpectateTarget.Victim)
			{
				MurderController obj2 = murderController;
				if ((Object)(object)((obj2 != null) ? obj2.currentVictim : null) != (Object)null)
				{
					val = ((Component)murderController.currentVictim).transform;
				}
			}
			goto IL_006e;
			IL_006e:
			if ((Object)(object)val != (Object)null)
			{
				Quaternion rotation = val.rotation;
				Quaternion val2 = rotation * Quaternion.Euler(cameraRotationX, cameraYOffset, 0f);
				targetCameraRotation = val2;
				lastSignificantTargetRotation = rotation;
			}
			InterfaceControls instance = InterfaceControls.Instance;
			if ((Object)(object)instance != (Object)null && ((Component)instance.hudCanvas).gameObject.activeSelf)
			{
				((Component)instance.hudCanvas).gameObject.SetActive(false);
				KillerCam.Logger.LogInfo((object)"Interface controls disabled");
			}
			bool flag = default(bool);
			try
			{
				Human val3 = null;
				if (targetType == SpectateTarget.Murderer)
				{
					MurderController obj3 = murderController;
					if ((Object)(object)((obj3 != null) ? obj3.currentMurderer : null) != (Object)null)
					{
						val3 = ((Component)murderController.currentMurderer).GetComponent<Human>();
						goto IL_016d;
					}
				}
				if (targetType == SpectateTarget.Victim)
				{
					MurderController obj4 = murderController;
					if ((Object)(object)((obj4 != null) ? obj4.currentVictim : null) != (Object)null)
					{
						val3 = ((Component)murderController.currentVictim).GetComponent<Human>();
					}
				}
				goto IL_016d;
				IL_016d:
				if ((Object)(object)val3 != (Object)null && (Object)(object)Player.Instance != (Object)null)
				{
					if (!isAudioNodeModified)
					{
						originalPlayerNode = ((Actor)Player.Instance).currentNode;
						isAudioNodeModified = true;
						ManualLogSource logger = KillerCam.Logger;
						BepInExInfoLogInterpolatedStringHandler val4 = new BepInExInfoLogInterpolatedStringHandler(31, 1, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("Stored player's original node: ");
							((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<NewNode>(originalPlayerNode);
						}
						logger.LogInfo(val4);
					}
					if (((Actor)val3).currentNode != null)
					{
						((Actor)Player.Instance).currentNode = ((Actor)val3).currentNode;
						ManualLogSource logger2 = KillerCam.Logger;
						BepInExInfoLogInterpolatedStringHandler val4 = new BepInExInfoLogInterpolatedStringHandler(40, 2, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("Updated player's node to match ");
							((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<SpectateTarget>(targetType);
							((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("'s node: ");
							((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<NewNode>(((Actor)val3).currentNode);
						}
						logger2.LogInfo(val4);
					}
				}
			}
			catch (Exception ex)
			{
				ManualLogSource logger3 = KillerCam.Logger;
				BepInExErrorLogInterpolatedStringHandler val5 = new BepInExErrorLogInterpolatedStringHandler(28, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val5).AppendLiteral("Error updating player node: ");
					((BepInExLogInterpolatedStringHandler)val5).AppendFormatted<string>(ex.Message);
				}
				logger3.LogError(val5);
			}
			if (isTransitioning)
			{
				KillerCam.Logger.LogInfo((object)"Camera transition already in progress, ignoring switch request");
				return;
			}
			if ((Object)(object)playerCamera == (Object)null)
			{
				playerCamera = FindActiveCamera();
				ManualLogSource logger4 = KillerCam.Logger;
				flag = (Object)(object)playerCamera != (Object)null;
				logger4.LogInfo((object)("Found active camera: " + flag + (((Object)(object)playerCamera != (Object)null) ? (", Name: " + ((Object)playerCamera).name) : "")));
			}
			Camera val6 = null;
			Human val7 = null;
			NewRoom val8 = null;
			switch (targetType)
			{
			case SpectateTarget.Murderer:
				if ((Object)(object)murdererCamera == (Object)null)
				{
					CreateMurdererCamera();
					ManualLogSource logger5 = KillerCam.Logger;
					flag = (Object)(object)murdererCamera != (Object)null;
					logger5.LogInfo((object)("Created murderer camera: " + flag));
				}
				val6 = murdererCamera;
				if ((Object)(object)murderController != (Object)null && (Object)(object)murderController.currentMurderer != (Object)null)
				{
					val7 = ((Component)murderController.currentMurderer).GetComponent<Human>();
					val8 = ((val7 != null) ? ((Actor)val7).currentRoom : null);
				}
				break;
			case SpectateTarget.Victim:
				if ((Object)(object)victimCamera == (Object)null)
				{
					GameObject val9 = new GameObject("VictimCamera");
					victimCamera = val9.AddComponent<Camera>();
					if ((Object)(object)playerCamera != (Object)null)
					{
						victimCamera.CopyFrom(playerCamera);
						victimCamera.depth = playerCamera.depth + 1f;
						victimCamera.useOcclusionCulling = false;
					}
					else
					{
						KillerCam.Logger.LogWarning((object)"Could not find player camera to copy settings for VictimCamera.");
						victimCamera.depth = 1f;
						victimCamera.useOcclusionCulling = false;
					}
					KillerCam.Logger.LogInfo((object)"Created victim camera");
				}
				val6 = victimCamera;
				if ((Object)(object)murderController != (Object)null && (Object)(object)murderController.currentVictim != (Object)null)
				{
					val7 = ((Component)murderController.currentVictim).GetComponent<Human>();
					val8 = ((val7 != null) ? ((Actor)val7).currentRoom : null);
				}
				break;
			}
			if ((Object)(object)val7 == (Object)null)
			{
				SwitchToPlayerCamera();
				Lib.GameMessage.ShowPlayerSpeech($"No current {targetType} available", 2f, true);
			}
			else
			{
				if (!((Object)(object)val6 != (Object)null))
				{
					return;
				}
				Camera val10;
				if (isSpectatingMurderer && (Object)(object)murdererCamera != (Object)null && ((Behaviour)murdererCamera).enabled)
				{
					val10 = murdererCamera;
					KillerCam.Logger.LogInfo((object)"Using murderer camera as transition source");
				}
				else if (isSpectatingVictim && (Object)(object)victimCamera != (Object)null && ((Behaviour)victimCamera).enabled)
				{
					val10 = victimCamera;
					KillerCam.Logger.LogInfo((object)"Using victim camera as transition source");
				}
				else
				{
					val10 = playerCamera;
					KillerCam.Logger.LogInfo((object)"Using player camera as transition source");
				}
				if ((Object)(object)val10 == (Object)(object)val6)
				{
					KillerCam.Logger.LogWarning((object)"Source and target cameras are the same, no transition needed");
					return;
				}
				TransitionCamera(val10, val6, targetType);
				string text = targetType.ToString();
				string text2 = text;
				if ((Object)(object)val7 != (Object)null && !string.IsNullOrEmpty(val7.firstName) && !string.IsNullOrEmpty(val7.surName))
				{
					text2 = $"{text} ({val7.firstName} {val7.surName})";
				}
				else
				{
					ManualLogSource logger6 = KillerCam.Logger;
					BepInExWarningLogInterpolatedStringHandler val11 = new BepInExWarningLogInterpolatedStringHandler(29, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val11).AppendLiteral("Could not retrieve name for ");
						((BepInExLogInterpolatedStringHandler)val11).AppendFormatted<string>(text);
						((BepInExLogInterpolatedStringHandler)val11).AppendLiteral(".");
					}
					logger6.LogWarning(val11);
				}
				SpectatorUI.UpdateText("Spectating: " + text2);
				if ((Object)(object)val8 != (Object)null)
				{
					SpectatorRoomTracker.StartSpectating(val8);
					KillerCam.Logger.LogInfo((object)("Activated room tracking and forced culling update for room: " + val8.name));
				}
			}
		}
		catch (Exception ex2)
		{
			KillerCam.Logger.LogError((object)("Error switching to " + targetType.ToString() + " camera: " + ex2.Message));
		}
	}

	private static void CreateMurdererCamera()
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Expected O, but got Unknown
		try
		{
			KillerCam.Logger.LogInfo((object)"Creating murderer camera");
			murdererCameraObject = new GameObject("MurdererCamera");
			murdererCamera = murdererCameraObject.AddComponent<Camera>();
			if ((Object)(object)playerCamera == (Object)null)
			{
				playerCamera = FindActiveCamera();
			}
			if ((Object)(object)playerCamera != (Object)null)
			{
				murdererCamera.CopyFrom(playerCamera);
				murdererCamera.depth = playerCamera.depth;
				murdererCamera.useOcclusionCulling = false;
			}
			else
			{
				murdererCamera.fieldOfView = 60f;
				murdererCamera.nearClipPlane = 0.1f;
				murdererCamera.farClipPlane = 1000f;
				murdererCamera.useOcclusionCulling = false;
				KillerCam.Logger.LogWarning((object)"No player camera found, using default settings");
			}
			cameraRotationX = 0f;
			cameraYOffset = 0f;
			((Behaviour)murdererCamera).enabled = false;
			KillerCam.Logger.LogInfo((object)"Successfully created murderer camera");
		}
		catch (Exception ex)
		{
			murdererCamera = null;
			murdererCameraObject = null;
			isSpectatingMurderer = false;
			KillerCam.Logger.LogError((object)("Error creating murderer camera: " + ex.Message));
		}
	}

	private static Camera FindActiveCamera()
	{
		try
		{
			if ((Object)(object)Camera.main != (Object)null)
			{
				KillerCam.Logger.LogInfo((object)("Found Camera.main: " + ((Object)Camera.main).name));
				return Camera.main;
			}
			if ((Object)(object)Player.Instance != (Object)null)
			{
				Camera componentInChildren = ((Component)Player.Instance).GetComponentInChildren<Camera>();
				if ((Object)(object)componentInChildren != (Object)null)
				{
					KillerCam.Logger.LogInfo((object)("Found camera on player: " + ((Object)componentInChildren).name));
					return componentInChildren;
				}
			}
			if ((Object)(object)CameraController.Instance != (Object)null)
			{
				try
				{
					Camera component = ((Component)CameraController.Instance).GetComponent<Camera>();
					if ((Object)(object)component != (Object)null)
					{
						KillerCam.Logger.LogInfo((object)("Found camera on CameraController: " + ((Object)component).name));
						return component;
					}
				}
				catch (Exception)
				{
				}
			}
			KillerCam.Logger.LogError((object)"No suitable camera found in the scene");
			return null;
		}
		catch (Exception ex2)
		{
			KillerCam.Logger.LogError((object)("Error finding active camera: " + ex2.Message));
			return null;
		}
	}

	private static void UpdateMurdererCamera()
	{
		//IL_00ab: 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_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_0139: Unknown result type (might be due to invalid IL or missing references)
		//IL_013a: Unknown result type (might be due to invalid IL or missing references)
		//IL_013f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0149: Unknown result type (might be due to invalid IL or missing references)
		//IL_014e: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_012d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0132: Unknown result type (might be due to invalid IL or missing references)
		//IL_0133: Unknown result type (might be due to invalid IL or missing references)
		//IL_0173: Unknown result type (might be due to invalid IL or missing references)
		//IL_0174: Unknown result type (might be due to invalid IL or missing references)
		//IL_0296: 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_029b: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_02a7: 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_02c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ce: Unknown result type (might be due to invalid IL or missing references)
		//IL_02d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0300: Unknown result type (might be due to invalid IL or missing references)
		//IL_0305: Unknown result type (might be due to invalid IL or missing references)
		//IL_030c: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0203: Unknown result type (might be due to invalid IL or missing references)
		//IL_0218: Unknown result type (might be due to invalid IL or missing references)
		//IL_021d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0220: Unknown result type (might be due to invalid IL or missing references)
		//IL_0221: Unknown result type (might be due to invalid IL or missing references)
		//IL_0223: Unknown result type (might be due to invalid IL or missing references)
		//IL_0228: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if ((Object)(object)murdererCameraObject == (Object)null)
			{
				return;
			}
			murderController = MurderController.Instance;
			if ((Object)(object)murderController == (Object)null || (Object)(object)murderController.currentMurderer == (Object)null)
			{
				return;
			}
			Transform transform = ((Component)murderController.currentMurderer).transform;
			if ((Object)(object)transform == (Object)null)
			{
				return;
			}
			if ((Object)(object)murderController.currentMurderer == (Object)null)
			{
				SwitchToPlayerCamera();
				Lib.GameMessage.ShowPlayerSpeech("No current murderer available", 2f, true);
				return;
			}
			Vector3 val = transform.position + new Vector3(0f, 1.7f, 0f);
			Quaternion rotation = transform.rotation;
			Quaternion val2 = rotation * Quaternion.Euler(cameraRotationX, cameraYOffset, 0f);
			float num = Quaternion.Angle(lastSignificantTargetRotation, rotation);
			bool flag = cameraRotationX != prevCameraRotationX || cameraYOffset != prevCameraYOffset;
			if (num > rotationThresholdDegrees || flag)
			{
				targetCameraRotation = val2;
				lastSignificantTargetRotation = rotation;
			}
			Vector3 val3 = val2 * Vector3.forward * -1f;
			float num2 = defaultCameraDistance;
			if (!isTransitioning)
			{
				bool flag2 = false;
				float distance = defaultCameraDistance;
				RaycastHit val4 = default(RaycastHit);
				if (Physics.Raycast(val, val3, ref val4, defaultCameraDistance))
				{
					flag2 = true;
					distance = ((RaycastHit)(ref val4)).distance;
				}
				Vector3[] array = (Vector3[])(object)new Vector3[4]
				{
					new Vector3(0.2f, 0f, 0f),
					new Vector3(-0.2f, 0f, 0f),
					new Vector3(0f, 0.2f, 0f),
					new Vector3(0f, -0.2f, 0f)
				};
				Vector3[] array2 = array;
				foreach (Vector3 val5 in array2)
				{
					if (Physics.Raycast(val + val5, val3, ref val4, defaultCameraDistance))
					{
						flag2 = true;
						if (((RaycastHit)(ref val4)).distance < distance)
						{
							distance = ((RaycastHit)(ref val4)).distance;
						}
					}
				}
				if (flag2)
				{
					num2 = Mathf.Max(distance * 0.9f, minCameraDistance);
				}
			}
			else
			{
				num2 = defaultCameraDistance;
			}
			Vector3 val6 = val + val3 * num2;
			Vector3 zero = Vector3.zero;
			float num3 = 0.12f;
			murdererCameraObject.transform.position = Vector3.SmoothDamp(murdererCameraObject.transform.position, val6, ref zero, num3);
			float num4 = rotationSmoothFactor * Time.deltaTime;
			((Component)murdererCamera).transform.rotation = Quaternion.Slerp(((Component)murdererCamera).transform.rotation, targetCameraRotation, num4);
			Human component = ((Component)murderController.currentMurderer).GetComponent<Human>();
			if ((Object)(object)component != (Object)null && (Object)(object)((Actor)component).currentRoom != (Object)null)
			{
				SpectatorRoomTracker.UpdateTargetRoom(((Actor)component).currentRoom);
			}
			prevCameraRotationX = cameraRotationX;
			prevCameraYOffset = cameraYOffset;
		}
		catch (Exception ex)
		{
			KillerCam.Logger.LogError((object)("Error updating murderer camera: " + ex.Message));
		}
	}

	private static void UpdateVictimCamera()
	{
		//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00de: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0131: Unknown result type (might be due to invalid IL or missing references)
		//IL_0132: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: 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_0146: Unknown result type (might be due to invalid IL or missing references)
		//IL_0124: Unknown result type (might be due to invalid IL or missing references)
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_012a: Unknown result type (might be due to invalid IL or missing references)
		//IL_012b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0173: Unknown result type (might be due to invalid IL or missing references)
		//IL_0178: Unknown result type (might be due to invalid IL or missing references)
		//IL_018e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0193: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01df: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0277: Unknown result type (might be due to invalid IL or missing references)
		//IL_0278: Unknown result type (might be due to invalid IL or missing references)
		//IL_027c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0281: Unknown result type (might be due to invalid IL or missing references)
		//IL_0286: Unknown result type (might be due to invalid IL or missing references)
		//IL_0288: Unknown result type (might be due to invalid IL or missing references)
		//IL_028d: Unknown result type (might be due to invalid IL or missing references)
		//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_02af: Unknown result type (might be due to invalid IL or missing references)
		//IL_02b5: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_02e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fe: Unknown result type (might be due to invalid IL or missing references)
		//IL_0201: Unknown result type (might be due to invalid IL or missing references)
		//IL_0202: Unknown result type (might be due to invalid IL or missing references)
		//IL_0204: Unknown result type (might be due to invalid IL or missing references)
		//IL_0209: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if ((Object)(object)victimCamera == (Object)null || !((Behaviour)victimCamera).enabled || (Object)(object)murderController == (Object)null || (Object)(object)murderController.currentVictim == (Object)null)
			{
				return;
			}
			Transform transform = ((Component)murderController.currentVictim).transform;
			if ((Object)(object)transform == (Object)null)
			{
				return;
			}
			if ((Object)(object)murderController.currentVictim == (Object)null)
			{
				SwitchToPlayerCamera();
				Lib.GameMessage.ShowPlayerSpeech("No current victim available", 2f, true);
				return;
			}
			Vector3 val = transform.position + new Vector3(0f, 1.7f, 0f);
			Quaternion rotation = transform.rotation;
			Quaternion val2 = rotation * Quaternion.Euler(cameraRotationX, cameraYOffset, 0f);
			float num = Quaternion.Angle(lastSignificantTargetRotation, rotation);
			bool flag = cameraRotationX != prevCameraRotationX || cameraYOffset != prevCameraYOffset;
			if (num > rotationThresholdDegrees || flag)
			{
				targetCameraRotation = val2;
				lastSignificantTargetRotation = rotation;
			}
			Vector3 val3 = val2 * Vector3.forward * -1f;
			float num2 = defaultCameraDistance;
			if (!isTransitioning)
			{
				bool flag2 = false;
				float num3 = float.MaxValue;
				Vector3[] array = (Vector3[])(object)new Vector3[5]
				{
					Vector3.zero,
					new Vector3(0.2f, 0f, 0f),
					new Vector3(-0.2f, 0f, 0f),
					new Vector3(0f, 0.2f, 0f),
					new Vector3(0f, -0.2f, 0f)
				};
				Vector3[] array2 = array;
				RaycastHit val5 = default(RaycastHit);
				foreach (Vector3 val4 in array2)
				{
					if (Physics.Raycast(val + val4, val3, ref val5, defaultCameraDistance))
					{
						flag2 = true;
						if (((RaycastHit)(ref val5)).distance < num3)
						{
							num3 = ((RaycastHit)(ref val5)).distance;
						}
					}
				}
				if (flag2)
				{
					num2 = Mathf.Max(num3 * 0.9f, minCameraDistance);
				}
			}
			else
			{
				num2 = defaultCameraDistance;
			}
			Vector3 val6 = val + val3 * num2;
			Vector3 zero = Vector3.zero;
			float num4 = 0.12f;
			((Component)victimCamera).transform.position = Vector3.SmoothDamp(((Component)victimCamera).transform.position, val6, ref zero, num4);
			float num5 = rotationSmoothFactor * Time.deltaTime;
			((Component)victimCamera).transform.rotation = Quaternion.Slerp(((Component)victimCamera).transform.rotation, targetCameraRotation, num5);
			Human component = ((Component)murderController.currentVictim).GetComponent<Human>();
			if ((Object)(object)component != (Object)null && (Object)(object)((Actor)component).currentRoom != (Object)null)
			{
				SpectatorRoomTracker.UpdateTargetRoom(((Actor)component).currentRoom);
			}
			prevCameraRotationX = cameraRotationX;
			prevCameraYOffset = cameraYOffset;
		}
		catch (Exception ex)
		{
			KillerCam.Logger.LogError((object)("Error updating victim camera: " + ex.Message));
		}
	}

	private static void CreateVictimCamera()
	{
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0023: Expected O, but got Unknown
		if ((Object)(object)victimCameraObject == (Object)null)
		{
			victimCameraObject = new GameObject("VictimCamera");
			victimCamera = victimCameraObject.AddComponent<Camera>();
			if ((Object)(object)playerCamera == (Object)null)
			{
				playerCamera = FindActiveCamera();
			}
			if ((Object)(object)playerCamera != (Object)null)
			{
				victimCamera.CopyFrom(playerCamera);
				victimCamera.depth = playerCamera.depth + 1f;
				victimCamera.useOcclusionCulling = false;
			}
			else
			{
				KillerCam.Logger.LogWarning((object)"Could not find player camera to copy settings for VictimCamera.");
				victimCamera.depth = 1f;
				victimCamera.useOcclusionCulling = false;
			}
			((Behaviour)victimCamera).enabled = false;
			Object.DontDestroyOnLoad((Object)(object)victimCameraObject);
			KillerCam.Logger.LogInfo((object)"Victim camera created.");
		}
	}

	public static Transform GetActiveSpectatorCameraTransform()
	{
		if (isTransitioning && (Object)(object)activeCamera != (Object)null)
		{
			return ((Component)activeCamera).transform;
		}
		if (!isTransitioning)
		{
			switch (currentSpectateTarget)
			{
			case SpectateTarget.Murderer:
			{
				Camera obj2 = murdererCamera;
				return (obj2 != null) ? ((Component)obj2).transform : null;
			}
			case SpectateTarget.Victim:
			{
				Camera obj = victimCamera;
				return (obj != null) ? ((Component)obj).transform : null;
			}
			}
		}
		return null;
	}

	public static void UpdatePlayerNodeForAudio()
	{
		//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0104: Expected O, but got Unknown
		try
		{
			if (!isAudioNodeModified || (Object)(object)Player.Instance == (Object)null || (currentSpectateTarget != SpectateTarget.Murderer && currentSpectateTarget != SpectateTarget.Victim))
			{
				return;
			}
			Human val = null;
			if (currentSpectateTarget == SpectateTarget.Murderer)
			{
				MurderController obj = murderController;
				if ((Object)(object)((obj != null) ? obj.currentMurderer : null) != (Object)null)
				{
					val = ((Component)murderController.currentMurderer).GetComponent<Human>();
					goto IL_00a9;
				}
			}
			if (currentSpectateTarget == SpectateTarget.Victim)
			{
				MurderController obj2 = murderController;
				if ((Object)(object)((obj2 != null) ? obj2.currentVictim : null) != (Object)null)
				{
					val = ((Component)murderController.currentVictim).GetComponent<Human>();
				}
			}
			goto IL_00a9;
			IL_00a9:
			if ((Object)(object)val != (Object)null && ((Actor)val).currentNode != null)
			{
				((Actor)Player.Instance).currentNode = ((Actor)val).currentNode;
			}
		}
		catch (Exception ex)
		{
			if (Time.frameCount % 300 == 0)
			{
				ManualLogSource logger = KillerCam.Logger;
				bool flag = default(bool);
				BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(28, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Error updating player node: ");
					((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
				}
				logger.LogError(val2);
			}
		}
	}
}
public class MurdererInfoProvider
{
	private MurderController murderController;

	private Human murderer = null;

	private FieldInfo currentMurdererField = null;

	public MurdererInfoProvider()
	{
		murderController = MurderController.Instance;
		KillerCam.Logger.LogInfo((object)("MurdererInfoProvider initialized, MurderController: " + (((Object)(object)murderController != (Object)null) ? "Not null" : "Null")));
		try
		{
			currentMurdererField = typeof(MurderController).GetField("currentMurderer", BindingFlags.Instance | BindingFlags.Public);
			KillerCam.Logger.LogInfo((object)("Found currentMurderer field: " + ((currentMurdererField != null) ? "Yes" : "No")));
		}
		catch (Exception ex)
		{
			KillerCam.Logger.LogError((object)("Error getting currentMurderer field: " + ex.Message));
		}
	}

	public Vector3Int GetMurdererLocation()
	{
		//IL_016d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0172: Unknown result type (might be due to invalid IL or missing references)
		//IL_0176: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: 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_00bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			murderController = MurderController.Instance;
			KillerCam.Logger.LogInfo((object)("GetMurdererLocation - MurderController: " + (((Object)(object)murderController != (Object)null) ? "Not null" : "Null")));
			if ((Object)(object)murderController != (Object)null)
			{
				try
				{
					KillerCam.Logger.LogInfo((object)"GetMurdererLocation - Attempting to access currentMurderer");
					Human currentMurderer = murderController.currentMurderer;
					if ((Object)(object)currentMurderer != (Object)null && (Object)(object)((Component)currentMurderer).transform != (Object)null)
					{
						Vector3 position = ((Component)currentMurderer).transform.position;
						ManualLogSource logger = KillerCam.Logger;
						Vector3 val = position;
						logger.LogInfo((object)("GetMurdererLocation - Got position: " + ((object)(Vector3)(ref val)).ToString()));
						return new Vector3Int(Mathf.RoundToInt(position.x), Mathf.RoundToInt(position.y), Mathf.RoundToInt(position.z));
					}
					KillerCam.Logger.LogWarning((object)"GetMurdererLocation - currentMurderer or transform is null");
				}
				catch (Exception ex)
				{
					KillerCam.Logger.LogError((object)("GetMurdererLocation - Error accessing currentMurderer: " + ex.Message));
				}
			}
			else
			{
				KillerCam.Logger.LogWarning((object)"GetMurdererLocation - MurderController is null");
			}
		}
		catch (Exception ex2)
		{
			KillerCam.Logger.LogError((object)("GetMurdererLocation - Unexpected error: " + ex2.Message));
		}
		KillerCam.Logger.LogInfo((object)"GetMurdererLocation - Returning default position (0, 70, 0)");
		return new Vector3Int(0, 70, 0);
	}

	public Human GetMurderer()
	{
		try
		{
			murderController = MurderController.Instance;
			KillerCam.Logger.LogInfo((object)("GetMurderer - MurderController: " + (((Object)(object)murderController != (Object)null) ? "Not null" : "Null")));
			if ((Object)(object)murderController != (Object)null)
			{
				try
				{
					KillerCam.Logger.LogInfo((object)"GetMurderer - Attempting to access currentMurderer");
					murderer = murderController.currentMurderer;
					if ((Object)(object)murderer != (Object)null)
					{
						KillerCam.Logger.LogInfo((object)("GetMurderer - Got murderer: " + ((Object)murderer).name));
						return murderer;
					}
					KillerCam.Logger.LogWarning((object)"GetMurderer - currentMurderer is null");
				}
				catch (Exception ex)
				{
					KillerCam.Logger.LogError((object)("GetMurderer - Error accessing currentMurderer: " + ex.Message));
				}
			}
			else
			{
				KillerCam.Logger.LogWarning((object)"GetMurderer - MurderController is null");
			}
		}
		catch (Exception ex2)
		{
			KillerCam.Logger.LogError((object)("GetMurderer - Unexpected error: " + ex2.Message));
		}
		return null;
	}

	public void SetMurdererLocation(Vector3 loc)
	{
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		((Component)murderController.currentMurderer).transform.position = loc;
	}

	public string GetMurdererFullName()
	{
		if ((Object)(object)murderController != (Object)null)
		{
			string text = murderController.currentMurderer.firstName.ToString();
			string text2 = murderController.currentMurderer.surName.ToString();
			return text + " " + text2;
		}
		return "murderController is null!";
	}

	public void AddPoisoned(float amount, Human who)
	{
		Player instance = Player.Instance;
		murderController.currentMurderer.AddPoisoned(amount, (Human)(object)instance);
	}

	public void KillMurderer()
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_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)
		((Actor)murderController.currentMurderer).RecieveDamage(99999f, (Actor)(object)Player.Instance, Vector2.op_Implicit(Vector2.zero), Vector2.op_Implicit(Vector2.zero), (SpatterPatternPreset)null, (SpatterPatternPreset)null, (EraseMode)3, true, false, 0f, 1f, true, true, 1f);
	}

	public void KOMurderer()
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_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)
		((Actor)murderController.currentMurderer).RecieveDamage(99999f, (Actor)(object)Player.Instance, Vector2.op_Implicit(Vector2.zero), Vector2.op_Implicit(Vector2.zero), (SpatterPatternPreset)null, (SpatterPatternPreset)null, (EraseMode)3, true, false, 0f, 1f, false, true, 1f);
	}

	public string GetJob()
	{
		string result = "Citizen is jobless.";
		if (murderController.currentMurderer.job.employer != null)
		{
			string text = murderController.currentMurderer.job.employer.name.ToString();
			string text2 = murderController.currentMurderer.job.name.ToString();
			string text3 = murderController.currentMurderer.job.salaryString.ToString();
			return "Employer: " + text + Environment.NewLine + "Job: " + text2 + Environment.NewLine + "Salary: " + text3;
		}
		return result;
	}
}
public class NewGameHandler : MonoBehaviour
{
	public static Murder murder;

	public static Toolbox toolbox;

	private GameObject cube;

	public static MurdererInfoProvider murdererInfoProvider;

	public NewGameHandler()
	{
		Lib.SaveGame.OnAfterLoad += HandleGameLoaded;
		Lib.SaveGame.OnAfterNewGame += HandleNewGameStarted;
	}

	private void HandleNewGameStarted(object sender, EventArgs e)
	{
		CamPatch.SwitchToPlayerCamera();
	}

	private void HandleGameLoaded(object sender, EventArgs e)
	{
		CamPatch.SwitchToPlayerCamera();
	}
}
public static class ObjectVisibilityPatch
{
	private static Camera originalCamera;

	private static bool isCameraModified;

	public static void UpdateCameraForSpectating()
	{
		//IL_0154: Unknown result type (might be due to invalid IL or missing references)
		//IL_015b: Expected O, but got Unknown
		try
		{
			if (!CamPatch.isSpectatingMurderer && !CamPatch.isSpectatingVictim)
			{
				if (isCameraModified)
				{
					RestoreOriginalCamera();
				}
				return;
			}
			Transform activeSpectatorCameraTransform = CamPatch.GetActiveSpectatorCameraTransform();
			if ((Object)(object)activeSpectatorCameraTransform == (Object)null)
			{
				KillerCam.Logger.LogWarning((object)"Spectator camera transform is null, cannot update camera for object visibility");
				return;
			}
			if ((Object)(object)CameraController.Instance == (Object)null || (Object)(object)CameraController.Instance.cam == (Object)null)
			{
				KillerCam.Logger.LogWarning((object)"CameraController.Instance or its camera is null");
				return;
			}
			if (!isCameraModified)
			{
				originalCamera = CameraController.Instance.cam;
				KillerCam.Logger.LogInfo((object)"Stored original camera for later restoration");
			}
			Camera val = null;
			if (CamPatch.isSpectatingMurderer && (Object)(object)CamPatch.murdererCamera != (Object)null)
			{
				val = CamPatch.murdererCamera;
			}
			else if (CamPatch.isSpectatingVictim && (Object)(object)CamPatch.victimCamera != (Object)null)
			{
				val = CamPatch.victimCamera;
			}
			if ((Object)(object)val != (Object)null)
			{
				CameraController.Instance.cam = val;
				isCameraModified = true;
			}
			else
			{
				KillerCam.Logger.LogWarning((object)"Could not find active spectator camera");
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger = KillerCam.Logger;
			bool flag = default(bool);
			BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(36, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Error in UpdateCameraForSpectating: ");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
			}
			logger.LogError(val2);
		}
	}

	public static void RestoreOriginalCamera()
	{
		//IL_0050: Unknown result type (might be due to invalid IL or missing references)
		//IL_0056: Expected O, but got Unknown
		try
		{
			if (isCameraModified && !((Object)(object)originalCamera == (Object)null) && !((Object)(object)CameraController.Instance == (Object)null))
			{
				CameraController.Instance.cam = originalCamera;
				isCameraModified = false;
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger = KillerCam.Logger;
			bool flag = default(bool);
			BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(32, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Error in RestoreOriginalCamera: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
			}
			logger.LogError(val);
		}
	}
}
[HarmonyPatch(typeof(CamPatch), "ToggleSpectateCamera")]
public class ToggleSpectateCameraPatch
{
	[HarmonyPostfix]
	public static void Postfix()
	{
		ObjectVisibilityPatch.UpdateCameraForSpectating();
	}
}
[HarmonyPatch(typeof(Player), "Update")]
public class PlayerUpdatePatch
{
	[HarmonyPostfix]
	public static void Postfix()
	{
		ObjectVisibilityPatch.UpdateCameraForSpectating();
		CamPatch.UpdatePlayerNodeForAudio();
	}
}
[BepInPlugin("KillerCam", "KillerCam", "1.0.0")]
public class KillerCam : BasePlugin
{
	public static ManualLogSource Logger;

	private Harmony harmony;

	public static ConfigEntry<bool> hideTargetName;

	public override void Load()
	{
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Expected O, but got Unknown
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Expected O, but got Unknown
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: Expected O, but got Unknown
		Logger = ((BasePlugin)this).Log;
		NewGameHandler newGameHandler = new NewGameHandler();
		Logger.LogInfo((object)"Loading Killer Cam...");
		hideTargetName = ((BasePlugin)this).Config.Bind<bool>("General", "HideTargetName", false, new ConfigDescription("Hide the Murderer/Victim name while spectating.", (AcceptableValueBase)null, Array.Empty<object>()));
		try
		{
			harmony = new Harmony("KillerCam");
			harmony.PatchAll();
			Logger.LogInfo((object)"All patches applied successfully.");
		}
		catch (Exception ex)
		{
			ManualLogSource logger = Logger;
			bool flag = default(bool);
			BepInExErrorLogInterpolatedStringHandler val = new BepInExErrorLogInterpolatedStringHandler(19, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Error during Load: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<Exception>(ex);
			}
			logger.LogError(val);
		}
	}
}
[HarmonyPatch(typeof(SessionData))]
[HarmonyPatch("PauseGame")]
public class PauseManager
{
	public static void Prefix(ref bool showPauseText, ref bool delayOverride, ref bool openDesktopMode)
	{
		CamPatch.SwitchToPlayerCamera();
		SpectatorUI.HideText();
	}
}
[HarmonyPatch(typeof(SessionData))]
[HarmonyPatch("ResumeGame")]
public class ResumeGameManager
{
	public static void Prefix()
	{
	}
}
public static class SpectatorRoomTracker
{
	private static NewRoom playerRoom;

	public static NewRoom TargetRoom { get; private set; }

	public static bool IsActive { get; private set; }

	public static void StartSpectating(NewRoom target)
	{
		//IL_004f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0055: Expected O, but got Unknown
		if ((Object)(object)target == (Object)null)
		{
			KillerCam.Logger.LogError((object)"StartSpectating called with null target room!");
			return;
		}
		if (!IsActive)
		{
			SavePlayerRoom();
		}
		TargetRoom = target;
		IsActive = true;
		ManualLogSource logger = KillerCam.Logger;
		bool flag = default(bool);
		BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(46, 1, ref flag);
		if (flag)
		{
			((BepInExLogInterpolatedStringHandler)val).AppendLiteral("SpectatorRoomTracker: Started spectating room ");
			NewRoom targetRoom = TargetRoom;
			((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>((targetRoom != null) ? targetRoom.name : null);
		}
		logger.LogInfo(val);
		if ((Object)(object)GeometryCullingController.Instance != (Object)null)
		{
			UpdateCullingForTarget(GeometryCullingController.Instance, TargetRoom, immediateLoad: true);
		}
	}

	public static void StopSpectating()
	{
		if (IsActive)
		{
			IsActive = false;
			TargetRoom = null;
			KillerCam.Logger.LogInfo((object)"SpectatorRoomTracker: Stopped spectating.");
			RestorePlayerRoom();
		}
	}

	public static void UpdateTargetRoom(NewRoom newTargetRoom)
	{
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Expected O, but got Unknown
		//IL_00b7: 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_00d4: Expected O, but got Unknown
		if (!IsActive || (Object)(object)newTargetRoom == (Object)null || (Object)(object)newTargetRoom == (Object)(object)TargetRoom)
		{
			return;
		}
		TargetRoom = newTargetRoom;
		ManualLogSource logger = KillerCam.Logger;
		bool flag = default(bool);
		BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(45, 1, ref flag);
		if (flag)
		{
			((BepInExLogInterpolatedStringHandler)val).AppendLiteral("SpectatorRoomTracker: Updated target room to ");
			BepInExInfoLogInterpolatedStringHandler obj = val;
			NewRoom targetRoom = TargetRoom;
			((BepInExLogInterpolatedStringHandler)obj).AppendFormatted<string>((targetRoom != null) ? targetRoom.name : null);
		}
		logger.LogInfo(val);
		if ((Object)(object)AudioController.Instance != (Object)null && (Object)(object)AudioController.Instance.playerListener != (Object)null && (Object)(object)TargetRoom != (Object)null)
		{
			((Component)AudioController.Instance.playerListener).transform.position = TargetRoom.middleRoomPosition;
			ManualLogSource logger2 = KillerCam.Logger;
			val = new BepInExInfoLogInterpolatedStringHandler(64, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Moved playerListener transform to ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(TargetRoom.name);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" center for audio calculation.");
			}
			logger2.LogInfo(val);
		}
		if ((Object)(object)GeometryCullingController.Instance != (Object)null)
		{
			UpdateCullingForTarget(GeometryCullingController.Instance, TargetRoom, immediateLoad: true);
		}
	}

	private static void SavePlayerRoom()
	{
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0095: Expected O, but got Unknown
		//IL_002c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0032: Expected O, but got Unknown
		bool flag = default(bool);
		try
		{
			if ((Object)(object)Player.Instance != (Object)null)
			{
				playerRoom = ((Actor)Player.Instance).currentRoom;
				ManualLogSource logger = KillerCam.Logger;
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(41, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("SpectatorRoomTracker: Saved player room: ");
					NewRoom obj = playerRoom;
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>((obj != null) ? obj.name : null);
				}
				logger.LogInfo(val);
			}
			else
			{
				playerRoom = null;
				KillerCam.Logger.LogWarning((object)"SpectatorRoomTracker: Player instance not found, cannot save room.");
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger2 = KillerCam.Logger;
			BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(48, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("SpectatorRoomTracker: Error saving player room: ");
				((BepInExLogInterpolatedStringHandler)val2).AppendFormatted<string>(ex.Message);
			}
			logger2.LogError(val2);
			playerRoom = null;
		}
	}

	private static void RestorePlayerRoom()
	{
		//IL_0165: Unknown result type (might be due to invalid IL or missing references)
		//IL_016c: Expected O, but got Unknown
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Expected O, but got Unknown
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00db: Expected O, but got Unknown
		bool flag = default(bool);
		try
		{
			if ((Object)(object)GeometryCullingController.Instance != (Object)null && (Object)(object)playerRoom != (Object)null)
			{
				ManualLogSource logger = KillerCam.Logger;
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(74, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("SpectatorRoomTracker: Requesting game to restore culling for player room: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(playerRoom.name);
				}
				logger.LogInfo(val);
				bool flag2 = (Object)(object)Player.Instance != (Object)null && ((Actor)Player.Instance).inAirVent;
				GeometryCullingController.Instance.UpdateCullingForRoom(playerRoom, true, flag2, (AirDuctGroup)null, true);
			}
			else if ((Object)(object)GeometryCullingController.Instance != (Object)null)
			{
				Player instance = Player.Instance;
				NewRoom val2 = ((instance != null) ? ((Actor)instance).currentRoom : null);
				if ((Object)(object)val2 != (Object)null)
				{
					ManualLogSource logger2 = KillerCam.Logger;
					BepInExWarningLogInterpolatedStringHandler val3 = new BepInExWarningLogInterpolatedStringHandler(59, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("SpectatorRoomTracker: No saved player room, using current: ");
						((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(val2.name);
					}
					logger2.LogWarning(val3);
					bool inAirVent = ((Actor)Player.Instance).inAirVent;
					GeometryCullingController.Instance.UpdateCullingForRoom(val2, true, inAirVent, (AirDuctGroup)null, true);
				}
				else
				{
					KillerCam.Logger.LogError((object)"SpectatorRoomTracker: Cannot restore player room - no saved room and player has no current room.");
				}
			}
			else
			{
				KillerCam.Logger.LogError((object)"SpectatorRoomTracker: Cannot restore player room - GeometryCullingController instance not found.");
			}
			playerRoom = null;
		}
		catch (Exception ex)
		{
			ManualLogSource logger3 = KillerCam.Logger;
			BepInExErrorLogInterpolatedStringHandler val4 = new BepInExErrorLogInterpolatedStringHandler(51, 1, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("SpectatorRoomTracker: Error restoring player room: ");
				((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(ex.Message);
			}
			logger3.LogError(val4);
			playerRoom = null;
		}
	}

	public static void UpdateCullingForTarget(GeometryCullingController controller, NewRoom targetRoom, bool immediateLoad)
	{
		//IL_0df0: Unknown result type (might be due to invalid IL or missing references)
		//IL_0df7: Expected O, but got Unknown
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Expected O, but got Unknown
		//IL_0d74: Unknown result type (might be due to invalid IL or missing references)
		//IL_0d7a: Expected O, but got Unknown
		//IL_04d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_04da: Expected O, but got Unknown
		//IL_036d: Unknown result type (might be due to invalid IL or missing references)
		//IL_052e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0535: Expected O, but got Unknown
		//IL_0588: Unknown result type (might be due to invalid IL or missing references)
		//IL_058f: Expected O, but got Unknown
		//IL_0a74: Unknown result type (might be due to invalid IL or missing references)
		//IL_0a7a: Expected O, but got Unknown
		//IL_05fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0603: Expected O, but got Unknown
		//IL_0b21: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b44: Unknown result type (might be due to invalid IL or missing references)
		//IL_0b50: Unknown result type (might be due to invalid IL or missing references)
		//IL_0778: Unknown result type (might be due to invalid IL or missing references)
		//IL_077f: Expected O, but got Unknown
		if ((Object)(object)controller == (Object)null)
		{
			KillerCam.Logger.LogError((object)"UpdateCullingForTarget: GeometryCullingController instance is null!");
			return;
		}
		if ((Object)(object)targetRoom == (Object)null)
		{
			KillerCam.Logger.LogError((object)"UpdateCullingForTarget: targetRoom is null!");
			return;
		}
		ManualLogSource logger = KillerCam.Logger;
		bool flag = default(bool);
		BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(56, 1, ref flag);
		if (flag)
		{
			((BepInExLogInterpolatedStringHandler)val).AppendLiteral("SpectatorRoomTracker: Updating culling for target room: ");
			((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(targetRoom.name);
		}
		logger.LogInfo(val);
		try
		{
			controller.currentRoomsCullingTree.Clear();
			controller.currentRoomsCullingWithImmediateStuffLoad.Clear();
			controller.currentDuctsCullingTree.Clear();
			if (!SessionData.Instance.isFloorEdit && !SessionData.Instance.isTestScene)
			{
				if ((Object)(object)MurderController.Instance != (Object)null && MurderController.Instance.activeMurders != null)
				{
					Enumerator<Murder> enumerator = MurderController.Instance.activeMurders.GetEnumerator();
					while (enumerator.MoveNext())
					{
						Murder current = enumerator.Current;
						if (((current != null) ? current.cullingActiveRooms : null) == null)
						{
							continue;
						}
						Enumerator<int> enumerator2 = current.cullingActiveRooms.GetEnumerator();
						while (enumerator2.MoveNext())
						{
							int current2 = enumerator2.Current;
							NewRoom val2 = null;
							if (CityData.Instance.roomDictionary.TryGetValue(current2, ref val2) && (Object)(object)val2 != (Object)null)
							{
								controller.currentRoomsCullingTree.Add(val2);
								controller.currentRoomsCullingWithImmediateStuffLoad.Add(val2);
							}
						}
					}
				}
				else
				{
					KillerCam.Logger.LogWarning((object)"MurderController instance or activeMurders is null.");
				}
				if ((Object)(object)GameplayController.Instance != (Object)null && GameplayController.Instance.activeRagdolls != null)
				{
					Enumerator<Human> enumerator3 = GameplayController.Instance.activeRagdolls.GetEnumerator();
					while (enumerator3.MoveNext())
					{
						Human current3 = enumerator3.Current;
						if (!((Object)(object)((current3 != null) ? ((Actor)current3).currentRoom : null) != (Object)null))
						{
							continue;
						}
						if (!controller.currentRoomsCullingTree.Contains(((Actor)current3).currentRoom))
						{
							controller.currentRoomsCullingTree.Add(((Actor)current3).currentRoom);
							controller.currentRoomsCullingWithImmediateStuffLoad.Add(((Actor)current3).currentRoom);
						}
						if (((Actor)current3).currentRoom.adjacentRooms == null)
						{
							continue;
						}
						Enumerator<NewRoom> enumerator4 = ((Actor)current3).currentRoom.adjacentRooms.GetEnumerator();
						while (enumerator4.MoveNext())
						{
							NewRoom current4 = enumerator4.Current;
							if ((Object)(object)current4 != (Object)null && !controller.currentRoomsCullingTree.Contains(current4))
							{
								controller.currentRoomsCullingTree.Add(current4);
								controller.currentRoomsCullingWithImmediateStuffLoad.Add(current4);
							}
						}
					}
				}
				else
				{
					KillerCam.Logger.LogWarning((object)"GameplayController instance or activeRagdolls is null.");
				}
				if ((Object)(object)GameplayController.Instance != (Object)null && GameplayController.Instance.activePhysics != null)
				{
					Enumerator<Interactable> enumerator5 = GameplayController.Instance.activePhysics.GetEnumerator();
					while (enumerator5.MoveNext())
					{
						Interactable current5 = enumerator5.Current;
						if (current5 == null)
						{
							continue;
						}
						if (current5.node == null)
						{
							current5.UpdateWorldPositionAndNode(false, false);
							if (current5.node == null)
							{
								continue;
							}
						}
						NewNode node = current5.node;
						if (!((Object)(object)((node != null) ? node.room : null) != (Object)null))
						{
							continue;
						}
						if (!controller.currentRoomsCullingTree.Contains(current5.node.room))
						{
							controller.currentRoomsCullingTree.Add(current5.node.room);
							controller.currentRoomsCullingWithImmediateStuffLoad.Add(current5.node.room);
						}
						if (current5.node.room.adjacentRooms == null)
						{
							continue;
						}
						Enumerator<NewRoom> enumerator6 = current5.node.room.adjacentRooms.GetEnumerator();
						while (enumerator6.MoveNext())
						{
							NewRoom current6 = enumerator6.Current;
							if ((Object)(object)current6 != (Object)null && !controller.currentRoomsCullingTree.Contains(current6))
							{
								controller.currentRoomsCullingTree.Add(current6);
								controller.currentRoomsCullingWithImmediateStuffLoad.Add(current6);
							}
						}
					}
				}
				else
				{
					KillerCam.Logger.LogWarning((object)"GameplayController instance or activePhysics is null.");
				}
				if (Game.Instance.enableNewRealtimeTimeCullingSystem)
				{
					controller.GenerateDynamicCulling(targetRoom, 0);
					ManualLogSource logger2 = KillerCam.Logger;
					val = new BepInExInfoLogInterpolatedStringHandler(41, 1, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Using game's dynamic culling for target: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(targetRoom.name);
					}
					logger2.LogInfo(val);
				}
				else
				{
					if (!targetRoom.completedTreeCull && !targetRoom.loadedCullTreeFromSave)
					{
						ManualLogSource logger3 = KillerCam.Logger;
						BepInExWarningLogInterpolatedStringHandler val3 = new BepInExWarningLogInterpolatedStringHandler(79, 1, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("Target room ");
							((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(targetRoom.name);
							((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" culling tree not generated/loaded. Visibility might be incomplete.");
						}
						logger3.LogWarning(val3);
					}
					if (targetRoom.cullingTree == null)
					{
						ManualLogSource logger4 = KillerCam.Logger;
						BepInExErrorLogInterpolatedStringHandler val4 = new BepInExErrorLogInterpolatedStringHandler(37, 1, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("Target room ");
							((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(targetRoom.name);
							((BepInExLogInterpolatedStringHandler)val4).AppendLiteral(" has a null culling tree!");
						}
						logger4.LogError(val4);
					}
					else if (targetRoom.cullingTree.Count <= 0 && !SessionData.Instance.isTestScene)
					{
						ManualLogSource logger5 = KillerCam.Logger;
						BepInExWarningLogInterpolatedStringHandler val3 = new BepInExWarningLogInterpolatedStringHandler(40, 1, ref flag);
						if (flag)
						{
							((BepInExLogInterpolatedStringHandler)val3).AppendLiteral("Target room ");
							((BepInExLogInterpolatedStringHandler)val3).AppendFormatted<string>(targetRoom.name);
							((BepInExLogInterpolatedStringHandler)val3).AppendLiteral(" culling tree count is zero!");
						}
						logger5.LogWarning(val3);
					}
					else if (targetRoom.cullingTree != null)
					{
						Enumerator<NewRoom, List<CullTreeEntry>> enumerator7 = targetRoom.cullingTree.GetEnumerator();
						while (enumerator7.MoveNext())
						{
							KeyValuePair<NewRoom, List<CullTreeEntry>> current7 = enumerator7.Current;
							if ((Object)(object)current7.Key != (Object)null && !controller.currentRoomsCullingTree.Contains(current7.Key))
							{
								if (current7.Value == null)
								{
									continue;
								}
								Enumerator<CullTreeEntry> enumerator8 = current7.Value.GetEnumerator();
								while (enumerator8.MoveNext())
								{
									CullTreeEntry current8 = enumerator8.Current;
									bool flag2 = false;
									if (current8.requiredOpenDoors == null || current8.requiredOpenDoors.Count <= 0)
									{
										flag2 = true;
									}
									else
									{
										bool flag3 = true;
										Enumerator<int> enumerator9 = current8.requiredOpenDoors.GetEnumerator();
										while (enumerator9.MoveNext())
										{
											int current9 = enumerator9.Current;
											NewDoor val5 = null;
											if (CityData.Instance.doorDictionary.TryGetValue(current9, ref val5))
											{
												if (val5.isClosed && !val5.peekedUnder)
												{
													flag3 = false;
													break;
												}
												continue;
											}
											ManualLogSource logger6 = KillerCam.Logger;
											BepInExErrorLogInterpolatedStringHandler val4 = new BepInExErrorLogInterpolatedStringHandler(56, 3, ref flag);
											if (flag)
											{
												((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("Cannot find door ");
												((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<int>(current9);
												((BepInExLogInterpolatedStringHandler)val4).AppendLiteral(" required by culling tree of ");
												((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(targetRoom.name);
												((BepInExLogInterpolatedStringHandler)val4).AppendLiteral(" for room ");
												((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(current7.Key.name);
											}
											logger6.LogError(val4);
										}
										if (flag3)
										{
											flag2 = true;
										}
									}
									if (!flag2)
									{
										continue;
									}
									controller.currentRoomsCullingTree.Add(current7.Key);
									if ((Object)(object)current7.Key.atriumTop != (Object)null && !controller.currentRoomsCullingTree.Contains(current7.Key.atriumTop))
									{
										controller.currentRoomsCullingTree.Add(current7.Key.atriumTop);
									}
									if (current7.Key.atriumRooms != null)
									{
										Enumerator<NewRoom> enumerator10 = current7.Key.atriumRooms.GetEnumerator();
										while (enumerator10.MoveNext())
										{
											NewRoom current10 = enumerator10.Current;
											if ((Object)(object)current10 != (Object)null && !controller.currentRoomsCullingTree.Contains(current10))
											{
												controller.currentRoomsCullingTree.Add(current10);
											}
										}
									}
									NewGameLocation gameLocation = current7.Key.gameLocation;
									if (!((Object)(object)((gameLocation != null) ? gameLocation.thisAsStreet : null) != (Object)null) || current7.Key.gameLocation.thisAsStreet.sharedGroundElements == null)
									{
										break;
									}
									Enumerator<StreetController> enumerator11 = current7.Key.gameLocation.thisAsStreet.sharedGroundElements.GetEnumerator();
									while (enumerator11.MoveNext())
									{
										StreetController current11 = enumerator11.Current;
										if (((current11 != null) ? ((NewGameLocation)current11).rooms : null) == null)
										{
											continue;
										}
										Enumerator<NewRoom> enumerator12 = ((NewGameLocation)current11).rooms.GetEnumerator();
										while (enumerator12.MoveNext())
										{
											NewRoom current12 = enumerator12.Current;
											if ((Object)(object)current12 != (Object)null && !controller.currentRoomsCullingTree.Contains(current12))
											{
												controller.currentRoomsCullingTree.Add(current12);
											}
										}
									}
									break;
								}
							}
							NewRoom key = current7.Key;
							if (((key != null) ? key.ductGroups : null) == null)
							{
								continue;
							}
							Enumerator<AirDuctGroup> enumerator13 = current7.Key.ductGroups.GetEnumerator();
							while (enumerator13.MoveNext())
							{
								AirDuctGroup current13 = enumerator13.Current;
								if ((Object)(object)current13 != (Object)null && !controller.currentDuctsCullingTree.Contains(current13))
								{
									controller.currentDuctsCullingTree.Add(current13);
								}
							}
						}
					}
					ManualLogSource logger7 = KillerCam.Logger;
					val = new BepInExInfoLogInterpolatedStringHandler(50, 2, ref flag);
					if (flag)
					{
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Processed culling tree for target: ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(targetRoom.name);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(". Found ");
						((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(controller.currentRoomsCullingTree.Count);
						((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" rooms.");
					}
					logger7.LogInfo(val);
				}
				CityBuildings instance = HighlanderSingleton<CityBuildings>.Instance;
				if (((instance != null) ? instance.buildingDirectory : null) != null)
				{
					Enumerator<NewBuilding> enumerator14 = HighlanderSingleton<CityBuildings>.Instance.buildingDirectory.GetEnumerator();
					while (enumerator14.MoveNext())
					{
						NewBuilding current14 = enumerator14.Current;
						if (!((Object)(object)current14 != (Object)null) || !current14.displayBuildingModel || targetRoom == null)
						{
							continue;
						}
						_ = targetRoom.middleRoomPosition;
						if (!((Object)(object)((Component)current14).transform != (Object)null) || !((Object)(object)CullingControls.Instance != (Object)null) || !(Vector3.Distance(targetRoom.middleRoomPosition, ((Component)current14).transform.position) < CullingControls.Instance.exteriorDuctCullingRange) || current14.airDucts == null)
						{
							continue;
						}
						Enumerator<AirDuctGroup> enumerator15 = current14.airDucts.GetEnumerator();
						while (enumerator15.MoveNext())
						{
							AirDuctGroup current15 = enumerator15.Current;
							if ((Object)(object)current15 != (Object)null && current15.isExterior && !controller.currentDuctsCullingTree.Contains(current15))
							{
								controller.currentDuctsCullingTree.Add(current15);
							}
						}
					}
				}
				else
				{
					KillerCam.Logger.LogWarning((object)"CityBuildings instance or buildingDirectory is null.");
				}
				if ((Object)(object)ObjectPoolingController.Instance != (Object)null && ObjectPoolingController.Instance.allowGradualRoomLoading && !controller.currentRoomsCullingWithImmediateStuffLoad.Contains(targetRoom))
				{
					controller.currentRoomsCullingWithImmediateStuffLoad.Add(targetRoom);
				}
				else if ((Object)(object)ObjectPoolingController.Instance == (Object)null)
				{
					KillerCam.Logger.LogWarning((object)"ObjectPoolingController instance is null.");
				}
			}
			else
			{
				if (SessionData.Instance.isFloorEdit)
				{
					FloorEditController instance2 = FloorEditController.Instance;
					object obj;
					if (instance2 == null)
					{
						obj = null;
					}
					else
					{
						NewFloor editFloor = instance2.editFloor;
						obj = ((editFloor != null) ? editFloor.addresses : null);
					}
					if (obj != null)
					{
						Enumerator<NewAddress> enumerator16 = FloorEditController.Instance.editFloor.addresses.GetEnumerator();
						while (enumerator16.MoveNext())
						{
							NewAddress current16 = enumerator16.Current;
							if (((current16 != null) ? ((NewGameLocation)current16).rooms : null) == null)
							{
								continue;
							}
							Enumerator<NewRoom> enumerator17 = ((NewGameLocation)current16).rooms.GetEnumerator();
							while (enumerator17.MoveNext())
							{
								NewRoom current17 = enumerator17.Current;
								if ((Object)(object)current17 != (Object)null)
								{
									controller.currentRoomsCullingTree.Add(current17);
								}
							}
						}
						KillerCam.Logger.LogInfo((object)"Using FloorEdit culling logic.");
						goto IL_0d68;
					}
				}
				if (SessionData.Instance.isTestScene)
				{
					KillerCam.Logger.LogInfo((object)"TestScene: Culling might be simplified/disabled.");
				}
			}
			goto IL_0d68;
			IL_0d68:
			ManualLogSource logger8 = KillerCam.Logger;
			val = new BepInExInfoLogInterpolatedStringHandler(40, 2, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Executing culling tree. Rooms: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(controller.currentRoomsCullingTree.Count);
				((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", Ducts: ");
				((BepInExLogInterpolatedStringHandler)val).AppendFormatted<int>(controller.currentDuctsCullingTree.Count);
			}
			logger8.LogInfo(val);
			controller.ExecuteCurrentCullingTree(immediateLoad);
			KillerCam.Logger.LogInfo((object)"Finished executing culling tree for target.");
		}
		catch (Exception ex)
		{
			ManualLogSource logger9 = KillerCam.Logger;
			BepInExErrorLogInterpolatedStringHandler val4 = new BepInExErrorLogInterpolatedStringHandler(44, 3, ref flag);
			if (flag)
			{
				((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("Error in UpdateCullingForTarget for room ");
				((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>((targetRoom != null) ? targetRoom.name : null);
				((BepInExLogInterpolatedStringHandler)val4).AppendLiteral(": ");
				((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(ex.Message);
				((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("\n");
				((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(ex.StackTrace);
			}
			logger9.LogError(val4);
		}
	}
}
[HarmonyPatch(typeof(GeometryCullingController), "UpdateCullingForRoom")]
public class GeometryCullingPatch
{
	[HarmonyPrefix]
	public static bool Prefix(GeometryCullingController __instance, ref NewRoom currentRoom, bool updateSound, bool inAirVent, AirDuctGroup currentDuct, bool immediateLoad)
	{
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Expected O, but got Unknown
		//IL_002a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Expected O, but got Unknown
		bool flag = default(bool);
		if (SpectatorRoomTracker.IsActive && (Object)(object)SpectatorRoomTracker.TargetRoom != (Object)null)
		{
			try
			{
				ManualLogSource logger = KillerCam.Logger;
				BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(68, 1, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val).AppendLiteral("GeometryCullingPatch: Intercepting UpdateCullingForRoom for target: ");
					((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(SpectatorRoomTracker.TargetRoom.name);
				}
				logger.LogInfo(val);
				SpectatorRoomTracker.UpdateCullingForTarget(__instance, SpectatorRoomTracker.TargetRoom, immediateLoad);
				return false;
			}
			catch (Exception ex)
			{
				ManualLogSource logger2 = KillerCam.Logger;
				BepInExErrorLogInterpolatedStringHandler val2 = new BepInExErrorLogInterpolatedStringHandler(57, 2, ref flag);
				if (flag)
				{
					((BepInExLogInterpolatedStringHandler)val2).AppendLiter