Decompiled source of AlwaysCubeRoom v1.2.0

AlwaysCubeRoom.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using UnityEngine;

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

[BepInPlugin("f0xb0t.AlwaysCubeRoom", "AlwaysCubeRoom", "1.2.0")]
public class Plugin : BaseUnityPlugin
{
	public const string PluginGuid = "f0xb0t.AlwaysCubeRoom";

	public const string PluginName = "AlwaysCubeRoom";

	public const string PluginVersion = "1.2.0";

	internal static ManualLogSource Log;

	internal static ConfigEntry<bool> Enabled;

	internal static ConfigEntry<bool> AllowLargeVolumeFallback;

	internal static ConfigEntry<bool> ForceRoom;

	internal static ConfigEntry<bool> PrankRooms;

	internal static ConfigEntry<bool> PrankCubes;

	internal static ConfigEntry<int> PrankRoomCount;

	private void Awake()
	{
		//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ba: Expected O, but got Unknown
		//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
		Log = ((BaseUnityPlugin)this).Logger;
		Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Force the Cube of Knowledge to spawn on every level whose valuable pool contains it (Swiftbroom Academy).");
		ForceRoom = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ForceCursedTreeRoom", true, "Force the Cursed Tree room (where the Knowledge Platforms for the cube puzzle are) to generate.");
		AllowLargeVolumeFallback = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "AllowLargeVolumeFallback", true, "If a level has no Big valuable spots, put the cube in a Wide / Very Tall spot instead.");
		PrankRooms = ((BaseUnityPlugin)this).Config.Bind<bool>("Prank", "EveryRoomIsCursedTree", false, "Rebuild Swiftbroom Academy's layout as a grid of 2x2 blocks and make every block the Cursed Tree. Only the truck and the extraction rooms (needed to finish the level) stay normal.");
		PrankRoomCount = ((BaseUnityPlugin)this).Config.Bind<int>("Prank", "CursedTreeCount", 12, new ConfigDescription("How many Cursed Tree rooms to build per level when EveryRoomIsCursedTree is on.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 40), new object[0]));
		PrankCubes = ((BaseUnityPlugin)this).Config.Bind<bool>("Prank", "EveryValuableIsCube", false, "Replace every valuable on Swiftbroom Academy with the Cube of Knowledge, whatever the spot size.");
		new Harmony("f0xb0t.AlwaysCubeRoom").PatchAll(typeof(Patches));
		Log.LogInfo((object)"AlwaysCubeRoom 1.2.0 loaded.");
	}
}
internal static class Cube
{
	internal static bool PlacedThisLevel;

	internal static bool LoggedMissingThisLevel;

	internal static int PrankSwaps;

	private static readonly Dictionary<string, bool> Cache = new Dictionary<string, bool>();

	private static readonly FieldInfo BigValuablesField = AccessTools.Field(typeof(ValuableDirector), "bigValuables");

	private static readonly FieldInfo BigVolumesField = AccessTools.Field(typeof(ValuableDirector), "bigVolumes");

	internal static string Key(PrefabRef r)
	{
		if (r == null)
		{
			return null;
		}
		string text = null;
		try
		{
			text = ((PrefabRef<GameObject>)(object)r).ResourcePath;
		}
		catch (Exception)
		{
		}
		if (string.IsNullOrEmpty(text))
		{
			try
			{
				text = ((PrefabRef<GameObject>)(object)r).PrefabName;
			}
			catch (Exception)
			{
			}
		}
		return text;
	}

	internal static bool IsCube(PrefabRef r)
	{
		string text = Key(r);
		if (text == null)
		{
			return false;
		}
		if (Cache.TryGetValue(text, out var value))
		{
			return value;
		}
		value = text.IndexOf("Cube of Knowledge", StringComparison.OrdinalIgnoreCase) >= 0;
		if (!value)
		{
			try
			{
				GameObject prefab = ((PrefabRef<GameObject>)(object)r).Prefab;
				value = (Object)(object)prefab != (Object)null && (Object)(object)prefab.GetComponentInChildren<ValuableCubeOfKnowledge>(true) != (Object)null;
			}
			catch (Exception)
			{
			}
		}
		Cache[text] = value;
		return value;
	}

	internal static PrefabRef FindIn(List<PrefabRef> list)
	{
		if (list == null)
		{
			return null;
		}
		for (int i = 0; i < list.Count; i++)
		{
			if (IsCube(list[i]))
			{
				return list[i];
			}
		}
		return null;
	}

	internal static List<PrefabRef> BigValuablesOf(ValuableDirector d)
	{
		try
		{
			return (List<PrefabRef>)BigValuablesField.GetValue(d);
		}
		catch (Exception)
		{
			return null;
		}
	}

	internal static int BigVolumeCount(ValuableDirector d)
	{
		try
		{
			return ((List<ValuableVolume>)BigVolumesField.GetValue(d))?.Count ?? 0;
		}
		catch (Exception)
		{
			return 0;
		}
	}
}
internal static class Room
{
	internal static bool Boosted;

	internal static bool Placed;

	internal static bool Pending;

	internal static int PlacedCount;

	private static int savedCooldown;

	private static int savedBadLuck;

	private static readonly FieldInfo CooldownField = AccessTools.Field(typeof(RunManager), "megaModuleCooldownCount");

	private static readonly FieldInfo BadLuckField = AccessTools.Field(typeof(RunManager), "megaModuleBadLuckCount");

	private static readonly Dictionary<string, bool> Cache = new Dictionary<string, bool>();

	internal static void Reset()
	{
		if (Boosted)
		{
			Restore();
		}
		Placed = false;
		Pending = false;
		PlacedCount = 0;
	}

	internal static void Release()
	{
		Boosted = false;
	}

	internal static void Boost()
	{
		RunManager instance = RunManager.instance;
		if (!((Object)(object)instance == (Object)null) && !(CooldownField == null) && !(BadLuckField == null))
		{
			savedCooldown = (int)CooldownField.GetValue(instance);
			savedBadLuck = (int)BadLuckField.GetValue(instance);
			CooldownField.SetValue(instance, 0);
			BadLuckField.SetValue(instance, 1000);
			Boosted = true;
			Plugin.Log.LogInfo((object)("Level can have the Cursed Tree; forcing a mega room (real cooldown " + savedCooldown + ", bad luck " + savedBadLuck + ")."));
		}
	}

	internal static void Restore()
	{
		if (Boosted)
		{
			Boosted = false;
			RunManager instance = RunManager.instance;
			if (!((Object)(object)instance == (Object)null))
			{
				CooldownField.SetValue(instance, savedCooldown);
				BadLuckField.SetValue(instance, savedBadLuck);
			}
		}
	}

	internal static bool IsRoom(PrefabRef r)
	{
		string text = Cube.Key(r);
		if (text == null)
		{
			return false;
		}
		if (Cache.TryGetValue(text, out var value))
		{
			return value;
		}
		value = text.IndexOf("Cursed Tree", StringComparison.OrdinalIgnoreCase) >= 0;
		if (!value)
		{
			try
			{
				GameObject prefab = ((PrefabRef<GameObject>)(object)r).Prefab;
				value = (Object)(object)prefab != (Object)null && (Object)(object)prefab.GetComponentInChildren<KnowledgePlatform>(true) != (Object)null;
			}
			catch (Exception)
			{
			}
		}
		Cache[text] = value;
		return value;
	}

	internal static PrefabRef FindIn(List<PrefabRef> list)
	{
		if (list == null)
		{
			return null;
		}
		for (int i = 0; i < list.Count; i++)
		{
			if (IsRoom(list[i]))
			{
				return list[i];
			}
		}
		return null;
	}
}
internal static class Layout
{
	internal static bool Applied;

	internal static int Blocks;

	internal static int BlocksWide;

	private static readonly FieldInfo GridField = AccessTools.Field(typeof(LevelGenerator), "LevelGrid");

	private static readonly FieldInfo MegaTilesField = AccessTools.Field(typeof(LevelGenerator), "MegaTiles");

	private static readonly FieldInfo DeadEndField = AccessTools.Field(typeof(LevelGenerator), "DeadEndAmount");

	private static readonly FieldInfo SpecialField = AccessTools.Field(typeof(LevelGenerator), "SpecialAmount");

	private static readonly FieldInfo ExtractionField = AccessTools.Field(typeof(LevelGenerator), "ExtractionAmount");

	private static LevelGenerator resizedGen;

	private static int origWidth;

	private static int origHeight;

	internal static bool Wanted(LevelGenerator gen)
	{
		if (!Plugin.Enabled.Value || !Plugin.ForceRoom.Value || !Plugin.PrankRooms.Value)
		{
			return false;
		}
		if ((Object)(object)gen == (Object)null || (Object)(object)gen.Level == (Object)null || !SemiFunc.RunIsLevel())
		{
			return false;
		}
		Level level = gen.Level;
		if (Room.FindIn(level.ModulesMega1) == null && Room.FindIn(level.ModulesMega2) == null)
		{
			return Room.FindIn(level.ModulesMega3) != null;
		}
		return true;
	}

	internal static void Unsize(LevelGenerator gen)
	{
		if (!((Object)(object)gen == (Object)null) && !((Object)(object)gen != (Object)(object)resizedGen))
		{
			gen.LevelWidth = origWidth;
			gen.LevelHeight = origHeight;
			resizedGen = null;
		}
	}

	internal static void Size(LevelGenerator gen)
	{
		Applied = false;
		if ((Object)(object)resizedGen != (Object)(object)gen)
		{
			resizedGen = gen;
			origWidth = gen.LevelWidth;
			origHeight = gen.LevelHeight;
		}
		Blocks = Math.Max(1, Plugin.PrankRoomCount.Value);
		BlocksWide = Math.Max(2, (int)Math.Ceiling(Math.Sqrt(Blocks)));
		int num = (Blocks + BlocksWide - 1) / BlocksWide;
		gen.LevelWidth = 2 * BlocksWide + 2;
		gen.LevelHeight = 2 * num + 1;
	}

	internal static void Apply(LevelGenerator gen)
	{
		if (!(GridField.GetValue(gen) is Tile[,] array))
		{
			return;
		}
		int length = array.GetLength(0);
		int length2 = array.GetLength(1);
		int num = (Blocks + BlocksWide - 1) / BlocksWide;
		if (length < 2 * BlocksWide + 2 || length2 < 2 * num + 1)
		{
			Plugin.Log.LogWarning((object)("Prank layout skipped: grid is " + length + "x" + length2 + ", smaller than planned."));
			return;
		}
		for (int i = 0; i < length; i++)
		{
			for (int j = 0; j < length2; j++)
			{
				array[i, j].active = false;
			}
		}
		for (int k = 0; k < Blocks; k++)
		{
			int num2 = 1 + 2 * (k % BlocksWide);
			int num3 = 2 * (k / BlocksWide);
			array[num2, num3].active = true;
			array[num2 + 1, num3].active = true;
			array[num2, num3 + 1].active = true;
			array[num2 + 1, num3 + 1].active = true;
		}
		if (!array[length / 2, 0].active)
		{
			Plugin.Log.LogWarning((object)"Prank layout: start tile not covered; keeping it.");
			array[length / 2, 0].active = true;
		}
		DeadEndField.SetValue(gen, 0);
		SpecialField.SetValue(gen, 0);
		if ((int)ExtractionField.GetValue(gen) < 1)
		{
			ExtractionField.SetValue(gen, 1);
		}
		Applied = true;
		Plugin.Log.LogInfo((object)string.Concat("Prank layout: ", Blocks, " 2x2 blocks on a ", length, "x", length2, " grid, extraction rooms: ", ExtractionField.GetValue(gen), "."));
	}

	internal static void SetMegaTiles(LevelGenerator gen)
	{
		//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c2: Expected O, but got Unknown
		if (!Applied || !(GridField.GetValue(gen) is Tile[,] array))
		{
			return;
		}
		List<MegaTile> list = new List<MegaTile>();
		for (int i = 0; i < Blocks; i++)
		{
			int num = 1 + 2 * (i % BlocksWide);
			int num2 = 2 * (i / BlocksWide);
			Tile[] array2 = (Tile[])(object)new Tile[4]
			{
				array[num, num2 + 1],
				array[num, num2],
				array[num + 1, num2 + 1],
				array[num + 1, num2]
			};
			bool flag = true;
			for (int j = 0; j < 4; j++)
			{
				if (!array2[j].active || (int)array2[j].type != 0)
				{
					flag = false;
				}
			}
			if (flag)
			{
				MegaTile val = new MegaTile();
				val.tiles = array2;
				list.Add(val);
			}
		}
		MegaTilesField.SetValue(gen, list);
		Plugin.Log.LogInfo((object)("Prank layout: offering " + list.Count + " mega spots."));
	}

	internal static IEnumerator Wrap(IEnumerator inner, LevelGenerator gen)
	{
		bool first = true;
		while (inner.MoveNext())
		{
			if (first)
			{
				first = false;
				try
				{
					Apply(gen);
				}
				catch (Exception ex)
				{
					Plugin.Log.LogError((object)("Prank layout failed: " + ex));
				}
			}
			yield return inner.Current;
		}
	}
}
internal static class Patches
{
	[HarmonyPrefix]
	[HarmonyPatch(typeof(LevelGenerator), "TileGeneration")]
	private static void TileGenerationPrefix(LevelGenerator __instance)
	{
		Layout.Applied = false;
		if (Layout.Wanted(__instance))
		{
			Layout.Size(__instance);
		}
		else
		{
			Layout.Unsize(__instance);
		}
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(LevelGenerator), "TileGeneration")]
	private static void TileGenerationPostfix(LevelGenerator __instance, ref IEnumerator __result)
	{
		if (Layout.Wanted(__instance))
		{
			__result = Layout.Wrap(__result, __instance);
		}
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(ValuableDirector), "SetupHost")]
	private static void SetupHostPrefix()
	{
		if (Cube.PrankSwaps > 0)
		{
			Plugin.Log.LogInfo((object)("Prank: turned " + Cube.PrankSwaps + " valuables into cubes last level."));
		}
		Cube.PlacedThisLevel = false;
		Cube.LoggedMissingThisLevel = false;
		Cube.PrankSwaps = 0;
		string text = (((Object)(object)LevelGenerator.Instance != (Object)null && (Object)(object)LevelGenerator.Instance.Level != (Object)null) ? ((Object)LevelGenerator.Instance.Level).name : "?");
		Plugin.Log.LogInfo((object)("Spawning valuables as host for level: " + text));
	}

	[HarmonyPatch(typeof(ValuableDirector), "SpawnValuable")]
	[HarmonyPrefix]
	private static void SpawnValuablePrefix(ValuableDirector __instance, ref PrefabRef __0, ValuableVolume __1)
	{
		//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)
		//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Invalid comparison between Unknown and I4
		//IL_0120: 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_00bf: Invalid comparison between Unknown and I4
		//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Invalid comparison between Unknown and I4
		if (!Plugin.Enabled.Value || (Object)(object)__1 == (Object)null || (Cube.PlacedThisLevel && !Plugin.PrankCubes.Value))
		{
			return;
		}
		PrefabRef val = Cube.FindIn(Cube.BigValuablesOf(__instance));
		if (val != null && Plugin.PrankCubes.Value)
		{
			if (Cube.PrankSwaps == 0)
			{
				Plugin.Log.LogInfo((object)"Prank: every valuable on this level becomes the Cube of Knowledge.");
			}
			if (!Cube.IsCube(__0))
			{
				Cube.PrankSwaps++;
			}
			__0 = val;
			Cube.PlacedThisLevel = true;
			return;
		}
		if (val == null)
		{
			if (!Cube.LoggedMissingThisLevel)
			{
				Cube.LoggedMissingThisLevel = true;
				Plugin.Log.LogInfo((object)"This level's valuable pool has no Cube of Knowledge; leaving it alone.");
			}
			return;
		}
		Type volumeType = __1.VolumeType;
		bool flag = (int)volumeType == 3;
		if (!flag && Plugin.AllowLargeVolumeFallback.Value && Cube.BigVolumeCount(__instance) == 0)
		{
			flag = (int)volumeType == 4 || (int)volumeType == 6;
		}
		if (flag)
		{
			if (Cube.IsCube(__0))
			{
				Plugin.Log.LogInfo((object)"The game picked the Cube of Knowledge on its own this time.");
			}
			else
			{
				Plugin.Log.LogInfo((object)string.Concat("Swapping ", Cube.Key(__0), " for ", Cube.Key(val), " in a ", volumeType, " spot."));
			}
			__0 = val;
			Cube.PlacedThisLevel = true;
		}
	}

	[HarmonyPrefix]
	[HarmonyPatch(typeof(LevelGenerator), "ModuleGeneration")]
	private static void ModuleGenerationPrefix(LevelGenerator __instance)
	{
		Room.Reset();
		if (!Plugin.Enabled.Value || !Plugin.ForceRoom.Value)
		{
			return;
		}
		Level level = __instance.Level;
		if (!((Object)(object)level == (Object)null) && (Room.FindIn(level.ModulesMega1) != null || Room.FindIn(level.ModulesMega2) != null || Room.FindIn(level.ModulesMega3) != null))
		{
			try
			{
				Layout.SetMegaTiles(__instance);
			}
			catch (Exception ex)
			{
				Plugin.Log.LogError((object)("Prank mega spots failed: " + ex));
			}
			Room.Boost();
		}
	}

	[HarmonyPatch(typeof(LevelGenerator), "PickModule")]
	[HarmonyPrefix]
	private static bool PickModulePrefix(List<PrefabRef> __0, List<PrefabRef> __1, List<PrefabRef> __2, ref PrefabRef __result)
	{
		if (!Room.Boosted || (Room.Placed && !Plugin.PrankRooms.Value))
		{
			return true;
		}
		PrefabRef val = Room.FindIn(__0);
		if (val == null)
		{
			val = Room.FindIn(__1);
		}
		if (val == null)
		{
			val = Room.FindIn(__2);
		}
		if (val == null)
		{
			return true;
		}
		Room.Pending = true;
		__result = val;
		return false;
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(LevelGenerator), "PlanModule")]
	private static void PlanModulePostfix(Type _type, bool __result)
	{
		//IL_0008: Unknown result type (might be due to invalid IL or missing references)
		//IL_000a: Invalid comparison between Unknown and I4
		if (!Room.Boosted)
		{
			return;
		}
		if ((int)_type == 5)
		{
			if (!Room.Pending)
			{
				return;
			}
			Room.Pending = false;
			if (__result)
			{
				Room.Placed = true;
				Room.PlacedCount++;
				if (!Plugin.PrankRooms.Value)
				{
					Plugin.Log.LogInfo((object)"Placed the Cursed Tree (Cube of Knowledge puzzle room).");
					Room.Restore();
				}
			}
		}
		else
		{
			if (!Room.Placed)
			{
				Plugin.Log.LogWarning((object)"Could not place the Cursed Tree this level (the layout had no free 2x2 spot for a mega room).");
			}
			else if (Plugin.PrankRooms.Value)
			{
				Plugin.Log.LogInfo((object)("Prank: placed " + Room.PlacedCount + " Cursed Tree rooms (every free 2x2 spot)."));
				Room.Release();
				return;
			}
			Room.Restore();
		}
	}

	[HarmonyPatch(typeof(KnowledgePlatform), "Awake")]
	[HarmonyPostfix]
	private static void KnowledgePlatformAwakePostfix(KnowledgePlatform __instance)
	{
		Transform root = ((Component)__instance).transform.root;
		Plugin.Log.LogInfo((object)("Knowledge Platform '" + ((Object)__instance).name + "' is inside '" + (((Object)(object)root != (Object)null) ? ((Object)root).name : "?") + "'"));
	}
}