Decompiled source of Connected Doors v1.0.1

ConnectedDoors.dll

Decompiled 11 hours ago
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using Dawn;
using HarmonyLib;
using Unity.Netcode;
using UnityEngine;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace ConnectedDoors;

internal static class ModInfo
{
	public const string GUID = "ConnectedDoors.PocketRoomAddon";

	public const string NAME = "Connected Doors";

	public const string VERSION = "1.0.1";

	public const string PocketRoomsGuid = "MelanieMelicious.pocketRoom";

	public const string PocketRoomLibGuid = "MelanieMelicious.pocketRoomLib";

	public const string DawnLibGuid = "com.github.teamxiaolan.dawnlib";

	public const string DawnNamespace = "ConnectedDoors";
}
internal class DoorEntry
{
	public int Index;

	public string Name;

	public GameObject Prefab;

	public UnlockableItem Item;
}
[BepInPlugin("ConnectedDoors.PocketRoomAddon", "Connected Doors", "1.0.1")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class Plugin : BaseUnityPlugin
{
	internal static ManualLogSource Log;

	internal static readonly List<DoorEntry> Doors = new List<DoorEntry>();

	private static UnlockableItem template;

	private static GameObject holder;

	private ConfigEntry<int> price;

	internal static void Info(string text)
	{
		if (Log != null)
		{
			Log.LogInfo((object)text);
		}
	}

	internal static void Warn(string text)
	{
		if (Log != null)
		{
			Log.LogWarning((object)text);
		}
	}

	internal static void Error(string text)
	{
		if (Log != null)
		{
			Log.LogError((object)text);
		}
	}

	private void Awake()
	{
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		price = ((BaseUnityPlugin)this).Config.Bind<int>("General", "Price", 0, "Price of each connected door in the terminal store. 0 = free.");
		try
		{
			BuildDoors(Math.Max(0, price.Value));
			Harmony harmony = new Harmony("ConnectedDoors.PocketRoomAddon");
			TryPatch(harmony, typeof(StartOfRoundAwakePatch));
			TryPatch(harmony, typeof(TerminalAwakePatch));
			TryPatch(harmony, typeof(LoadUnlockablesPatch));
			Info("Connected Doors 1.0.1 loaded: " + Doors.Count + " doors registered.");
		}
		catch (Exception ex)
		{
			Error("Connected Doors could not start, no doors were added: " + ex);
		}
	}

	private static void TryPatch(Harmony harmony, Type patchClass)
	{
		try
		{
			harmony.PatchAll(patchClass);
		}
		catch (Exception ex)
		{
			Warn("Could not apply " + patchClass.Name + " (the doors are still registered): " + ex.Message);
		}
	}

	private static void BuildDoors(int cost)
	{
		template = FindTemplate();
		Info("Using the door of '" + template.unlockableName + "' (" + ((Object)template.prefabObject).name + ") as the model.");
		for (int i = 0; i < 2; i++)
		{
			DoorEntry doorEntry = CreateDoor(i, template);
			Doors.Add(doorEntry);
			DawnBridge.Register(doorEntry.Prefab, doorEntry.Item, doorEntry.Name, cost);
			Info("Registered '" + doorEntry.Name + "' (price " + cost + ").");
		}
	}

	private static UnlockableItem FindTemplate()
	{
		if (!Chainloader.PluginInfos.TryGetValue("MelanieMelicious.pocketRoom", out var value) || (Object)(object)value.Instance == (Object)null)
		{
			throw new InvalidOperationException("The Pocket Rooms plugin is not loaded.");
		}
		object instance = value.Instance;
		FieldInfo field = instance.GetType().GetField("unlockList", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		UnlockableItem[] array = ((!(field == null)) ? (field.GetValue(instance) as UnlockableItem[]) : null);
		if (array == null || array.Length == 0)
		{
			throw new InvalidOperationException("Pocket Rooms did not provide its furniture list (unsupported Pocket Rooms version?).");
		}
		UnlockableItem[] array2 = array;
		foreach (UnlockableItem val in array2)
		{
			if (val != null && !((Object)(object)val.prefabObject == (Object)null))
			{
				GameObject prefabObject = val.prefabObject;
				if (!((Object)(object)prefabObject.GetComponent<NetworkObject>() == (Object)null) && (Object)(object)FindDoorTeleport(prefabObject) != (Object)null)
				{
					return val;
				}
			}
		}
		throw new InvalidOperationException("No usable door was found in the Pocket Rooms furniture.");
	}

	private static RoomTeleport FindDoorTeleport(GameObject root)
	{
		RoomTeleport[] componentsInChildren = root.GetComponentsInChildren<RoomTeleport>(true);
		for (int i = 0; i < componentsInChildren.Length; i++)
		{
			if (!componentsInChildren[i].isExit)
			{
				return componentsInChildren[i];
			}
		}
		return null;
	}

	private static void EnsureHolder()
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Expected O, but got Unknown
		if (!((Object)(object)holder != (Object)null))
		{
			holder = new GameObject("ConnectedDoors_PrefabHolder");
			((Object)holder).hideFlags = (HideFlags)61;
			holder.SetActive(false);
		}
	}

	private static string DoorName(int index)
	{
		return "connecteddoor" + index;
	}

	private static GameObject BuildPrefab(int index, UnlockableItem source)
	{
		EnsureHolder();
		GameObject val = Object.Instantiate<GameObject>(source.prefabObject, holder.transform);
		((Object)val).hideFlags = (HideFlags)61;
		((Object)val).name = "ConnectedDoor" + index;
		RoomTeleport val2 = FindDoorTeleport(val);
		if ((Object)(object)val2 == (Object)null)
		{
			throw new InvalidOperationException("The door teleport went missing while cloning.");
		}
		Transform val3 = ((Component)val2).transform;
		while ((Object)(object)val3 != (Object)null && (Object)(object)val3.parent != (Object)(object)val.transform)
		{
			val3 = val3.parent;
		}
		if ((Object)(object)val3 == (Object)null)
		{
			throw new InvalidOperationException("The door teleport is not inside a door object.");
		}
		PocketRoom component = val.GetComponent<PocketRoom>();
		if ((Object)(object)component != (Object)null)
		{
			Object.DestroyImmediate((Object)(object)component);
		}
		Transform transform = val.transform;
		for (int num = transform.childCount - 1; num >= 0; num--)
		{
			Transform child = transform.GetChild(num);
			if ((Object)(object)child != (Object)(object)val3)
			{
				Object.DestroyImmediate((Object)(object)((Component)child).gameObject);
			}
		}
		InteractTrigger componentInChildren = ((Component)val3).GetComponentInChildren<InteractTrigger>(true);
		if ((Object)(object)componentInChildren != (Object)null)
		{
			componentInChildren.hoverTip = "Go through : [LMB]";
			componentInChildren.disabledHoverTip = "Nothing on the other side";
		}
		NetworkObject component2 = val.GetComponent<NetworkObject>();
		if ((Object)(object)component2 == (Object)null)
		{
			throw new InvalidOperationException("The door prefab has no NetworkObject.");
		}
		SetNetworkObjectHash(component2, StableHash("ConnectedDoors/" + DoorName(index)));
		ConnectedDoorLink connectedDoorLink = val.AddComponent<ConnectedDoorLink>();
		connectedDoorLink.index = index;
		connectedDoorLink.teleport = val2;
		connectedDoorLink.walkTrigger = ((Component)val2).GetComponent<Collider>();
		connectedDoorLink.interact = componentInChildren;
		connectedDoorLink.ship = val.GetComponent<AutoParentToShip>();
		int num2 = val.GetComponentsInChildren<NetworkBehaviour>(true).Length;
		Info(((Object)val).name + ": " + num2 + " network behaviours, " + transform.childCount + " child object(s) kept.");
		return val;
	}

	private static DoorEntry CreateDoor(int index, UnlockableItem source)
	{
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0015: Expected O, but got Unknown
		string text = DoorName(index);
		GameObject val = BuildPrefab(index, source);
		UnlockableItem val2 = new UnlockableItem();
		FieldInfo[] fields = typeof(UnlockableItem).GetFields(BindingFlags.Instance | BindingFlags.Public);
		foreach (FieldInfo fieldInfo in fields)
		{
			fieldInfo.SetValue(val2, fieldInfo.GetValue(source));
		}
		val2.unlockableName = text;
		val2.prefabObject = val;
		SetFieldIfPresent(val2, "shopSelectionNode", null);
		SetFieldIfPresent(val2, "alreadyUnlocked", false);
		SetFieldIfPresent(val2, "hasBeenUnlockedByPlayer", false);
		SetFieldIfPresent(val2, "inStorage", false);
		SetFieldIfPresent(val2, "hasBeenMoved", false);
		DoorEntry doorEntry = new DoorEntry();
		doorEntry.Index = index;
		doorEntry.Name = text;
		doorEntry.Prefab = val;
		doorEntry.Item = val2;
		return doorEntry;
	}

	private static void SetFieldIfPresent(object target, string fieldName, object value)
	{
		FieldInfo field = target.GetType().GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (field != null)
		{
			field.SetValue(target, value);
		}
	}

	private static void SetNetworkObjectHash(NetworkObject netObj, uint hash)
	{
		FieldInfo field = typeof(NetworkObject).GetField("GlobalObjectIdHash", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
		if (field == null)
		{
			throw new MissingFieldException("NetworkObject.GlobalObjectIdHash was not found (unsupported Netcode version?).");
		}
		field.SetValue(netObj, hash);
	}

	internal static uint StableHash(string text)
	{
		uint num = 2166136261u;
		for (int i = 0; i < text.Length; i++)
		{
			num ^= text[i];
			num *= 16777619;
		}
		return (num == 0) ? 1u : num;
	}

	internal static void EnsureDoorsAlive(string when)
	{
		foreach (DoorEntry door in Doors)
		{
			if ((Object)(object)door.Prefab != (Object)null)
			{
				continue;
			}
			Error("'" + door.Name + "' lost its prefab before " + when + " (something destroyed it). Rebuilding it so the game can still start.");
			try
			{
				GameObject val = (door.Prefab = BuildPrefab(door.Index, template));
				door.Item.prefabObject = val;
				try
				{
					if ((Object)(object)NetworkManager.Singleton != (Object)null)
					{
						NetworkManager.Singleton.AddNetworkPrefab(val);
					}
				}
				catch (Exception ex)
				{
					Warn("Could not register the rebuilt '" + door.Name + "' with Netcode: " + ex.Message);
				}
			}
			catch (Exception ex2)
			{
				Error("Could not rebuild '" + door.Name + "': " + ex2);
			}
		}
	}

	internal static void AssignUnlockableIds(string when)
	{
		StartOfRound instance = StartOfRound.Instance;
		if ((Object)(object)instance == (Object)null || (Object)(object)instance.unlockablesList == (Object)null)
		{
			return;
		}
		List<UnlockableItem> unlockables = instance.unlockablesList.unlockables;
		foreach (DoorEntry door in Doors)
		{
			if ((Object)(object)door.Prefab == (Object)null)
			{
				continue;
			}
			int num = unlockables.IndexOf(door.Item);
			if (num < 0)
			{
				Warn("'" + door.Name + "' is not in the unlockables list (" + when + "); was it registered by DawnLib?");
				continue;
			}
			PlaceableShipObject[] componentsInChildren = door.Prefab.GetComponentsInChildren<PlaceableShipObject>(true);
			foreach (PlaceableShipObject val in componentsInChildren)
			{
				val.unlockableID = num;
			}
			AutoParentToShip[] componentsInChildren2 = door.Prefab.GetComponentsInChildren<AutoParentToShip>(true);
			foreach (AutoParentToShip val2 in componentsInChildren2)
			{
				val2.unlockableID = num;
			}
			Info("'" + door.Name + "' has unlockable id " + num + " (" + when + ").");
		}
	}
}
[HarmonyPatch(typeof(StartOfRound), "Awake")]
internal static class StartOfRoundAwakePatch
{
	[HarmonyPriority(800)]
	[HarmonyPrefix]
	private static void CheckPrefabs()
	{
		try
		{
			Plugin.EnsureDoorsAlive("StartOfRound.Awake");
		}
		catch (Exception ex)
		{
			Plugin.Error("Connected Doors prefab check failed: " + ex);
		}
	}
}
[HarmonyPatch(typeof(Terminal), "Awake")]
internal static class TerminalAwakePatch
{
	[HarmonyPostfix]
	private static void AssignIds()
	{
		try
		{
			Plugin.AssignUnlockableIds("after Terminal.Awake");
		}
		catch (Exception ex)
		{
			Plugin.Error("Connected Doors could not assign unlockable ids: " + ex);
		}
	}
}
[HarmonyPatch(typeof(StartOfRound), "LoadUnlockables")]
internal static class LoadUnlockablesPatch
{
	[HarmonyPriority(800)]
	[HarmonyPrefix]
	private static void AssignIds()
	{
		try
		{
			Plugin.EnsureDoorsAlive("StartOfRound.LoadUnlockables");
			Plugin.AssignUnlockableIds("before LoadUnlockables");
		}
		catch (Exception ex)
		{
			Plugin.Error("Connected Doors could not assign unlockable ids: " + ex);
		}
	}
}
internal static class DawnBridge
{
	internal static void Register(GameObject prefab, UnlockableItem item, string key, int cost)
	{
		DawnLib.RegisterNetworkPrefab(prefab);
		DawnLib.DefineUnlockable(NamespacedKey<DawnUnlockableItemInfo>.From("ConnectedDoors", key), item, (Action<UnlockableInfoBuilder>)delegate(UnlockableInfoBuilder builder)
		{
			builder.SetCost(cost);
		});
	}
}
public class ConnectedDoorLink : MonoBehaviour
{
	public int index;

	public RoomTeleport teleport;

	public Collider walkTrigger;

	public InteractTrigger interact;

	public AutoParentToShip ship;

	private static readonly List<ConnectedDoorLink> Live = new List<ConnectedDoorLink>();

	private int frame;

	private bool? lastConnected;

	private void OnEnable()
	{
		if (!Live.Contains(this))
		{
			Live.Add(this);
		}
		Plugin.Info("Door " + index + " appeared.");
		RefreshAll();
	}

	private void OnDisable()
	{
		Live.Remove(this);
		RefreshAll();
	}

	private void OnDestroy()
	{
		Live.Remove(this);
	}

	private void Update()
	{
		frame++;
		if (frame % 15 == 0)
		{
			RefreshAll();
		}
	}

	private bool IsStored()
	{
		return (Object)(object)ship != (Object)null && ship.disableObject;
	}

	private static void RefreshAll()
	{
		for (int i = 0; i < Live.Count; i++)
		{
			ConnectedDoorLink connectedDoorLink = Live[i];
			ConnectedDoorLink partner = null;
			for (int j = 0; j < Live.Count; j++)
			{
				ConnectedDoorLink connectedDoorLink2 = Live[j];
				if ((Object)(object)connectedDoorLink2 != (Object)(object)connectedDoorLink && connectedDoorLink2.index != connectedDoorLink.index && !connectedDoorLink2.IsStored())
				{
					partner = connectedDoorLink2;
					break;
				}
			}
			connectedDoorLink.Link(partner);
		}
	}

	private void Link(ConnectedDoorLink partner)
	{
		if (!((Object)(object)teleport == (Object)null))
		{
			bool flag = (Object)(object)partner != (Object)null && (Object)(object)partner.teleport != (Object)null && (Object)(object)partner.teleport.entrancePoint != (Object)null && (Object)(object)teleport.entrancePoint != (Object)null && !IsStored();
			teleport.exitPoint = ((!flag) ? teleport.entrancePoint : partner.teleport.entrancePoint);
			if ((Object)(object)walkTrigger != (Object)null)
			{
				walkTrigger.enabled = flag;
			}
			if ((Object)(object)interact != (Object)null)
			{
				interact.interactable = flag;
			}
			bool? flag2 = lastConnected;
			if (!flag2.HasValue || lastConnected.Value != flag)
			{
				lastConnected = flag;
				Plugin.Info((!flag) ? ("Door " + index + " has no door to connect to (the other door is not placed or is in storage).") : ("Door " + index + " is now connected to door " + partner.index + "."));
			}
		}
	}
}