Decompiled source of MoonsHaunted v1.0.6

BepInEx/plugins/MoonsHaunted/MoonsHaunted.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Mono.Cecil;
using Mono.Cecil.Cil;
using MonoMod.Cil;
using MonoMod.RuntimeDetour;
using RoR2;
using RoR2.Items;
using RoR2.Projectile;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace MoonsHaunted;

[BepInPlugin("com.moonishaunted", "Moon's Haunted", "1.0.5")]
public sealed class Plugin : BaseUnityPlugin
{
	private delegate void OrigSetStack(CharacterBody body, ref BaseItemBodyBehavior behavior, Type behaviorType, int count);

	private delegate void SetStackHook(OrigSetStack original, CharacterBody body, ref BaseItemBodyBehavior behavior, Type behaviorType, int count);

	private Hook stackHook;

	private Hook hauntHook;

	private Hook sceneHook;

	private Hook targetHook;

	private Hook limitHook;

	private Hook attributesEnableHook;

	private Hook purchaseEnableHook;

	private ILHook triggerHook;

	private ILHook scheduleHook;

	private ILHook memoryHook;

	private ILHook expiryHook;

	private static readonly MethodInfo RefreshStacks = typeof(BaseItemBodyBehavior).GetMethod("UpdateBodyItemBehaviorStacks", BindingFlags.Static | BindingFlags.NonPublic);

	private static ManualLogSource log;

	private static readonly HashSet<int> activated = new HashSet<int>();

	private static readonly HashSet<int> succeeded = new HashSet<int>();

	private static readonly Dictionary<int, float> nextReport = new Dictionary<int, float>();

	[ThreadStatic]
	private static bool moonPlayerHaunting;

	private float nextCheck;

	private string lastScene;

	private bool lastServer;

	private ConfigEntry<bool> startOnMoon;

	private static ConfigEntry<float> hauntFrequency;

	private float nextSync;

	private bool hauntingReady;

	private Run observedRun;

	private bool startOnMoonThisRun;

	private bool testWarpSent;

	private float firstStageReadyAt;

	private static bool OnMoon()
	{
		SceneDef sceneDefForCurrentScene = SceneCatalog.GetSceneDefForCurrentScene();
		if (Object.op_Implicit((Object)(object)Run.instance) && Object.op_Implicit((Object)(object)sceneDefForCurrentScene))
		{
			if (!(sceneDefForCurrentScene.baseSceneName == "moon2"))
			{
				return sceneDefForCurrentScene.baseSceneName == "moon";
			}
			return true;
		}
		return false;
	}

	private static bool IsPlayer(CharacterBody body)
	{
		if (Object.op_Implicit((Object)(object)body) && Object.op_Implicit((Object)(object)body.master))
		{
			return Object.op_Implicit((Object)(object)((Component)body.master).GetComponent<PlayerCharacterMasterController>());
		}
		return false;
	}

	private void Awake()
	{
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0065: Expected O, but got Unknown
		//IL_0092: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Expected O, but got Unknown
		//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c9: Expected O, but got Unknown
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f4: Expected O, but got Unknown
		//IL_011a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0124: Expected O, but got Unknown
		//IL_0145: Unknown result type (might be due to invalid IL or missing references)
		//IL_014f: Expected O, but got Unknown
		//IL_016d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0177: Expected O, but got Unknown
		//IL_0172: Unknown result type (might be due to invalid IL or missing references)
		//IL_017c: Expected O, but got Unknown
		//IL_019a: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a4: Expected O, but got Unknown
		//IL_019f: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a9: Expected O, but got Unknown
		//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d1: Expected O, but got Unknown
		//IL_01cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d6: Expected O, but got Unknown
		//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_01fe: Expected O, but got Unknown
		//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
		//IL_0203: Expected O, but got Unknown
		//IL_0226: Unknown result type (might be due to invalid IL or missing references)
		//IL_0230: Expected O, but got Unknown
		//IL_0253: Unknown result type (might be due to invalid IL or missing references)
		//IL_025d: Expected O, but got Unknown
		log = ((BaseUnityPlugin)this).Logger;
		startOnMoon = ((BaseUnityPlugin)this).Config.Bind<bool>("Testing", "StartOnMoon", false, "Host lobby toggle: skip the first stage and travel immediately to Commencement for testing.");
		hauntFrequency = ((BaseUnityPlugin)this).Config.Bind<float>("Haunting", "FrequencyMultiplier", 10f, new ConfigDescription("Divide the moon haunting cooldown by this number. 10 means ten times as many opportunities to haunt. Keeps native animation and simultaneous-haunt cap.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), new object[0]));
		MoonTargets.Initialize();
		try
		{
			stackHook = new Hook((MethodBase)typeof(BaseItemBodyBehavior).GetMethod("SetItemStack", BindingFlags.Static | BindingFlags.NonPublic), (Delegate)new SetStackHook(SetStack));
			hauntHook = new Hook((MethodBase)typeof(HexHauntingBehaviour).GetMethod("TryHauntNearbyInteractable", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic), (Delegate)new Func<Func<HexHauntingBehaviour, bool>, HexHauntingBehaviour, bool>(Haunt));
			sceneHook = new Hook((MethodBase)typeof(DLC4ProgressionController).GetMethod("SceneAllowsSpawningForPoltergeistOrMimics"), (Delegate)new Func<Func<DLC4ProgressionController, bool>, DLC4ProgressionController, bool>(AllowScene));
			targetHook = new Hook((MethodBase)typeof(SpecialObjectAttributes).GetProperty("isTargetable").GetGetMethod(), (Delegate)new Func<Func<SpecialObjectAttributes, bool>, SpecialObjectAttributes, bool>(Targetable));
			limitHook = new Hook((MethodBase)typeof(CharacterMaster).GetMethod("GetDeployableSameSlotLimit"), (Delegate)new Func<Func<CharacterMaster, DeployableSlot, int>, CharacterMaster, DeployableSlot, int>(DeployableLimit));
			triggerHook = new ILHook((MethodBase)typeof(HauntedInteractable).GetMethod("OnTriggerStay", BindingFlags.Instance | BindingFlags.NonPublic), new Manipulator(PatchTrigger));
			scheduleHook = new ILHook((MethodBase)typeof(HexHauntingBehaviour).GetMethod("StepHauntNextInteractable", BindingFlags.Instance | BindingFlags.NonPublic), new Manipulator(PatchCooldownCalls));
			expiryHook = new ILHook((MethodBase)typeof(HexHauntingBehaviour).GetMethod("TryHauntNearbyInteractable", BindingFlags.Instance | BindingFlags.NonPublic), new Manipulator(PatchCooldownCalls));
			memoryHook = new ILHook((MethodBase)typeof(HexHauntingBehaviour).GetMethod("ScheduleNextClearHauntMemory", BindingFlags.Instance | BindingFlags.NonPublic), new Manipulator(PatchCooldownCalls));
			attributesEnableHook = new Hook((MethodBase)typeof(SpecialObjectAttributes).GetMethod("OnEnable", BindingFlags.Instance | BindingFlags.NonPublic), (Delegate)new Action<Action<SpecialObjectAttributes>, SpecialObjectAttributes>(AttributesEnabled));
			purchaseEnableHook = new Hook((MethodBase)typeof(PurchaseInteraction).GetMethod("OnEnable", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic), (Delegate)new Action<Action<PurchaseInteraction>, PurchaseInteraction>(PurchaseEnabled));
			Stage.onServerStageBegin += StageBegin;
			hauntingReady = true;
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Moon's Haunted 1.0.5 loaded. Server activates five hidden haunting stacks per moon player.");
		}
		catch (Exception ex)
		{
			DisposeHooks();
			((BaseUnityPlugin)this).Logger.LogError((object)ex);
			MoonTargets.Shutdown();
			hauntingReady = false;
		}
	}

	private static void SetStack(OrigSetStack original, CharacterBody body, ref BaseItemBodyBehavior behavior, Type behaviorType, int count)
	{
		bool flag = behaviorType == typeof(HexHauntingBehaviour) && NetworkServer.active && OnMoon() && IsPlayer(body);
		original(body, ref behavior, behaviorType, flag ? (count + 5) : count);
		if (flag && Object.op_Implicit((Object)(object)behavior) && activated.Add(((Object)body).GetInstanceID()))
		{
			log.LogInfo((object)("Activated hidden haunting for " + ((Object)body).name + ": native stacks=" + count + ", bonus=5, behavior stacks=" + behavior.stack));
		}
	}

	private static bool Haunt(Func<HexHauntingBehaviour, bool> original, HexHauntingBehaviour behavior)
	{
		bool flag = moonPlayerHaunting;
		moonPlayerHaunting = NetworkServer.active && OnMoon() && IsPlayer(((BaseItemBodyBehavior)behavior).body);
		try
		{
			bool flag2 = original(behavior);
			if (moonPlayerHaunting)
			{
				int instanceID = ((Object)((BaseItemBodyBehavior)behavior).body).GetInstanceID();
				if (flag2 && succeeded.Add(instanceID))
				{
					log.LogInfo((object)("Haunting succeeded for " + ((Object)((BaseItemBodyBehavior)behavior).body).name + "; an interactable is now haunted."));
				}
				if (!flag2 && (!nextReport.TryGetValue(instanceID, out var value) || Time.unscaledTime >= value))
				{
					nextReport[instanceID] = Time.unscaledTime + 30f;
					log.LogInfo((object)("Haunting attempt unsuccessful for " + ((Object)((BaseItemBodyBehavior)behavior).body).name + "; stacks=" + ((BaseItemBodyBehavior)behavior).stack + ", DLC controller=" + Object.op_Implicit((Object)(object)DLC4ProgressionController.instance) + ", trigger prefab=" + ReadHauntPrefab("hauntInteractableTriggerPrefab") + ", ghost prefab=" + ReadHauntPrefab("poltergeistMasterPrefab") + ". Native haunting needs an eligible interactable within 120m; this message repeats at most every 30s per body."));
				}
			}
			return flag2;
		}
		finally
		{
			moonPlayerHaunting = flag;
		}
	}

	private static void AttributesEnabled(Action<SpecialObjectAttributes> original, SpecialObjectAttributes attributes)
	{
		original(attributes);
		if (OnMoon())
		{
			MoonTargets.QueueTarget(((Component)attributes).gameObject);
		}
	}

	private static void PurchaseEnabled(Action<PurchaseInteraction> original, PurchaseInteraction purchase)
	{
		original(purchase);
		if (OnMoon())
		{
			MoonTargets.QueueTarget(((Component)purchase).gameObject);
		}
	}

	private static void PatchCooldownCalls(ILContext context)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Expected O, but got Unknown
		//IL_000c: Unknown result type (might be due to invalid IL or missing references)
		ILCursor val = new ILCursor(context);
		int num = 0;
		while (val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1] { HauntTiming.IsCooldownRead }))
		{
			val.Emit(OpCodes.Ldarg_0);
			val.EmitDelegate<Func<float, HexHauntingBehaviour, float>>((Func<float, HexHauntingBehaviour, float>)ScaleCooldown);
			num++;
		}
		if (num == 0)
		{
			throw new InvalidOperationException("Native haunt scheduling changed; no cooldown call was found in " + ((MemberReference)context.Method).Name);
		}
	}

	private static float ScaleCooldown(float seconds, HexHauntingBehaviour behavior)
	{
		if (!NetworkServer.active || !OnMoon() || !IsPlayer(((BaseItemBodyBehavior)behavior).body))
		{
			return seconds;
		}
		return seconds / hauntFrequency.Value;
	}

	private static bool ReadHauntPrefab(string name)
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_002e: Expected O, but got Unknown
		PropertyInfo property = typeof(HexHauntingBehaviour).GetProperty(name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
		if (property != null)
		{
			return Object.op_Implicit((Object)property.GetValue(null, null));
		}
		return false;
	}

	private static void PatchTrigger(ILContext context)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Expected O, but got Unknown
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		ILCursor val = new ILCursor(context);
		if (!val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[1]
		{
			delegate(Instruction instruction)
			{
				object operand = instruction.Operand;
				MethodReference val2 = (MethodReference)((operand is MethodReference) ? operand : null);
				return val2 != null && ((MemberReference)((MemberReference)val2).DeclaringType).FullName == "RoR2.Inventory" && ((MemberReference)val2).Name == "GetItemCountEffective";
			}
		}))
		{
			throw new InvalidOperationException("HauntedInteractable inventory check changed; refusing to silently skip the trigger patch.");
		}
		val.Emit(OpCodes.Ldarg_1);
		val.EmitDelegate<Func<int, Collider, int>>((Func<int, Collider, int>)delegate(int count, Collider collider)
		{
			CharacterBody body = (Object.op_Implicit((Object)(object)collider) ? ((Component)collider).GetComponent<CharacterBody>() : null);
			return (!NetworkServer.active || !OnMoon() || !IsPlayer(body)) ? count : (count + 5);
		});
	}

	private static bool Targetable(Func<SpecialObjectAttributes, bool> original, SpecialObjectAttributes attributes)
	{
		if (!moonPlayerHaunting || !Object.op_Implicit((Object)(object)((Component)attributes).GetComponent<MoonHauntTarget>()))
		{
			return original(attributes);
		}
		return true;
	}

	private static int DeployableLimit(Func<CharacterMaster, DeployableSlot, int> original, CharacterMaster master, DeployableSlot slot)
	{
		//IL_0002: 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)
		//IL_000c: Invalid comparison between Unknown and I4
		int num = original(master, slot);
		if ((int)slot != 38 || !NetworkServer.active || !OnMoon() || !Object.op_Implicit((Object)(object)master) || !Object.op_Implicit((Object)(object)((Component)master).GetComponent<PlayerCharacterMasterController>()))
		{
			return num;
		}
		return num + 5;
	}

	private static bool AllowScene(Func<DLC4ProgressionController, bool> original, DLC4ProgressionController controller)
	{
		if (!moonPlayerHaunting)
		{
			return original(controller);
		}
		return true;
	}

	private void OnGUI()
	{
		//IL_004f: 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_00da: Unknown result type (might be due to invalid IL or missing references)
		if (((Behaviour)this).enabled && NetworkServer.active && Object.op_Implicit((Object)(object)PreGameController.instance) && !Object.op_Implicit((Object)(object)Run.instance))
		{
			float num = Mathf.Min(360f, (float)Screen.width - 24f);
			GUI.Box(new Rect(12f, 12f, num, 108f), "Moon's Haunted - Test Settings");
			bool flag = GUI.Toggle(new Rect(24f, 42f, num - 24f, 26f), startOnMoon.Value, " Start on Moon (testing)");
			if (flag != startOnMoon.Value)
			{
				startOnMoon.Value = flag;
				((BaseUnityPlugin)this).Logger.LogInfo((object)("Host lobby Start on Moon changed to " + flag));
			}
			GUI.Label(new Rect(24f, 72f, num - 24f, 38f), "Next run skips directly to Commencement.\nNormal difficulty selection still applies.");
		}
	}

	private void CheckTestTravel()
	{
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_008e: Invalid comparison between Unknown and I4
		Run instance = Run.instance;
		if ((Object)(object)instance != (Object)(object)observedRun)
		{
			observedRun = instance;
			startOnMoonThisRun = startOnMoon.Value;
			testWarpSent = false;
			firstStageReadyAt = 0f;
		}
		if (!Object.op_Implicit((Object)(object)instance) || !NetworkServer.active || !startOnMoonThisRun || testWarpSent || OnMoon() || instance.stageClearCount != 0 || !Object.op_Implicit((Object)(object)Stage.instance))
		{
			return;
		}
		SceneDef sceneDefForCurrentScene = SceneCatalog.GetSceneDefForCurrentScene();
		if (!Object.op_Implicit((Object)(object)sceneDefForCurrentScene) || (int)sceneDefForCurrentScene.sceneType != 1)
		{
			return;
		}
		bool flag = false;
		foreach (PlayerCharacterMasterController instance2 in PlayerCharacterMasterController.instances)
		{
			if (Object.op_Implicit((Object)(object)instance2) && Object.op_Implicit((Object)(object)instance2.master) && Object.op_Implicit((Object)(object)instance2.master.GetBody()))
			{
				flag = true;
				break;
			}
		}
		if (!flag)
		{
			return;
		}
		if (firstStageReadyAt == 0f)
		{
			firstStageReadyAt = Time.unscaledTime + 3f;
		}
		else if (!(Time.unscaledTime < firstStageReadyAt))
		{
			SceneDef val = SceneCatalog.FindSceneDef("moon2");
			testWarpSent = true;
			if (!Object.op_Implicit((Object)(object)val))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)"Testing travel failed: moon2 scene is unavailable.");
				return;
			}
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Start on Moon testing enabled: host is advancing all players to moon2.");
			instance.AdvanceStage(val);
		}
	}

	private void Update()
	{
		CheckTestTravel();
		if (hauntingReady && OnMoon())
		{
			int num = MoonTargets.Prepare();
			if (num > 0)
			{
				((BaseUnityPlugin)this).Logger.LogDebug((object)("Prepared " + num + " more moon targets."));
			}
			if (Time.unscaledTime >= nextSync)
			{
				nextSync = Time.unscaledTime + 0.2f;
				MoonTargets.Synchronize();
			}
		}
		else
		{
			MoonTargets.Clear();
		}
		if (Time.unscaledTime < nextCheck)
		{
			return;
		}
		nextCheck = Time.unscaledTime + 1f;
		SceneDef sceneDefForCurrentScene = SceneCatalog.GetSceneDefForCurrentScene();
		string text = (Object.op_Implicit((Object)(object)sceneDefForCurrentScene) ? sceneDefForCurrentScene.baseSceneName : "<none>");
		if (text != lastScene || NetworkServer.active != lastServer)
		{
			lastScene = text;
			lastServer = NetworkServer.active;
			activated.Clear();
			succeeded.Clear();
			nextReport.Clear();
			((BaseUnityPlugin)this).Logger.LogInfo((object)("Scene=" + text + "; role=" + (lastServer ? "server/host" : "client") + "; moon bonus=" + (OnMoon() && lastServer)));
		}
		if (!hauntingReady || !OnMoon() || !NetworkServer.active)
		{
			return;
		}
		foreach (PlayerCharacterMasterController instance in PlayerCharacterMasterController.instances)
		{
			if (!Object.op_Implicit((Object)(object)instance) || !Object.op_Implicit((Object)(object)instance.master))
			{
				continue;
			}
			CharacterBody body = instance.master.GetBody();
			if (!Object.op_Implicit((Object)(object)body) || !Object.op_Implicit((Object)(object)body.inventory) || !Object.op_Implicit((Object)(object)Items.HexHaunting))
			{
				continue;
			}
			HexHauntingBehaviour component = ((Component)body).GetComponent<HexHauntingBehaviour>();
			int num2 = body.inventory.GetItemCountEffective(Items.HexHaunting) + 5;
			if (!Object.op_Implicit((Object)(object)component) || ((BaseItemBodyBehavior)component).stack != num2)
			{
				try
				{
					RefreshStacks.Invoke(null, new object[1] { body });
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogError((object)ex);
					hauntingReady = false;
					DisposeHooks();
					MoonTargets.Shutdown();
					break;
				}
			}
		}
	}

	private static void StageBegin(Stage stage)
	{
		foreach (PlayerCharacterMasterController instance in PlayerCharacterMasterController.instances)
		{
			if (Object.op_Implicit((Object)(object)instance) && Object.op_Implicit((Object)(object)instance.master))
			{
				CharacterBody body = instance.master.GetBody();
				if (Object.op_Implicit((Object)(object)body))
				{
					RefreshStacks.Invoke(null, new object[1] { body });
				}
			}
		}
	}

	private void DisposeHooks()
	{
		if (expiryHook != null)
		{
			expiryHook.Dispose();
		}
		if (scheduleHook != null)
		{
			scheduleHook.Dispose();
		}
		if (memoryHook != null)
		{
			memoryHook.Dispose();
		}
		if (attributesEnableHook != null)
		{
			attributesEnableHook.Dispose();
		}
		if (purchaseEnableHook != null)
		{
			purchaseEnableHook.Dispose();
		}
		if (triggerHook != null)
		{
			triggerHook.Dispose();
		}
		if (targetHook != null)
		{
			targetHook.Dispose();
		}
		if (limitHook != null)
		{
			limitHook.Dispose();
		}
		if (sceneHook != null)
		{
			sceneHook.Dispose();
		}
		if (hauntHook != null)
		{
			hauntHook.Dispose();
		}
		if (stackHook != null)
		{
			stackHook.Dispose();
		}
	}

	private void OnDestroy()
	{
		Stage.onServerStageBegin -= StageBegin;
		MoonTargets.Shutdown();
		DisposeHooks();
		activated.Clear();
		succeeded.Clear();
		nextReport.Clear();
	}
}
public sealed class MoonHauntTarget : MonoBehaviour, IInteractable
{
	public string key;

	public string GetContextString(Interactor interactor)
	{
		return "Haunted moon object";
	}

	public Interactability GetInteractability(Interactor interactor)
	{
		return (Interactability)0;
	}

	public void OnInteractionBegin(Interactor interactor)
	{
	}

	public bool ShouldIgnoreSpherecastForInteractibility(Interactor interactor)
	{
		return true;
	}

	public bool ShouldShowOnScanner()
	{
		return false;
	}

	public bool ShouldProximityHighlight()
	{
		return false;
	}

	public int GetInteractablePriority(Interactor interactor)
	{
		return 0;
	}

	public bool ShouldSpawnLesserInteractionRewards(Interactor interactor)
	{
		return false;
	}
}
internal static class MoonTargets
{
	internal sealed class Snapshot : MessageBase
	{
		internal string scene;

		internal readonly List<NetworkInstanceId> seats = new List<NetworkInstanceId>();

		internal readonly List<string> keys = new List<string>();

		public override void Serialize(NetworkWriter writer)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			writer.Write(scene);
			writer.Write((ushort)seats.Count);
			for (int i = 0; i < seats.Count; i++)
			{
				writer.Write(seats[i]);
				writer.Write(keys[i]);
			}
		}

		public override void Deserialize(NetworkReader reader)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			scene = reader.ReadString();
			int num = reader.ReadUInt16();
			for (int i = 0; i < num; i++)
			{
				seats.Add(reader.ReadNetworkId());
				keys.Add(reader.ReadString());
			}
		}
	}

	private const short SnapshotMessage = 28713;

	private static readonly Dictionary<string, GameObject> targets = new Dictionary<string, GameObject>();

	private static readonly HashSet<NetworkClient> registeredClients = new HashSet<NetworkClient>();

	private static readonly Dictionary<VehicleSeat, GameObject> mirroredPassengers = new Dictionary<VehicleSeat, GameObject>();

	private static readonly Dictionary<VehicleSeat, MoonHauntTarget> occupiedSeats = new Dictionary<VehicleSeat, MoonHauntTarget>();

	private static readonly Queue<GameObject> queuedTargets = new Queue<GameObject>();

	private static readonly HashSet<int> queuedIds = new HashSet<int>();

	private static readonly List<VehicleSeat> seatScratch = new List<VehicleSeat>();

	private static readonly HashSet<VehicleSeat> activeMirrors = new HashSet<VehicleSeat>();

	private static readonly MethodInfo SetPassenger = typeof(VehicleSeat).GetMethod("SetPassenger", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static readonly FieldInfo[] AttributeFields = typeof(SpecialObjectAttributes).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

	private static Snapshot pending;

	private static string currentScene;

	private static bool initialized;

	private static bool dirty;

	private static float nextHeartbeat;

	internal static void Initialize()
	{
		if (!initialized)
		{
			initialized = true;
			VehicleSeat.onPassengerEnterGlobal += PassengerEnter;
			VehicleSeat.onPassengerExitGlobal += PassengerExit;
		}
	}

	private static void PassengerEnter(VehicleSeat seat, GameObject passenger)
	{
		if (NetworkServer.active && currentScene != null && Object.op_Implicit((Object)(object)seat) && Object.op_Implicit((Object)(object)passenger))
		{
			MoonHauntTarget component = passenger.GetComponent<MoonHauntTarget>();
			if (Object.op_Implicit((Object)(object)component))
			{
				RefreshKey(component);
				occupiedSeats[seat] = component;
				dirty = true;
			}
		}
	}

	private static void PassengerExit(VehicleSeat seat, GameObject passenger)
	{
		if (occupiedSeats.Remove(seat))
		{
			dirty = true;
		}
	}

	internal static void QueueTarget(GameObject target)
	{
		//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)
		if (Object.op_Implicit((Object)(object)target) && currentScene != null)
		{
			Scene scene = target.scene;
			if (!(((Scene)(ref scene)).name != currentScene) && !Object.op_Implicit((Object)(object)target.GetComponent<MoonHauntTarget>()) && queuedIds.Add(((Object)target).GetInstanceID()))
			{
				queuedTargets.Enqueue(target);
			}
		}
	}

	internal static int Prepare()
	{
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_0162: Unknown result type (might be due to invalid IL or missing references)
		//IL_0167: Unknown result type (might be due to invalid IL or missing references)
		string cachedName = SceneCatalog.GetSceneDefForCurrentScene().cachedName;
		if (currentScene != cachedName)
		{
			Clear();
			currentScene = cachedName;
			dirty = true;
			MonoBehaviour[] array = Object.FindObjectsOfType<MonoBehaviour>(true);
			foreach (MonoBehaviour val in array)
			{
				if (Object.op_Implicit((Object)(object)val))
				{
					Scene scene = ((Component)val).gameObject.scene;
					if (!(((Scene)(ref scene)).name != cachedName) && (val is IInteractable || val is HoldoutZoneController || val is SpecialObjectAttributes || val is JumpVolume || val is SceneExitController || val is EscapeSequenceExtractionZone))
					{
						QueueTarget(((Component)val).gameObject);
					}
				}
			}
		}
		int num = 0;
		for (int j = 0; j < 8 && queuedTargets.Count > 0; j++)
		{
			GameObject val2 = queuedTargets.Dequeue();
			if (!Object.op_Implicit((Object)(object)val2))
			{
				continue;
			}
			queuedIds.Remove(((Object)val2).GetInstanceID());
			Scene scene2 = val2.scene;
			if (((Scene)(ref scene2)).name != cachedName || Object.op_Implicit((Object)(object)val2.GetComponentInParent<CharacterBody>()) || Object.op_Implicit((Object)(object)val2.GetComponentInParent<ProjectileController>()) || (Object.op_Implicit((Object)(object)val2.GetComponent<VehicleSeat>()) && !Object.op_Implicit((Object)(object)val2.GetComponent<EscapeSequenceExtractionZone>()) && !Object.op_Implicit((Object)(object)val2.GetComponent<HoldoutZoneController>())))
			{
				continue;
			}
			NetworkIdentity componentInParent = val2.GetComponentInParent<NetworkIdentity>();
			GameObject obj;
			if (Object.op_Implicit((Object)(object)componentInParent))
			{
				Scene scene3 = ((Component)componentInParent).gameObject.scene;
				if (((Scene)(ref scene3)).name == cachedName)
				{
					obj = ((Component)componentInParent).gameObject;
					goto IL_0183;
				}
			}
			obj = val2;
			goto IL_0183;
			IL_0183:
			GameObject val3 = obj;
			if (Object.op_Implicit((Object)(object)val3.GetComponent<CharacterBody>()) || Object.op_Implicit((Object)(object)val3.GetComponent<ProjectileController>()))
			{
				continue;
			}
			MoonHauntTarget component = val3.GetComponent<MoonHauntTarget>();
			if (Object.op_Implicit((Object)(object)component))
			{
				RefreshKey(component);
				continue;
			}
			component = val3.AddComponent<MoonHauntTarget>();
			component.key = MakeKey(val3.transform);
			targets[component.key] = val3;
			SpecialObjectAttributes val4 = val3.GetComponent<SpecialObjectAttributes>();
			bool flag = !Object.op_Implicit((Object)(object)val4);
			if (flag)
			{
				val4 = val3.AddComponent<SpecialObjectAttributes>();
			}
			FieldInfo[] attributeFields = AttributeFields;
			foreach (FieldInfo fieldInfo in attributeFields)
			{
				if (fieldInfo.FieldType.IsGenericType && fieldInfo.FieldType.GetGenericTypeDefinition() == typeof(List<>) && fieldInfo.GetValue(val4) == null)
				{
					fieldInfo.SetValue(val4, Activator.CreateInstance(fieldInfo.FieldType));
				}
			}
			val4.grabbable = true;
			val4.locked = false;
			((Behaviour)val4).enabled = true;
			if (flag)
			{
				val4.renderersToDisable.AddRange(val3.GetComponentsInChildren<Renderer>(true));
				HashSet<GameObject> hashSet = new HashSet<GameObject>();
				Collider[] componentsInChildren = val3.GetComponentsInChildren<Collider>(true);
				foreach (Collider val5 in componentsInChildren)
				{
					hashSet.Add(((Component)val5).gameObject);
				}
				val4.collisionToDisable.AddRange(hashSet);
				MonoBehaviour[] components = val3.GetComponents<MonoBehaviour>();
				foreach (MonoBehaviour val6 in components)
				{
					if (Object.op_Implicit((Object)(object)val6) && (Object)(object)val6 != (Object)(object)component && (Object)(object)val6 != (Object)(object)val4 && (val6 is IInteractable || val6 is HoldoutZoneController))
					{
						val4.behavioursToDisable.Add(val6);
					}
				}
				val4.maxDurability = 20;
				val4.bestName = ((Object)val3).name;
			}
			num++;
		}
		return num;
	}

	private static void RefreshKey(MoonHauntTarget marker)
	{
		//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_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)
		NetworkIdentity component = ((Component)marker).GetComponent<NetworkIdentity>();
		if (!Object.op_Implicit((Object)(object)component))
		{
			return;
		}
		NetworkInstanceId netId = component.netId;
		if (((NetworkInstanceId)(ref netId)).Value == 0)
		{
			return;
		}
		NetworkInstanceId netId2 = component.netId;
		string text = "net:" + ((NetworkInstanceId)(ref netId2)).Value;
		if (!(text == marker.key))
		{
			if (marker.key != null)
			{
				targets.Remove(marker.key);
			}
			marker.key = text;
			targets[text] = ((Component)marker).gameObject;
		}
	}

	private static string MakeKey(Transform target)
	{
		//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_0026: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0130: 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)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		NetworkIdentity component = ((Component)target).GetComponent<NetworkIdentity>();
		if (Object.op_Implicit((Object)(object)component))
		{
			NetworkInstanceId netId = component.netId;
			if (((NetworkInstanceId)(ref netId)).Value != 0)
			{
				NetworkInstanceId netId2 = component.netId;
				return "net:" + ((NetworkInstanceId)(ref netId2)).Value;
			}
		}
		List<string> list = new List<string>();
		Transform val = target;
		while (Object.op_Implicit((Object)(object)val))
		{
			int num = 0;
			if (Object.op_Implicit((Object)(object)val.parent))
			{
				for (int i = 0; i < val.GetSiblingIndex(); i++)
				{
					if (((Object)val.parent.GetChild(i)).name == ((Object)val).name)
					{
						num++;
					}
				}
			}
			else
			{
				Scene scene = ((Component)val).gameObject.scene;
				GameObject[] rootGameObjects = ((Scene)(ref scene)).GetRootGameObjects();
				foreach (GameObject val2 in rootGameObjects)
				{
					if ((Object)(object)val2 == (Object)(object)((Component)val).gameObject)
					{
						break;
					}
					if (((Object)val2).name == ((Object)val).name)
					{
						num++;
					}
				}
			}
			list.Add(Uri.EscapeDataString(((Object)val).name) + "#" + num);
			val = val.parent;
		}
		list.Reverse();
		Scene scene2 = ((Component)target).gameObject.scene;
		return "scene:" + ((Scene)(ref scene2)).name + "/" + string.Join("/", list.ToArray());
	}

	private static void RegisterClients()
	{
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Expected O, but got Unknown
		foreach (NetworkClient allClient in NetworkClient.allClients)
		{
			if (allClient == null || !registeredClients.Add(allClient))
			{
				continue;
			}
			allClient.RegisterHandler((short)28713, (NetworkMessageDelegate)delegate(NetworkMessage message)
			{
				if (!NetworkServer.active)
				{
					pending = message.ReadMessage<Snapshot>();
				}
			});
		}
	}

	internal static void Synchronize()
	{
		//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_020d: 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_00af: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		RegisterClients();
		if (NetworkServer.active)
		{
			if (!dirty && Time.unscaledTime < nextHeartbeat)
			{
				return;
			}
			Snapshot snapshot = new Snapshot();
			snapshot.scene = currentScene;
			Snapshot snapshot2 = snapshot;
			seatScratch.Clear();
			foreach (KeyValuePair<VehicleSeat, MoonHauntTarget> occupiedSeat in occupiedSeats)
			{
				VehicleSeat key = occupiedSeat.Key;
				MoonHauntTarget value = occupiedSeat.Value;
				if (!Object.op_Implicit((Object)(object)key) || !Object.op_Implicit((Object)(object)value) || !Object.op_Implicit((Object)(object)key.currentPassengerTransform) || (Object)(object)key.currentPassengerTransform != (Object)(object)((Component)value).transform)
				{
					seatScratch.Add(key);
					continue;
				}
				NetworkInstanceId netId = ((NetworkBehaviour)key).netId;
				if (((NetworkInstanceId)(ref netId)).Value != 0)
				{
					RefreshKey(value);
					snapshot2.seats.Add(((NetworkBehaviour)key).netId);
					snapshot2.keys.Add(value.key);
				}
			}
			foreach (VehicleSeat item in seatScratch)
			{
				occupiedSeats.Remove(item);
			}
			NetworkServer.SendToAll((short)28713, (MessageBase)(object)snapshot2);
			dirty = false;
			nextHeartbeat = Time.unscaledTime + 5f;
		}
		else
		{
			if (pending == null || !(pending.scene == currentScene))
			{
				return;
			}
			activeMirrors.Clear();
			bool flag = false;
			for (int i = 0; i < pending.seats.Count; i++)
			{
				GameObject val = ClientScene.FindLocalObject(pending.seats[i]);
				if (!targets.TryGetValue(pending.keys[i], out var value2) && pending.keys[i].StartsWith("net:", StringComparison.Ordinal) && uint.TryParse(pending.keys[i].Substring(4), out var result))
				{
					value2 = ClientScene.FindLocalObject(new NetworkInstanceId(result));
				}
				if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)value2) || !Object.op_Implicit((Object)(object)value2.GetComponent<MoonHauntTarget>()))
				{
					flag = true;
					continue;
				}
				VehicleSeat component = val.GetComponent<VehicleSeat>();
				if (Object.op_Implicit((Object)(object)component))
				{
					if ((Object)(object)component.currentPassengerTransform != (Object)(object)value2.transform)
					{
						SetPassenger.Invoke(component, new object[1] { value2 });
					}
					mirroredPassengers[component] = value2;
					activeMirrors.Add(component);
				}
			}
			seatScratch.Clear();
			foreach (KeyValuePair<VehicleSeat, GameObject> mirroredPassenger in mirroredPassengers)
			{
				if (!Object.op_Implicit((Object)(object)mirroredPassenger.Key) || !activeMirrors.Contains(mirroredPassenger.Key))
				{
					seatScratch.Add(mirroredPassenger.Key);
				}
			}
			foreach (VehicleSeat item2 in seatScratch)
			{
				if (Object.op_Implicit((Object)(object)item2) && Object.op_Implicit((Object)(object)mirroredPassengers[item2]) && (Object)(object)item2.currentPassengerTransform == (Object)(object)mirroredPassengers[item2].transform)
				{
					MethodInfo setPassenger = SetPassenger;
					object[] parameters = new object[1];
					setPassenger.Invoke(item2, parameters);
				}
				mirroredPassengers.Remove(item2);
			}
			if (!flag)
			{
				pending = null;
			}
		}
	}

	internal static void Clear()
	{
		if (currentScene == null && registeredClients.Count == 0 && queuedTargets.Count == 0)
		{
			return;
		}
		foreach (NetworkClient registeredClient in registeredClients)
		{
			if (registeredClient != null)
			{
				registeredClient.UnregisterHandler((short)28713);
			}
		}
		registeredClients.Clear();
		targets.Clear();
		mirroredPassengers.Clear();
		occupiedSeats.Clear();
		queuedTargets.Clear();
		queuedIds.Clear();
		seatScratch.Clear();
		activeMirrors.Clear();
		pending = null;
		currentScene = null;
		dirty = false;
		nextHeartbeat = 0f;
	}

	internal static void Shutdown()
	{
		if (initialized)
		{
			VehicleSeat.onPassengerEnterGlobal -= PassengerEnter;
			VehicleSeat.onPassengerExitGlobal -= PassengerExit;
			initialized = false;
		}
		Clear();
	}
}
internal static class HauntTiming
{
	internal static bool IsCooldownRead(Instruction instruction)
	{
		object operand = instruction.Operand;
		MethodReference val = (MethodReference)((operand is MethodReference) ? operand : null);
		object operand2 = instruction.Operand;
		FieldReference val2 = (FieldReference)((operand2 is FieldReference) ? operand2 : null);
		if (val == null || !(((MemberReference)((MemberReference)val).DeclaringType).FullName == "RoR2.Items.HexHauntingBehaviour") || !(((MemberReference)val).Name == "GetCooldownValue"))
		{
			if (val2 != null && ((MemberReference)((MemberReference)val2).DeclaringType).FullName == "RoR2.Items.HexHauntingBehaviour")
			{
				return ((MemberReference)val2).Name == "baseHauntCooldown";
			}
			return false;
		}
		return true;
	}
}