Decompiled source of Open Cierzo v1.3.0

plugins/OpenCierzo/OpenCierzoPrototype.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using BepInEx;
using HarmonyLib;
using NodeCanvas.BehaviourTrees;
using NodeCanvas.DialogueTrees;
using NodeCanvas.Framework;
using NodeCanvas.Tasks.Actions;
using Photon;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using uNature.Core.FoliageClasses;
using uNature.Core.Utility;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: AssemblyVersion("1.2.0.0")]
namespace OpenCierzo;

[BepInPlugin("Outwardz.OpenCierzoPrototype", "Open Cierzo", "1.2.0")]
public sealed class Plugin : BaseUnityPlugin
{
	public const string Id = "Outwardz.OpenCierzoPrototype";

	internal const string City = "CierzoNewTerrain";

	internal const string Region = "ChersoneseNewTerrain";

	internal static Plugin Instance;

	internal static volatile bool Armed;

	internal static volatile bool Loading;

	private Vector3 regionOffset;

	private Vector3 seam;

	private Vector3 outward;

	private HingedGate physicalGate;

	private CoopSession coop;

	private readonly Dictionary<int, GatePressLatch> localGatePresses = new Dictionary<int, GatePressLatch>();

	private LoadingNotice loadingNotice;

	private bool starting;

	private ImportedBoundaryScope importedBoundary;

	private WorldResources resources = new WorldResources();

	private WorldFixtures fixtures = new WorldFixtures();

	private WorldActors actors = new WorldActors();

	private OutdoorNavigation navigation = new OutdoorNavigation();

	private TownNavigation townNavigation = new TownNavigation();

	private CityDefense cityDefense = new CityDefense();

	private DroppedItemRecovery droppedItems = new DroppedItemRecovery();

	private OutdoorDetails outdoorDetails = new OutdoorDetails();

	private ResourceEffects resourceEffects = new ResourceEffects();

	private OutdoorEnvironment outdoorEnvironment = new OutdoorEnvironment();

	private RegionalEnvironment regionalEnvironment = new RegionalEnvironment();

	private OutdoorTerrain outdoorTerrain = new OutdoorTerrain();

	private WorldCoverage coverage = new WorldCoverage();

	private RegionalMapMarkers mapMarkers = new RegionalMapMarkers();

	private SceneNetworkViews networkViews = new SceneNetworkViews();

	private GatePressLatch gatePress = new GatePressLatch();

	private bool travelPending;

	private bool hooksReady;

	private bool menuAfterItemRecovery;

	private readonly SessionExit sessionExit = new SessionExit();

	private Harmony patches;

	private bool ready;

	private bool failed;

	private bool outside;

	private Scene cityScene;

	private Scene regionScene;

	private Character player;

	private Vector3 cityGate;

	private Vector3 regionGate;

	private Vector3 cityArrival;

	private Vector3 regionArrival;

	private Quaternion cityRotation;

	private Quaternion regionRotation;

	private float started;

	private float nextAutoStartCheck;

	private float presentationStarted;

	private string presentationPhase;

	private string status = "Waiting for Cierzo.";

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

	private readonly HashSet<CharacterAI> waitingAgentBindings = new HashSet<CharacterAI>();

	private readonly Dictionary<Camera, bool> originalOcclusion = new Dictionary<Camera, bool>();

	private readonly VerifiedBuild verifiedBuild = new VerifiedBuild();

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

	private readonly Dictionary<string, KeyValuePair<Vector3, Quaternion>> travelPoses = new Dictionary<string, KeyValuePair<Vector3, Quaternion>>();

	private bool bringLocalsOutside;

	private bool unloading => sessionExit.Unloading;

	internal bool GateIsOpen
	{
		get
		{
			if (physicalGate != null)
			{
				return physicalGate.IsOpen;
			}
			return false;
		}
	}

	internal static bool SessionEligible
	{
		get
		{
			if (Object.op_Implicit((Object)(object)SplitScreenManager.Instance))
			{
				return SplitScreenManager.Instance.LocalPlayerCount <= 2;
			}
			return true;
		}
	}

	internal static bool PersistenceEligible => CoopSession.Authority;

	internal string GateText
	{
		get
		{
			if (!GatePermission.Allowed)
			{
				return "Gate Locked";
			}
			return physicalGate.Prompt;
		}
	}

	internal bool GateFocus => GateFocusFor(player);

	internal static bool HasMultipleLocalPlayers
	{
		get
		{
			if (Object.op_Implicit((Object)(object)SplitScreenManager.Instance))
			{
				return SplitScreenManager.Instance.LocalPlayerCount > 1;
			}
			return false;
		}
	}

	internal Dictionary<string, int> NetworkMap => networkViews.Export();

	internal IEnumerable<string> ResourceIDs => resources.IDs;

	internal IEnumerable<string> FixtureIDs => fixtures.IDs;

	internal IEnumerable<string> LooseIDs => resources.LooseIDs;

	internal IEnumerable<string> ActorIDs => actors.IDs.Concat(cityDefense.IDs);

	internal bool ActorsReady
	{
		get
		{
			if (actors.Ready)
			{
				return cityDefense.Ready;
			}
			return false;
		}
	}

	internal bool WorldReady
	{
		get
		{
			if (ready && !failed)
			{
				return !unloading;
			}
			return false;
		}
	}

	internal bool ItemRecoveryActive
	{
		get
		{
			if (WorldReady && !travelPending && !sessionExit.MenuPending && !Loading && CoopSession.Authority && CoopSession.Supported && Object.op_Implicit((Object)(object)NetworkLevelLoader.Instance) && NetworkLevelLoader.Instance.IsOverallLoadingDone && !NetworkLevelLoader.Instance.IsGameplayPaused && !NetworkLevelLoader.Instance.IsSaveInProgress && SaveManager.Instance != null && !SaveManager.Instance.SaveInProgress && Object.op_Implicit((Object)(object)player) && player.Alive && actors.Ready)
			{
				return cityDefense.Ready;
			}
			return false;
		}
	}

	internal bool ItemsSafeToSave => droppedItems.SavingSafe;

	internal bool CanTravel
	{
		get
		{
			if (ready && !failed && !Loading && !unloading && !travelPending)
			{
				return WorldPersistence.AllowSave();
			}
			return false;
		}
	}

	internal bool SuppressRegionUnload
	{
		get
		{
			if (!Armed)
			{
				return unloading;
			}
			return true;
		}
	}

	internal bool UseOutdoorMap
	{
		get
		{
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			if (Armed && ready && !failed && Object.op_Implicit((Object)(object)player))
			{
				return WorldPersistence.IsOutside(((Component)player).transform.position);
			}
			return false;
		}
	}

	internal Vector3 RegionTranslation => regionOffset;

	private void Awake()
	{
		//IL_0017: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Expected O, but got Unknown
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_0030: Expected O, but got Unknown
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		//IL_003f: Expected O, but got Unknown
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Expected O, but got Unknown
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: Expected O, but got Unknown
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Expected O, but got Unknown
		Instance = this;
		coop = new CoopSession(this);
		try
		{
			WorldPersistence.Install(new Harmony("Outwardz.OpenCierzoPrototype.persistence"));
			LoadingPresentation.Install(new Harmony("Outwardz.OpenCierzoPrototype.loading"));
			ActorEvents.Install(new Harmony("Outwardz.OpenCierzoPrototype.actors"));
			CoopItemSync.Install(new Harmony("Outwardz.OpenCierzoPrototype.coopitems"));
			CoopInboundSync.Install(new Harmony("Outwardz.OpenCierzoPrototype.coopreceive"));
			Guards.InstallSessionHooks(new Harmony("Outwardz.OpenCierzoPrototype.session"));
			hooksReady = true;
		}
		catch (Exception error)
		{
			WorldPersistence.Stop("Save hooks could not be installed. Close the game before loading a character.", error);
		}
		((BaseUnityPlugin)this).Logger.LogInfo((object)"Open Cierzo 1.2.0 loaded. Experimental online and split-screen co-op; same mod version required on every peer.");
	}

	internal void SetStatus(string text)
	{
		if (!(status == text))
		{
			status = text;
			((BaseUnityPlugin)this).Logger.LogInfo((object)text);
		}
	}

	internal void LogEvent(string text)
	{
		((BaseUnityPlugin)this).Logger.LogInfo((object)text);
	}

	private void Update()
	{
		//IL_018f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0194: Unknown result type (might be due to invalid IL or missing references)
		//IL_0195: Unknown result type (might be due to invalid IL or missing references)
		//IL_0197: Unknown result type (might be due to invalid IL or missing references)
		//IL_019c: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_02ec: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			coop.Tick();
			if (sessionExit.MenuPending || unloading)
			{
				return;
			}
			if (!Armed && !failed && !starting && Time.realtimeSinceStartup >= nextAutoStartCheck)
			{
				nextAutoStartCheck = Time.realtimeSinceStartup + (LoadingPresentation.Holding ? 0.05f : 0.5f);
				Begin();
			}
			if (!Armed)
			{
				return;
			}
			if (Loading && Time.realtimeSinceStartup - started > 180f && !failed)
			{
				Fail("Preload exceeded three minutes. Restart the game; do not keep waiting indefinitely.", null);
			}
			if (!failed && !unloading && !travelPending)
			{
				outdoorTerrain.Tick();
			}
			if (!ready || failed || !Object.op_Implicit((Object)(object)player) || travelPending)
			{
				cityDefense.ResetRecovery();
				return;
			}
			if (menuAfterItemRecovery)
			{
				droppedItems.FinishPending(SetStatus, Time.realtimeSinceStartup);
				if (droppedItems.SavingSafe)
				{
					menuAfterItemRecovery = false;
					NetworkLevelLoader.Instance.GoMainMenu();
				}
				return;
			}
			if (ItemRecoveryActive)
			{
				droppedItems.Tick(SetStatus);
			}
			if (!CoopSession.Supported)
			{
				Fail("Open Cierzo requires one or two local players.", null);
				return;
			}
			Vector3 position = ((Component)player).transform.position;
			float num = Vector3.Dot(position - seam, outward);
			outside = num > 0f;
			if (outside)
			{
				RegionReset.Visit();
			}
			regionalEnvironment.Update(num);
			if (importedBoundary != null)
			{
				importedBoundary.Update(PlayerGateDistances(num).Min());
			}
			if (physicalGate != null)
			{
				physicalGate.Update(from c in CharacterManager.Instance.Characters.Values
					where Object.op_Implicit((Object)(object)c) && (Object)(object)c.OwnerPlayerSys != (Object)null && c.Alive
					select ((Component)c).transform.position);
			}
			navigation.Update(GateIsOpen);
			townNavigation.Update(GateIsOpen);
			if (CoopSession.Authority && Object.op_Implicit((Object)(object)NetworkLevelLoader.Instance) && !NetworkLevelLoader.Instance.IsGameplayPaused)
			{
				cityDefense.Tick();
			}
			else
			{
				cityDefense.ResetRecovery();
				if (!CoopSession.Authority)
				{
					cityDefense.RemoteTick();
				}
			}
			Character val = (CoopSession.Authority ? CharacterManager.Instance.GetWorldHostCharacter() : player);
			ActorEvents.Update(Object.op_Implicit((Object)(object)val) && WorldPersistence.IsOutside(((Component)val).transform.position));
			CampingNotice.Tick();
			actors.Tick(PlayerGateDistances(num).Any((float d) => d > 0f), SetStatus);
			foreach (SplitPlayer localPlayer in SplitScreenManager.Instance.LocalPlayers)
			{
				Character assignedCharacter = localPlayer.AssignedCharacter;
				if (!Object.op_Implicit((Object)(object)assignedCharacter) || (Object)(object)assignedCharacter.OwnerPlayerSys == (Object)null)
				{
					continue;
				}
				int playerID = assignedCharacter.OwnerPlayerSys.PlayerID;
				if (!localGatePresses.TryGetValue(playerID, out var value))
				{
					value = new GatePressLatch();
					localGatePresses.Add(playerID, value);
				}
				if (value.Sample(ControlsInput.InteractHold(playerID), Time.frameCount, Time.unscaledTime) && GateFocusFor(assignedCharacter) && !ControlsInput.QuickSlotToggled(playerID))
				{
					if (GatePermission.Allowed)
					{
						coop.Toggle(assignedCharacter);
					}
					else
					{
						assignedCharacter.CharacterUI.ShowInfoNotification("Speak to Burac Carillon and complete his preparation check before opening the gate.");
					}
				}
			}
		}
		catch (Exception e)
		{
			Fail("Open Cierzo stopped after an error. Restart the game.", e);
		}
	}

	private void Begin()
	{
		//IL_000d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if (!LoadingPresentation.NativeReady)
			{
				return;
			}
			cityScene = SceneManager.GetActiveScene();
			if (((Scene)(ref cityScene)).name != "CierzoNewTerrain" || !Object.op_Implicit((Object)(object)SplitScreenManager.Instance) || SaveManager.Instance == null || !Object.op_Implicit((Object)(object)CharacterManager.Instance))
			{
				return;
			}
			player = CharacterManager.Instance.GetFirstLocalCharacter();
			if (!Object.op_Implicit((Object)(object)player) || (Object)(object)player.OwnerPlayerSys == (Object)null || !Object.op_Implicit((Object)(object)player.CharacterUI) || !CoopSession.Supported)
			{
				return;
			}
			if (!coop.SnapshotReady)
			{
				SetStatus("Waiting for the host to send the Open Cierzo world. Both players need version 1.2.0.");
				return;
			}
			if (SaveManager.Instance.SaveInProgress || NetworkLevelLoader.Instance.IsSaveInProgress)
			{
				SetStatus("Waiting for the current save to finish before loading Open Cierzo.");
				return;
			}
			Scene sceneByName = SceneManager.GetSceneByName("ChersoneseNewTerrain");
			if (((Scene)(ref sceneByName)).isLoaded)
			{
				LoadingPresentation.Cancel();
				SetStatus("Chersonese is already loaded unexpectedly. Restart the game before continuing.");
				return;
			}
			if (!ActorEvents.CanInitialize(player))
			{
				LoadingPresentation.Cancel();
				SetStatus("Open Cierzo is waiting for a character without an active summon.");
				return;
			}
			if (player.IsDead)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Native defeat recovery pending: rebuilding event containers before the game's resurrection callbacks.");
			}
			starting = true;
			presentationStarted = Time.realtimeSinceStartup;
			presentationPhase = null;
			LoadingPresentation.Ensure((WorldPersistence.ResumingOutside || NativeTravel.ResumingOutside) ? "ChersoneseNewTerrain" : "CierzoNewTerrain");
			loadingNotice = new LoadingNotice(player.CharacterUI, ((Component)this).transform);
			((MonoBehaviour)this).StartCoroutine(StartAfterNotice());
		}
		catch (Exception e)
		{
			Fail("Open Cierzo could not start. Restart before retrying.", e);
		}
	}

	private IEnumerator StartAfterNotice()
	{
		yield return null;
		yield return (object)new WaitForEndOfFrame();
		if (failed)
		{
			yield break;
		}
		if (LoadingPresentation.NativeReady)
		{
			Scene activeScene = SceneManager.GetActiveScene();
			if (!(((Scene)(ref activeScene)).name != "CierzoNewTerrain") && SaveManager.Instance != null && !SaveManager.Instance.SaveInProgress && !NetworkLevelLoader.Instance.IsSaveInProgress && CoopSession.Supported && ActorEvents.CanInitialize(player))
			{
				StartPreload();
				yield break;
			}
		}
		HideLoadingNotice();
		LoadingPresentation.Cancel();
		starting = false;
	}

	private void StartPreload()
	{
		//IL_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dc: 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_00f2: 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_00fd: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		//IL_010c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0111: Unknown result type (might be due to invalid IL or missing references)
		//IL_016e: 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_012e: Expected O, but got Unknown
		try
		{
			Notice("Preparing the world...", 0.08f);
			coop.PrepareGuest();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Game build verification: " + verifiedBuild.Verify(Application.dataPath, KnownBuild.Files) + " file hashes; unchanged verified files reused within this session."));
			StaticBatchRelocator.ValidateAccessor();
			Transform val = Find(cityScene, "Environment/AreaSwitchChersonese");
			cityRotation = Quaternion.LookRotation(Flat(val.forward));
			outward = -Flat(Find(cityScene, "Environment/Assets/Buildings/Deck/Walls/mdl_env_cierzoWallAssets/mdl_env_buildingWoodWallGateA").forward);
			seam = Find(cityScene, "Environment/Assets/Buildings/Deck/Walls/mdl_env_cierzoWallAssets/mdl_env_buildingWoodWallGateA").position;
			cityGate = val.position + Flat(val.forward) * 1.5f;
			cityArrival = Find(cityScene, "Environment/Spawn1").position + Flat(val.forward) * 5f;
			if (patches == null)
			{
				patches = new Harmony("Outwardz.OpenCierzoPrototype");
				Guards.Install(patches);
				DroppedItemRecovery.Install(patches);
			}
			Armed = true;
			Loading = true;
			started = Time.realtimeSinceStartup;
			WorldPersistence.Waiting = true;
			regionalEnvironment.CaptureHost(cityScene);
			SetStatus("Preloading Chersonese. A pause or hitch is expected. Saving resumes after the world is ready.");
			navigation.BeforeLoad();
			townNavigation.BeforeLoad();
			AsyncOperation val2 = SceneManager.LoadSceneAsync("ChersoneseNewTerrain", (LoadSceneMode)1);
			if (val2 == null)
			{
				throw new InvalidOperationException("Unity returned no scene-loading operation.");
			}
			((MonoBehaviour)this).StartCoroutine(FinishPreload(val2));
		}
		catch (Exception e)
		{
			if (!Armed && patches != null)
			{
				patches.UnpatchSelf();
			}
			Fail(Armed ? "Preload failed. Restart the game; saving remains blocked." : "Test could not start. No area was loaded. Restart before retrying.", e);
		}
	}

	private IEnumerator FinishPreload(AsyncOperation op)
	{
		while (!op.isDone)
		{
			Notice("Loading landscape...", 0.12f + 0.48f * Mathf.Clamp01(op.progress / 0.9f));
			yield return null;
		}
		if (failed)
		{
			yield break;
		}
		try
		{
			if (!((Scene)(ref regionScene)).IsValid() || !((Scene)(ref regionScene)).isLoaded)
			{
				throw new InvalidOperationException("The donor scene did not pass the scene-loaded guard.");
			}
			Notice("Preparing paths, resources and actors...", 0.64f);
			Physics.SyncTransforms();
			cityArrival = Ground(cityArrival, cityScene);
			regionArrival = Ground(regionArrival, regionScene);
			Camera[] allCameras = Camera.allCameras;
			foreach (Camera val in allCameras)
			{
				if (!originalOcclusion.ContainsKey(val))
				{
					originalOcclusion.Add(val, val.useOcclusionCulling);
				}
				val.useOcclusionCulling = false;
			}
			DisableCityExit("Environment/AreaSwitchChersonese");
			DisableCityExit("Environment/AreaSwitchChersonese (NoCondition)");
			player.ClearMostRelevantInteractableIndex();
			physicalGate = WorldJoin.Build(cityScene, regionScene, seam, outward, Quarantine, SetStatus);
			importedBoundary = new ImportedBoundaryScope(((Component)Find(regionScene, "Environment/MapBounds/Cube (41)")).GetComponent<BoxCollider>(), delegate(string s)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)s);
			});
			importedBoundary.Update(Vector3.Dot(((Component)player).transform.position - seam, outward));
			Loading = false;
			WorldPersistence.PrepareInteractionStates();
			navigation.Activate(regionOffset, seam, outward, SetStatus);
			townNavigation.Begin(cityScene, regionScene, seam, outward, navigation.JoinedApproach, SetStatus);
			navigation.JoinedApproach = default(NavMeshTriangulation);
			cityDefense.Prepare(cityScene, actors, townNavigation, networkViews, seam, outward, WorldPersistence.SavedCharacters, SetStatus);
			resources.Activate(cityScene, seam, outward, ReleaseResourceScript, SetStatus, WorldPersistence.SavedResourceIDs, WorldPersistence.SavedLooseIDs);
			fixtures.Activate(cityScene, seam, outward, regionOffset, ReleaseResourceScript, SetStatus, WorldPersistence.SavedFixtureIDs, WorldPersistence.SavedSoulSpots);
			ActorEvents.Prepare(actors, WorldPersistence.SavedEventStates);
			actors.Activate(cityScene, ReleaseResourceScript, WorldPersistence.SavedCharacters, SetStatus, WorldPersistence.SavedSoulSpots, WorldPersistence.SavedItems);
			CoopInboundSync.Release();
			WorldPersistence.DiscardRepeatedEquipment(actors.DiscardedEquipment);
			OutdoorFoliage.Join(regionScene, seam, outward, SetStatus);
			coverage.RememberOwnership(CoverageOwner);
		}
		catch (Exception ex)
		{
			Fail("Gate setup stopped: " + (string.IsNullOrEmpty(ex.Message) ? ex.GetType().Name : ex.Message) + ". Close the game; saving remains blocked.", ex);
		}
		if (failed)
		{
			yield break;
		}
		yield return null;
		yield return null;
		float resourceStarted = Time.realtimeSinceStartup;
		Notice("Preparing interactions...", 0.76f);
		bool resourcesReady = false;
		while (!resourcesReady && !failed)
		{
			try
			{
				resources.Validate(WorldPersistence.SavedResourceIDs);
				resourcesReady = resources.Initialized();
				if (!resourcesReady && Time.realtimeSinceStartup - resourceStarted > 30f)
				{
					Fail("Outdoor resource initialization timed out: " + resources.WaitingFor + ". Close the game; saving remains blocked.", null);
				}
			}
			catch (Exception e)
			{
				Fail("Outdoor resource initialization failed. Close the game; saving remains blocked.", e);
			}
			if (!resourcesReady)
			{
				yield return null;
			}
		}
		if (failed)
		{
			yield break;
		}
		try
		{
			WorldPersistence.BeginRestore(player, physicalGate);
		}
		catch (Exception e2)
		{
			Fail("Outdoor resource setup failed. Close the game; saving remains blocked.", e2);
		}
		if (failed)
		{
			yield break;
		}
		float restoreStarted = Time.realtimeSinceStartup;
		Notice("Restoring your world...", 0.84f);
		bool restored = false;
		while (!restored && !failed)
		{
			try
			{
				restored = WorldPersistence.PollRestore(player);
				if (!restored && Time.realtimeSinceStartup - restoreStarted > 60f)
				{
					Fail("Saved world objects did not finish loading (" + WorldPersistence.RestoreWaitReason + "). Close the game; the previous save has not been overwritten.", null);
				}
			}
			catch (Exception e3)
			{
				Fail("World restore failed. Close the game; saving remains blocked.", e3);
			}
			if (!restored)
			{
				yield return null;
			}
		}
		if (failed)
		{
			yield break;
		}
		float townPathsStarted = Time.realtimeSinceStartup;
		bool townPathsReady = false;
		while (!townPathsReady && !failed)
		{
			try
			{
				townPathsReady = townNavigation.Poll();
				if (!townPathsReady && Time.realtimeSinceStartup - townPathsStarted > 90f)
				{
					Fail("Town enemy paths did not finish loading; saving remains blocked.", null);
				}
			}
			catch (Exception e4)
			{
				Fail("Town enemy navigation setup failed; saving remains blocked.", e4);
			}
			if (!townPathsReady)
			{
				Notice("Preparing town paths...", 0.91f);
				yield return null;
			}
		}
		if (failed)
		{
			yield break;
		}
		Notice("Finishing the environment...", 0.93f);
		try
		{
			networkViews.ValidateHost();
			outdoorDetails.Activate(cityScene, seam, outward, ReleaseResourceScript, SetStatus);
			resourceEffects.Activate(resources.Contains, SetStatus);
			outdoorEnvironment.Activate(cityScene, seam, outward, ReleaseResourceScript, SetStatus);
			regionalEnvironment.Activate(cityScene, Vector3.Dot(((Component)player).transform.position - seam, outward), ReleaseResourceScript, SetStatus);
			outdoorTerrain.Activate(ReleaseResourceScript, SetStatus);
			mapMarkers.Activate(ReleaseResourceScript, SetStatus);
		}
		catch (Exception e5)
		{
			Fail("Outdoor details setup failed. Close the game; saving remains blocked.", e5);
		}
		if (failed)
		{
			yield break;
		}
		bool? nativeDefeatOutside = null;
		try
		{
			nativeDefeatOutside = NativeTravel.RestoreArrival(player, regionScene);
			RestoreLocalTravel();
			fixtures.BeginQuestChecks();
			actors.Complete(SetStatus);
			cityDefense.Complete();
			networkViews.ValidateRuntime();
			navigation.ValidateSpawns(actors.SpawnPositions, SetStatus);
			navigation.Update(GateIsOpen);
			townNavigation.Update(GateIsOpen);
			regionalEnvironment.Update(Vector3.Dot(((Component)player).transform.position - seam, outward));
			if (nativeDefeatOutside != false && WorldPersistence.IsOutside(((Component)player).transform.position))
			{
				RegionReset.Visit();
			}
		}
		catch (Exception e6)
		{
			Fail("World arrival or quest initialization failed; saving remains blocked.", e6);
		}
		if (failed)
		{
			yield break;
		}
		importedBoundary.Update(Vector3.Dot(((Component)player).transform.position - seam, outward));
		Physics.SyncTransforms();
		try
		{
			coverage.Report(Path.Combine(Paths.BepInExRootPath, "OpenCierzo-world-coverage.tsv"), CoverageOwner, delegate(string s)
			{
				((BaseUnityPlugin)this).Logger.LogInfo((object)s);
			}, delegate(string s)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)s);
			});
		}
		catch (Exception ex2)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("World coverage report could not be written: " + ex2.Message));
		}
		Notice("Ready", 1f);
		yield return null;
		yield return (object)new WaitForEndOfFrame();
		if (!failed)
		{
			ready = true;
			try
			{
				ActorEvents.Complete(nativeDefeatOutside ?? WorldPersistence.IsOutside(((Component)player).transform.position));
			}
			catch (Exception e7)
			{
				Fail("Regional actor event startup failed; saving remains blocked.", e7);
				yield break;
			}
			HideLoadingNotice();
			LoadingPresentation.Complete();
			try
			{
				actors.Tick(WorldPersistence.IsOutside(((Component)player).transform.position), SetStatus);
			}
			catch (Exception e8)
			{
				Fail("Outdoor encounter startup failed; saving remains blocked.", e8);
				yield break;
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Concat("Joined-world setup completed in ", (Time.realtimeSinceStartup - presentationStarted).ToString("F2"), " seconds; restored position=", ((Component)player).transform.position, "."));
			if (CoopSession.Authority)
			{
				SaveManager.Instance.SetSaveRequired();
			}
			SetStatus("Open Cierzo ready. Saving and native area travel enabled.");
		}
	}

	internal void PrepareRegion(Scene scene)
	{
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		//IL_0034: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00de: Unknown result type (might be due to invalid IL or missing references)
		//IL_03b6: Unknown result type (might be due to invalid IL or missing references)
		//IL_03c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_03c6: Unknown result type (might be due to invalid IL or missing references)
		//IL_03d3: Unknown result type (might be due to invalid IL or missing references)
		//IL_03d9: Unknown result type (might be due to invalid IL or missing references)
		//IL_03f3: Unknown result type (might be due to invalid IL or missing references)
		//IL_0410: Unknown result type (might be due to invalid IL or missing references)
		//IL_041b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0420: Unknown result type (might be due to invalid IL or missing references)
		//IL_0425: Unknown result type (might be due to invalid IL or missing references)
		//IL_0429: Unknown result type (might be due to invalid IL or missing references)
		//IL_0435: Unknown result type (might be due to invalid IL or missing references)
		//IL_0445: Unknown result type (might be due to invalid IL or missing references)
		//IL_044c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0451: Unknown result type (might be due to invalid IL or missing references)
		//IL_045b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0460: Unknown result type (might be due to invalid IL or missing references)
		//IL_0465: Unknown result type (might be due to invalid IL or missing references)
		//IL_046b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0476: Unknown result type (might be due to invalid IL or missing references)
		//IL_047b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0483: Unknown result type (might be due to invalid IL or missing references)
		//IL_0488: Unknown result type (might be due to invalid IL or missing references)
		//IL_048d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0492: Unknown result type (might be due to invalid IL or missing references)
		//IL_0501: Unknown result type (might be due to invalid IL or missing references)
		//IL_051b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0535: Unknown result type (might be due to invalid IL or missing references)
		if (failed)
		{
			GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects();
			foreach (GameObject val in rootGameObjects)
			{
				val.SetActive(false);
			}
			return;
		}
		try
		{
			regionScene = scene;
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			GameObject[] rootGameObjects2 = ((Scene)(ref scene)).GetRootGameObjects();
			coverage.Capture(scene);
			networkViews.Prepare(cityScene, scene, SetStatus);
			resources.Capture(scene);
			actors.Capture(scene);
			NativeTravel.Capture(scene);
			fixtures.Capture(scene);
			mapMarkers.Capture(scene);
			outdoorDetails.Capture(scene);
			resourceEffects.Capture(scene);
			outdoorEnvironment.Capture(scene);
			regionalEnvironment.Capture(scene);
			outdoorTerrain.Capture(scene);
			GameObject[] array = rootGameObjects2;
			foreach (GameObject val2 in array)
			{
				MonoBehaviour[] componentsInChildren = val2.GetComponentsInChildren<MonoBehaviour>(true);
				foreach (MonoBehaviour val3 in componentsInChildren)
				{
					if (Object.op_Implicit((Object)(object)val3))
					{
						blockedInstances.Add(((Object)val3).GetInstanceID());
						((Behaviour)val3).enabled = false;
						num++;
					}
				}
				Camera[] componentsInChildren2 = val2.GetComponentsInChildren<Camera>(true);
				foreach (Camera val4 in componentsInChildren2)
				{
					((Behaviour)val4).enabled = false;
				}
				AudioListener[] componentsInChildren3 = val2.GetComponentsInChildren<AudioListener>(true);
				foreach (AudioListener val5 in componentsInChildren3)
				{
					((Behaviour)val5).enabled = false;
				}
				AudioSource[] componentsInChildren4 = val2.GetComponentsInChildren<AudioSource>(true);
				foreach (AudioSource val6 in componentsInChildren4)
				{
					val6.Stop();
					((Behaviour)val6).enabled = false;
				}
				Light[] componentsInChildren5 = val2.GetComponentsInChildren<Light>(true);
				foreach (Light val7 in componentsInChildren5)
				{
					((Behaviour)val7).enabled = false;
				}
				Animator[] componentsInChildren6 = val2.GetComponentsInChildren<Animator>(true);
				foreach (Animator val8 in componentsInChildren6)
				{
					((Behaviour)val8).enabled = false;
				}
				Animation[] componentsInChildren7 = val2.GetComponentsInChildren<Animation>(true);
				foreach (Animation val9 in componentsInChildren7)
				{
					((Behaviour)val9).enabled = false;
				}
				NavMeshAgent[] componentsInChildren8 = val2.GetComponentsInChildren<NavMeshAgent>(true);
				foreach (NavMeshAgent val10 in componentsInChildren8)
				{
					((Behaviour)val10).enabled = false;
				}
				ParticleSystem[] componentsInChildren9 = val2.GetComponentsInChildren<ParticleSystem>(true);
				foreach (ParticleSystem val11 in componentsInChildren9)
				{
					val11.Stop(true, (ParticleSystemStopBehavior)0);
				}
				Rigidbody[] componentsInChildren10 = val2.GetComponentsInChildren<Rigidbody>(true);
				foreach (Rigidbody val12 in componentsInChildren10)
				{
					val12.isKinematic = true;
					val12.detectCollisions = false;
				}
				Character[] componentsInChildren11 = val2.GetComponentsInChildren<Character>(true);
				foreach (Character val13 in componentsInChildren11)
				{
					((Component)val13).gameObject.SetActive(false);
				}
				Collider[] componentsInChildren12 = val2.GetComponentsInChildren<Collider>(true);
				foreach (Collider val14 in componentsInChildren12)
				{
					if (val14.isTrigger)
					{
						val14.enabled = false;
					}
					else if (val14.enabled)
					{
						num2++;
					}
				}
				Renderer[] componentsInChildren13 = val2.GetComponentsInChildren<Renderer>(true);
				foreach (Renderer val15 in componentsInChildren13)
				{
					if (val15.enabled)
					{
						num3++;
					}
				}
				if (((Object)val2).name != "Environment")
				{
					val2.SetActive(false);
				}
			}
			Physics.SyncTransforms();
			Transform val16 = Find(scene, "Interactions/NormalCierzo/mdl_env_buildingWoodWallGateA (2)");
			float num13 = GroundJoin.AtGate(scene, val16.position);
			float num14 = GroundJoin.AtGate(cityScene, seam);
			regionOffset = new Vector3(seam.x - val16.position.x, num14 - num13, seam.z - val16.position.z);
			int num15 = StaticBatchRelocator.Relocate(scene, rootGameObjects2, regionOffset);
			Transform val17 = Find(scene, "Environment/AreaSwitches/0_AreaSwitchCierzo");
			regionGate = val17.position + Flat(val17.forward) * 2.5f;
			regionArrival = Find(scene, "Environment/Spawn1").position;
			regionRotation = Quaternion.LookRotation(Flat(val17.forward));
			((BaseUnityPlugin)this).Logger.LogInfo((object)string.Concat("Donor scenery prepared: ", num, " disabled scripts; ", num2, " solid colliders; ", num3, " renderers; ", num15, " batched renderers relocated. Join offset=", regionOffset, "; region gate=", regionGate, "; arrival=", regionArrival));
		}
		catch (Exception ex)
		{
			GameObject[] rootGameObjects3 = ((Scene)(ref scene)).GetRootGameObjects();
			foreach (GameObject val18 in rootGameObjects3)
			{
				val18.SetActive(false);
			}
			Fail("Chersonese setup stopped: " + ex.Message + " Close the game before retrying.", ex);
		}
	}

	private void DisableCityExit(string path)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		Transform val = Find(cityScene, path);
		InteractionTriggerBase[] componentsInChildren = ((Component)val).GetComponentsInChildren<InteractionTriggerBase>(true);
		foreach (InteractionTriggerBase val2 in componentsInChildren)
		{
			removedTravelTriggers.Add(((Object)val2).GetInstanceID());
			val2.CanBeTriggered = false;
			if (Object.op_Implicit((Object)(object)val2.InteractionCollider))
			{
				val2.InteractionCollider.enabled = false;
			}
			((Behaviour)val2).enabled = false;
		}
		((Component)val).gameObject.SetActive(false);
	}

	internal bool IsRemovedTravel(InteractionTriggerBase trigger)
	{
		if (Armed && Object.op_Implicit((Object)(object)trigger))
		{
			return removedTravelTriggers.Contains(((Object)trigger).GetInstanceID());
		}
		return false;
	}

	internal bool IsTestPlayer(Character character)
	{
		if (Object.op_Implicit((Object)(object)character) && Object.op_Implicit((Object)(object)SplitScreenManager.Instance))
		{
			return SplitScreenManager.Instance.LocalPlayers.Any((SplitPlayer p) => (Object)(object)p.AssignedCharacter == (Object)(object)character);
		}
		return false;
	}

	internal bool GateFocusFor(Character user)
	{
		//IL_0097: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_0113: Unknown result type (might be due to invalid IL or missing references)
		//IL_0105: Unknown result type (might be due to invalid IL or missing references)
		//IL_0118: 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_011c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: Unknown result type (might be due to invalid IL or missing references)
		if (!ready || failed || !Object.op_Implicit((Object)(object)user) || physicalGate == null || user.IsDead)
		{
			return false;
		}
		CharacterUI characterUI = user.CharacterUI;
		if (!Object.op_Implicit((Object)(object)characterUI) || characterUI.IsMenuFocused || characterUI.IsDialogueInProgress || characterUI.IsChatPanelFocused || characterUI.IsInputFieldFocused)
		{
			return false;
		}
		InteractionTriggerBase interactable = user.Interactable;
		if (Object.op_Implicit((Object)(object)interactable) && !IsRemovedTravel(interactable))
		{
			return false;
		}
		if (!string.IsNullOrEmpty(user.PressText) || !string.IsNullOrEmpty(user.HoldText))
		{
			return false;
		}
		Vector3 val = physicalGate.Position - ((Component)user).transform.position;
		Vector3 val2 = val;
		val2.y = 0f;
		if (((Vector3)(ref val2)).sqrMagnitude > 100f || Mathf.Abs(val.y) > 5f)
		{
			return false;
		}
		if (((Vector3)(ref val2)).sqrMagnitude <= 16f)
		{
			return true;
		}
		Camera componentInChildren = ((Component)user).GetComponentInChildren<Camera>();
		Vector3 v = (Object.op_Implicit((Object)(object)componentInChildren) ? ((Component)componentInChildren).transform.forward : ((Component)user).transform.forward);
		return Vector3.Dot(Flat(v), Flat(val)) > -0.1f;
	}

	internal static Character FindLocalPlayer(string uid)
	{
		if (!Object.op_Implicit((Object)(object)SplitScreenManager.Instance))
		{
			return null;
		}
		return SplitScreenManager.Instance.LocalPlayers.Select((SplitPlayer p) => p.AssignedCharacter).FirstOrDefault((Func<Character, bool>)((Character c) => Object.op_Implicit((Object)(object)c) && UID.op_Implicit(c.UID) == uid));
	}

	internal void RecordLocalTravel(string area, int spawn, bool defeat = false)
	{
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		travelPoses.Clear();
		bringLocalsOutside = area == "ChersoneseNewTerrain" && !defeat;
		if (defeat || !Armed || !(area == "CierzoNewTerrain") || spawn >= 0)
		{
			return;
		}
		foreach (SplitPlayer localPlayer in SplitScreenManager.Instance.LocalPlayers)
		{
			Character assignedCharacter = localPlayer.AssignedCharacter;
			if (Object.op_Implicit((Object)(object)assignedCharacter))
			{
				travelPoses[UID.op_Implicit(assignedCharacter.UID)] = new KeyValuePair<Vector3, Quaternion>(((Component)assignedCharacter).transform.position, ((Component)assignedCharacter).transform.rotation);
			}
		}
	}

	private void RestoreLocalTravel()
	{
		//IL_0039: 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_0056: 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_0089: 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_00a5: 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_00ba: Unknown result type (might be due to invalid IL or missing references)
		int num = 0;
		foreach (SplitPlayer localPlayer in SplitScreenManager.Instance.LocalPlayers)
		{
			Character assignedCharacter = localPlayer.AssignedCharacter;
			if (Object.op_Implicit((Object)(object)assignedCharacter))
			{
				if (travelPoses.TryGetValue(UID.op_Implicit(assignedCharacter.UID), out var value))
				{
					assignedCharacter.Teleport(value.Key, value.Value);
				}
				else if (bringLocalsOutside && (Object)(object)assignedCharacter != (Object)(object)player)
				{
					assignedCharacter.Teleport(Ground(((Component)player).transform.position + Vector3.right * (1.25f * (float)(++num)), regionScene), ((Component)player).transform.rotation);
				}
			}
		}
		travelPoses.Clear();
		bringLocalsOutside = false;
	}

	private IEnumerable<float> PlayerGateDistances(float fallback)
	{
		return (from c in CharacterManager.Instance.Characters.Values
			where Object.op_Implicit((Object)(object)c) && (Object)(object)c.OwnerPlayerSys != (Object)null && c.Alive
			select Vector3.Dot(((Component)c).transform.position - seam, outward)).DefaultIfEmpty(fallback);
	}

	internal bool NearGate(Character user)
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0021: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		if (Object.op_Implicit((Object)(object)user) && physicalGate != null)
		{
			Vector3 val = ((Component)user).transform.position - physicalGate.Position;
			return ((Vector3)(ref val)).sqrMagnitude <= 100f;
		}
		return false;
	}

	internal void ToggleGate()
	{
		if (physicalGate != null)
		{
			physicalGate.Toggle();
		}
	}

	internal void SetGateState(bool value)
	{
		if (physicalGate != null && physicalGate.IsOpen != value)
		{
			physicalGate.SetOpen(value);
		}
	}

	internal bool CoopPeerReady(int id)
	{
		return coop.PeerReady(id);
	}

	internal bool DeferCoopItem(Item item)
	{
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		HashSet<string> hashSet = new HashSet<string>();
		while (Object.op_Implicit((Object)(object)item) && hashSet.Add(item.UID))
		{
			Character ownerCharacter = ((EffectSynchronizer)item).OwnerCharacter;
			if (Object.op_Implicit((Object)(object)ownerCharacter) && (Object)(object)ownerCharacter.OwnerPlayerSys != (Object)null)
			{
				return false;
			}
			if (resources.Contains((Component)(object)item) || actors.Contains((Component)(object)item) || (Object.op_Implicit((Object)(object)ownerCharacter) && (actors.Contains((Component)(object)ownerCharacter) || cityDefense.Owns(ownerCharacter))))
			{
				return true;
			}
			if (item.IsInWorld && WorldPersistence.IsOutside(((Component)item).transform.position))
			{
				return true;
			}
			item = (Item)(object)item.ParentContainer;
		}
		return false;
	}

	internal void SetNetworkMap(Dictionary<string, int> map)
	{
		networkViews.Expect(map);
	}

	private static Vector3 Flat(Vector3 v)
	{
		//IL_000e: Unknown result type (might be due to invalid IL or missing references)
		v.y = 0f;
		return ((Vector3)(ref v)).normalized;
	}

	internal static Transform Find(Scene scene, string path)
	{
		int num = path.IndexOf('/');
		string rootName = ((num < 0) ? path : path.Substring(0, num));
		GameObject val = ((IEnumerable<GameObject>)((Scene)(ref scene)).GetRootGameObjects()).FirstOrDefault((Func<GameObject, bool>)((GameObject g) => ((Object)g).name == rootName));
		Transform val2 = (((Object)(object)val == (Object)null) ? null : ((num < 0) ? val.transform : val.transform.Find(path.Substring(num + 1))));
		if (!Object.op_Implicit((Object)(object)val2))
		{
			throw new InvalidOperationException("Missing scene anchor: " + ((Scene)(ref scene)).name + "/" + path);
		}
		return val2;
	}

	internal static Vector3 Ground(Vector3 intended, Scene scene)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0010: Unknown result type (might be due to invalid IL or missing references)
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ed: 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_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_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_0101: Unknown result type (might be due to invalid IL or missing references)
		RaycastHit[] source = Physics.RaycastAll(intended + Vector3.up * 3f, Vector3.down, 8f, -1, (QueryTriggerInteraction)1);
		foreach (RaycastHit item in source.OrderBy((RaycastHit h) => ((RaycastHit)(ref h)).distance))
		{
			RaycastHit current = item;
			if (Object.op_Implicit((Object)(object)((RaycastHit)(ref current)).collider) && ((Component)((RaycastHit)(ref current)).collider).gameObject.scene == scene && ((RaycastHit)(ref current)).normal.y > 0.6f)
			{
				return ((RaycastHit)(ref current)).point + Vector3.up * 0.2f;
			}
		}
		throw new InvalidOperationException("No walkable ground found at " + ((Scene)(ref scene)).name + " " + intended);
	}

	internal void Quarantine(MonoBehaviour component)
	{
		if (Object.op_Implicit((Object)(object)component))
		{
			blockedInstances.Add(((Object)component).GetInstanceID());
			((Behaviour)component).enabled = false;
		}
	}

	private void ReleaseResourceScript(MonoBehaviour component)
	{
		blockedInstances.Remove(((Object)component).GetInstanceID());
	}

	private string CoverageOwner(Component c)
	{
		//IL_0124: 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_012f: 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 (resources.RetiredSource(c))
		{
			return "saved loose-item source retired; native item save is authoritative";
		}
		if (actors.Contains(c))
		{
			return "native actor, quest or event hierarchy";
		}
		MonoBehaviour val = (MonoBehaviour)(object)((c is MonoBehaviour) ? c : null);
		if (resources.Contains(c) && (!Object.op_Implicit((Object)(object)val) || !IsDonor((Component)(object)val)))
		{
			return "native resource and item helpers";
		}
		if (fixtures.Contains(c) && (!Object.op_Implicit((Object)(object)val) || fixtures.Owns(val)))
		{
			return "native independent fixture";
		}
		if (Object.op_Implicit((Object)(object)val) && (outdoorEnvironment.Owns(val) || regionalEnvironment.Owns(val)))
		{
			return "regional environment adapter";
		}
		if (Object.op_Implicit((Object)(object)val) && mapMarkers.Owns(val))
		{
			return "regional native map markers and UI";
		}
		if (Object.op_Implicit((Object)(object)val) && outdoorTerrain.Discarded(val))
		{
			return "non-rendering imported terrain tile; streaming intentionally disabled";
		}
		if (Object.op_Implicit((Object)(object)val) && outdoorTerrain.Owns(val))
		{
			return "native terrain streaming adapter";
		}
		CentralGatherableAccessPoint val2 = (CentralGatherableAccessPoint)(object)((c is CentralGatherableAccessPoint) ? c : null);
		if (Object.op_Implicit((Object)(object)val2) && Object.op_Implicit((Object)(object)val2.InteractionCollider))
		{
			return "native shared wood interaction";
		}
		if (Vector3.Dot(c.transform.position - seam, outward) <= 5f)
		{
			return "town-side join overlap";
		}
		return null;
	}

	internal bool AllowEnvironmentSoundStart(SoundPlayer component)
	{
		if (outdoorEnvironment.AllowSoundStart(component))
		{
			return regionalEnvironment.AllowStart((MonoBehaviour)(object)component);
		}
		return false;
	}

	internal bool AllowEnvironmentStart(MonoBehaviour component)
	{
		if (regionalEnvironment.AllowStart(component))
		{
			return outdoorEnvironment.AllowStart(component);
		}
		return false;
	}

	internal bool AllowRegionalAmbience(AmbienceSound component)
	{
		return regionalEnvironment.AllowAmbience(component);
	}

	internal bool AllowRegionalAmbiencePlay(AmbienceSound component)
	{
		return regionalEnvironment.AllowAmbiencePlay(component);
	}

	internal bool AllowResourceLootStart(MonoBehaviour component)
	{
		return resources.AllowLootStart(component);
	}

	internal bool ActorsSettled(out string reason)
	{
		if (actors.Settled(out reason))
		{
			return cityDefense.Settled(out reason);
		}
		return false;
	}

	internal bool OwnsCampingSquad(AISquad squad)
	{
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		if (CoopSession.Authority && Armed && WorldReady && !travelPending && !sessionExit.MenuPending && !Loading && actors.Ready && Object.op_Implicit((Object)(object)squad) && actors.Contains((Component)(object)squad) && Object.op_Implicit((Object)(object)player))
		{
			return WorldPersistence.IsOutside(((Component)player).transform.position);
		}
		return false;
	}

	internal bool CampingQueryFilter(Vector3 center, out NavMeshQueryFilter filter)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		return townNavigation.CampingFilter(center, out filter);
	}

	internal void BindCampingActor(CharacterAI ai, NavMeshQueryFilter filter)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		if (!townNavigation.BindCamping(ai, filter))
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)("Native camping actor could not bind at its selected position: " + ((Object.op_Implicit((Object)(object)ai) && Object.op_Implicit((Object)(object)((CharacterControl)ai).Character)) ? UID.op_Implicit(((CharacterControl)ai).Character.UID) : "missing actor") + "."));
		}
	}

	internal void RecordFallenItem(Item item)
	{
		try
		{
			droppedItems.BeforeDestroy(item);
		}
		catch (Exception error)
		{
			WorldPersistence.Stop("Fallen item state could not be preserved; saving remains blocked.", error);
		}
	}

	internal void CancelItemRecovery(string id, bool collateral = false)
	{
		droppedItems.Cancel(id, collateral);
	}

	internal void SelectMapMarkers(bool outsideMap)
	{
		mapMarkers.Select(outsideMap);
	}

	internal bool AllowFixtureLifecycle(MonoBehaviour c, MethodBase method)
	{
		if (mapMarkers.AllowLifecycle(c, method) && actors.AllowLifecycle(c, method) && fixtures.AllowLifecycle(c, method))
		{
			return resources.AllowInteractionStart(c, method);
		}
		return false;
	}

	internal bool AllowActorMessage(Component c, string name)
	{
		return actors.AllowMessage(c, name);
	}

	internal void RouteTownActor(CharacterAI ai, bool destinationChanged)
	{
		if (CoopSession.Authority && WorldReady && !travelPending && !sessionExit.MenuPending && !Loading && Object.op_Implicit((Object)(object)NetworkLevelLoader.Instance) && !NetworkLevelLoader.Instance.IsGameplayPaused && ((actors.Contains((Component)(object)ai) && Object.op_Implicit((Object)(object)((CharacterControl)ai).Character) && ((CharacterControl)ai).Character.IsAI) || cityDefense.Owns(ai)))
		{
			townNavigation.Route(ai, destinationChanged);
		}
	}

	internal bool TownQueryFilter(CharacterAI ai, out NavMeshQueryFilter filter)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		filter = default(NavMeshQueryFilter);
		if (WorldReady && !travelPending && !sessionExit.MenuPending && !Loading && ((actors.Contains((Component)(object)ai) && Object.op_Implicit((Object)(object)((CharacterControl)ai).Character) && ((CharacterControl)ai).Character.IsAI) || cityDefense.Owns(ai)))
		{
			return townNavigation.Filter(ai, out filter);
		}
		return false;
	}

	internal bool TownRecovering(CharacterAI ai)
	{
		if (TownQueryFilter(ai, out var _))
		{
			return townNavigation.Recovering(ai);
		}
		return false;
	}

	internal bool EnableOwnedAgent(CharacterAI ai, NavMeshAgent agent)
	{
		//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_0117: Unknown result type (might be due to invalid IL or missing references)
		//IL_0118: Unknown result type (might be due to invalid IL or missing references)
		//IL_016d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0188: Unknown result type (might be due to invalid IL or missing references)
		if (!Object.op_Implicit((Object)(object)ai) || !Object.op_Implicit((Object)(object)agent) || (Object)(object)ai.NavMeshAgent != (Object)(object)agent || ((!actors.Contains((Component)(object)ai) || !Object.op_Implicit((Object)(object)((CharacterControl)ai).Character) || !((CharacterControl)ai).Character.IsAI) && !cityDefense.Owns(ai)))
		{
			return false;
		}
		if (!WorldReady || travelPending || sessionExit.MenuPending || Loading || !Object.op_Implicit((Object)(object)((CharacterControl)ai).Character) || !((CharacterControl)ai).Character.Alive || !Object.op_Implicit((Object)(object)NetworkLevelLoader.Instance) || NetworkLevelLoader.Instance.IsGameplayPaused || NetworkLevelLoader.Instance.IsGameplayLoading || TownRecovering(ai))
		{
			((Behaviour)agent).enabled = false;
			return true;
		}
		NavMeshQueryFilter filter;
		bool town = TownQueryFilter(ai, out filter);
		if (!town)
		{
			NavMeshQueryFilter val = default(NavMeshQueryFilter);
			((NavMeshQueryFilter)(ref val)).agentTypeID = agent.agentTypeID;
			((NavMeshQueryFilter)(ref val)).areaMask = agent.areaMask;
			filter = val;
		}
		if (AgentActivation.Enable(agent, delegate(Vector3 point, out NavMeshHit hit)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			return (!town) ? NavMesh.SamplePosition(point, ref hit, 1.5f, filter) : townNavigation.Sample(point, out hit, 1.5f, filter);
		}))
		{
			waitingAgentBindings.Remove(ai);
		}
		else if (waitingAgentBindings.Add(ai))
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)string.Concat("Native navigation activation deferred: ", UID.op_Implicit(((CharacterControl)ai).Character.UID), "; body=", ((Component)ai).transform.position, "; agent type=", agent.agentTypeID, "; no valid nearby binding floor; physical body and native destination retained."));
		}
		return true;
	}

	internal Vector3 TownMovementTarget(CharacterAI ai, Vector3 requested)
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		if (!TownQueryFilter(ai, out var _))
		{
			return requested;
		}
		return townNavigation.MovementTarget(ai, requested);
	}

	internal bool TownDestinationAgentValid(CharacterAI ai, bool nativeValid)
	{
		if (!TownQueryFilter(ai, out var _))
		{
			return nativeValid;
		}
		return townNavigation.DestinationAgentValid(ai, nativeValid);
	}

	internal bool TownRequestDestination(CharacterAI ai, NavMeshAgent agent, Vector3 position)
	{
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		if (!TownQueryFilter(ai, out var _))
		{
			return agent.SetDestination(position);
		}
		return townNavigation.RequestDestination(ai, agent, position);
	}

	internal void TownMotionObserved(CharacterAI ai)
	{
	}

	internal bool TownSample(Vector3 point, out NavMeshHit hit, float distance, NavMeshQueryFilter filter)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0009: Unknown result type (might be due to invalid IL or missing references)
		return townNavigation.Sample(point, out hit, distance, filter);
	}

	internal bool GuardAIUpdate(CharacterAI ai)
	{
		if (cityDefense.Owns(ai) || actors.Contains((Component)(object)ai))
		{
			if (CoopSession.Authority && WorldReady && !travelPending && !sessionExit.MenuPending && !Loading && Object.op_Implicit((Object)(object)NetworkLevelLoader.Instance) && !NetworkLevelLoader.Instance.IsGameplayPaused)
			{
				return !NetworkLevelLoader.Instance.IsGameplayLoading;
			}
			return false;
		}
		return true;
	}

	internal void GuardFacing(CharacterAI ai)
	{
		if (CoopSession.Authority && Object.op_Implicit((Object)(object)NetworkLevelLoader.Instance) && !NetworkLevelLoader.Instance.IsGameplayPaused)
		{
			cityDefense.FaceReturnPath(ai);
		}
	}

	internal void GuardAnimationState(Character body)
	{
		if (cityDefense.Owns(body))
		{
			CityGuardAnimation.KeepAnimating(body);
		}
	}

	internal bool GuardTargetAllowed(TargetingSystem source, Character target)
	{
		return cityDefense.TargetAllowed(source, target);
	}

	internal void GuardDestination(CharacterAI ai, ref Vector3 position)
	{
		cityDefense.Destination(ai, ref position);
	}

	internal Vector3 GuardMovement(CharacterController controller, Vector3 move)
	{
		//IL_0007: Unknown result type (might be due to invalid IL or missing references)
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		return cityDefense.Clamp(controller, move);
	}

	internal bool AllowRegionEntry(NodeTreeWaitForLoading waiter)
	{
		if (actors.AllowRegionEntry(waiter))
		{
			return fixtures.AllowQuestStart(waiter);
		}
		return false;
	}

	internal bool SuppressQuestActivation(GameObject target)
	{
		if (!actors.Contains(target))
		{
			return fixtures.SuppressActivation(target);
		}
		return false;
	}

	internal void MarkTravel()
	{
		travelPending = true;
	}

	internal void BeginMenuReturn()
	{
		if (Armed || starting || WorldPersistence.Waiting)
		{
			sessionExit.RequestMenu(delegate
			{
				((MonoBehaviour)this).StopAllCoroutines();
				starting = false;
				LoadingPresentation.ReleaseForMenu();
				SetStatus("Returning to the main menu; waiting for the native save before releasing the joined world.");
			});
		}
	}

	internal bool TryBeginMenuReturn()
	{
		if (Armed && ready && !failed && !droppedItems.SavingSafe)
		{
			menuAfterItemRecovery = true;
			SetStatus("Recovering fallen items before the native quit save; menu return will continue when item synchronization finishes.");
			return false;
		}
		BeginMenuReturn();
		return true;
	}

	internal void BeforeMenuReset()
	{
		if (sessionExit.MenuPending)
		{
			BeforeNativeUnload();
		}
	}

	internal void BeforeNativeUnload()
	{
		sessionExit.BeforeUnload(travelPending, ReleaseWorldForUnload);
	}

	private void ReleaseWorldForUnload()
	{
		ready = false;
		Armed = (Loading = false);
		waitingAgentBindings.Clear();
		coop.Reset();
		CoopInboundSync.Reset();
		droppedItems.Clear();
		fixtures.Dispose();
		mapMarkers.Dispose();
		ActorEvents.Dispose();
		cityDefense.Dispose();
		actors.Dispose();
		townNavigation.Dispose();
		navigation.Dispose();
		foreach (KeyValuePair<Camera, bool> item in originalOcclusion)
		{
			if (Object.op_Implicit((Object)(object)item.Key))
			{
				item.Key.useOcclusionCulling = item.Value;
			}
		}
		originalOcclusion.Clear();
		WorldPersistence.EndSession();
		OutdoorFoliage.Clear();
		WaterSeam.Clear();
		HideLoadingNotice();
		if (sessionExit.MenuPending)
		{
			SetStatus("Native quit save finished; joined-world systems released for the main menu.");
		}
	}

	internal void NativeSceneLoaded(bool mainMenu)
	{
		sessionExit.Arrived(mainMenu, ResetWorldSession, delegate
		{
			WorldPersistence.EndSession();
			ActorEvents.ClearPending();
			if (hooksReady)
			{
				failed = false;
				WorldPersistence.Faulted = false;
			}
			LoadingPresentation.ReleaseForMenu();
			SetStatus("Main menu ready; Open Cities will initialize on the next Cierzo load.");
		});
	}

	private void ResetWorldSession()
	{
		LiveTerrainSeam.Clear();
		mapMarkers.Dispose();
		mapMarkers = new RegionalMapMarkers();
		outdoorTerrain.Dispose();
		outdoorTerrain = new OutdoorTerrain();
		OwnedAssets.Release();
		resources = new WorldResources();
		fixtures = new WorldFixtures();
		outdoorDetails = new OutdoorDetails();
		actors = new WorldActors();
		navigation = new OutdoorNavigation();
		townNavigation = new TownNavigation();
		cityDefense = new CityDefense();
		droppedItems = new DroppedItemRecovery();
		resourceEffects = new ResourceEffects();
		outdoorEnvironment = new OutdoorEnvironment();
		regionalEnvironment = new RegionalEnvironment();
		coverage = new WorldCoverage();
		networkViews = new SceneNetworkViews();
		localGatePresses.Clear();
		gatePress = new GatePressLatch();
		physicalGate = null;
		importedBoundary = null;
		blockedInstances.Clear();
		removedTravelTriggers.Clear();
		waitingAgentBindings.Clear();
		ready = (starting = (travelPending = (outside = (Loading = (menuAfterItemRecovery = false)))));
		player = null;
		nextAutoStartCheck = 0f;
		SetStatus("Waiting for Cierzo; the joined outdoor world rebuilds automatically on return.");
	}

	internal bool ResourceInteractionBlocked(Component component)
	{
		//IL_006e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_007f: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		if (!Armed)
		{
			return false;
		}
		if ((!ready || !WorldPersistence.Ready) && (resources.Contains(component) || fixtures.Contains(component) || actors.Contains(component)))
		{
			return true;
		}
		if (fixtures.IsWaterTrigger(component))
		{
			if (Object.op_Implicit((Object)(object)player) && !(Vector3.Dot(((Component)player).transform.position - seam, outward) < 0f))
			{
				return !WaterSeam.NearWater(((Component)player).transform.position);
			}
			return true;
		}
		return false;
	}

	internal bool AllowNetworkRegistration(PhotonView view)
	{
		if (!Armed)
		{
			return true;
		}
		if (Loading && IsDonor((Component)(object)view))
		{
			return false;
		}
		try
		{
			networkViews.ValidateRegistration(view);
			return true;
		}
		catch (Exception e)
		{
			Fail("Outdoor network registration failed; saving remains blocked.", e);
			return false;
		}
	}

	internal bool IsDonor(Component c)
	{
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
		if ((!Armed && !unloading) || object.ReferenceEquals(c, null))
		{
			return false;
		}
		MonoBehaviour val = (MonoBehaviour)(object)((c is MonoBehaviour) ? c : null);
		if (Object.op_Implicit((Object)(object)val) && (actors.Owns(val) || mapMarkers.Owns(val) || outdoorEnvironment.Owns(val) || regionalEnvironment.Owns(val) || fixtures.Owns(val) || outdoorTerrain.Owns(val)))
		{
			return false;
		}
		if (blockedInstances.Contains(((Object)c).GetInstanceID()))
		{
			return true;
		}
		if (!Object.op_Implicit((Object)(object)c))
		{
			return false;
		}
		Scene scene = c.gameObject.scene;
		bool flag = ((Scene)(ref scene)).name == "ChersoneseNewTerrain";
		if (flag)
		{
			blockedInstances.Add(((Object)c).GetInstanceID());
		}
		return flag;
	}

	internal void Fail(string message, Exception e)
	{
		if (failed)
		{
			return;
		}
		failed = true;
		ready = false;
		actors.Suspend();
		cityDefense.Suspend();
		WorldPersistence.Faulted = true;
		WorldPersistence.Ready = false;
		HideLoadingNotice();
		LoadingPresentation.Cancel();
		SetStatus(message);
		((BaseUnityPlugin)this).Logger.LogError((object)message);
		try
		{
			if (Object.op_Implicit((Object)(object)player) && Object.op_Implicit((Object)(object)player.CharacterUI))
			{
				player.CharacterUI.ShowInfoNotification("Open Cierzo: " + message);
			}
		}
		catch (Exception ex)
		{
			((BaseUnityPlugin)this).Logger.LogWarning((object)ex);
		}
		if (e != null)
		{
			((BaseUnityPlugin)this).Logger.LogError((object)e);
		}
	}

	private void HideLoadingNotice()
	{
		if (loadingNotice != null)
		{
			loadingNotice.Hide();
			loadingNotice = null;
		}
	}

	internal void ClearLoadingNotice()
	{
		HideLoadingNotice();
	}

	private void Notice(string phase, float progress)
	{
		if (loadingNotice != null)
		{
			loadingNotice.SetPhase(phase, progress);
		}
		if (presentationPhase != phase)
		{
			presentationPhase = phase;
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Joined load phase at " + (Time.realtimeSinceStartup - presentationStarted).ToString("F2") + "s: " + phase));
		}
	}

	private void OnDestroy()
	{
		if (coop != null)
		{
			coop.Dispose();
		}
		HideLoadingNotice();
		LoadingPresentation.Cancel();
	}
}
internal static class WorldPersistence
{
	private sealed class LocalPose
	{
		internal PlayerSaveData Data;

		internal Vector3 Position;

		internal Vector3 Forward;
	}

	internal sealed class WorldRecords
	{
		internal readonly List<BasicSaveData> Items;

		internal readonly List<BasicSaveData> Activators;

		internal readonly List<BasicSaveData> DropTables;

		internal readonly List<CharacterSaveData> Characters;

		internal List<BasicSaveData> DeferredItems;

		internal List<CharacterSaveData> DeferredCharacters;

		internal readonly bool GateOpen;

		internal readonly HashSet<string> ResourceIDs;

		internal readonly HashSet<string> FixtureIDs;

		internal readonly HashSet<string> LooseIDs;

		internal readonly HashSet<string> ActorIDs;

		internal readonly List<string> UsedSoulSpots;

		internal WorldRecords Original;

		internal bool ReplayStates;

		internal readonly bool HasRegionalClock;

		internal WorldRecords(EnvironmentSave save, bool? gate = null)
		{
			Items = save.ItemList;
			Characters = save.CharList;
			Activators = save.InteractionActivatorList;
			DropTables = save.DropTablesList;
			GateOpen = gate ?? Activators.Any((BasicSaveData r) => object.Equals(r.Identifier, "Outwardz.OpenCierzo.Gate.v1") && r.SyncData == "open");
			ResourceIDs = ResourceCatalog.Read(Activators);
			FixtureIDs = ResourceCatalog.Read(Activators, "Outwardz.OpenCierzo.Fixtures.v1");
			LooseIDs = ResourceCatalog.Read(Activators, "Outwardz.OpenCierzo.LooseSources.v1");
			ActorIDs = ActorCatalog.Read(Activators);
			UsedSoulSpots = new List<string>(save.UsedSoulSpots ?? new List<string>());
			HasRegionalClock = RegionReset.HasClock(Activators);
		}

		internal void RestoreLists(EnvironmentSave save)
		{
			WorldRecords worldRecords = Original ?? this;
			save.ItemList = worldRecords.Items;
			save.CharList = worldRecords.Characters;
			save.InteractionActivatorList = worldRecords.Activators;
			save.DropTablesList = worldRecords.DropTables;
			save.UsedSoulSpots = worldRecords.UsedSoulSpots;
		}

		internal WorldRecords OutdoorSnapshot(bool? gate = null)
		{
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Expected O, but got Unknown
			HashSet<string> outdoor = SavedItemPlacement.OutdoorItems(Items, new HashSet<string>(ResourceIDs.Concat(LooseIDs)), ActorIDs);
			HashSet<string> ids = new HashSet<string>(ResourceIDs.Concat(LooseIDs).Concat(FixtureIDs).Concat(ActorIDs));
			EnvironmentSave val = new EnvironmentSave();
			val.ItemList = Items.Where((BasicSaveData r) => outdoor.Contains(r.Identifier.ToString())).ToList();
			val.CharList = Characters.Where((CharacterSaveData c) => ActorCatalog.Outdoor(c, ActorIDs)).ToList();
			val.InteractionActivatorList = Activators.Where((BasicSaveData r) => ids.Contains(r.Identifier.ToString()) || ResourceCatalog.IsRecord(r) || object.Equals(r.Identifier, "Outwardz.OpenCierzo.Fixtures.v1") || object.Equals(r.Identifier, "Outwardz.OpenCierzo.LooseSources.v1") || object.Equals(r.Identifier, "Outwardz.OpenCierzo.Actors.v1") || ActorEvents.IsRecord(r) || object.Equals(r.Identifier, "Outwardz.OpenCierzo.RegionVisit.v1")).ToList();
			val.DropTablesList = DropTables.Where((BasicSaveData r) => RegionReset.BelongsTo(r, ids)).ToList();
			val.UsedSoulSpots = UsedSoulSpots.Where((string id) => RegionReset.Owned(id, ids)).ToList();
			WorldRecords worldRecords = new WorldRecords(val, gate);
			worldRecords.DeferredItems = worldRecords.Items;
			worldRecords.DeferredCharacters = worldRecords.Characters;
			return worldRecords;
		}

		internal WorldRecords Snapshot()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			EnvironmentSave val = new EnvironmentSave();
			val.ItemList = new List<BasicSaveData>(Items);
			val.CharList = new List<CharacterSaveData>(Characters);
			val.InteractionActivatorList = new List<BasicSaveData>(Activators);
			val.DropTablesList = new List<BasicSaveData>(DropTables);
			val.UsedSoulSpots = new List<string>(UsedSoulSpots);
			return new WorldRecords(val);
		}

		internal void KeepUnloadedRecords(EnvironmentSave save)
		{
			HashSet<string> hashSet = new HashSet<string>(save.CharList.Select((CharacterSaveData c) => c.UID));
			foreach (CharacterSaveData character in Characters)
			{
				if (!CharacterManager.Instance.Characters.ContainsKey(character.UID) && hashSet.Add(character.UID))
				{
					save.CharList.Add(character);
				}
			}
			MergeMissing(save.InteractionActivatorList, Activators);
			MergeMissing(save.DropTablesList, DropTables);
			if (save.UsedSoulSpots == null)
			{
				save.UsedSoulSpots = new List<string>();
			}
			foreach (string usedSoulSpot in UsedSoulSpots)
			{
				if (!save.UsedSoulSpots.Contains(usedSoulSpot))
				{
					save.UsedSoulSpots.Add(usedSoulSpot);
				}
			}
		}

		private static void MergeMissing(List<BasicSaveData> current, List<BasicSaveData> old)
		{
			HashSet<string> hashSet = new HashSet<string>(current.Select((BasicSaveData r) => r.Identifier.ToString()));
			foreach (BasicSaveData item in old)
			{
				if (!object.Equals(item.Identifier, "Outwardz.OpenCierzo.Gate.v1") && !object.Equals(item.Identifier, "Outwardz.OpenCierzo.Fixtures.v1") && !object.Equals(item.Identifier, "Outwardz.OpenCierzo.LooseSources.v1") && !object.Equals(item.Identifier, "Outwardz.OpenCierzo.Actors.v1") && !ActorEvents.IsRecord(item) && !object.Equals(item.Identifier, "Outwardz.OpenCierzo.RegionVisit.v1") && !RecoveryLedger.IsLedger(item) && !ResourceCatalog.IsRecord(item) && hashSet.Add(item.Identifier.ToString()))
				{
					current.Add(item);
				}
			}
		}
	}

	internal const string GateRecord = "Outwardz.OpenCierzo.Gate.v1";

	internal const string FixtureRecord = "Outwardz.OpenCierzo.Fixtures.v1";

	internal const string LooseRecord = "Outwardz.OpenCierzo.LooseSources.v1";

	internal static readonly bool RecoveryEnabled = true;

	internal static volatile bool Waiting;

	internal static volatile bool Ready;

	internal static volatile bool Faulted;

	private static PlayerSaveData selectedPlayer;

	private static readonly Dictionary<string, LocalPose> localPoses = new Dictionary<string, LocalPose>();

	private static Vector3 savedPosition;

	private static Vector3 savedForward;

	private static bool resumeOutside;

	private static WorldRecords pending;

	private static WorldRecords retained;

	private static bool itemsSubmitted;

	private static bool charactersApplied;

	private static ItemRecovery recovery;

	internal static string RestoreWaitReason = "native item synchronization";

	private static readonly BindingFlags All = BindingFlags.Static | BindingFlags.NonPublic;

	internal static bool ResumingOutside => resumeOutside;

	internal static IEnumerable<string> SavedResourceIDs
	{
		get
		{
			if (pending != null)
			{
				return pending.ResourceIDs;
			}
			return new HashSet<string>();
		}
	}

	internal static IEnumerable<string> SavedFixtureIDs
	{
		get
		{
			if (pending != null)
			{
				return pending.FixtureIDs;
			}
			return new HashSet<string>();
		}
	}

	internal static IEnumerable<string> SavedLooseIDs
	{
		get
		{
			if (pending != null)
			{
				return pending.LooseIDs;
			}
			return new HashSet<string>();
		}
	}

	internal static IEnumerable<string> SavedSoulSpots
	{
		get
		{
			if (pending != null)
			{
				return pending.UsedSoulSpots;
			}
			return new List<string>();
		}
	}

	internal static IEnumerable<CharacterSaveData> SavedCharacters
	{
		get
		{
			List<CharacterSaveData> list;
			if (pending != null)
			{
				list = pending.DeferredCharacters;
				if (list == null)
				{
					return pending.Characters;
				}
			}
			else
			{
				list = new List<CharacterSaveData>();
			}
			return list;
		}
	}

	internal static IEnumerable<BasicSaveData> SavedItems
	{
		get
		{
			List<BasicSaveData> list;
			if (pending != null)
			{
				list = pending.DeferredItems;
				if (list == null)
				{
					return pending.Items;
				}
			}
			else
			{
				list = new List<BasicSaveData>();
			}
			return list;
		}
	}

	internal static IEnumerable<BasicSaveData> SavedEventStates
	{
		get
		{
			if (pending != null)
			{
				return pending.Activators;
			}
			return new List<BasicSaveData>();
		}
	}

	internal static bool AllowSave()
	{
		if (!Faulted && !Waiting)
		{
			if (Plugin.Armed)
			{
				if (Ready && Plugin.Instance.WorldReady && Plugin.Instance.ActorsReady)
				{
					return Plugin.Instance.ItemsSafeToSave;
				}
				return false;
			}
			return true;
		}
		return false;
	}

	internal static void EndSession()
	{
		RestoreSelectedData();
		pending = (retained = null);
		recovery = null;
		itemsSubmitted = (charactersApplied = (resumeOutside = false));
		Waiting = (Ready = false);
		RegionReset.Clear();
	}

	internal static void Install(Harmony harmony)
	{
		Type[] array = new Type[4]
		{
			typeof(SaveManager),
			typeof(SaveInstance),
			typeof(CharacterSaveInstanceHolder),
			typeof(NetworkLevelLoader)
		};
		foreach (Type type in array)
		{
			string[] array2 = ((type == typeof(SaveManager)) ? new string[5] { "Save", "SaveSync", "Save_Internal", "ForceSaveEnvironment", "SetSaveRequired" } : ((type == typeof(SaveInstance)) ? new string[2] { "Save", "PrepareSave" } : ((type == typeof(NetworkLevelLoader)) ? new string[2] { "Save", "ForceSaveEnvironment" } : new string[1] { "Save" })));
			string[] array3 = array2;
			foreach (string name in array3)
			{
				MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				Func<MethodInfo, bool> predicate = (MethodInfo m) => m.Name == name;
				MethodInfo[] array4 = methods.Where(predicate).ToArray();
				if (array4.Length == 0)
				{
					throw new InvalidOperationException("Missing save hook: " + type.Name + "." + name);
				}
				MethodInfo[] array5 = array4;
				foreach (MethodInfo methodInfo in array5)
				{
					harmony.Patch((MethodBase)methodInfo, Hook("SaveAllowed"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
		}
		Patch(harmony, typeof(CharacterSaveInstanceHolder), "ChooseCharacterSaveInstance", null, "Selected");
		Patch(harmony, typeof(CharacterSave), "ApplyLoadedSaveToChar", "LoadingPlayer", null);
		Patch(harmony, typeof(EnvironmentSave), "ApplyData", "BeforeEnvironment", "AfterEnvironment", "EnvironmentFinally");
		Patch(harmony, typeof(EnvironmentSave), "PrepareSave", null, "EnvironmentPrepared");
	}

	private static HarmonyMethod Hook(string name)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001e: Expected O, but got Unknown
		if (name != null)
		{
			return new HarmonyMethod(typeof(WorldPersistence).GetMethod(name, All));
		}
		return null;
	}

	private static void Patch(Harmony harmony, Type type, string method, string before, string after, string final = null)
	{
		MethodInfo methodInfo = AccessTools.Method(type, method, (Type[])null, (Type[])null);
		if (methodInfo == null || methodInfo.GetMethodBody() == null)
		{
			throw new InvalidOperationException("Missing persistence hook: " + type.Name + "." + method);
		}
		harmony.Patch((MethodBase)methodInfo, Hook(before), Hook(after), (HarmonyMethod)null, Hook(final), (HarmonyMethod)null);
	}

	private static bool SaveAllowed()
	{
		return AllowSave();
	}

	private static void Selected(CharacterSave __result)
	{
		if (!Plugin.Armed)
		{
			RestoreSelectedData(!Plugin.HasMultipleLocalPlayers);
			LoadingPresentation.RememberSave(__result?.PSave);
			pending = (retained = null);
			recovery = null;
			itemsSubmitted = (charactersApplied = false);
			RegionReset.Clear();
			Waiting = (Ready = false);
			if (__result != null)
			{
				SelectPlayer(__result.PSave);
			}
		}
	}

	private static void LoadingPlayer(CharacterSave __instance)
	{
		if (!Plugin.Armed && selectedPlayer == null)
		{
			SelectPlayer(__instance.PSave);
		}
	}

	private static void SelectPlayer(PlayerSaveData data)
	{
		//IL_0083: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: 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_00e2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
		if (data == null || ((CharacterSaveData)data).NewSave || data.AreaName != "CierzoNewTerrain" || !Plugin.PersistenceEligible)
		{
			return;
		}
		Waiting = true;
		if (Plugin.HasMultipleLocalPlayers && !localPoses.ContainsKey(((CharacterSaveData)data).UID))
		{
			localPoses.Add(((CharacterSaveData)data).UID, new LocalPose
			{
				Data = data,
				Position = ((CharacterSaveData)data).Position,
				Forward = ((CharacterSaveData)data).Forward
			});
		}
		selectedPlayer = data;
		savedPosition = ((CharacterSaveData)data).Position;
		savedForward = ((CharacterSaveData)data).Forward;
		if (!Finite(savedPosition) || !Finite(savedForward))
		{
			Stop("The saved position is invalid.", null);
			return;
		}
		resumeOutside = IsOutside(savedPosition);
		if (resumeOutside)
		{
			((CharacterSaveData)data).Position = new Vector3(1436.28f, 9f, 1662.37f);
			Plugin.Instance.SetStatus("Outdoor save selected. Restoring the joined world before returning to your saved position.");
		}
	}

	internal static bool IsOutside(Vector3 position)
	{
		return (position.x - 1443.9841f) * 0.96306515f - (position.z - 1660.2166f) * 0.26927066f > 0f;
	}

	private static bool Finite(Vector3 v)
	{
		if (!float.IsNaN(v.x) && !float.IsNaN(v.y) && !float.IsNaN(v.z) && !float.IsInfinity(v.x) && !float.IsInfinity(v.y))
		{
			return !float.IsInfinity(v.z);
		}
		return false;
	}

	private static void BeforeEnvironment(EnvironmentSave __instance, out WorldRecords __state, double __0 = 0.0)
	{
		__state = null;
		if (Plugin.Armed || __instance.AreaName != "CierzoNewTerrain" || !Plugin.PersistenceEligible)
		{
			return;
		}
		Waiting = true;
		try
		{
			int num = (RecoveryEnabled ? RecoveryLedger.RepairRecords(__instance) : 0);
			if (num != 0)
			{
				Plugin.Instance.SetStatus("Recovery ledger restored " + num + " missing native save records.");
			}
			__state = new WorldRecords(__instance);
			WorldRecords original = __state;
			RegionReset.Prepare(__instance, __0);
			__state = new WorldRecords(__instance)
			{
				Original = original
			};
			HashSet<string> actorIds = __state.ActorIDs;
			HashSet<string> outdoors = SavedItemPlacement.OutdoorItems(__state.Items, new HashSet<string>(__state.ResourceIDs.Concat(__state.LooseIDs)), __state.ActorIDs);
			__state.DeferredItems = __state.Items.Where((BasicSaveData r) => outdoors.Contains(r.Identifier.ToString())).ToList();
			__state.DeferredCharacters = __state.Characters.Where((CharacterSaveData c) => ActorCatalog.Outdoor(c, actorIds)).ToList();
			__instance.ItemList = __state.Items.Where((BasicSaveData r) => !outdoors.Contains(r.Identifier.ToString())).ToList();
			__instance.CharList = __state.Characters.Where((CharacterSaveData c) => !ActorCatalog.Outdoor(c, actorIds)).ToList();
		}
		catch (Exception error)
		{
			Stop("Saved world placement could not be read. Saving remains blocked.", error);
			throw;
		}
	}

	private static void AfterEnvironment(EnvironmentSave __instance, bool __result, WorldRecords __state)
	{
		if (__state != null)
		{
			__state.RestoreLists(__instance);
			pending = ((!__result) ? __state : (__state.HasRegionalClock ? __state.OutdoorSnapshot() : null));
			if (pending != null)
			{
				pending.ReplayStates = __result;
			}
			retained = ((pending == null) ? null : pending.Snapshot());
		}
	}

	private static Exception EnvironmentFinally(EnvironmentSave __instance, WorldRecords __state, Exception __exception)
	{
		__state?.RestoreLists(__instance);
		if (__exception != null && __state != null)
		{
			Stop("Native world loading failed; saving remains blocked.", __exception);
		}
		return __exception;
	}

	internal static void BeginRestore(Character player, HingedGate gate)
	{
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		if (Faulted)
		{
			throw new InvalidOperationException("A previous save-load error prevents world restoration.");
		}
		Waiting = true;
		Ready = false;
		if (selectedPlayer != null && ((CharacterSaveData)selectedPlayer).UID != UID.op_Implicit(player.UID) && (!Plugin.HasMultipleLocalPlayers || !((Object)(object)Plugin.FindLocalPlayer(((CharacterSaveData)selectedPlayer).UID) != (Object)null)))
		{
			throw new InvalidOperationException("The selected save belongs to a different character.");
		}
		if (pending != null)
		{
			recovery = new ItemRecovery(pending.Items, Time.realtimeSinceStartup, RecoveryEnabled);
			gate.Restore(pending.GateOpen && GatePermission.Allowed);
			Plugin.Instance.SetStatus("Restoring " + pending.DeferredItems.Count + " outdoor item records, including container contents.");
			if (pending.DeferredItems.Count != 0)
			{
				ItemManager.Instance.LoadItems(pending.DeferredItems, false);
			}
		}
		itemsSubmitted = true;
	}

	internal static void AcceptCoop(EnvironmentSave save)
	{
		if (!CoopSession.Guest || Plugin.Armed)
		{
			throw new InvalidOperationException("Co-op snapshot cannot replace a live or host world.");
		}
		WorldRecords worldRecords = new WorldRecords(save);
		WorldRecords worldRecords2 = worldRecords.OutdoorSnapshot(worldRecords.GateOpen);
		pending = worldRecords2;
		retained = null;
		pending.ReplayStates = true;
		pending.Activators.AddRange(save.InteractionActivatorList.Where((BasicSaveData r) => object.Equals(r.Identifier, "Outwardz.OpenCierzo.Gate.v1")));
		recovery = null;
		itemsSubmitted = (charactersApplied = false);
		Waiting = true;
		Ready = false;
	}

	internal static void PrepareInteractionStates()
	{
		if (pending != null && pending.ReplayStates)
		{
			SceneInteractionManager.Instance.LoadInteractableStates(pending.Activators);
			SceneInteractionManager.Instance.LoadDropTableStates(pending.DropTables);
			pending.ReplayStates = false;
		}
	}

	internal static void DiscardRepeatedEquipment(HashSet<string> ids)
	{
		if (pending == null || ids.Count == 0)
		{
			return;
		}
		pending.Items.RemoveAll((BasicSaveData r) => ids.Contains(r.Identifier.ToString()));
		pending.DeferredItems.RemoveAll((BasicSaveData r) => ids.Contains(r.Identifier.ToString()));
		if (retained != null)
		{
			retained.Items.RemoveAll((BasicSaveData r) => ids.Contains(r.Identifier.ToString()));
		}
	}

	internal static bool PollRestore(Character player)
	{
		//IL_013b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0153: Unknown result type (might be due to invalid IL or missing references)
		//IL_0158: Unknown result type (might be due to invalid IL or missing references)
		//IL_017a: Unknown result type (might be due to invalid IL or missing references)
		//IL_017f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0180: Unknown result type (might be due to invalid IL or missing references)
		//IL_018b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0190: 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_01ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01dc: 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_0206: Unknown result type (might be due to invalid IL or missing references)
		//IL_0208: Unknown result type (might be due to invalid IL or missing references)
		//IL_0214: Unknown result type (might be due to invalid IL or missing references)
		//IL_0219: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fd: Unknown result type (might be due to invalid IL or missing references)
		RestoreWaitReason = "native item synchronization";
		if (!itemsSubmitted)
		{
			return false;
		}
		if (recovery != null && !recovery.Reconcile(Time.realtimeSinceStartup, ProbeItem, (string id) => Object.op_Implicit((Object)(object)ItemManager.Instance.GetItem(id)), RetryItems, ItemManager.Instance.IsAllItemSynced))
		{
			RestoreWaitReason = recovery.WaitingFor;
			return false;
		}
		if (!ItemManager.Instance.IsAllItemSynced)
		{
			return false;
		}
		if (!Plugin.Instance.ActorsSettled(out RestoreWaitReason))
		{
			return false;
		}
		if (pending != null && !charactersApplied)
		{
			string[] array = (from c in pending.DeferredCharacters
				where pending.ActorIDs.Contains(c.UID) && !Object.op_Implicit((Object)(object)CharacterManager.Instance.GetCharacter(c.UID))
				select c.UID).ToArray();
			if (array.Length != 0)
			{
				throw new InvalidOperationException("Saved outdoor actors were not registered: " + string.Join(", ", array));
			}
			charactersApplied = true;
			return false;
		}
		if (selectedPlayer != null && ((CharacterSaveData)selectedPlayer).UID == UID.op_Implicit(player.UID) && resumeOutside)
		{
			Vector3 forward = savedForward;
			forward.y = 0f;
			if (((Vector3)(ref forward)).sqrMagnitude < 0.001f)
			{
				forward = Vector3.forward;
			}
			player.Teleport(savedPosition, Quaternion.LookRotation(forward));
			player.LastPosOwnWorld = savedPosition;
		}
		foreach (LocalPose value in localPoses.Values)
		{
			Character val = Plugin.FindLocalPlayer(((CharacterSaveData)value.Data).UID);
			if (Object.op_Implicit((Object)(object)val) && IsOutside(value.Position))
			{
				Vector3 forward2 = value.Forward;
				forward2.y = 0f;
				if (((Vector3)(ref forward2)).sqrMagnitude < 0.001f)
				{
					forward2 = Vector3.forward;
				}
				val.Teleport(value.Position, Quaternion.LookRotation(forward2));
				val.LastPosOwnWorld = value.Position;
			}
		}
		RestoreSelectedData();
		Plugin.Instance.SetStatus("World restored: " + ((pending != null) ? pending.Items.Count : 0) + " native world-item records; " + ((pending != null) ? pending.Characters.Count : 0) + " actor records. Final world setup continues before saving is enabled.");
		pending = null;
		recovery = null;
		Waiting = false;
		Ready = true;
		return true;
	}

	private static RestorePresence ProbeItem(string id, string parent)
	{
		Item item = ItemManager.Instance.GetItem(id);
		if (!Object.op_Implicit((Object)(object)item))
		{
			return RestorePresence.Missing;
		}
		if (!item.IsFirstSyncDone)
		{
			return RestorePresence.Initializing;
		}
		if (parent != null && (!Object.op_Implicit((Object)(object)item.ParentContainer) || ((Item)item.ParentContainer).UID != parent))
		{
			return RestorePresence.WrongContainer;
		}
		return RestorePresence.Ready;
	}

	private static void RetryItems(List<BasicSaveData> records)
	{
		Plugin.Instance.SetStatus("Recovery retry for " + records.Count + " missing or detached world items, preserving original IDs and saved state.");
		ItemManager.Instance.LoadItems(records, false);
	}

	private static void RestoreSelectedData(bool clearLocals = true)
	{
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0011: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_0055: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		if (selectedPlayer != null)
		{
			((CharacterSaveData)selectedPlayer).Position = savedPosition;
			((CharacterSaveData)selectedPlayer).Forward = savedForward;
		}
		selectedPlayer = null;
		resumeOutside = false;
		if (!clearLocals)
		{
			return;
		}
		foreach (LocalPose value in localPoses.Values)
		{
			((CharacterSaveData)value.Data).Position = value.Position;
			((CharacterSaveData)value.Data).Forward = value.Forward;
		}
		localPoses.Clear();
	}

	private static void EnvironmentPrepared(EnvironmentSave __instance)
	{
		//IL_008a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0094: Expected O, but got Unknown
		if (!Plugin.Armed || !Ready || __instance.AreaName != "CierzoNewTerrain" || !CoopSession.Authority)
		{
			return;
		}
		try
		{
			if (retained != null)
			{
				retained.KeepUnloadedRecords(__instance);
			}
			__instance.InteractionActivatorList.RemoveAll((BasicSaveData r) => object.Equals(r.Identifier, "Outwardz.OpenCierzo.Gate.v1"));
			__instance.InteractionActivatorList.Add(new BasicSaveData((object)"Outwardz.OpenCierzo.Gate.v1", Plugin.Instance.GateIsOpen ? "open" : "closed"));
			ResourceCatalog.Write(__instance.InteractionActivatorList, Plugin.Instance.ResourceIDs);
			ResourceCatalog.Write(__instance.InteractionActivatorList, Plugin.Instance.FixtureIDs, "Outwardz.OpenCierzo.Fixtures.v1");
			ResourceCatalog.Write(__instance.InteractionActivatorList, Plugin.Instance.LooseIDs, "Outwardz.OpenCierzo.LooseSources.v1");
			ResourceCatalog.Write(__instance.InteractionActivatorList, Plugin.Instance.ActorIDs, "Outwardz.OpenCierzo.Actors.v1");
			ActorEvents.Save(__instance);
			RegionReset.Save(__instance);
			if (RecoveryEnabled)
			{
				RecoveryLedger.Capture(__instance);
			}
			else
			{
				__instance.InteractionActivatorList.RemoveAll(RecoveryLedger.IsLedger);
			}
			retained = new WorldRecords(__instance).Snapshot();
		}
		catch (Exception error)
		{
			Stop("World save preparation failed; saving remains blocked.", error);
			throw;
		}
	}

	internal static void Stop(string message, Exception error)
	{
		Faulted = true;
		Ready = false;
		Plugin.Instance.Fail(message, error);
	}
}
internal static class RegionReset
{
	internal const string Record = "Outwardz.OpenCierzo.RegionVisit.v1";

	private static double lastVisit = double.NaN;

	internal static void Clear()
	{
		lastVisit = double.NaN;
	}

	internal static void Visit()
	{
		lastVisit = EnvironmentConditions.GameTime;
	}

	internal static bool HasClock(IEnumerable<BasicSaveData> records)
	{
		return records.Any((BasicSaveData r) => object.Equals(r.Identifier, "Outwardz.OpenCierzo.RegionVisit.v1"));
	}

	private static double Read(EnvironmentSave save)
	{
		BasicSaveData[] array = save.InteractionActivatorList.Where((BasicSaveData r) => object.Equals(r.Identifier, "Outwardz.OpenCierzo.RegionVisit.v1")).ToArray();
		if (array.Length == 0)
		{
			return save.GameTime;
		}
		if (array.Length != 1 || !double.TryParse(array[0].SyncData, NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || double.IsNaN(result) || double.IsInfinity(result))
		{
			throw new InvalidDataException("Invalid outdoor region visit clock.");
		}
		return result;
	}

	internal static void Prepare(EnvironmentSave save, double suppliedTime)
	{
		double num = ((suppliedTime != 0.0) ? suppliedTime : Math.Max(save.GameTime, EnvironmentConditions.GameTime));
		lastVisit = Read(save);
		if (NativeTravel.ResumingOutside)
		{
			lastVisit = num;
		}
		else if (AreaManager.Instance.IsAreaExpired("ChersoneseNewTerrain", (float)(lastVisit - num)))
		{
			HashSet<string> first = ResourceCatalog.Read(save.InteractionActivatorList);
			HashSet<string> second = ResourceCatalog.Read(save.InteractionActivatorList, "Outwardz.OpenCierzo.LooseSources.v1");
			HashSet<string> second2 = ResourceCatalog.Read(save.InteractionActivatorList, "Outwardz.OpenCierzo.Fixtures.v1");
			HashSet<string> actors = ActorCatalog.Read(save.InteractionActivatorList);
			HashSet<string> ids = new HashSet<string>(first.Concat(second).Concat(second2).Concat(actors));
			HashSet<string> outdoor = SavedItemPlacement.OutdoorItems(save.ItemList, new HashSet<string>(first.Concat(second)), actors);
			save.ItemList = save.ItemList.Where((BasicSaveData r) => !outdoor.Contains(r.Identifier.ToString())).ToList();
			save.CharList = save.CharList.Where((CharacterSaveData c) => !ActorCatalog.Outdoor(c, actors)).ToList();
			save.InteractionActivatorList = save.InteractionActivatorList.Where((BasicSaveData r) => !ids.Contains(r.Identifier.ToString()) && !ResourceCatalog.IsRecord(r) && !object.Equals(r.Identifier, "Outwardz.OpenCierzo.LooseSources.v1") && !object.Equals(r.Identifier, "Outwardz.OpenCierzo.Fixtures.v1") && !object.Equals(r.Identifier, "Outwardz.OpenCierzo.Actors.v1") && !ActorEvents.IsRecord(r) && !object.Equals(r.Identifier, "Outwardz.OpenCierzo.RegionVisit.v1") && !RecoveryLedger.IsLedger(r)).ToList();
			save.DropTablesList = save.DropTablesList.Where((BasicSaveData r) => !BelongsTo(r, ids)).ToList();
			save.UsedSoulSpots = (save.UsedSoulSpots ?? new List<string>()).Where((string id) => !Owned(id, ids)).ToList();
			lastVisit = num;
			Write(save.InteractionActivatorList, num);
			Plugin.Instance.SetStatus("Chersonese reached its native reset interval; outdoor resources and ordinary world drops reset. Town records are preserved.");
		}
	}

	internal static bool BelongsTo(BasicSaveData row, HashSet<string> ids)
	{
		return Owned(row.Identifier.ToString(), ids);
	}

	internal static bool Owned(string id, HashSet<string> ids)
	{
		if (!ids.Contains(id))
		{
			return ids.Any((string source) => id.StartsWith(source + "_", StringComparison.Ordinal) || (source.StartsWith("MerchantPouch_", StringComparison.Ordinal) && id.StartsWith("MerchantDrop_", StringComparison.Ordinal) && id.EndsWith("_" + source.Substring("MerchantPouch_".Length), StringComparison.Ordinal)));
		}
		return true;
	}

	internal static void Save(EnvironmentSave save)
	{
		if (double.IsNaN(lastVisit))
		{
			lastVisit = save.GameTime;
		}
		Write(save.InteractionActivatorList, lastVisit);
	}

	private static void Write(List<BasicSaveData> rows, double value)
	{
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0045: Expected O, but got Unknown
		rows.RemoveAll((BasicSaveData r) => object.Equals(r.Identifier, "Outwardz.OpenCierzo.RegionVisit.v1"));
		rows.Add(new BasicSaveData((object)"Outwardz.OpenCierzo.RegionVisit.v1", value.ToString("R", CultureInfo.InvariantCulture)));
	}
}
internal sealed class WorldResources
{
	private sealed class Candidate
	{
		internal Item Item;

		internal bool Loose;

		internal string SourceUID;

		internal string SceneName;

		internal string SaveUID;

		internal MonoBehaviour[] Scripts;

		internal Dictionary<Behaviour, bool> Behaviours;

		internal Dictionary<Collider, bool> Colliders;

		internal HashSet<int> AwakeObjects;

		internal Dictionary<Rigidbody, bool[]> Bodies;
	}

	private readonly HashSet<Component> retiredSources = new HashSet<Component>();

	private readonly List<Candidate> candidates = new List<Candidate>();

	private readonly List<Item> active = new List<Item>();

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

	private readonly HashSet<string> ids = new HashSet<string>();

	private readonly HashSet<string> looseIDs = new HashSet<string>();

	private readonly ResourceLootStartup lootStartup = new ResourceLootStartup();

	private static readonly FieldInfo QuestOnGather = typeof(SelfFilledItemContainer).GetField("m_questEventOnGather", BindingFlags.Instance | BindingFlags.NonPublic);

	internal string WaitingFor = "resource initialization";

	internal IEnumerable<string> IDs => ids;

	internal IEnumerable<string> LooseIDs => looseIDs;

	internal bool RetiredSource(Component c)
	{
		return retiredSources.Contains(c);
	}

	internal void Capture(Scene scene)
	{
		if (QuestOnGather == null)
		{
			throw new InvalidOperationException("Missing native resource quest-event field.");
		}
		GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects();
		foreach (GameObject val in rootGameObjects)
		{
			Item[] componentsInChildren = val.GetComponentsInChildren<Item>(true);
			foreach (Item item in componentsInChildren)
			{
				bool flag = ((object)item).GetType() == typeof(Item) && item.ItemID == 9200010 && ((Component)item).GetComponentsI