Decompiled source of HostInteriorSelector v0.1.3

plugins/HostInteriorSelector.dll

Decompiled an hour ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("HostInteriorSelector")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.3.0")]
[assembly: AssemblyInformationalVersion("0.1.3")]
[assembly: AssemblyProduct("HostInteriorSelector")]
[assembly: AssemblyTitle("HostInteriorSelector")]
[assembly: AssemblyVersion("0.1.3.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace HostInteriorSelector
{
	[HarmonyPatch(typeof(RoundManager), "GenerateNewFloor")]
	internal static class InteriorFilterPatch
	{
		private enum VanillaInterior
		{
			Unknown,
			Factory,
			Mansion,
			Mineshaft
		}

		internal sealed class FilterState
		{
			internal SelectableLevel? Level;

			internal IntWithRarity[]? Original;
		}

		[HarmonyPrefix]
		private static void Prefix(RoundManager __instance, out FilterState __state)
		{
			__state = new FilterState();
			try
			{
				if ((Object)(object)NetworkManager.Singleton == (Object)null || !NetworkManager.Singleton.IsHost || (Plugin.FactoryEnabled.Value && Plugin.MansionEnabled.Value && Plugin.MineshaftEnabled.Value))
				{
					return;
				}
				SelectableLevel currentLevel = __instance.currentLevel;
				IntWithRarity[] array = currentLevel?.dungeonFlowTypes;
				if ((Object)(object)currentLevel == (Object)null || array == null || array.Length == 0)
				{
					Plugin.Log.LogWarning((object)"Interior filtering skipped: the current level has no dungeon-flow choices.");
					return;
				}
				Dictionary<int, VanillaInterior> discovered = new Dictionary<int, VanillaInterior>();
				for (int i = 0; i < __instance.dungeonFlowTypes.Length; i++)
				{
					string displayName;
					VanillaInterior vanillaInterior = Identify(__instance.dungeonFlowTypes[i], i, out displayName);
					discovered[i] = vanillaInterior;
					Plugin.Log.LogInfo((object)$"Interior definition [{i}] '{displayName}' classified as {vanillaInterior}.");
				}
				IntWithRarity[] array2 = array.Where((IntWithRarity entry) => IsAllowed(entry, discovered)).ToArray();
				if (array2.Length == array.Length)
				{
					Plugin.Log.LogInfo((object)"Host interior settings did not remove any choices for this level.");
					return;
				}
				if (array2.Length == 0)
				{
					array2 = CreateFallbackChoices(discovered);
					if (array2.Length == 0)
					{
						Plugin.Log.LogWarning((object)"Interior settings left no valid choice and no enabled vanilla definition could be found; using the game's unmodified list for safe generation.");
						return;
					}
					Plugin.Log.LogInfo((object)$"This moon offered none of the enabled vanilla interiors; supplied {array2.Length} enabled global choice(s) to generation.");
				}
				__state.Level = currentLevel;
				__state.Original = array;
				currentLevel.dungeonFlowTypes = array2;
				Plugin.Log.LogInfo((object)$"Host filtered interior choices from {array.Length} to {array2.Length}; unknown/custom interiors were preserved.");
			}
			catch (Exception arg)
			{
				Restore(__state);
				Plugin.Log.LogError((object)$"Interior filtering failed safely; vanilla selection will be used. {arg}");
			}
		}

		[HarmonyFinalizer]
		private static Exception? Finalizer(Exception? __exception, RoundManager __instance, FilterState __state)
		{
			if (__state.Original != null && __instance.currentDungeonType >= 0 && __instance.currentDungeonType < __instance.dungeonFlowTypes.Length)
			{
				string displayName;
				VanillaInterior vanillaInterior = Identify(__instance.dungeonFlowTypes[__instance.currentDungeonType], __instance.currentDungeonType, out displayName);
				Plugin.Log.LogInfo((object)$"Host selected interior [{__instance.currentDungeonType}] '{displayName}' classified as {vanillaInterior}.");
				if (!IsKindEnabled(vanillaInterior))
				{
					Plugin.Log.LogWarning((object)$"The game selected disabled interior {vanillaInterior}; please report the surrounding Interior Selector log lines.");
				}
			}
			Restore(__state);
			return __exception;
		}

		private static bool IsAllowed(IntWithRarity entry, Dictionary<int, VanillaInterior> discovered)
		{
			if (!discovered.TryGetValue(entry.id, out var value))
			{
				return true;
			}
			return IsKindEnabled(value);
		}

		private static IntWithRarity[] CreateFallbackChoices(Dictionary<int, VanillaInterior> discovered)
		{
			return (from pair in discovered
				where pair.Value != VanillaInterior.Unknown && IsKindEnabled(pair.Value)
				group pair by pair.Value).Select((Func<IGrouping<VanillaInterior, KeyValuePair<int, VanillaInterior>>, IntWithRarity>)((IGrouping<VanillaInterior, KeyValuePair<int, VanillaInterior>> group) => new IntWithRarity(group.First().Key, 1, (LevelAmbienceLibrary)null))).ToArray();
		}

		private static bool IsKindEnabled(VanillaInterior kind)
		{
			return kind switch
			{
				VanillaInterior.Factory => Plugin.FactoryEnabled.Value, 
				VanillaInterior.Mansion => Plugin.MansionEnabled.Value, 
				VanillaInterior.Mineshaft => Plugin.MineshaftEnabled.Value, 
				_ => true, 
			};
		}

		private static VanillaInterior Identify(IndoorMapType map, int index, out string displayName)
		{
			object obj = ((object)map)?.GetType().GetField("dungeonFlow", BindingFlags.Instance | BindingFlags.Public)?.GetValue(map);
			Object val = (Object)((obj is Object) ? obj : null);
			displayName = ((val != null) ? val.name : (obj?.ToString() ?? "<null>"));
			string text = new string(displayName.Where(char.IsLetterOrDigit).Select(char.ToLowerInvariant).ToArray());
			if (text.Contains("mine") || text.Contains("cave"))
			{
				return VanillaInterior.Mineshaft;
			}
			if (text.Contains("mansion") || text.Contains("manor"))
			{
				return VanillaInterior.Mansion;
			}
			if (text.Contains("factory") || text.Contains("facility"))
			{
				return VanillaInterior.Factory;
			}
			if (text.StartsWith("level1flow", StringComparison.Ordinal))
			{
				return VanillaInterior.Factory;
			}
			if (text.StartsWith("level2flow", StringComparison.Ordinal))
			{
				return VanillaInterior.Mansion;
			}
			if (text.StartsWith("level3flow", StringComparison.Ordinal) || text.StartsWith("level4flow", StringComparison.Ordinal))
			{
				return VanillaInterior.Mineshaft;
			}
			switch (index)
			{
			case 0:
			case 2:
			case 3:
				return VanillaInterior.Factory;
			case 1:
				return VanillaInterior.Mansion;
			case 4:
				return VanillaInterior.Mineshaft;
			default:
				return VanillaInterior.Unknown;
			}
		}

		private static void Restore(FilterState? state)
		{
			if ((Object)(object)state?.Level != (Object)null && state.Original != null)
			{
				state.Level.dungeonFlowTypes = state.Original;
				state.Level = null;
				state.Original = null;
			}
		}
	}
	[HarmonyPatch(typeof(MenuManager), "Start")]
	internal static class MenuPatch
	{
		[HarmonyPostfix]
		private static void Postfix(MenuManager __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && !__instance.isInitScene && !((Object)(object)((Component)__instance).GetComponent<InteriorSelectorMenu>() != (Object)null))
			{
				Transform val = (((Object)(object)__instance.menuButtons != (Object)null) ? __instance.menuButtons.transform.parent : null);
				if ((Object)(object)val == (Object)null)
				{
					Plugin.Log.LogWarning((object)"Main-menu canvas was not found; Interior Selector UI was not created.");
				}
				else
				{
					((Component)__instance).gameObject.AddComponent<InteriorSelectorMenu>().Build(val, __instance.versionNumberText);
				}
			}
		}
	}
	internal sealed class InteriorSelectorMenu : MonoBehaviour
	{
		private GameObject _panel;

		private Toggle _factory;

		private Toggle _mansion;

		private Toggle _mineshaft;

		private TextMeshProUGUI _validationText;

		private TMP_FontAsset? _font;

		internal void Build(Transform menuRoot, TextMeshProUGUI fontSource)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			//IL_0078: 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_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_013d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0213: Unknown result type (might be due to invalid IL or missing references)
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			//IL_0247: Unknown result type (might be due to invalid IL or missing references)
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			//IL_026c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0276: Expected O, but got Unknown
			_font = (((Object)(object)fontSource != (Object)null) ? ((TMP_Text)fontSource).font : null);
			((UnityEvent)CreateButton(menuRoot, "Interior Selector", new Vector2(0.5f, 0f), new Vector2(0f, 72f), new Vector2(260f, 48f)).onClick).AddListener(new UnityAction(Open));
			_panel = CreateRect("InteriorSelectorPanel", menuRoot, new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(460f, 410f));
			_panel.transform.SetAsLastSibling();
			((Graphic)_panel.AddComponent<Image>()).color = new Color(0.035f, 0.055f, 0.045f, 0.97f);
			CreateText(_panel.transform, "Interior Selector", new Vector2(0f, 150f), new Vector2(400f, 55f), 30f, (TextAlignmentOptions)514);
			CreateText(_panel.transform, "Used only when you host", new Vector2(0f, 112f), new Vector2(400f, 35f), 17f, (TextAlignmentOptions)514);
			_factory = CreateToggle(_panel.transform, "Factory", 55f);
			_mansion = CreateToggle(_panel.transform, "Mansion", 0f);
			_mineshaft = CreateToggle(_panel.transform, "Mineshaft", -55f);
			_validationText = CreateText(_panel.transform, string.Empty, new Vector2(0f, -98f), new Vector2(400f, 32f), 16f, (TextAlignmentOptions)514);
			((Graphic)_validationText).color = new Color(1f, 0.45f, 0.4f, 1f);
			((UnityEvent)CreateButton(_panel.transform, "Save & Close", new Vector2(0.5f, 0.5f), new Vector2(0f, -140f), new Vector2(220f, 48f)).onClick).AddListener(new UnityAction(SaveAndClose));
			_panel.SetActive(false);
			Plugin.Log.LogInfo((object)"Interior Selector button added to the current main menu.");
		}

		private void Open()
		{
			_factory.isOn = Plugin.FactoryEnabled.Value;
			_mansion.isOn = Plugin.MansionEnabled.Value;
			_mineshaft.isOn = Plugin.MineshaftEnabled.Value;
			((TMP_Text)_validationText).text = string.Empty;
			_panel.SetActive(true);
		}

		private void SaveAndClose()
		{
			if (!_factory.isOn && !_mansion.isOn && !_mineshaft.isOn)
			{
				((TMP_Text)_validationText).text = "Select at least one interior.";
				Plugin.Log.LogWarning((object)"Interior settings were not saved because every option was disabled.");
				return;
			}
			Plugin.FactoryEnabled.Value = _factory.isOn;
			Plugin.MansionEnabled.Value = _mansion.isOn;
			Plugin.MineshaftEnabled.Value = _mineshaft.isOn;
			Plugin.Settings.Save();
			_panel.SetActive(false);
			Plugin.Log.LogInfo((object)$"Saved interior settings: Factory={_factory.isOn}, Mansion={_mansion.isOn}, Mineshaft={_mineshaft.isOn}.");
		}

		private Toggle CreateToggle(Transform parent, string label, float y)
		{
			//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_0030: 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_0065: 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_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = CreateRect(label + "Toggle", parent, new Vector2(0.5f, 0.5f), new Vector2(0f, y), new Vector2(310f, 44f));
			Toggle obj = val.AddComponent<Toggle>();
			GameObject val2 = CreateRect("Box", val.transform, new Vector2(0f, 0.5f), new Vector2(22f, 0f), new Vector2(32f, 32f));
			Image val3 = val2.AddComponent<Image>();
			((Graphic)val3).color = new Color(0.16f, 0.2f, 0.17f, 1f);
			Image val4 = CreateRect("Checkmark", val2.transform, new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(22f, 22f)).AddComponent<Image>();
			((Graphic)val4).color = new Color(0.42f, 0.85f, 0.5f, 1f);
			((Selectable)obj).targetGraphic = (Graphic)(object)val3;
			obj.graphic = (Graphic)(object)val4;
			CreateText(val.transform, label, new Vector2(48f, 0f), new Vector2(250f, 44f), 23f, (TextAlignmentOptions)513);
			return obj;
		}

		private Button CreateButton(Transform parent, string label, Vector2 anchor, Vector2 position, Vector2 size)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: 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_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = CreateRect(label.Replace(" ", "") + "Button", parent, anchor, position, size);
			Image val2 = val.AddComponent<Image>();
			((Graphic)val2).color = new Color(0.12f, 0.24f, 0.16f, 0.98f);
			Button obj = val.AddComponent<Button>();
			((Selectable)obj).targetGraphic = (Graphic)(object)val2;
			ColorBlock colors = ((Selectable)obj).colors;
			((ColorBlock)(ref colors)).highlightedColor = new Color(0.2f, 0.42f, 0.25f, 1f);
			((Selectable)obj).colors = colors;
			CreateText(val.transform, label, Vector2.zero, size, 21f, (TextAlignmentOptions)514);
			return obj;
		}

		private TextMeshProUGUI CreateText(Transform parent, string value, Vector2 position, Vector2 size, float fontSize, TextAlignmentOptions alignment)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: 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_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			TextMeshProUGUI val = CreateRect(value.Replace(" ", "") + "Text", parent, new Vector2(0.5f, 0.5f), position, size).AddComponent<TextMeshProUGUI>();
			((TMP_Text)val).text = value;
			((TMP_Text)val).fontSize = fontSize;
			((TMP_Text)val).alignment = alignment;
			((Graphic)val).color = Color.white;
			((Graphic)val).raycastTarget = false;
			if ((Object)(object)_font != (Object)null)
			{
				((TMP_Text)val).font = _font;
			}
			return val;
		}

		private static GameObject CreateRect(string name, Transform parent, Vector2 anchor, Vector2 position, Vector2 size)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//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_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: 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_0050: 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_005e: Expected O, but got Unknown
			GameObject val = new GameObject(name, new Type[1] { typeof(RectTransform) });
			RectTransform val2 = (RectTransform)val.transform;
			((Transform)val2).SetParent(parent, false);
			val2.anchorMin = anchor;
			val2.anchorMax = anchor;
			val2.pivot = new Vector2(0.5f, 0.5f);
			val2.anchoredPosition = position;
			val2.sizeDelta = size;
			return val;
		}
	}
	[BepInPlugin("com.hostinteriorselector.mod", "Host Interior Selector", "0.1.3")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "com.hostinteriorselector.mod";

		public const string PluginName = "Host Interior Selector";

		public const string PluginVersion = "0.1.3";

		private Harmony? _harmony;

		internal static ManualLogSource Log { get; private set; }

		internal static ConfigEntry<bool> FactoryEnabled { get; private set; }

		internal static ConfigEntry<bool> MansionEnabled { get; private set; }

		internal static ConfigEntry<bool> MineshaftEnabled { get; private set; }

		internal static ConfigFile Settings { get; private set; }

		private void Awake()
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			Settings = ((BaseUnityPlugin)this).Config;
			FactoryEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Interiors", "Factory", true, "Allow the vanilla Factory interior when you are host.");
			MansionEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Interiors", "Mansion", true, "Allow the vanilla Mansion interior when you are host.");
			MineshaftEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Interiors", "Mineshaft", true, "Allow the vanilla Mineshaft interior when you are host.");
			_harmony = new Harmony("com.hostinteriorselector.mod");
			_harmony.PatchAll();
			Log.LogInfo((object)"Host Interior Selector 0.1.3 loaded. Interior filtering is host-only; local menu settings never affect a remote host.");
		}
	}
}